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