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