View Javadoc
1   package org.apache.maven.cli;
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.Console;
23  import java.io.File;
24  import java.io.FileNotFoundException;
25  import java.io.FileOutputStream;
26  import java.io.PrintStream;
27  import java.util.ArrayList;
28  import java.util.LinkedHashMap;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Properties;
32  import java.util.StringTokenizer;
33  
34  import org.apache.commons.cli.CommandLine;
35  import org.apache.commons.cli.ParseException;
36  import org.apache.commons.cli.UnrecognizedOptionException;
37  import org.apache.maven.BuildAbort;
38  import org.apache.maven.InternalErrorException;
39  import org.apache.maven.Maven;
40  import org.apache.maven.cli.event.DefaultEventSpyContext;
41  import org.apache.maven.cli.event.ExecutionEventLogger;
42  import org.apache.maven.cli.logging.Slf4jConfiguration;
43  import org.apache.maven.cli.logging.Slf4jConfigurationFactory;
44  import org.apache.maven.cli.logging.Slf4jLoggerManager;
45  import org.apache.maven.cli.logging.Slf4jStdoutLogger;
46  import org.apache.maven.cli.transfer.ConsoleMavenTransferListener;
47  import org.apache.maven.cli.transfer.QuietMavenTransferListener;
48  import org.apache.maven.cli.transfer.Slf4jMavenTransferListener;
49  import org.apache.maven.eventspy.internal.EventSpyDispatcher;
50  import org.apache.maven.exception.DefaultExceptionHandler;
51  import org.apache.maven.exception.ExceptionHandler;
52  import org.apache.maven.exception.ExceptionSummary;
53  import org.apache.maven.execution.DefaultMavenExecutionRequest;
54  import org.apache.maven.execution.ExecutionListener;
55  import org.apache.maven.execution.MavenExecutionRequest;
56  import org.apache.maven.execution.MavenExecutionRequestPopulator;
57  import org.apache.maven.execution.MavenExecutionResult;
58  import org.apache.maven.lifecycle.LifecycleExecutionException;
59  import org.apache.maven.model.building.ModelProcessor;
60  import org.apache.maven.project.MavenProject;
61  import org.apache.maven.properties.internal.EnvironmentUtils;
62  import org.apache.maven.properties.internal.SystemProperties;
63  import org.apache.maven.settings.building.DefaultSettingsBuildingRequest;
64  import org.apache.maven.settings.building.SettingsBuilder;
65  import org.apache.maven.settings.building.SettingsBuildingRequest;
66  import org.apache.maven.settings.building.SettingsBuildingResult;
67  import org.apache.maven.settings.building.SettingsProblem;
68  import org.apache.maven.settings.building.SettingsSource;
69  import org.codehaus.plexus.ContainerConfiguration;
70  import org.codehaus.plexus.DefaultContainerConfiguration;
71  import org.codehaus.plexus.DefaultPlexusContainer;
72  import org.codehaus.plexus.PlexusConstants;
73  import org.codehaus.plexus.PlexusContainer;
74  import org.codehaus.plexus.classworlds.ClassWorld;
75  import org.codehaus.plexus.classworlds.realm.ClassRealm;
76  import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
77  import org.codehaus.plexus.logging.LoggerManager;
78  import org.codehaus.plexus.util.StringUtils;
79  import org.eclipse.aether.transfer.TransferListener;
80  import org.slf4j.ILoggerFactory;
81  import org.slf4j.Logger;
82  import org.slf4j.LoggerFactory;
83  import org.sonatype.plexus.components.cipher.DefaultPlexusCipher;
84  import org.sonatype.plexus.components.sec.dispatcher.DefaultSecDispatcher;
85  import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher;
86  import org.sonatype.plexus.components.sec.dispatcher.SecUtil;
87  import org.sonatype.plexus.components.sec.dispatcher.model.SettingsSecurity;
88  
89  import com.google.inject.AbstractModule;
90  
91  // TODO: push all common bits back to plexus cli and prepare for transition to Guice. We don't need 50 ways to make CLIs
92  
93  /**
94   * @author Jason van Zyl
95   * @noinspection UseOfSystemOutOrSystemErr,ACCESS_STATIC_VIA_INSTANCE
96   */
97  public class MavenCli
98  {
99      public static final String LOCAL_REPO_PROPERTY = "maven.repo.local";
100 
101     public static final String THREADS_DEPRECATED = "maven.threads.experimental";
102 
103     @SuppressWarnings( "checkstyle:constantname" )
104     public static final String userHome = System.getProperty( "user.home" );
105 
106     @SuppressWarnings( "checkstyle:constantname" )
107     public static final File userMavenConfigurationHome = new File( userHome, ".m2" );
108 
109     public static final File DEFAULT_USER_SETTINGS_FILE = new File( userMavenConfigurationHome, "settings.xml" );
110 
111     public static final File DEFAULT_GLOBAL_SETTINGS_FILE =
112         new File( System.getProperty( "maven.home", System.getProperty( "user.dir", "" ) ), "conf/settings.xml" );
113 
114     public static final File DEFAULT_USER_TOOLCHAINS_FILE = new File( userMavenConfigurationHome, "toolchains.xml" );
115 
116     private static final String EXT_CLASS_PATH = "maven.ext.class.path";
117 
118     private ClassWorld classWorld;
119 
120     private LoggerManager plexusLoggerManager;
121 
122     private ILoggerFactory slf4jLoggerFactory;
123 
124     private Logger slf4jLogger;
125 
126     private EventSpyDispatcher eventSpyDispatcher;
127 
128     private ModelProcessor modelProcessor;
129 
130     private Maven maven;
131 
132     private MavenExecutionRequestPopulator executionRequestPopulator;
133 
134     private SettingsBuilder settingsBuilder;
135 
136     private DefaultSecDispatcher dispatcher;
137 
138     public MavenCli()
139     {
140         this( null );
141     }
142 
143     // This supports painless invocation by the Verifier during embedded execution of the core ITs
144     public MavenCli( ClassWorld classWorld )
145     {
146         this.classWorld = classWorld;
147     }
148 
149     public static void main( String[] args )
150     {
151         int result = main( args, null );
152 
153         System.exit( result );
154     }
155 
156     /** @noinspection ConfusingMainMethod */
157     public static int main( String[] args, ClassWorld classWorld )
158     {
159         MavenCli cli = new MavenCli();
160         return cli.doMain( new CliRequest( args, classWorld ) );
161     }
162 
163     // TODO: need to externalize CliRequest
164     public static int doMain( String[] args, ClassWorld classWorld )
165     {
166         MavenCli cli = new MavenCli();
167         return cli.doMain( new CliRequest( args, classWorld ) );
168     }
169 
170     // This supports painless invocation by the Verifier during embedded execution of the core ITs
171     public int doMain( String[] args, String workingDirectory, PrintStream stdout, PrintStream stderr )
172     {
173         PrintStream oldout = System.out;
174         PrintStream olderr = System.err;
175 
176         try
177         {
178             if ( stdout != null )
179             {
180                 System.setOut( stdout );
181             }
182             if ( stderr != null )
183             {
184                 System.setErr( stderr );
185             }
186 
187             CliRequest cliRequest = new CliRequest( args, classWorld );
188             cliRequest.workingDirectory = workingDirectory;
189 
190             return doMain( cliRequest );
191         }
192         finally
193         {
194             System.setOut( oldout );
195             System.setErr( olderr );
196         }
197     }
198 
199     // TODO: need to externalize CliRequest
200     public int doMain( CliRequest cliRequest )
201     {
202         PlexusContainer localContainer = null;
203         try
204         {
205             initialize( cliRequest );
206             cli( cliRequest );
207             logging( cliRequest );
208             version( cliRequest );
209             properties( cliRequest );
210             localContainer = container( cliRequest );
211             commands( cliRequest );
212             settings( cliRequest );
213             populateRequest( cliRequest );
214             encryption( cliRequest );
215             repository( cliRequest );
216             return execute( cliRequest );
217         }
218         catch ( ExitException e )
219         {
220             return e.exitCode;
221         }
222         catch ( UnrecognizedOptionException e )
223         {
224             // pure user error, suppress stack trace
225             return 1;
226         }
227         catch ( BuildAbort e )
228         {
229             CLIReportingUtils.showError( slf4jLogger, "ABORTED", e, cliRequest.showErrors );
230 
231             return 2;
232         }
233         catch ( Exception e )
234         {
235             CLIReportingUtils.showError( slf4jLogger, "Error executing Maven.", e, cliRequest.showErrors );
236 
237             return 1;
238         }
239         finally
240         {
241             if ( localContainer != null )
242             {
243                 localContainer.dispose();
244             }
245         }
246     }
247 
248     private void initialize( CliRequest cliRequest )
249     {
250         if ( cliRequest.workingDirectory == null )
251         {
252             cliRequest.workingDirectory = System.getProperty( "user.dir" );
253         }
254 
255         //
256         // Make sure the Maven home directory is an absolute path to save us from confusion with say drive-relative
257         // Windows paths.
258         //
259         String mavenHome = System.getProperty( "maven.home" );
260 
261         if ( mavenHome != null )
262         {
263             System.setProperty( "maven.home", new File( mavenHome ).getAbsolutePath() );
264         }
265     }
266 
267     private void cli( CliRequest cliRequest )
268         throws Exception
269     {
270         //
271         // Parsing errors can happen during the processing of the arguments and we prefer not having to check if
272         // the logger is null and construct this so we can use an SLF4J logger everywhere.
273         //
274         slf4jLogger = new Slf4jStdoutLogger();
275 
276         CLIManager cliManager = new CLIManager();
277 
278         try
279         {
280             cliRequest.commandLine = cliManager.parse( cliRequest.args );
281         }
282         catch ( ParseException e )
283         {
284             System.err.println( "Unable to parse command line options: " + e.getMessage() );
285             cliManager.displayHelp( System.out );
286             throw e;
287         }
288 
289         if ( cliRequest.commandLine.hasOption( CLIManager.HELP ) )
290         {
291             cliManager.displayHelp( System.out );
292             throw new ExitException( 0 );
293         }
294 
295         if ( cliRequest.commandLine.hasOption( CLIManager.VERSION ) )
296         {
297             System.out.println( CLIReportingUtils.showVersion() );
298             throw new ExitException( 0 );
299         }
300     }
301 
302     /**
303      * configure logging
304      */
305     private void logging( CliRequest cliRequest )
306     {
307         cliRequest.debug = cliRequest.commandLine.hasOption( CLIManager.DEBUG );
308         cliRequest.quiet = !cliRequest.debug && cliRequest.commandLine.hasOption( CLIManager.QUIET );
309         cliRequest.showErrors = cliRequest.debug || cliRequest.commandLine.hasOption( CLIManager.ERRORS );
310 
311         slf4jLoggerFactory = LoggerFactory.getILoggerFactory();
312         Slf4jConfiguration slf4jConfiguration = Slf4jConfigurationFactory.getConfiguration( slf4jLoggerFactory );
313 
314         if ( cliRequest.debug )
315         {
316             cliRequest.request.setLoggingLevel( MavenExecutionRequest.LOGGING_LEVEL_DEBUG );
317             slf4jConfiguration.setRootLoggerLevel( Slf4jConfiguration.Level.DEBUG );
318         }
319         else if ( cliRequest.quiet )
320         {
321             cliRequest.request.setLoggingLevel( MavenExecutionRequest.LOGGING_LEVEL_ERROR );
322             slf4jConfiguration.setRootLoggerLevel( Slf4jConfiguration.Level.ERROR );
323         }
324         // else fall back to default log level specified in conf
325         // see http://jira.codehaus.org/browse/MNG-2570
326 
327         if ( cliRequest.commandLine.hasOption( CLIManager.LOG_FILE ) )
328         {
329             File logFile = new File( cliRequest.commandLine.getOptionValue( CLIManager.LOG_FILE ) );
330             logFile = resolveFile( logFile, cliRequest.workingDirectory );
331 
332             // redirect stdout and stderr to file
333             try
334             {
335                 PrintStream ps = new PrintStream( new FileOutputStream( logFile ) );
336                 System.setOut( ps );
337                 System.setErr( ps );
338             }
339             catch ( FileNotFoundException e )
340             {
341                 //
342                 // Ignore
343                 //
344             }
345         }
346 
347         slf4jConfiguration.activate();
348 
349         plexusLoggerManager = new Slf4jLoggerManager();
350         slf4jLogger = slf4jLoggerFactory.getLogger( this.getClass().getName() );
351     }
352 
353     private void version( CliRequest cliRequest )
354     {
355         if ( cliRequest.debug || cliRequest.commandLine.hasOption( CLIManager.SHOW_VERSION ) )
356         {
357             System.out.println( CLIReportingUtils.showVersion() );
358         }
359     }
360 
361     private void commands( CliRequest cliRequest )
362     {
363         if ( cliRequest.showErrors )
364         {
365             slf4jLogger.info( "Error stacktraces are turned on." );
366         }
367 
368         if ( MavenExecutionRequest.CHECKSUM_POLICY_WARN.equals( cliRequest.request.getGlobalChecksumPolicy() ) )
369         {
370             slf4jLogger.info( "Disabling strict checksum verification on all artifact downloads." );
371         }
372         else if ( MavenExecutionRequest.CHECKSUM_POLICY_FAIL.equals( cliRequest.request.getGlobalChecksumPolicy() ) )
373         {
374             slf4jLogger.info( "Enabling strict checksum verification on all artifact downloads." );
375         }
376     }
377 
378     private void properties( CliRequest cliRequest )
379     {
380         populateProperties( cliRequest.commandLine, cliRequest.systemProperties, cliRequest.userProperties );
381     }
382 
383     private PlexusContainer container( CliRequest cliRequest )
384         throws Exception
385     {
386         if ( cliRequest.classWorld == null )
387         {
388             cliRequest.classWorld = new ClassWorld( "plexus.core", Thread.currentThread().getContextClassLoader() );
389         }
390 
391         DefaultPlexusContainer container;
392 
393         ContainerConfiguration cc = new DefaultContainerConfiguration()
394             .setClassWorld( cliRequest.classWorld )
395             .setRealm( setupContainerRealm( cliRequest ) )
396             .setClassPathScanning( PlexusConstants.SCANNING_INDEX )
397             .setAutoWiring( true )
398             .setName( "maven" );
399 
400         container = new DefaultPlexusContainer( cc, new AbstractModule()
401         {
402             protected void configure()
403             {
404                 bind( ILoggerFactory.class ).toInstance( slf4jLoggerFactory );
405             }
406         } );
407 
408         // NOTE: To avoid inconsistencies, we'll use the TCCL exclusively for lookups
409         container.setLookupRealm( null );
410 
411         container.setLoggerManager( plexusLoggerManager );
412 
413         customizeContainer( container );
414 
415         container.getLoggerManager().setThresholds( cliRequest.request.getLoggingLevel() );
416 
417         Thread.currentThread().setContextClassLoader( container.getContainerRealm() );
418 
419         eventSpyDispatcher = container.lookup( EventSpyDispatcher.class );
420 
421         DefaultEventSpyContext eventSpyContext = new DefaultEventSpyContext();
422         Map<String, Object> data = eventSpyContext.getData();
423         data.put( "plexus", container );
424         data.put( "workingDirectory", cliRequest.workingDirectory );
425         data.put( "systemProperties", cliRequest.systemProperties );
426         data.put( "userProperties", cliRequest.userProperties );
427         data.put( "versionProperties", CLIReportingUtils.getBuildProperties() );
428         eventSpyDispatcher.init( eventSpyContext );
429 
430         // refresh logger in case container got customized by spy
431         slf4jLogger = slf4jLoggerFactory.getLogger( this.getClass().getName() );
432 
433         maven = container.lookup( Maven.class );
434 
435         executionRequestPopulator = container.lookup( MavenExecutionRequestPopulator.class );
436 
437         modelProcessor = createModelProcessor( container );
438 
439         settingsBuilder = container.lookup( SettingsBuilder.class );
440 
441         dispatcher = (DefaultSecDispatcher) container.lookup( SecDispatcher.class, "maven" );
442 
443         return container;
444     }
445 
446     private ClassRealm setupContainerRealm( CliRequest cliRequest )
447         throws Exception
448     {
449         ClassRealm containerRealm = null;
450 
451         String extClassPath = cliRequest.userProperties.getProperty( EXT_CLASS_PATH );
452         if ( extClassPath == null )
453         {
454             extClassPath = cliRequest.systemProperties.getProperty( EXT_CLASS_PATH );
455         }
456 
457         if ( StringUtils.isNotEmpty( extClassPath ) )
458         {
459             String[] jars = StringUtils.split( extClassPath, File.pathSeparator );
460 
461             if ( jars.length > 0 )
462             {
463                 ClassRealm coreRealm = cliRequest.classWorld.getClassRealm( "plexus.core" );
464                 if ( coreRealm == null )
465                 {
466                     coreRealm = cliRequest.classWorld.getRealms().iterator().next();
467                 }
468 
469                 ClassRealm extRealm = cliRequest.classWorld.newRealm( "maven.ext", null );
470 
471                 slf4jLogger.debug( "Populating class realm " + extRealm.getId() );
472 
473                 for ( String jar : jars )
474                 {
475                     File file = resolveFile( new File( jar ), cliRequest.workingDirectory );
476 
477                     slf4jLogger.debug( "  Included " + file );
478 
479                     extRealm.addURL( file.toURI().toURL() );
480                 }
481 
482                 extRealm.setParentRealm( coreRealm );
483 
484                 containerRealm = extRealm;
485             }
486         }
487 
488         return containerRealm;
489     }
490 
491     //
492     // This should probably be a separate tool and not be baked into Maven.
493     //
494     private void encryption( CliRequest cliRequest )
495         throws Exception
496     {
497         if ( cliRequest.commandLine.hasOption( CLIManager.ENCRYPT_MASTER_PASSWORD ) )
498         {
499             String passwd = cliRequest.commandLine.getOptionValue( CLIManager.ENCRYPT_MASTER_PASSWORD );
500 
501             if ( passwd == null )
502             {
503                 Console cons = System.console();
504                 char[] password = ( cons == null ) ? null : cons.readPassword( "Master password: " );
505                 if ( password != null )
506                 {
507                     // Cipher uses Strings
508                     passwd = String.copyValueOf( password );
509 
510                     // Sun/Oracle advises to empty the char array
511                     java.util.Arrays.fill( password, ' ' );
512                 }
513             }
514 
515             DefaultPlexusCipher cipher = new DefaultPlexusCipher();
516 
517             System.out.println( cipher.encryptAndDecorate( passwd,
518                                                            DefaultSecDispatcher.SYSTEM_PROPERTY_SEC_LOCATION ) );
519 
520             throw new ExitException( 0 );
521         }
522         else if ( cliRequest.commandLine.hasOption( CLIManager.ENCRYPT_PASSWORD ) )
523         {
524             String passwd = cliRequest.commandLine.getOptionValue( CLIManager.ENCRYPT_PASSWORD );
525 
526             if ( passwd == null )
527             {
528                 Console cons = System.console();
529                 char[] password = ( cons == null ) ? null : cons.readPassword( "Password: " );
530                 if ( password != null )
531                 {
532                     // Cipher uses Strings
533                     passwd = String.copyValueOf( password );
534 
535                     // Sun/Oracle advises to empty the char array
536                     java.util.Arrays.fill( password, ' ' );
537                 }
538             }
539 
540             String configurationFile = dispatcher.getConfigurationFile();
541 
542             if ( configurationFile.startsWith( "~" ) )
543             {
544                 configurationFile = System.getProperty( "user.home" ) + configurationFile.substring( 1 );
545             }
546 
547             String file = System.getProperty( DefaultSecDispatcher.SYSTEM_PROPERTY_SEC_LOCATION, configurationFile );
548 
549             String master = null;
550 
551             SettingsSecurity sec = SecUtil.read( file, true );
552             if ( sec != null )
553             {
554                 master = sec.getMaster();
555             }
556 
557             if ( master == null )
558             {
559                 throw new IllegalStateException( "Master password is not set in the setting security file: " + file );
560             }
561 
562             DefaultPlexusCipher cipher = new DefaultPlexusCipher();
563             String masterPasswd = cipher.decryptDecorated( master, DefaultSecDispatcher.SYSTEM_PROPERTY_SEC_LOCATION );
564             System.out.println( cipher.encryptAndDecorate( passwd, masterPasswd ) );
565 
566             throw new ExitException( 0 );
567         }
568     }
569 
570     private void repository( CliRequest cliRequest )
571         throws Exception
572     {
573         if ( cliRequest.commandLine.hasOption( CLIManager.LEGACY_LOCAL_REPOSITORY )
574             || Boolean.getBoolean( "maven.legacyLocalRepo" ) )
575         {
576            cliRequest.request.setUseLegacyLocalRepository( true );
577         }
578     }
579 
580     private int execute( CliRequest cliRequest )
581     {
582         eventSpyDispatcher.onEvent( cliRequest.request );
583 
584         MavenExecutionResult result = maven.execute( cliRequest.request );
585 
586         eventSpyDispatcher.onEvent( result );
587 
588         eventSpyDispatcher.close();
589 
590         if ( result.hasExceptions() )
591         {
592             ExceptionHandler handler = new DefaultExceptionHandler();
593 
594             Map<String, String> references = new LinkedHashMap<String, String>();
595 
596             MavenProject project = null;
597 
598             for ( Throwable exception : result.getExceptions() )
599             {
600                 ExceptionSummary summary = handler.handleException( exception );
601 
602                 logSummary( summary, references, "", cliRequest.showErrors );
603 
604                 if ( project == null && exception instanceof LifecycleExecutionException )
605                 {
606                     project = ( (LifecycleExecutionException) exception ).getProject();
607                 }
608             }
609 
610             slf4jLogger.error( "" );
611 
612             if ( !cliRequest.showErrors )
613             {
614                 slf4jLogger.error( "To see the full stack trace of the errors, re-run Maven with the -e switch." );
615             }
616             if ( !slf4jLogger.isDebugEnabled() )
617             {
618                 slf4jLogger.error( "Re-run Maven using the -X switch to enable full debug logging." );
619             }
620 
621             if ( !references.isEmpty() )
622             {
623                 slf4jLogger.error( "" );
624                 slf4jLogger.error( "For more information about the errors and possible solutions"
625                               + ", please read the following articles:" );
626 
627                 for ( Map.Entry<String, String> entry : references.entrySet() )
628                 {
629                     slf4jLogger.error( entry.getValue() + " " + entry.getKey() );
630                 }
631             }
632 
633             if ( project != null && !project.equals( result.getTopologicallySortedProjects().get( 0 ) ) )
634             {
635                 slf4jLogger.error( "" );
636                 slf4jLogger.error( "After correcting the problems, you can resume the build with the command" );
637                 slf4jLogger.error( "  mvn <goals> -rf :" + project.getArtifactId() );
638             }
639 
640             if ( MavenExecutionRequest.REACTOR_FAIL_NEVER.equals( cliRequest.request.getReactorFailureBehavior() ) )
641             {
642                 slf4jLogger.info( "Build failures were ignored." );
643 
644                 return 0;
645             }
646             else
647             {
648                 return 1;
649             }
650         }
651         else
652         {
653             return 0;
654         }
655     }
656 
657     private void logSummary( ExceptionSummary summary, Map<String, String> references, String indent,
658                              boolean showErrors )
659     {
660         String referenceKey = "";
661 
662         if ( StringUtils.isNotEmpty( summary.getReference() ) )
663         {
664             referenceKey = references.get( summary.getReference() );
665             if ( referenceKey == null )
666             {
667                 referenceKey = "[Help " + ( references.size() + 1 ) + "]";
668                 references.put( summary.getReference(), referenceKey );
669             }
670         }
671 
672         String msg = summary.getMessage();
673 
674         if ( StringUtils.isNotEmpty( referenceKey ) )
675         {
676             if ( msg.indexOf( '\n' ) < 0 )
677             {
678                 msg += " -> " + referenceKey;
679             }
680             else
681             {
682                 msg += "\n-> " + referenceKey;
683             }
684         }
685 
686         String[] lines = msg.split( "(\r\n)|(\r)|(\n)" );
687 
688         for ( int i = 0; i < lines.length; i++ )
689         {
690             String line = indent + lines[i].trim();
691 
692             if ( ( i == lines.length - 1 )
693                 && ( showErrors || ( summary.getException() instanceof InternalErrorException ) ) )
694             {
695                 slf4jLogger.error( line, summary.getException() );
696             }
697             else
698             {
699                 slf4jLogger.error( line );
700             }
701         }
702 
703         indent += "  ";
704 
705         for ( ExceptionSummary child : summary.getChildren() )
706         {
707             logSummary( child, references, indent, showErrors );
708         }
709     }
710 
711     @SuppressWarnings( "checkstyle:methodlength" )
712     private void settings( CliRequest cliRequest )
713         throws Exception
714     {
715         File userSettingsFile;
716 
717         if ( cliRequest.commandLine.hasOption( CLIManager.ALTERNATE_USER_SETTINGS ) )
718         {
719             userSettingsFile = new File( cliRequest.commandLine.getOptionValue( CLIManager.ALTERNATE_USER_SETTINGS ) );
720             userSettingsFile = resolveFile( userSettingsFile, cliRequest.workingDirectory );
721 
722             if ( !userSettingsFile.isFile() )
723             {
724                 throw new FileNotFoundException( "The specified user settings file does not exist: "
725                     + userSettingsFile );
726             }
727         }
728         else
729         {
730             userSettingsFile = DEFAULT_USER_SETTINGS_FILE;
731         }
732 
733         File globalSettingsFile;
734 
735         if ( cliRequest.commandLine.hasOption( CLIManager.ALTERNATE_GLOBAL_SETTINGS ) )
736         {
737             globalSettingsFile =
738                 new File( cliRequest.commandLine.getOptionValue( CLIManager.ALTERNATE_GLOBAL_SETTINGS ) );
739             globalSettingsFile = resolveFile( globalSettingsFile, cliRequest.workingDirectory );
740 
741             if ( !globalSettingsFile.isFile() )
742             {
743                 throw new FileNotFoundException( "The specified global settings file does not exist: "
744                     + globalSettingsFile );
745             }
746         }
747         else
748         {
749             globalSettingsFile = DEFAULT_GLOBAL_SETTINGS_FILE;
750         }
751 
752         cliRequest.request.setGlobalSettingsFile( globalSettingsFile );
753         cliRequest.request.setUserSettingsFile( userSettingsFile );
754 
755         SettingsBuildingRequest settingsRequest = new DefaultSettingsBuildingRequest();
756         settingsRequest.setGlobalSettingsFile( globalSettingsFile );
757         settingsRequest.setUserSettingsFile( userSettingsFile );
758         settingsRequest.setSystemProperties( cliRequest.systemProperties );
759         settingsRequest.setUserProperties( cliRequest.userProperties );
760 
761         eventSpyDispatcher.onEvent( settingsRequest );
762 
763         slf4jLogger.debug( "Reading global settings from "
764             + getSettingsLocation( settingsRequest.getGlobalSettingsSource(),
765                                    settingsRequest.getGlobalSettingsFile() ) );
766         slf4jLogger.debug( "Reading user settings from "
767             + getSettingsLocation( settingsRequest.getUserSettingsSource(), settingsRequest.getUserSettingsFile() ) );
768 
769         SettingsBuildingResult settingsResult = settingsBuilder.build( settingsRequest );
770 
771         eventSpyDispatcher.onEvent( settingsResult );
772 
773         executionRequestPopulator.populateFromSettings( cliRequest.request, settingsResult.getEffectiveSettings() );
774 
775         if ( !settingsResult.getProblems().isEmpty() && slf4jLogger.isWarnEnabled() )
776         {
777             slf4jLogger.warn( "" );
778             slf4jLogger.warn( "Some problems were encountered while building the effective settings" );
779 
780             for ( SettingsProblem problem : settingsResult.getProblems() )
781             {
782                 slf4jLogger.warn( problem.getMessage() + " @ " + problem.getLocation() );
783             }
784 
785             slf4jLogger.warn( "" );
786         }
787     }
788 
789     private Object getSettingsLocation( SettingsSource source, File file )
790     {
791         if ( source != null )
792         {
793             return source.getLocation();
794         }
795         return file;
796     }
797 
798     private MavenExecutionRequest populateRequest( CliRequest cliRequest )
799     {
800         MavenExecutionRequest request = cliRequest.request;
801         CommandLine commandLine = cliRequest.commandLine;
802         String workingDirectory = cliRequest.workingDirectory;
803         boolean quiet = cliRequest.quiet;
804         boolean showErrors = cliRequest.showErrors;
805 
806         String[] deprecatedOptions = { "up", "npu", "cpu", "npr" };
807         for ( String deprecatedOption : deprecatedOptions )
808         {
809             if ( commandLine.hasOption( deprecatedOption ) )
810             {
811                 slf4jLogger.warn( "Command line option -" + deprecatedOption
812                     + " is deprecated and will be removed in future Maven versions." );
813             }
814         }
815 
816         // ----------------------------------------------------------------------
817         // Now that we have everything that we need we will fire up plexus and
818         // bring the maven component to life for use.
819         // ----------------------------------------------------------------------
820 
821         if ( commandLine.hasOption( CLIManager.BATCH_MODE ) )
822         {
823             request.setInteractiveMode( false );
824         }
825 
826         boolean noSnapshotUpdates = false;
827         if ( commandLine.hasOption( CLIManager.SUPRESS_SNAPSHOT_UPDATES ) )
828         {
829             noSnapshotUpdates = true;
830         }
831 
832         // ----------------------------------------------------------------------
833         //
834         // ----------------------------------------------------------------------
835 
836         @SuppressWarnings( "unchecked" )
837         List<String> goals = commandLine.getArgList();
838 
839         boolean recursive = true;
840 
841         // this is the default behavior.
842         String reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_FAST;
843 
844         if ( commandLine.hasOption( CLIManager.NON_RECURSIVE ) )
845         {
846             recursive = false;
847         }
848 
849         if ( commandLine.hasOption( CLIManager.FAIL_FAST ) )
850         {
851             reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_FAST;
852         }
853         else if ( commandLine.hasOption( CLIManager.FAIL_AT_END ) )
854         {
855             reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_AT_END;
856         }
857         else if ( commandLine.hasOption( CLIManager.FAIL_NEVER ) )
858         {
859             reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_NEVER;
860         }
861 
862         if ( commandLine.hasOption( CLIManager.OFFLINE ) )
863         {
864             request.setOffline( true );
865         }
866 
867         boolean updateSnapshots = false;
868 
869         if ( commandLine.hasOption( CLIManager.UPDATE_SNAPSHOTS ) )
870         {
871             updateSnapshots = true;
872         }
873 
874         String globalChecksumPolicy = null;
875 
876         if ( commandLine.hasOption( CLIManager.CHECKSUM_FAILURE_POLICY ) )
877         {
878             globalChecksumPolicy = MavenExecutionRequest.CHECKSUM_POLICY_FAIL;
879         }
880         else if ( commandLine.hasOption( CLIManager.CHECKSUM_WARNING_POLICY ) )
881         {
882             globalChecksumPolicy = MavenExecutionRequest.CHECKSUM_POLICY_WARN;
883         }
884 
885         File baseDirectory = new File( workingDirectory, "" ).getAbsoluteFile();
886 
887         // ----------------------------------------------------------------------
888         // Profile Activation
889         // ----------------------------------------------------------------------
890 
891         List<String> activeProfiles = new ArrayList<String>();
892 
893         List<String> inactiveProfiles = new ArrayList<String>();
894 
895         if ( commandLine.hasOption( CLIManager.ACTIVATE_PROFILES ) )
896         {
897             String[] profileOptionValues = commandLine.getOptionValues( CLIManager.ACTIVATE_PROFILES );
898             if ( profileOptionValues != null )
899             {
900                 for ( String profileOptionValue : profileOptionValues )
901                 {
902                     StringTokenizer profileTokens = new StringTokenizer( profileOptionValue, "," );
903 
904                     while ( profileTokens.hasMoreTokens() )
905                     {
906                         String profileAction = profileTokens.nextToken().trim();
907 
908                         if ( profileAction.startsWith( "-" ) || profileAction.startsWith( "!" ) )
909                         {
910                             inactiveProfiles.add( profileAction.substring( 1 ) );
911                         }
912                         else if ( profileAction.startsWith( "+" ) )
913                         {
914                             activeProfiles.add( profileAction.substring( 1 ) );
915                         }
916                         else
917                         {
918                             activeProfiles.add( profileAction );
919                         }
920                     }
921                 }
922             }
923         }
924 
925         TransferListener transferListener;
926 
927         if ( quiet )
928         {
929             transferListener = new QuietMavenTransferListener();
930         }
931         else if ( request.isInteractiveMode() && !cliRequest.commandLine.hasOption( CLIManager.LOG_FILE ) )
932         {
933             //
934             // If we're logging to a file then we don't want the console transfer listener as it will spew
935             // download progress all over the place
936             //
937             transferListener = getConsoleTransferListener();
938         }
939         else
940         {
941             transferListener = getBatchTransferListener();
942         }
943 
944         ExecutionListener executionListener = new ExecutionEventLogger();
945         executionListener = eventSpyDispatcher.chainListener( executionListener );
946 
947         String alternatePomFile = null;
948         if ( commandLine.hasOption( CLIManager.ALTERNATE_POM_FILE ) )
949         {
950             alternatePomFile = commandLine.getOptionValue( CLIManager.ALTERNATE_POM_FILE );
951         }
952 
953         File userToolchainsFile;
954         if ( commandLine.hasOption( CLIManager.ALTERNATE_USER_TOOLCHAINS ) )
955         {
956             userToolchainsFile = new File( commandLine.getOptionValue( CLIManager.ALTERNATE_USER_TOOLCHAINS ) );
957             userToolchainsFile = resolveFile( userToolchainsFile, workingDirectory );
958         }
959         else
960         {
961             userToolchainsFile = MavenCli.DEFAULT_USER_TOOLCHAINS_FILE;
962         }
963 
964         request.setBaseDirectory( baseDirectory ).setGoals( goals )
965             .setSystemProperties( cliRequest.systemProperties )
966             .setUserProperties( cliRequest.userProperties )
967             .setReactorFailureBehavior( reactorFailureBehaviour ) // default: fail fast
968             .setRecursive( recursive ) // default: true
969             .setShowErrors( showErrors ) // default: false
970             .addActiveProfiles( activeProfiles ) // optional
971             .addInactiveProfiles( inactiveProfiles ) // optional
972             .setExecutionListener( executionListener )
973             .setTransferListener( transferListener ) // default: batch mode which goes along with interactive
974             .setUpdateSnapshots( updateSnapshots ) // default: false
975             .setNoSnapshotUpdates( noSnapshotUpdates ) // default: false
976             .setGlobalChecksumPolicy( globalChecksumPolicy ) // default: warn
977             .setUserToolchainsFile( userToolchainsFile );
978 
979         if ( alternatePomFile != null )
980         {
981             File pom = resolveFile( new File( alternatePomFile ), workingDirectory );
982             if ( pom.isDirectory() )
983             {
984                 pom = new File( pom, "pom.xml" );
985             }
986 
987             request.setPom( pom );
988         }
989         else
990         {
991             File pom = modelProcessor.locatePom( baseDirectory );
992 
993             if ( pom.isFile() )
994             {
995                 request.setPom( pom );
996             }
997         }
998 
999         if ( ( request.getPom() != null ) && ( request.getPom().getParentFile() != null ) )
1000         {
1001             request.setBaseDirectory( request.getPom().getParentFile() );
1002         }
1003 
1004         if ( commandLine.hasOption( CLIManager.RESUME_FROM ) )
1005         {
1006             request.setResumeFrom( commandLine.getOptionValue( CLIManager.RESUME_FROM ) );
1007         }
1008 
1009         if ( commandLine.hasOption( CLIManager.PROJECT_LIST ) )
1010         {
1011             String[] projectOptionValues = commandLine.getOptionValues( CLIManager.PROJECT_LIST );
1012 
1013             List<String> inclProjects = new ArrayList<String>();
1014             List<String> exclProjects = new ArrayList<String>();
1015 
1016             if ( projectOptionValues != null )
1017             {
1018                 for ( String projectOptionValue : projectOptionValues )
1019                 {
1020                     StringTokenizer projectTokens = new StringTokenizer( projectOptionValue, "," );
1021 
1022                     while ( projectTokens.hasMoreTokens() )
1023                     {
1024                         String projectAction = projectTokens.nextToken().trim();
1025 
1026                         if ( projectAction.startsWith( "-" ) || projectAction.startsWith( "!" ) )
1027                         {
1028                             exclProjects.add( projectAction.substring( 1 ) );
1029                         }
1030                         else if ( projectAction.startsWith( "+" ) )
1031                         {
1032                             inclProjects.add( projectAction.substring( 1 ) );
1033                         }
1034                         else
1035                         {
1036                             inclProjects.add( projectAction );
1037                         }
1038                     }
1039                 }
1040             }
1041 
1042             request.setSelectedProjects( inclProjects );
1043             request.setExcludedProjects( exclProjects );
1044         }
1045 
1046         if ( commandLine.hasOption( CLIManager.ALSO_MAKE )
1047                         && !commandLine.hasOption( CLIManager.ALSO_MAKE_DEPENDENTS ) )
1048         {
1049             request.setMakeBehavior( MavenExecutionRequest.REACTOR_MAKE_UPSTREAM );
1050         }
1051         else if ( !commandLine.hasOption( CLIManager.ALSO_MAKE )
1052                         && commandLine.hasOption( CLIManager.ALSO_MAKE_DEPENDENTS ) )
1053         {
1054             request.setMakeBehavior( MavenExecutionRequest.REACTOR_MAKE_DOWNSTREAM );
1055         }
1056         else if ( commandLine.hasOption( CLIManager.ALSO_MAKE )
1057                         && commandLine.hasOption( CLIManager.ALSO_MAKE_DEPENDENTS ) )
1058         {
1059             request.setMakeBehavior( MavenExecutionRequest.REACTOR_MAKE_BOTH );
1060         }
1061 
1062         String localRepoProperty = request.getUserProperties().getProperty( MavenCli.LOCAL_REPO_PROPERTY );
1063 
1064         if ( localRepoProperty == null )
1065         {
1066             localRepoProperty = request.getSystemProperties().getProperty( MavenCli.LOCAL_REPO_PROPERTY );
1067         }
1068 
1069         if ( localRepoProperty != null )
1070         {
1071             request.setLocalRepositoryPath( localRepoProperty );
1072         }
1073 
1074 
1075         request.setCacheNotFound( true );
1076         request.setCacheTransferError( false );
1077 
1078         //
1079         // Builder, concurrency and parallelism
1080         //
1081         // We preserve the existing methods for builder selection which is to look for various inputs in the threading
1082         // configuration. We don't have an easy way to allow a pluggable builder to provide its own configuration
1083         // parameters but this is sufficient for now. Ultimately we want components like Builders to provide a way to
1084         // extend the command line to accept its own configuration parameters.
1085         //
1086         final String threadConfiguration = commandLine.hasOption( CLIManager.THREADS )
1087             ? commandLine.getOptionValue( CLIManager.THREADS )
1088             : request.getSystemProperties().getProperty(
1089                 MavenCli.THREADS_DEPRECATED ); // TODO: Remove this setting. Note that the int-tests use it
1090 
1091         if ( threadConfiguration != null )
1092         {
1093             //
1094             // Default to the standard multithreaded builder
1095             //
1096             request.setBuilderId( "multithreaded" );
1097 
1098             if ( threadConfiguration.contains( "C" ) )
1099             {
1100                 request.setDegreeOfConcurrency( calculateDegreeOfConcurrencyWithCoreMultiplier( threadConfiguration ) );
1101             }
1102             else
1103             {
1104                 request.setDegreeOfConcurrency( Integer.valueOf( threadConfiguration ) );
1105             }
1106         }
1107 
1108         //
1109         // Allow the builder to be overriden by the user if requested. The builders are now pluggable.
1110         //
1111         if ( commandLine.hasOption( CLIManager.BUILDER ) )
1112         {
1113             request.setBuilderId( commandLine.getOptionValue( CLIManager.BUILDER ) );
1114         }
1115 
1116         return request;
1117     }
1118 
1119     int calculateDegreeOfConcurrencyWithCoreMultiplier( String threadConfiguration )
1120     {
1121         int procs = Runtime.getRuntime().availableProcessors();
1122         return (int) ( Float.valueOf( threadConfiguration.replace( "C", "" ) ) * procs );
1123     }
1124 
1125     static File resolveFile( File file, String workingDirectory )
1126     {
1127         if ( file == null )
1128         {
1129             return null;
1130         }
1131         else if ( file.isAbsolute() )
1132         {
1133             return file;
1134         }
1135         else if ( file.getPath().startsWith( File.separator ) )
1136         {
1137             // drive-relative Windows path
1138             return file.getAbsoluteFile();
1139         }
1140         else
1141         {
1142             return new File( workingDirectory, file.getPath() ).getAbsoluteFile();
1143         }
1144     }
1145 
1146     // ----------------------------------------------------------------------
1147     // System properties handling
1148     // ----------------------------------------------------------------------
1149 
1150     static void populateProperties( CommandLine commandLine, Properties systemProperties, Properties userProperties )
1151     {
1152         EnvironmentUtils.addEnvVars( systemProperties );
1153 
1154         // ----------------------------------------------------------------------
1155         // Options that are set on the command line become system properties
1156         // and therefore are set in the session properties. System properties
1157         // are most dominant.
1158         // ----------------------------------------------------------------------
1159 
1160         if ( commandLine.hasOption( CLIManager.SET_SYSTEM_PROPERTY ) )
1161         {
1162             String[] defStrs = commandLine.getOptionValues( CLIManager.SET_SYSTEM_PROPERTY );
1163 
1164             if ( defStrs != null )
1165             {
1166                 for ( String defStr : defStrs )
1167                 {
1168                     setCliProperty( defStr, userProperties );
1169                 }
1170             }
1171         }
1172 
1173         SystemProperties.addSystemProperties( systemProperties );
1174 
1175         // ----------------------------------------------------------------------
1176         // Properties containing info about the currently running version of Maven
1177         // These override any corresponding properties set on the command line
1178         // ----------------------------------------------------------------------
1179 
1180         Properties buildProperties = CLIReportingUtils.getBuildProperties();
1181 
1182         String mavenVersion = buildProperties.getProperty( CLIReportingUtils.BUILD_VERSION_PROPERTY );
1183         systemProperties.setProperty( "maven.version", mavenVersion );
1184 
1185         String mavenBuildVersion = CLIReportingUtils.createMavenVersionString( buildProperties );
1186         systemProperties.setProperty( "maven.build.version", mavenBuildVersion );
1187     }
1188 
1189     private static void setCliProperty( String property, Properties properties )
1190     {
1191         String name;
1192 
1193         String value;
1194 
1195         int i = property.indexOf( "=" );
1196 
1197         if ( i <= 0 )
1198         {
1199             name = property.trim();
1200 
1201             value = "true";
1202         }
1203         else
1204         {
1205             name = property.substring( 0, i ).trim();
1206 
1207             value = property.substring( i + 1 );
1208         }
1209 
1210         properties.setProperty( name, value );
1211 
1212         // ----------------------------------------------------------------------
1213         // I'm leaving the setting of system properties here as not to break
1214         // the SystemPropertyProfileActivator. This won't harm embedding. jvz.
1215         // ----------------------------------------------------------------------
1216 
1217         System.setProperty( name, value );
1218     }
1219 
1220     static class CliRequest
1221     {
1222         String[] args;
1223         CommandLine commandLine;
1224         ClassWorld classWorld;
1225         String workingDirectory;
1226         boolean debug;
1227         boolean quiet;
1228         boolean showErrors = true;
1229         Properties userProperties = new Properties();
1230         Properties systemProperties = new Properties();
1231         MavenExecutionRequest request;
1232 
1233         CliRequest( String[] args, ClassWorld classWorld )
1234         {
1235             this.args = args;
1236             this.classWorld = classWorld;
1237             this.request = new DefaultMavenExecutionRequest();
1238         }
1239     }
1240 
1241     static class ExitException
1242         extends Exception
1243     {
1244         @SuppressWarnings( "checkstyle:visibilitymodifier" )
1245         public int exitCode;
1246 
1247         public ExitException( int exitCode )
1248         {
1249             this.exitCode = exitCode;
1250         }
1251 
1252     }
1253 
1254     //
1255     // Customizations available via the CLI
1256     //
1257 
1258     protected TransferListener getConsoleTransferListener()
1259     {
1260         return new ConsoleMavenTransferListener( System.out );
1261     }
1262 
1263     protected TransferListener getBatchTransferListener()
1264     {
1265         return new Slf4jMavenTransferListener();
1266     }
1267 
1268     protected void customizeContainer( PlexusContainer container )
1269     {
1270     }
1271 
1272     protected ModelProcessor createModelProcessor( PlexusContainer container )
1273         throws ComponentLookupException
1274     {
1275         return container.lookup( ModelProcessor.class );
1276     }
1277 }