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