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