View Javadoc

1   package org.apache.maven.shared.invoker;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.io.IOException;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Properties;
28  
29  import org.codehaus.plexus.util.Os;
30  import org.codehaus.plexus.util.StringUtils;
31  import org.codehaus.plexus.util.cli.CommandLineUtils;
32  import org.codehaus.plexus.util.cli.Commandline;
33  
34  /**
35   * @version $Id: MavenCommandLineBuilder.java 1401842 2012-10-24 19:49:47Z rfscholte $
36   */
37  public class MavenCommandLineBuilder
38  {
39  
40      private static final InvokerLogger DEFAULT_LOGGER = new SystemOutLogger();
41  
42      private InvokerLogger logger = DEFAULT_LOGGER;
43  
44      private File workingDirectory;
45  
46      private File localRepositoryDirectory;
47  
48      private File mavenHome;
49  
50      private File mavenExecutable;
51  
52      private Properties systemEnvVars;
53  
54      public Commandline build( InvocationRequest request )
55          throws CommandLineConfigurationException
56      {
57          try
58          {
59              checkRequiredState();
60          }
61          catch ( IOException e )
62          {
63              throw new CommandLineConfigurationException( e.getMessage(), e );
64          }
65          File mvn = null;
66          try
67          {
68              mvn = findMavenExecutable();
69          }
70          catch ( IOException e )
71          {
72              throw new CommandLineConfigurationException( e.getMessage(), e );
73          }
74          Commandline cli = new Commandline();
75  
76          cli.setExecutable( mvn.getAbsolutePath() );
77  
78          // handling for OS-level envars
79          setShellEnvironment( request, cli );
80  
81          // interactive, offline, update-snapshots,
82          // debug/show-errors, checksum policy
83          setFlags( request, cli );
84  
85          // failure behavior and [eventually] forced-reactor
86          // includes/excludes, etc.
87          setReactorBehavior( request, cli );
88  
89          // working directory and local repository location
90          setEnvironmentPaths( request, cli );
91  
92          // pom-file and basedir handling
93          setPomLocation( request, cli );
94  
95          setSettingsLocation( request, cli );
96  
97          setToolchainsLocation( request, cli );
98          
99          setProperties( request, cli );
100 
101         setProfiles( request, cli );
102 
103         setGoals( request, cli );
104 
105         setThreads( request, cli );
106         
107         return cli;
108     }
109 
110     protected void checkRequiredState()
111         throws IOException
112     {
113         if ( logger == null )
114         {
115             throw new IllegalStateException( "A logger instance is required." );
116         }
117 
118         if ( ( mavenHome == null ) && ( System.getProperty( "maven.home" ) == null ) )
119         // can be restored with 1.5
120         // && ( System.getenv( "M2_HOME" ) != null ) )
121         {
122             if ( !getSystemEnvVars().containsKey( "M2_HOME" ) )
123             {
124                 throw new IllegalStateException( "Maven application directory was not "
125                     + "specified, and ${maven.home} is not provided in the system "
126                     + "properties. Please specify at least on of these." );
127             }
128         }
129     }
130 
131     protected void setSettingsLocation( InvocationRequest request, Commandline cli )
132     {
133         File userSettingsFile = request.getUserSettingsFile();
134 
135         if ( userSettingsFile != null )
136         {
137             try
138             {
139                 File canSet = userSettingsFile.getCanonicalFile();
140                 userSettingsFile = canSet;
141             }
142             catch ( IOException e )
143             {
144                 logger.debug( "Failed to canonicalize user settings path: " + userSettingsFile.getAbsolutePath()
145                     + ". Using as-is.", e );
146             }
147 
148             cli.createArg().setValue( "-s" );
149             cli.createArg().setValue( userSettingsFile.getPath() );
150         }
151         
152         File globalSettingsFile = request.getGlobalSettingsFile();
153 
154         if ( globalSettingsFile != null )
155         {
156             try
157             {
158                 File canSet = globalSettingsFile.getCanonicalFile();
159                 globalSettingsFile = canSet;
160             }
161             catch ( IOException e )
162             {
163                 logger.debug( "Failed to canonicalize global settings path: " + globalSettingsFile.getAbsolutePath()
164                     + ". Using as-is.", e );
165             }
166 
167             cli.createArg().setValue( "-gs" );
168             cli.createArg().setValue( globalSettingsFile.getPath() );
169         }
170 
171     }
172     
173     protected void setToolchainsLocation( InvocationRequest request, Commandline cli )
174     {
175         File toolchainsFile = request.getToolchainsFile();
176 
177         if ( toolchainsFile != null )
178         {
179             try
180             {
181                 File canSet = toolchainsFile.getCanonicalFile();
182                 toolchainsFile = canSet;
183             }
184             catch ( IOException e )
185             {
186                 logger.debug( "Failed to canonicalize toolchains path: " + toolchainsFile.getAbsolutePath()
187                     + ". Using as-is.", e );
188             }
189 
190             cli.createArg().setValue( "-t" );
191             cli.createArg().setValue( toolchainsFile.getPath() );
192         }
193     }
194 
195     protected void setShellEnvironment( InvocationRequest request, Commandline cli )
196         throws CommandLineConfigurationException
197     {
198         if ( request.isShellEnvironmentInherited() )
199         {
200             try
201             {
202                 cli.addSystemEnvironment();
203                 cli.addEnvironment( "MAVEN_TERMINATE_CMD", "on" );
204             }
205             catch ( IOException e )
206             {
207                 throw new CommandLineConfigurationException( "Error reading shell environment variables. Reason: "
208                     + e.getMessage(), e );
209             }
210             catch ( Exception e )
211             {
212                 if ( e instanceof RuntimeException )
213                 {
214                     throw (RuntimeException) e;
215                 }
216                 else
217                 {
218                     IllegalStateException error =
219                         new IllegalStateException( "Unknown error retrieving shell environment variables. Reason: "
220                             + e.getMessage() );
221                     error.initCause( e );
222 
223                     throw error;
224                 }
225             }
226         }
227 
228         if ( request.getJavaHome() != null )
229         {
230             cli.addEnvironment( "JAVA_HOME", request.getJavaHome().getAbsolutePath() );
231         }
232 
233         if ( request.getMavenOpts() != null )
234         {
235             cli.addEnvironment( "MAVEN_OPTS", request.getMavenOpts() );
236         }
237 
238         for ( Map.Entry<String, String> entry : request.getShellEnvironments().entrySet() )
239         {
240             cli.addEnvironment( entry.getKey(), entry.getValue() );
241         }
242     }
243 
244     protected void setProfiles( InvocationRequest request, Commandline cli )
245     {
246         List<String> profiles = request.getProfiles();
247 
248         if ( ( profiles != null ) && !profiles.isEmpty() )
249         {
250             cli.createArg().setValue( "-P" );
251             cli.createArg().setValue( StringUtils.join( profiles.iterator(), "," ) );
252         }
253 
254     }
255 
256     protected void setGoals( InvocationRequest request, Commandline cli )
257     {
258         List<String> goals = request.getGoals();
259 
260         if ( ( goals != null ) && !goals.isEmpty() )
261         {
262             cli.createArg().setLine( StringUtils.join( goals.iterator(), " " ) );
263         }
264     }
265 
266     protected void setProperties( InvocationRequest request, Commandline cli )
267     {
268         Properties properties = request.getProperties();
269 
270         if ( properties != null )
271         {
272             for ( Iterator it = properties.entrySet().iterator(); it.hasNext(); )
273             {
274                 Map.Entry entry = (Map.Entry) it.next();
275 
276                 String key = (String) entry.getKey();
277                 String value = (String) entry.getValue();
278 
279                 cli.createArg().setValue( "-D" );
280                 cli.createArg().setValue( key + '=' + value );
281             }
282         }
283     }
284 
285     protected void setPomLocation( InvocationRequest request, Commandline cli )
286     {
287         boolean pomSpecified = false;
288 
289         File pom = request.getPomFile();
290         String pomFilename = request.getPomFileName();
291         File baseDirectory = request.getBaseDirectory();
292 
293         if ( pom != null )
294         {
295             pomSpecified = true;
296         }
297         else if ( baseDirectory != null )
298         {
299             if ( baseDirectory.isDirectory() )
300             {
301                 if ( pomFilename != null )
302                 {
303                     pom = new File( baseDirectory, pomFilename );
304 
305                     pomSpecified = true;
306                 }
307                 else
308                 {
309                     pom = new File( baseDirectory, "pom.xml" );
310                 }
311             }
312             else
313             {
314                 logger.warn( "Base directory is a file. Using base directory as POM location." );
315 
316                 pom = baseDirectory;
317 
318                 pomSpecified = true;
319             }
320         }
321 
322         if ( pomSpecified )
323         {
324             try
325             {
326                 File canPom = pom.getCanonicalFile();
327                 pom = canPom;
328             }
329             catch ( IOException e )
330             {
331                 logger.debug( "Failed to canonicalize the POM path: " + pom + ". Using as-is.", e );
332             }
333 
334             if ( !"pom.xml".equals( pom.getName() ) )
335             {
336                 logger.debug( "Specified POM file is not named \'pom.xml\'. "
337                     + "Using the \'-f\' command-line option to accommodate non-standard filename..." );
338 
339                 cli.createArg().setValue( "-f" );
340                 cli.createArg().setValue( pom.getName() );
341             }
342         }
343     }
344 
345     protected void setEnvironmentPaths( InvocationRequest request, Commandline cli )
346     {
347         File workingDirectory = request.getBaseDirectory();
348 
349         if ( workingDirectory == null )
350         {
351             File pomFile = request.getPomFile();
352             if ( pomFile != null )
353             {
354                 workingDirectory = pomFile.getParentFile();
355             }
356         }
357 
358         if ( workingDirectory == null )
359         {
360             workingDirectory = this.workingDirectory;
361         }
362 
363         if ( workingDirectory == null )
364         {
365             workingDirectory = new File( System.getProperty( "user.dir" ) );
366         }
367         else if ( workingDirectory.isFile() )
368         {
369             logger.warn( "Specified base directory (" + workingDirectory + ") is a file."
370                 + " Using its parent directory..." );
371 
372             workingDirectory = workingDirectory.getParentFile();
373         }
374 
375         try
376         {
377             cli.setWorkingDirectory( workingDirectory.getCanonicalPath() );
378         }
379         catch ( IOException e )
380         {
381             logger.debug( "Failed to canonicalize base directory: " + workingDirectory + ". Using as-is.", e );
382 
383             cli.setWorkingDirectory( workingDirectory.getAbsolutePath() );
384         }
385 
386         File localRepositoryDirectory = request.getLocalRepositoryDirectory( this.localRepositoryDirectory );
387 
388         if ( localRepositoryDirectory != null )
389         {
390             try
391             {
392                 File canLRD = localRepositoryDirectory.getCanonicalFile();
393                 localRepositoryDirectory = canLRD;
394             }
395             catch ( IOException e )
396             {
397                 logger.debug( "Failed to canonicalize local repository directory: " + localRepositoryDirectory
398                     + ". Using as-is.", e );
399             }
400 
401             if ( !localRepositoryDirectory.isDirectory() )
402             {
403                 throw new IllegalArgumentException( "Local repository location: \'" + localRepositoryDirectory
404                     + "\' is NOT a directory." );
405             }
406 
407             cli.createArg().setValue( "-D" );
408             cli.createArg().setValue( "maven.repo.local=" + localRepositoryDirectory.getPath() );
409         }
410     }
411 
412     protected void setReactorBehavior( InvocationRequest request, Commandline cli )
413     {
414         // NOTE: The default is "fail-fast"
415         String failureBehavior = request.getFailureBehavior();
416 
417         if ( StringUtils.isNotEmpty( failureBehavior ) )
418         {
419             if ( InvocationRequest.REACTOR_FAIL_AT_END.equals( failureBehavior ) )
420             {
421                 cli.createArg().setValue( "-fae" );
422             }
423             else if ( InvocationRequest.REACTOR_FAIL_NEVER.equals( failureBehavior ) )
424             {
425                 cli.createArg().setValue( "-fn" );
426             }
427         }
428 
429         if ( request.isActivatedReactor() )
430         {
431             cli.createArg().setValue( "-r" );
432             String[] includes = request.getActivatedReactorIncludes();
433             String[] excludes = request.getActivatedReactorExcludes();
434             if ( includes != null )
435             {
436                 cli.createArg().setValue( "-D" );
437                 cli.createArg().setValue( "maven.reactor.includes=" + StringUtils.join( includes, "," ) );
438             }
439             if ( excludes != null )
440             {
441                 cli.createArg().setValue( "-D" );
442                 cli.createArg().setValue( "maven.reactor.excludes=" + StringUtils.join( excludes, "," ) );
443             }
444         }
445 
446         if( StringUtils.isNotEmpty( request.getResumeFrom() ) )
447         {
448             cli.createArg().setValue( "-rf" );
449             cli.createArg().setValue( request.getResumeFrom() );
450         }
451 
452         List<String> projectList = request.getProjects();
453         if ( projectList != null )
454         {
455             cli.createArg().setValue( "-pl" );
456             cli.createArg().setValue( StringUtils.join( projectList.iterator(), "," ) );
457 
458             if ( request.isAlsoMake() )
459             {
460                 cli.createArg().setValue( "-am" );
461             }
462 
463             if ( request.isAlsoMakeDependents() )
464             {
465                 cli.createArg().setValue( "-amd" );
466             }
467         }
468     }
469 
470     protected void setFlags( InvocationRequest request, Commandline cli )
471     {
472         if ( !request.isInteractive() )
473         {
474             cli.createArg().setValue( "-B" );
475         }
476 
477         if ( request.isOffline() )
478         {
479             cli.createArg().setValue( "-o" );
480         }
481 
482         if ( request.isUpdateSnapshots() )
483         {
484             cli.createArg().setValue( "-U" );
485         }
486 
487         if ( !request.isRecursive() )
488         {
489             cli.createArg().setValue( "-N" );
490         }
491 
492         if ( request.isDebug() )
493         {
494             cli.createArg().setValue( "-X" );
495         }
496         // this is superceded by -X, if it exists.
497         else if ( request.isShowErrors() )
498         {
499             cli.createArg().setValue( "-e" );
500         }
501 
502         String checksumPolicy = request.getGlobalChecksumPolicy();
503         if ( InvocationRequest.CHECKSUM_POLICY_FAIL.equals( checksumPolicy ) )
504         {
505             cli.createArg().setValue( "-C" );
506         }
507         else if ( InvocationRequest.CHECKSUM_POLICY_WARN.equals( checksumPolicy ) )
508         {
509             cli.createArg().setValue( "-c" );
510         }
511         if ( request.isNonPluginUpdates() )
512         {
513             cli.createArg().setValue( "-npu" );
514         }
515         
516         if ( request.isShowVersion() )
517         {
518             cli.createArg().setValue( "-V" );
519         }
520 	}
521 
522     protected void setThreads( InvocationRequest request, Commandline cli )
523     {
524         String threads = request.getThreads();
525         if ( StringUtils.isNotEmpty( threads ) )
526         {
527             cli.createArg().setValue( "-T" );
528             cli.createArg().setValue( threads );
529         }
530         
531     }
532 
533     protected File findMavenExecutable()
534         throws CommandLineConfigurationException, IOException
535     {
536         if ( mavenHome == null )
537         {
538             String mavenHomeProperty = System.getProperty( "maven.home" );
539             if ( mavenHomeProperty != null )
540             {
541                 mavenHome = new File( mavenHomeProperty );
542                 if ( !mavenHome.isDirectory() )
543                 {
544                     File binDir = mavenHome.getParentFile();
545                     if ( "bin".equals( binDir.getName() ) )
546                     {
547                         // ah, they specified the mvn
548                         // executable instead...
549                         mavenHome = binDir.getParentFile();
550                     }
551                     else
552                     {
553                         throw new IllegalStateException( "${maven.home} is not specified as a directory: \'"
554                             + mavenHomeProperty + "\'." );
555                     }
556                 }
557             }
558 
559             if ( ( mavenHome == null ) && ( getSystemEnvVars().getProperty( "M2_HOME" ) != null ) )
560             {
561                 mavenHome = new File( getSystemEnvVars().getProperty( "M2_HOME" ) );
562             }
563         }
564 
565         logger.debug( "Using ${maven.home} of: \'" + mavenHome + "\'." );
566 
567         if ( mavenExecutable == null || !mavenExecutable.isAbsolute() )
568         {
569             String executable;
570             if( mavenExecutable != null )
571             {
572                 executable = mavenExecutable.getPath();
573             }
574             else if ( Os.isFamily( "windows" ) )
575             {
576                 executable = "mvn.bat";
577             }
578             else
579             {
580                 executable = "mvn";
581             }
582             
583             mavenExecutable = new File( mavenHome, "/bin/" + executable );
584             
585             try
586             {
587                 File canonicalMvn = mavenExecutable.getCanonicalFile();
588                 mavenExecutable = canonicalMvn;
589             }
590             catch ( IOException e )
591             {
592                 logger.debug( "Failed to canonicalize maven executable: " + mavenExecutable + ". Using as-is.", e );
593             }
594 
595             if ( !mavenExecutable.isFile() )
596             {
597                 throw new CommandLineConfigurationException( "Maven executable not found at: " + mavenExecutable );
598             }
599         }
600 
601         return mavenExecutable;
602     }
603 
604     /**
605      * Wraps a path with quotes to handle paths with spaces. If no spaces are found, the original string is returned.
606      * 
607      * @param path string to wrap if containing spaces
608      * @return quote wrapped string
609      * @deprecated Quoting of command line arguments should be left to the Commandline from plexus-utils.
610      */
611     public String wrapStringWithQuotes( String path )
612     {
613         if ( path.indexOf( " " ) > -1 )
614         {
615             return "\"" + path + "\"";
616         }
617         else
618         {
619             return path;
620         }
621     }
622 
623     private Properties getSystemEnvVars()
624         throws IOException
625     {
626         if ( this.systemEnvVars == null )
627         {
628             // with 1.5 replace with System.getenv()
629             this.systemEnvVars = CommandLineUtils.getSystemEnvVars();
630         }
631         return this.systemEnvVars;
632     }
633 
634     public File getLocalRepositoryDirectory()
635     {
636         return localRepositoryDirectory;
637     }
638 
639     public void setLocalRepositoryDirectory( File localRepositoryDirectory )
640     {
641         this.localRepositoryDirectory = localRepositoryDirectory;
642     }
643 
644     public InvokerLogger getLogger()
645     {
646         return logger;
647     }
648 
649     public void setLogger( InvokerLogger logger )
650     {
651         this.logger = logger;
652     }
653 
654     public File getMavenHome()
655     {
656         return mavenHome;
657     }
658 
659     public void setMavenHome( File mavenHome )
660     {
661         this.mavenHome = mavenHome;
662     }
663 
664     public File getWorkingDirectory()
665     {
666         return workingDirectory;
667     }
668 
669     public void setWorkingDirectory( File workingDirectory )
670     {
671         this.workingDirectory = workingDirectory;
672     }
673 
674     /**
675      * {@code mavenExecutable} can either be relative to ${maven.home}/bin/ or absolute 
676      * 
677      * @param mavenExecutable the executable
678      */
679     public void setMavenExecutable( File mavenExecutable )
680     {
681         this.mavenExecutable = mavenExecutable;
682     }
683 
684     public File getMavenExecutable()
685     {
686         return mavenExecutable;
687     }
688 
689 }