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 1667628 2015-03-18 21:53:54Z 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                 // MSHARED-261: Ensure M2_HOME is not inherited, but gets a proper value
205                 cli.addEnvironment( "M2_HOME", getMavenHome().getAbsolutePath() );
206             }
207             catch ( IOException e )
208             {
209                 throw new CommandLineConfigurationException( "Error reading shell environment variables. Reason: "
210                     + e.getMessage(), e );
211             }
212             catch ( Exception e )
213             {
214                 if ( e instanceof RuntimeException )
215                 {
216                     throw (RuntimeException) e;
217                 }
218                 else
219                 {
220                     IllegalStateException error =
221                         new IllegalStateException( "Unknown error retrieving shell environment variables. Reason: "
222                             + e.getMessage() );
223                     error.initCause( e );
224 
225                     throw error;
226                 }
227             }
228         }
229 
230         if ( request.getJavaHome() != null )
231         {
232             cli.addEnvironment( "JAVA_HOME", request.getJavaHome().getAbsolutePath() );
233         }
234 
235         if ( request.getMavenOpts() != null )
236         {
237             cli.addEnvironment( "MAVEN_OPTS", request.getMavenOpts() );
238         }
239 
240         for ( Map.Entry<String, String> entry : request.getShellEnvironments().entrySet() )
241         {
242             cli.addEnvironment( entry.getKey(), entry.getValue() );
243         }
244     }
245 
246     protected void setProfiles( InvocationRequest request, Commandline cli )
247     {
248         List<String> profiles = request.getProfiles();
249 
250         if ( ( profiles != null ) && !profiles.isEmpty() )
251         {
252             cli.createArg().setValue( "-P" );
253             cli.createArg().setValue( StringUtils.join( profiles.iterator(), "," ) );
254         }
255 
256     }
257 
258     protected void setGoals( InvocationRequest request, Commandline cli )
259     {
260         List<String> goals = request.getGoals();
261 
262         if ( ( goals != null ) && !goals.isEmpty() )
263         {
264             cli.createArg().setLine( StringUtils.join( goals.iterator(), " " ) );
265         }
266     }
267 
268     protected void setProperties( InvocationRequest request, Commandline cli )
269     {
270         Properties properties = request.getProperties();
271 
272         if ( properties != null )
273         {
274             for ( Iterator it = properties.entrySet().iterator(); it.hasNext(); )
275             {
276                 Map.Entry entry = (Map.Entry) it.next();
277 
278                 String key = (String) entry.getKey();
279                 String value = (String) entry.getValue();
280 
281                 cli.createArg().setValue( "-D" );
282                 cli.createArg().setValue( key + '=' + value );
283             }
284         }
285     }
286 
287     protected void setPomLocation( InvocationRequest request, Commandline cli )
288     {
289         boolean pomSpecified = false;
290 
291         File pom = request.getPomFile();
292         String pomFilename = request.getPomFileName();
293         File baseDirectory = request.getBaseDirectory();
294 
295         if ( pom != null )
296         {
297             pomSpecified = true;
298         }
299         else if ( baseDirectory != null )
300         {
301             if ( baseDirectory.isDirectory() )
302             {
303                 if ( pomFilename != null )
304                 {
305                     pom = new File( baseDirectory, pomFilename );
306 
307                     pomSpecified = true;
308                 }
309                 else
310                 {
311                     pom = new File( baseDirectory, "pom.xml" );
312                 }
313             }
314             else
315             {
316                 logger.warn( "Base directory is a file. Using base directory as POM location." );
317 
318                 pom = baseDirectory;
319 
320                 pomSpecified = true;
321             }
322         }
323 
324         if ( pomSpecified )
325         {
326             try
327             {
328                 File canPom = pom.getCanonicalFile();
329                 pom = canPom;
330             }
331             catch ( IOException e )
332             {
333                 logger.debug( "Failed to canonicalize the POM path: " + pom + ". Using as-is.", e );
334             }
335 
336             if ( !"pom.xml".equals( pom.getName() ) )
337             {
338                 logger.debug( "Specified POM file is not named \'pom.xml\'. "
339                     + "Using the \'-f\' command-line option to accommodate non-standard filename..." );
340 
341                 cli.createArg().setValue( "-f" );
342                 cli.createArg().setValue( pom.getName() );
343             }
344         }
345     }
346 
347     protected void setEnvironmentPaths( InvocationRequest request, Commandline cli )
348     {
349         File workingDirectory = request.getBaseDirectory();
350 
351         if ( workingDirectory == null )
352         {
353             File pomFile = request.getPomFile();
354             if ( pomFile != null )
355             {
356                 workingDirectory = pomFile.getParentFile();
357             }
358         }
359 
360         if ( workingDirectory == null )
361         {
362             workingDirectory = this.workingDirectory;
363         }
364 
365         if ( workingDirectory == null )
366         {
367             workingDirectory = new File( System.getProperty( "user.dir" ) );
368         }
369         else if ( workingDirectory.isFile() )
370         {
371             logger.warn( "Specified base directory (" + workingDirectory + ") is a file."
372                 + " Using its parent directory..." );
373 
374             workingDirectory = workingDirectory.getParentFile();
375         }
376 
377         try
378         {
379             cli.setWorkingDirectory( workingDirectory.getCanonicalPath() );
380         }
381         catch ( IOException e )
382         {
383             logger.debug( "Failed to canonicalize base directory: " + workingDirectory + ". Using as-is.", e );
384 
385             cli.setWorkingDirectory( workingDirectory.getAbsolutePath() );
386         }
387 
388         File localRepositoryDirectory = request.getLocalRepositoryDirectory( this.localRepositoryDirectory );
389 
390         if ( localRepositoryDirectory != null )
391         {
392             try
393             {
394                 File canLRD = localRepositoryDirectory.getCanonicalFile();
395                 localRepositoryDirectory = canLRD;
396             }
397             catch ( IOException e )
398             {
399                 logger.debug( "Failed to canonicalize local repository directory: " + localRepositoryDirectory
400                     + ". Using as-is.", e );
401             }
402 
403             if ( !localRepositoryDirectory.isDirectory() )
404             {
405                 throw new IllegalArgumentException( "Local repository location: \'" + localRepositoryDirectory
406                     + "\' is NOT a directory." );
407             }
408 
409             cli.createArg().setValue( "-D" );
410             cli.createArg().setValue( "maven.repo.local=" + localRepositoryDirectory.getPath() );
411         }
412     }
413 
414     protected void setReactorBehavior( InvocationRequest request, Commandline cli )
415     {
416         // NOTE: The default is "fail-fast"
417         String failureBehavior = request.getFailureBehavior();
418 
419         if ( StringUtils.isNotEmpty( failureBehavior ) )
420         {
421             if ( InvocationRequest.REACTOR_FAIL_AT_END.equals( failureBehavior ) )
422             {
423                 cli.createArg().setValue( "-fae" );
424             }
425             else if ( InvocationRequest.REACTOR_FAIL_NEVER.equals( failureBehavior ) )
426             {
427                 cli.createArg().setValue( "-fn" );
428             }
429         }
430 
431         if ( request.isActivatedReactor() )
432         {
433             cli.createArg().setValue( "-r" );
434             String[] includes = request.getActivatedReactorIncludes();
435             String[] excludes = request.getActivatedReactorExcludes();
436             if ( includes != null )
437             {
438                 cli.createArg().setValue( "-D" );
439                 cli.createArg().setValue( "maven.reactor.includes=" + StringUtils.join( includes, "," ) );
440             }
441             if ( excludes != null )
442             {
443                 cli.createArg().setValue( "-D" );
444                 cli.createArg().setValue( "maven.reactor.excludes=" + StringUtils.join( excludes, "," ) );
445             }
446         }
447 
448         if ( StringUtils.isNotEmpty( request.getResumeFrom() ) )
449         {
450             cli.createArg().setValue( "-rf" );
451             cli.createArg().setValue( request.getResumeFrom() );
452         }
453 
454         List<String> projectList = request.getProjects();
455         if ( projectList != null )
456         {
457             cli.createArg().setValue( "-pl" );
458             cli.createArg().setValue( StringUtils.join( projectList.iterator(), "," ) );
459 
460             if ( request.isAlsoMake() )
461             {
462                 cli.createArg().setValue( "-am" );
463             }
464 
465             if ( request.isAlsoMakeDependents() )
466             {
467                 cli.createArg().setValue( "-amd" );
468             }
469         }
470     }
471 
472     protected void setFlags( InvocationRequest request, Commandline cli )
473     {
474         if ( !request.isInteractive() )
475         {
476             cli.createArg().setValue( "-B" );
477         }
478 
479         if ( request.isOffline() )
480         {
481             cli.createArg().setValue( "-o" );
482         }
483 
484         if ( request.isUpdateSnapshots() )
485         {
486             cli.createArg().setValue( "-U" );
487         }
488 
489         if ( !request.isRecursive() )
490         {
491             cli.createArg().setValue( "-N" );
492         }
493 
494         if ( request.isDebug() )
495         {
496             cli.createArg().setValue( "-X" );
497         }
498         // this is superceded by -X, if it exists.
499         else if ( request.isShowErrors() )
500         {
501             cli.createArg().setValue( "-e" );
502         }
503 
504         String checksumPolicy = request.getGlobalChecksumPolicy();
505         if ( InvocationRequest.CHECKSUM_POLICY_FAIL.equals( checksumPolicy ) )
506         {
507             cli.createArg().setValue( "-C" );
508         }
509         else if ( InvocationRequest.CHECKSUM_POLICY_WARN.equals( checksumPolicy ) )
510         {
511             cli.createArg().setValue( "-c" );
512         }
513         if ( request.isNonPluginUpdates() )
514         {
515             cli.createArg().setValue( "-npu" );
516         }
517         
518         if ( request.isShowVersion() )
519         {
520             cli.createArg().setValue( "-V" );
521         }
522     }
523 
524     protected void setThreads( InvocationRequest request, Commandline cli )
525     {
526         String threads = request.getThreads();
527         if ( StringUtils.isNotEmpty( threads ) )
528         {
529             cli.createArg().setValue( "-T" );
530             cli.createArg().setValue( threads );
531         }
532         
533     }
534 
535     protected File findMavenExecutable()
536         throws CommandLineConfigurationException, IOException
537     {
538         if ( mavenHome == null )
539         {
540             String mavenHomeProperty = System.getProperty( "maven.home" );
541             if ( mavenHomeProperty != null )
542             {
543                 mavenHome = new File( mavenHomeProperty );
544                 if ( !mavenHome.isDirectory() )
545                 {
546                     File binDir = mavenHome.getParentFile();
547                     if ( binDir != null && "bin".equals( binDir.getName() ) )
548                     {
549                         // ah, they specified the mvn
550                         // executable instead...
551                         mavenHome = binDir.getParentFile();
552                     }
553                     else
554                     {
555                         throw new IllegalStateException( "${maven.home} is not specified as a directory: \'"
556                             + mavenHomeProperty + "\'." );
557                     }
558                 }
559             }
560 
561             if ( ( mavenHome == null ) && ( getSystemEnvVars().getProperty( "M2_HOME" ) != null ) )
562             {
563                 mavenHome = new File( getSystemEnvVars().getProperty( "M2_HOME" ) );
564             }
565         }
566 
567         logger.debug( "Using ${maven.home} of: \'" + mavenHome + "\'." );
568 
569         if ( mavenExecutable == null || !mavenExecutable.isAbsolute() )
570         {
571             String executable;
572             if ( mavenExecutable != null )
573             {
574                 executable = mavenExecutable.getPath();
575             }
576             else if ( Os.isFamily( "windows" ) )
577             {
578                 if ( new File( mavenHome, "/bin/mvn.cmd" ).exists() )
579                 {
580                     executable = "mvn.cmd";
581                 }
582                 else
583                 {
584                     executable = "mvn.bat";
585                 }
586             }
587             else
588             {
589                 executable = "mvn";
590             }
591             
592             mavenExecutable = new File( mavenHome, "/bin/" + executable );
593             
594             try
595             {
596                 File canonicalMvn = mavenExecutable.getCanonicalFile();
597                 mavenExecutable = canonicalMvn;
598             }
599             catch ( IOException e )
600             {
601                 logger.debug( "Failed to canonicalize maven executable: " + mavenExecutable + ". Using as-is.", e );
602             }
603 
604             if ( !mavenExecutable.isFile() )
605             {
606                 throw new CommandLineConfigurationException( "Maven executable not found at: " + mavenExecutable );
607             }
608         }
609 
610         return mavenExecutable;
611     }
612 
613     /**
614      * Wraps a path with quotes to handle paths with spaces. If no spaces are found, the original string is returned.
615      * 
616      * @param path string to wrap if containing spaces
617      * @return quote wrapped string
618      * @deprecated Quoting of command line arguments should be left to the Commandline from plexus-utils.
619      */
620     public String wrapStringWithQuotes( String path )
621     {
622         if ( path.indexOf( " " ) > -1 )
623         {
624             return "\"" + path + "\"";
625         }
626         else
627         {
628             return path;
629         }
630     }
631 
632     private Properties getSystemEnvVars()
633         throws IOException
634     {
635         if ( this.systemEnvVars == null )
636         {
637             // with 1.5 replace with System.getenv()
638             this.systemEnvVars = CommandLineUtils.getSystemEnvVars();
639         }
640         return this.systemEnvVars;
641     }
642 
643     public File getLocalRepositoryDirectory()
644     {
645         return localRepositoryDirectory;
646     }
647 
648     public void setLocalRepositoryDirectory( File localRepositoryDirectory )
649     {
650         this.localRepositoryDirectory = localRepositoryDirectory;
651     }
652 
653     public InvokerLogger getLogger()
654     {
655         return logger;
656     }
657 
658     public void setLogger( InvokerLogger logger )
659     {
660         this.logger = logger;
661     }
662 
663     public File getMavenHome()
664     {
665         return mavenHome;
666     }
667 
668     public void setMavenHome( File mavenHome )
669     {
670         this.mavenHome = mavenHome;
671     }
672 
673     public File getWorkingDirectory()
674     {
675         return workingDirectory;
676     }
677 
678     public void setWorkingDirectory( File workingDirectory )
679     {
680         this.workingDirectory = workingDirectory;
681     }
682 
683     /**
684      * {@code mavenExecutable} can either be relative to ${maven.home}/bin/ or absolute 
685      * 
686      * @param mavenExecutable the executable
687      */
688     public void setMavenExecutable( File mavenExecutable )
689     {
690         this.mavenExecutable = mavenExecutable;
691     }
692 
693     public File getMavenExecutable()
694     {
695         return mavenExecutable;
696     }
697 
698 }