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 com.google.common.base.Charsets;
23  import com.google.common.io.Files;
24  import com.google.inject.AbstractModule;
25  import org.apache.commons.cli.CommandLine;
26  import org.apache.commons.cli.ParseException;
27  import org.apache.commons.cli.UnrecognizedOptionException;
28  import org.apache.maven.BuildAbort;
29  import org.apache.maven.InternalErrorException;
30  import org.apache.maven.Maven;
31  import org.apache.maven.building.FileSource;
32  import org.apache.maven.building.Problem;
33  import org.apache.maven.building.Source;
34  import org.apache.maven.cli.configuration.ConfigurationProcessor;
35  import org.apache.maven.cli.configuration.SettingsXmlConfigurationProcessor;
36  import org.apache.maven.cli.event.DefaultEventSpyContext;
37  import org.apache.maven.cli.event.ExecutionEventLogger;
38  import org.apache.maven.cli.internal.BootstrapCoreExtensionManager;
39  import org.apache.maven.cli.internal.extension.model.CoreExtension;
40  import org.apache.maven.cli.internal.extension.model.io.xpp3.CoreExtensionsXpp3Reader;
41  import org.apache.maven.cli.logging.Slf4jConfiguration;
42  import org.apache.maven.cli.logging.Slf4jConfigurationFactory;
43  import org.apache.maven.cli.logging.Slf4jLoggerManager;
44  import org.apache.maven.cli.logging.Slf4jStdoutLogger;
45  import org.apache.maven.cli.transfer.ConsoleMavenTransferListener;
46  import org.apache.maven.cli.transfer.QuietMavenTransferListener;
47  import org.apache.maven.cli.transfer.Slf4jMavenTransferListener;
48  import org.apache.maven.eventspy.internal.EventSpyDispatcher;
49  import org.apache.maven.exception.DefaultExceptionHandler;
50  import org.apache.maven.exception.ExceptionHandler;
51  import org.apache.maven.exception.ExceptionSummary;
52  import org.apache.maven.execution.DefaultMavenExecutionRequest;
53  import org.apache.maven.execution.ExecutionListener;
54  import org.apache.maven.execution.MavenExecutionRequest;
55  import org.apache.maven.execution.MavenExecutionRequestPopulationException;
56  import org.apache.maven.execution.MavenExecutionRequestPopulator;
57  import org.apache.maven.execution.MavenExecutionResult;
58  import org.apache.maven.extension.internal.CoreExports;
59  import org.apache.maven.extension.internal.CoreExtensionEntry;
60  import org.apache.maven.lifecycle.LifecycleExecutionException;
61  import org.apache.maven.model.building.ModelProcessor;
62  import org.apache.maven.project.MavenProject;
63  import org.apache.maven.properties.internal.EnvironmentUtils;
64  import org.apache.maven.properties.internal.SystemProperties;
65  import org.apache.maven.shared.utils.logging.MessageBuilder;
66  import org.apache.maven.shared.utils.logging.MessageUtils;
67  import org.apache.maven.toolchain.building.DefaultToolchainsBuildingRequest;
68  import org.apache.maven.toolchain.building.ToolchainsBuilder;
69  import org.apache.maven.toolchain.building.ToolchainsBuildingResult;
70  import org.codehaus.plexus.ContainerConfiguration;
71  import org.codehaus.plexus.DefaultContainerConfiguration;
72  import org.codehaus.plexus.DefaultPlexusContainer;
73  import org.codehaus.plexus.PlexusConstants;
74  import org.codehaus.plexus.PlexusContainer;
75  import org.codehaus.plexus.classworlds.ClassWorld;
76  import org.codehaus.plexus.classworlds.realm.ClassRealm;
77  import org.codehaus.plexus.classworlds.realm.NoSuchRealmException;
78  import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
79  import org.codehaus.plexus.logging.LoggerManager;
80  import org.codehaus.plexus.util.StringUtils;
81  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
82  import org.eclipse.aether.transfer.TransferListener;
83  import org.slf4j.ILoggerFactory;
84  import org.slf4j.Logger;
85  import org.slf4j.LoggerFactory;
86  import org.sonatype.plexus.components.cipher.DefaultPlexusCipher;
87  import org.sonatype.plexus.components.sec.dispatcher.DefaultSecDispatcher;
88  import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher;
89  import org.sonatype.plexus.components.sec.dispatcher.SecUtil;
90  import org.sonatype.plexus.components.sec.dispatcher.model.SettingsSecurity;
91  
92  import java.io.BufferedInputStream;
93  import java.io.Console;
94  import java.io.File;
95  import java.io.FileInputStream;
96  import java.io.FileNotFoundException;
97  import java.io.FileOutputStream;
98  import java.io.IOException;
99  import java.io.InputStream;
100 import java.io.PrintStream;
101 import java.util.ArrayList;
102 import java.util.Collections;
103 import java.util.HashSet;
104 import java.util.LinkedHashMap;
105 import java.util.List;
106 import java.util.Map;
107 import java.util.Map.Entry;
108 import java.util.Properties;
109 import java.util.Set;
110 import java.util.StringTokenizer;
111 import java.util.regex.Matcher;
112 import java.util.regex.Pattern;
113 
114 import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;
115 
116 // TODO push all common bits back to plexus cli and prepare for transition to Guice. We don't need 50 ways to make CLIs
117 
118 /**
119  * @author Jason van Zyl
120  * @noinspection UseOfSystemOutOrSystemErr, ACCESS_STATIC_VIA_INSTANCE
121  */
122 public class MavenCli
123 {
124     public static final String LOCAL_REPO_PROPERTY = "maven.repo.local";
125 
126     public static final String THREADS_DEPRECATED = "maven.threads.experimental";
127 
128     public static final String MULTIMODULE_PROJECT_DIRECTORY = "maven.multiModuleProjectDirectory";
129 
130     @SuppressWarnings( "checkstyle:constantname" )
131     public static final String userHome = System.getProperty( "user.home" );
132 
133     @SuppressWarnings( "checkstyle:constantname" )
134     public static final File userMavenConfigurationHome = new File( userHome, ".m2" );
135 
136     /**
137      * @deprecated use {@link SettingsXmlConfigurationProcessor#DEFAULT_USER_SETTINGS_FILE}
138      */
139     public static final File DEFAULT_USER_SETTINGS_FILE = SettingsXmlConfigurationProcessor.DEFAULT_USER_SETTINGS_FILE;
140 
141     /**
142      * @deprecated use {@link SettingsXmlConfigurationProcessor#DEFAULT_GLOBAL_SETTINGS_FILE}
143      */
144     public static final File DEFAULT_GLOBAL_SETTINGS_FILE =
145         SettingsXmlConfigurationProcessor.DEFAULT_GLOBAL_SETTINGS_FILE;
146 
147     public static final File DEFAULT_USER_TOOLCHAINS_FILE = new File( userMavenConfigurationHome, "toolchains.xml" );
148 
149     public static final File DEFAULT_GLOBAL_TOOLCHAINS_FILE =
150         new File( System.getProperty( "maven.conf" ), "toolchains.xml" );
151 
152     private static final String EXT_CLASS_PATH = "maven.ext.class.path";
153 
154     private static final String EXTENSIONS_FILENAME = ".mvn/extensions.xml";
155 
156     private static final String MVN_MAVEN_CONFIG = ".mvn/maven.config";
157 
158     private ClassWorld classWorld;
159 
160     private LoggerManager plexusLoggerManager;
161 
162     private ILoggerFactory slf4jLoggerFactory;
163 
164     private Logger slf4jLogger;
165 
166     private EventSpyDispatcher eventSpyDispatcher;
167 
168     private ModelProcessor modelProcessor;
169 
170     private Maven maven;
171 
172     private MavenExecutionRequestPopulator executionRequestPopulator;
173 
174     private ToolchainsBuilder toolchainsBuilder;
175 
176     private DefaultSecDispatcher dispatcher;
177 
178     private Map<String, ConfigurationProcessor> configurationProcessors;
179 
180     public MavenCli()
181     {
182         this( null );
183     }
184 
185     // This supports painless invocation by the Verifier during embedded execution of the core ITs
186     public MavenCli( ClassWorld classWorld )
187     {
188         this.classWorld = classWorld;
189     }
190 
191     public static void main( String[] args )
192     {
193         int result = main( args, null );
194 
195         System.exit( result );
196     }
197 
198     /**
199      * @noinspection ConfusingMainMethod
200      */
201     public static int main( String[] args, ClassWorld classWorld )
202     {
203         MavenCli cli = new MavenCli();
204 
205         MessageUtils.systemInstall();
206         int result = cli.doMain( new CliRequest( args, classWorld ) );
207         MessageUtils.systemUninstall();
208 
209         return result;
210     }
211 
212     // TODO need to externalize CliRequest
213     public static int doMain( String[] args, ClassWorld classWorld )
214     {
215         MavenCli cli = new MavenCli();
216         return cli.doMain( new CliRequest( args, classWorld ) );
217     }
218 
219     /**
220      * This supports painless invocation by the Verifier during embedded execution of the core ITs.
221      * See <a href="http://maven.apache.org/shared/maven-verifier/xref/org/apache/maven/it/Embedded3xLauncher.html">
222      * <code>Embedded3xLauncher</code> in <code>maven-verifier</code></a>
223      */
224     public int doMain( String[] args, String workingDirectory, PrintStream stdout, PrintStream stderr )
225     {
226         PrintStream oldout = System.out;
227         PrintStream olderr = System.err;
228 
229         final Set<String> realms;
230         if ( classWorld != null )
231         {
232             realms = new HashSet<>();
233             for ( ClassRealm realm : classWorld.getRealms() )
234             {
235                 realms.add( realm.getId() );
236             }
237         }
238         else
239         {
240             realms = Collections.emptySet();
241         }
242 
243         try
244         {
245             if ( stdout != null )
246             {
247                 System.setOut( stdout );
248             }
249             if ( stderr != null )
250             {
251                 System.setErr( stderr );
252             }
253 
254             CliRequest cliRequest = new CliRequest( args, classWorld );
255             cliRequest.workingDirectory = workingDirectory;
256 
257             return doMain( cliRequest );
258         }
259         finally
260         {
261             if ( classWorld != null )
262             {
263                 for ( ClassRealm realm : new ArrayList<>( classWorld.getRealms() ) )
264                 {
265                     String realmId = realm.getId();
266                     if ( !realms.contains( realmId ) )
267                     {
268                         try
269                         {
270                             classWorld.disposeRealm( realmId );
271                         }
272                         catch ( NoSuchRealmException ignored )
273                         {
274                             // can't happen
275                         }
276                     }
277                 }
278             }
279             System.setOut( oldout );
280             System.setErr( olderr );
281         }
282     }
283 
284     // TODO need to externalize CliRequest
285     public int doMain( CliRequest cliRequest )
286     {
287         PlexusContainer localContainer = null;
288         try
289         {
290             initialize( cliRequest );
291             cli( cliRequest );
292             logging( cliRequest );
293             version( cliRequest );
294             properties( cliRequest );
295             localContainer = container( cliRequest );
296             commands( cliRequest );
297             configure( cliRequest );
298             toolchains( cliRequest );
299             populateRequest( cliRequest );
300             encryption( cliRequest );
301             repository( cliRequest );
302             return execute( cliRequest );
303         }
304         catch ( ExitException e )
305         {
306             return e.exitCode;
307         }
308         catch ( UnrecognizedOptionException e )
309         {
310             // pure user error, suppress stack trace
311             return 1;
312         }
313         catch ( BuildAbort e )
314         {
315             CLIReportingUtils.showError( slf4jLogger, "ABORTED", e, cliRequest.showErrors );
316 
317             return 2;
318         }
319         catch ( Exception e )
320         {
321             CLIReportingUtils.showError( slf4jLogger, "Error executing Maven.", e, cliRequest.showErrors );
322 
323             return 1;
324         }
325         finally
326         {
327             if ( localContainer != null )
328             {
329                 localContainer.dispose();
330             }
331         }
332     }
333 
334     void initialize( CliRequest cliRequest )
335         throws ExitException
336     {
337         if ( cliRequest.workingDirectory == null )
338         {
339             cliRequest.workingDirectory = System.getProperty( "user.dir" );
340         }
341 
342         if ( cliRequest.multiModuleProjectDirectory == null )
343         {
344             String basedirProperty = System.getProperty( MULTIMODULE_PROJECT_DIRECTORY );
345             if ( basedirProperty == null )
346             {
347                 System.err.format(
348                     "-D%s system property is not set.", MULTIMODULE_PROJECT_DIRECTORY );
349                 throw new ExitException( 1 );
350             }
351             File basedir = basedirProperty != null ? new File( basedirProperty ) : new File( "" );
352             try
353             {
354                 cliRequest.multiModuleProjectDirectory = basedir.getCanonicalFile();
355             }
356             catch ( IOException e )
357             {
358                 cliRequest.multiModuleProjectDirectory = basedir.getAbsoluteFile();
359             }
360         }
361 
362         //
363         // Make sure the Maven home directory is an absolute path to save us from confusion with say drive-relative
364         // Windows paths.
365         //
366         String mavenHome = System.getProperty( "maven.home" );
367 
368         if ( mavenHome != null )
369         {
370             System.setProperty( "maven.home", new File( mavenHome ).getAbsolutePath() );
371         }
372     }
373 
374     void cli( CliRequest cliRequest )
375         throws Exception
376     {
377         //
378         // Parsing errors can happen during the processing of the arguments and we prefer not having to check if
379         // the logger is null and construct this so we can use an SLF4J logger everywhere.
380         //
381         slf4jLogger = new Slf4jStdoutLogger();
382 
383         CLIManager cliManager = new CLIManager();
384 
385         List<String> args = new ArrayList<>();
386         CommandLine mavenConfig = null;
387         try
388         {
389             File configFile = new File( cliRequest.multiModuleProjectDirectory, MVN_MAVEN_CONFIG );
390 
391             if ( configFile.isFile() )
392             {
393                 for ( String arg : Files.toString( configFile, Charsets.UTF_8 ).split( "\\s+" ) )
394                 {
395                     if ( !arg.isEmpty() )
396                     {
397                         args.add( arg );
398                     }
399                 }
400 
401                 mavenConfig = cliManager.parse( args.toArray( new String[args.size()] ) );
402                 List<?> unrecongized = mavenConfig.getArgList();
403                 if ( !unrecongized.isEmpty() )
404                 {
405                     throw new ParseException( "Unrecognized maven.config entries: " + unrecongized );
406                 }
407             }
408         }
409         catch ( ParseException e )
410         {
411             System.err.println( "Unable to parse maven.config: " + e.getMessage() );
412             cliManager.displayHelp( System.out );
413             throw e;
414         }
415 
416         try
417         {
418             if ( mavenConfig == null )
419             {
420                 cliRequest.commandLine = cliManager.parse( cliRequest.args );
421             }
422             else
423             {
424                 cliRequest.commandLine = new MergedCommandLine( cliManager.parse( cliRequest.args ), mavenConfig );
425             }
426         }
427         catch ( ParseException e )
428         {
429             System.err.println( "Unable to parse command line options: " + e.getMessage() );
430             cliManager.displayHelp( System.out );
431             throw e;
432         }
433 
434         if ( cliRequest.commandLine.hasOption( CLIManager.HELP ) )
435         {
436             cliManager.displayHelp( System.out );
437             throw new ExitException( 0 );
438         }
439 
440         if ( cliRequest.commandLine.hasOption( CLIManager.VERSION ) )
441         {
442             System.out.println( CLIReportingUtils.showVersion() );
443             throw new ExitException( 0 );
444         }
445     }
446 
447     /**
448      * configure logging
449      */
450     private void logging( CliRequest cliRequest )
451     {
452         cliRequest.debug = cliRequest.commandLine.hasOption( CLIManager.DEBUG );
453         cliRequest.quiet = !cliRequest.debug && cliRequest.commandLine.hasOption( CLIManager.QUIET );
454         cliRequest.showErrors = cliRequest.debug || cliRequest.commandLine.hasOption( CLIManager.ERRORS );
455 
456         slf4jLoggerFactory = LoggerFactory.getILoggerFactory();
457         Slf4jConfiguration slf4jConfiguration = Slf4jConfigurationFactory.getConfiguration( slf4jLoggerFactory );
458 
459         if ( cliRequest.debug )
460         {
461             cliRequest.request.setLoggingLevel( MavenExecutionRequest.LOGGING_LEVEL_DEBUG );
462             slf4jConfiguration.setRootLoggerLevel( Slf4jConfiguration.Level.DEBUG );
463         }
464         else if ( cliRequest.quiet )
465         {
466             cliRequest.request.setLoggingLevel( MavenExecutionRequest.LOGGING_LEVEL_ERROR );
467             slf4jConfiguration.setRootLoggerLevel( Slf4jConfiguration.Level.ERROR );
468         }
469         // else fall back to default log level specified in conf
470         // see https://issues.apache.org/jira/browse/MNG-2570
471 
472         if ( cliRequest.commandLine.hasOption( CLIManager.BATCH_MODE ) )
473         {
474             MessageUtils.setColorEnabled( false );
475         }
476 
477         if ( cliRequest.commandLine.hasOption( CLIManager.LOG_FILE ) )
478         {
479             File logFile = new File( cliRequest.commandLine.getOptionValue( CLIManager.LOG_FILE ) );
480             logFile = resolveFile( logFile, cliRequest.workingDirectory );
481 
482             MessageUtils.setColorEnabled( false );
483 
484             // redirect stdout and stderr to file
485             try
486             {
487                 PrintStream ps = new PrintStream( new FileOutputStream( logFile ) );
488                 System.setOut( ps );
489                 System.setErr( ps );
490             }
491             catch ( FileNotFoundException e )
492             {
493                 //
494                 // Ignore
495                 //
496             }
497         }
498 
499         slf4jConfiguration.activate();
500 
501         plexusLoggerManager = new Slf4jLoggerManager();
502         slf4jLogger = slf4jLoggerFactory.getLogger( this.getClass().getName() );
503     }
504 
505     private void version( CliRequest cliRequest )
506     {
507         if ( cliRequest.debug || cliRequest.commandLine.hasOption( CLIManager.SHOW_VERSION ) )
508         {
509             System.out.println( CLIReportingUtils.showVersion() );
510         }
511     }
512 
513     private void commands( CliRequest cliRequest )
514     {
515         if ( cliRequest.showErrors )
516         {
517             slf4jLogger.info( "Error stacktraces are turned on." );
518         }
519 
520         if ( MavenExecutionRequest.CHECKSUM_POLICY_WARN.equals( cliRequest.request.getGlobalChecksumPolicy() ) )
521         {
522             slf4jLogger.info( "Disabling strict checksum verification on all artifact downloads." );
523         }
524         else if ( MavenExecutionRequest.CHECKSUM_POLICY_FAIL.equals( cliRequest.request.getGlobalChecksumPolicy() ) )
525         {
526             slf4jLogger.info( "Enabling strict checksum verification on all artifact downloads." );
527         }
528 
529         if ( slf4jLogger.isDebugEnabled() )
530         {
531             slf4jLogger.debug( "Message scheme: " + ( MessageUtils.isColorEnabled() ? "color" : "plain" ) );
532             if ( MessageUtils.isColorEnabled() )
533             {
534                 MessageBuilder buff = MessageUtils.buffer();
535                 buff.a( "Message styles: " );
536                 buff.debug( "debug" ).a( ' ' );
537                 buff.info( "info" ).a( ' ' );
538                 buff.warning( "warning" ).a( ' ' );
539                 buff.error( "error" ).a( ' ' );
540                 buff.success( "success" ).a( ' ' );
541                 buff.failure( "failure" ).a( ' ' );
542                 buff.strong( "strong" ).a( ' ' );
543                 buff.mojo( "mojo" ).a( ' ' );
544                 buff.project( "project" );
545                 slf4jLogger.debug( buff.toString() );
546             }
547         }
548     }
549 
550     //Needed to make this method package visible to make writing a unit test possible
551     //Maybe it's better to move some of those methods to separate class (SoC).
552     void properties( CliRequest cliRequest )
553     {
554         populateProperties( cliRequest.commandLine, cliRequest.systemProperties, cliRequest.userProperties );
555     }
556 
557     private PlexusContainer container( CliRequest cliRequest )
558         throws Exception
559     {
560         if ( cliRequest.classWorld == null )
561         {
562             cliRequest.classWorld = new ClassWorld( "plexus.core", Thread.currentThread().getContextClassLoader() );
563         }
564 
565         ClassRealm coreRealm = cliRequest.classWorld.getClassRealm( "plexus.core" );
566         if ( coreRealm == null )
567         {
568             coreRealm = cliRequest.classWorld.getRealms().iterator().next();
569         }
570 
571         List<File> extClassPath = parseExtClasspath( cliRequest );
572 
573         CoreExtensionEntry coreEntry = CoreExtensionEntry.discoverFrom( coreRealm );
574         List<CoreExtensionEntry> extensions =
575             loadCoreExtensions( cliRequest, coreRealm, coreEntry.getExportedArtifacts() );
576 
577         ClassRealm containerRealm = setupContainerRealm( cliRequest.classWorld, coreRealm, extClassPath, extensions );
578 
579         ContainerConfiguration cc = new DefaultContainerConfiguration().setClassWorld( cliRequest.classWorld ).setRealm(
580             containerRealm ).setClassPathScanning( PlexusConstants.SCANNING_INDEX ).setAutoWiring( true ).setName(
581             "maven" );
582 
583         Set<String> exportedArtifacts = new HashSet<>( coreEntry.getExportedArtifacts() );
584         Set<String> exportedPackages = new HashSet<>( coreEntry.getExportedPackages() );
585         for ( CoreExtensionEntry extension : extensions )
586         {
587             exportedArtifacts.addAll( extension.getExportedArtifacts() );
588             exportedPackages.addAll( extension.getExportedPackages() );
589         }
590 
591         final CoreExports exports = new CoreExports( containerRealm, exportedArtifacts, exportedPackages );
592 
593         DefaultPlexusContainer container = new DefaultPlexusContainer( cc, new AbstractModule()
594         {
595             @Override
596             protected void configure()
597             {
598                 bind( ILoggerFactory.class ).toInstance( slf4jLoggerFactory );
599                 bind( CoreExports.class ).toInstance( exports );
600             }
601         } );
602 
603         // NOTE: To avoid inconsistencies, we'll use the TCCL exclusively for lookups
604         container.setLookupRealm( null );
605 
606         container.setLoggerManager( plexusLoggerManager );
607 
608         for ( CoreExtensionEntry extension : extensions )
609         {
610             container.discoverComponents( extension.getClassRealm() );
611         }
612 
613         customizeContainer( container );
614 
615         container.getLoggerManager().setThresholds( cliRequest.request.getLoggingLevel() );
616 
617         Thread.currentThread().setContextClassLoader( container.getContainerRealm() );
618 
619         eventSpyDispatcher = container.lookup( EventSpyDispatcher.class );
620 
621         DefaultEventSpyContext eventSpyContext = new DefaultEventSpyContext();
622         Map<String, Object> data = eventSpyContext.getData();
623         data.put( "plexus", container );
624         data.put( "workingDirectory", cliRequest.workingDirectory );
625         data.put( "systemProperties", cliRequest.systemProperties );
626         data.put( "userProperties", cliRequest.userProperties );
627         data.put( "versionProperties", CLIReportingUtils.getBuildProperties() );
628         eventSpyDispatcher.init( eventSpyContext );
629 
630         // refresh logger in case container got customized by spy
631         slf4jLogger = slf4jLoggerFactory.getLogger( this.getClass().getName() );
632 
633         maven = container.lookup( Maven.class );
634 
635         executionRequestPopulator = container.lookup( MavenExecutionRequestPopulator.class );
636 
637         modelProcessor = createModelProcessor( container );
638 
639         configurationProcessors = container.lookupMap( ConfigurationProcessor.class );
640 
641         toolchainsBuilder = container.lookup( ToolchainsBuilder.class );
642 
643         dispatcher = (DefaultSecDispatcher) container.lookup( SecDispatcher.class, "maven" );
644 
645         return container;
646     }
647 
648     private List<CoreExtensionEntry> loadCoreExtensions( CliRequest cliRequest, ClassRealm containerRealm,
649                                                          Set<String> providedArtifacts )
650     {
651         if ( cliRequest.multiModuleProjectDirectory == null )
652         {
653             return Collections.emptyList();
654         }
655 
656         File extensionsFile = new File( cliRequest.multiModuleProjectDirectory, EXTENSIONS_FILENAME );
657         if ( !extensionsFile.isFile() )
658         {
659             return Collections.emptyList();
660         }
661 
662         try
663         {
664             List<CoreExtension> extensions = readCoreExtensionsDescriptor( extensionsFile );
665             if ( extensions.isEmpty() )
666             {
667                 return Collections.emptyList();
668             }
669 
670             ContainerConfiguration cc = new DefaultContainerConfiguration() //
671                 .setClassWorld( cliRequest.classWorld ) //
672                 .setRealm( containerRealm ) //
673                 .setClassPathScanning( PlexusConstants.SCANNING_INDEX ) //
674                 .setAutoWiring( true ) //
675                 .setName( "maven" );
676 
677             DefaultPlexusContainer container = new DefaultPlexusContainer( cc, new AbstractModule()
678             {
679                 @Override
680                 protected void configure()
681                 {
682                     bind( ILoggerFactory.class ).toInstance( slf4jLoggerFactory );
683                 }
684             } );
685 
686             try
687             {
688                 container.setLookupRealm( null );
689 
690                 container.setLoggerManager( plexusLoggerManager );
691 
692                 container.getLoggerManager().setThresholds( cliRequest.request.getLoggingLevel() );
693 
694                 Thread.currentThread().setContextClassLoader( container.getContainerRealm() );
695 
696                 executionRequestPopulator = container.lookup( MavenExecutionRequestPopulator.class );
697 
698                 configurationProcessors = container.lookupMap( ConfigurationProcessor.class );
699 
700                 configure( cliRequest );
701 
702                 MavenExecutionRequest request = DefaultMavenExecutionRequest.copy( cliRequest.request );
703 
704                 request = populateRequest( cliRequest, request );
705 
706                 request = executionRequestPopulator.populateDefaults( request );
707 
708                 BootstrapCoreExtensionManager resolver = container.lookup( BootstrapCoreExtensionManager.class );
709 
710                 return resolver.loadCoreExtensions( request, providedArtifacts, extensions );
711             }
712             finally
713             {
714                 executionRequestPopulator = null;
715                 container.dispose();
716             }
717         }
718         catch ( RuntimeException e )
719         {
720             // runtime exceptions are most likely bugs in maven, let them bubble up to the user
721             throw e;
722         }
723         catch ( Exception e )
724         {
725             slf4jLogger.warn( "Failed to read extensions descriptor " + extensionsFile + ": " + e.getMessage() );
726         }
727         return Collections.emptyList();
728     }
729 
730     private List<CoreExtension> readCoreExtensionsDescriptor( File extensionsFile )
731         throws IOException, XmlPullParserException
732     {
733         CoreExtensionsXpp3Reader parser = new CoreExtensionsXpp3Reader();
734 
735         try ( InputStream is = new BufferedInputStream( new FileInputStream( extensionsFile ) ) )
736         {
737 
738             return parser.read( is ).getExtensions();
739         }
740 
741     }
742 
743     private ClassRealm setupContainerRealm( ClassWorld classWorld, ClassRealm coreRealm, List<File> extClassPath,
744                                             List<CoreExtensionEntry> extensions )
745         throws Exception
746     {
747         if ( !extClassPath.isEmpty() || !extensions.isEmpty() )
748         {
749             ClassRealm extRealm = classWorld.newRealm( "maven.ext", null );
750 
751             extRealm.setParentRealm( coreRealm );
752 
753             slf4jLogger.debug( "Populating class realm " + extRealm.getId() );
754 
755             for ( File file : extClassPath )
756             {
757                 slf4jLogger.debug( "  Included " + file );
758 
759                 extRealm.addURL( file.toURI().toURL() );
760             }
761 
762             for ( CoreExtensionEntry entry : reverse( extensions ) )
763             {
764                 Set<String> exportedPackages = entry.getExportedPackages();
765                 ClassRealm realm = entry.getClassRealm();
766                 for ( String exportedPackage : exportedPackages )
767                 {
768                     extRealm.importFrom( realm, exportedPackage );
769                 }
770                 if ( exportedPackages.isEmpty() )
771                 {
772                     // sisu uses realm imports to establish component visibility
773                     extRealm.importFrom( realm, realm.getId() );
774                 }
775             }
776 
777             return extRealm;
778         }
779 
780         return coreRealm;
781     }
782 
783     private static <T> List<T> reverse( List<T> list )
784     {
785         List<T> copy = new ArrayList<>( list );
786         Collections.reverse( copy );
787         return copy;
788     }
789 
790     private List<File> parseExtClasspath( CliRequest cliRequest )
791     {
792         String extClassPath = cliRequest.userProperties.getProperty( EXT_CLASS_PATH );
793         if ( extClassPath == null )
794         {
795             extClassPath = cliRequest.systemProperties.getProperty( EXT_CLASS_PATH );
796         }
797 
798         List<File> jars = new ArrayList<>();
799 
800         if ( StringUtils.isNotEmpty( extClassPath ) )
801         {
802             for ( String jar : StringUtils.split( extClassPath, File.pathSeparator ) )
803             {
804                 File file = resolveFile( new File( jar ), cliRequest.workingDirectory );
805 
806                 slf4jLogger.debug( "  Included " + file );
807 
808                 jars.add( file );
809             }
810         }
811 
812         return jars;
813     }
814 
815     //
816     // This should probably be a separate tool and not be baked into Maven.
817     //
818     private void encryption( CliRequest cliRequest )
819         throws Exception
820     {
821         if ( cliRequest.commandLine.hasOption( CLIManager.ENCRYPT_MASTER_PASSWORD ) )
822         {
823             String passwd = cliRequest.commandLine.getOptionValue( CLIManager.ENCRYPT_MASTER_PASSWORD );
824 
825             if ( passwd == null )
826             {
827                 Console cons = System.console();
828                 char[] password = ( cons == null ) ? null : cons.readPassword( "Master password: " );
829                 if ( password != null )
830                 {
831                     // Cipher uses Strings
832                     passwd = String.copyValueOf( password );
833 
834                     // Sun/Oracle advises to empty the char array
835                     java.util.Arrays.fill( password, ' ' );
836                 }
837             }
838 
839             DefaultPlexusCipher cipher = new DefaultPlexusCipher();
840 
841             System.out.println(
842                 cipher.encryptAndDecorate( passwd, DefaultSecDispatcher.SYSTEM_PROPERTY_SEC_LOCATION ) );
843 
844             throw new ExitException( 0 );
845         }
846         else if ( cliRequest.commandLine.hasOption( CLIManager.ENCRYPT_PASSWORD ) )
847         {
848             String passwd = cliRequest.commandLine.getOptionValue( CLIManager.ENCRYPT_PASSWORD );
849 
850             if ( passwd == null )
851             {
852                 Console cons = System.console();
853                 char[] password = ( cons == null ) ? null : cons.readPassword( "Password: " );
854                 if ( password != null )
855                 {
856                     // Cipher uses Strings
857                     passwd = String.copyValueOf( password );
858 
859                     // Sun/Oracle advises to empty the char array
860                     java.util.Arrays.fill( password, ' ' );
861                 }
862             }
863 
864             String configurationFile = dispatcher.getConfigurationFile();
865 
866             if ( configurationFile.startsWith( "~" ) )
867             {
868                 configurationFile = System.getProperty( "user.home" ) + configurationFile.substring( 1 );
869             }
870 
871             String file = System.getProperty( DefaultSecDispatcher.SYSTEM_PROPERTY_SEC_LOCATION, configurationFile );
872 
873             String master = null;
874 
875             SettingsSecurity sec = SecUtil.read( file, true );
876             if ( sec != null )
877             {
878                 master = sec.getMaster();
879             }
880 
881             if ( master == null )
882             {
883                 throw new IllegalStateException( "Master password is not set in the setting security file: " + file );
884             }
885 
886             DefaultPlexusCipher cipher = new DefaultPlexusCipher();
887             String masterPasswd = cipher.decryptDecorated( master, DefaultSecDispatcher.SYSTEM_PROPERTY_SEC_LOCATION );
888             System.out.println( cipher.encryptAndDecorate( passwd, masterPasswd ) );
889 
890             throw new ExitException( 0 );
891         }
892     }
893 
894     private void repository( CliRequest cliRequest )
895         throws Exception
896     {
897         if ( cliRequest.commandLine.hasOption( CLIManager.LEGACY_LOCAL_REPOSITORY ) || Boolean.getBoolean(
898             "maven.legacyLocalRepo" ) )
899         {
900             cliRequest.request.setUseLegacyLocalRepository( true );
901         }
902     }
903 
904     private int execute( CliRequest cliRequest )
905         throws MavenExecutionRequestPopulationException
906     {
907         MavenExecutionRequest request = executionRequestPopulator.populateDefaults( cliRequest.request );
908 
909         eventSpyDispatcher.onEvent( request );
910 
911         MavenExecutionResult result = maven.execute( request );
912 
913         eventSpyDispatcher.onEvent( result );
914 
915         eventSpyDispatcher.close();
916 
917         if ( result.hasExceptions() )
918         {
919             ExceptionHandler handler = new DefaultExceptionHandler();
920 
921             Map<String, String> references = new LinkedHashMap<>();
922 
923             MavenProject project = null;
924 
925             for ( Throwable exception : result.getExceptions() )
926             {
927                 ExceptionSummary summary = handler.handleException( exception );
928 
929                 logSummary( summary, references, "", cliRequest.showErrors );
930 
931                 if ( project == null && exception instanceof LifecycleExecutionException )
932                 {
933                     project = ( (LifecycleExecutionException) exception ).getProject();
934                 }
935             }
936 
937             slf4jLogger.error( "" );
938 
939             if ( !cliRequest.showErrors )
940             {
941                 slf4jLogger.error( "To see the full stack trace of the errors, re-run Maven with the "
942                     + buffer().strong( "-e" ) + " switch." );
943             }
944             if ( !slf4jLogger.isDebugEnabled() )
945             {
946                 slf4jLogger.error( "Re-run Maven using the " + buffer().strong( "-X" )
947                     + " switch to enable full debug logging." );
948             }
949 
950             if ( !references.isEmpty() )
951             {
952                 slf4jLogger.error( "" );
953                 slf4jLogger.error( "For more information about the errors and possible solutions"
954                                        + ", please read the following articles:" );
955 
956                 for ( Map.Entry<String, String> entry : references.entrySet() )
957                 {
958                     slf4jLogger.error( buffer().strong( entry.getValue() ) + " " + entry.getKey() );
959                 }
960             }
961 
962             if ( project != null && !project.equals( result.getTopologicallySortedProjects().get( 0 ) ) )
963             {
964                 slf4jLogger.error( "" );
965                 slf4jLogger.error( "After correcting the problems, you can resume the build with the command" );
966                 slf4jLogger.error( buffer().a( "  " ).strong( "mvn <goals> -rf :"
967                                 + project.getArtifactId() ).toString() );
968             }
969 
970             if ( MavenExecutionRequest.REACTOR_FAIL_NEVER.equals( cliRequest.request.getReactorFailureBehavior() ) )
971             {
972                 slf4jLogger.info( "Build failures were ignored." );
973 
974                 return 0;
975             }
976             else
977             {
978                 return 1;
979             }
980         }
981         else
982         {
983             return 0;
984         }
985     }
986 
987     private void logSummary( ExceptionSummary summary, Map<String, String> references, String indent,
988                              boolean showErrors )
989     {
990         String referenceKey = "";
991 
992         if ( StringUtils.isNotEmpty( summary.getReference() ) )
993         {
994             referenceKey = references.get( summary.getReference() );
995             if ( referenceKey == null )
996             {
997                 referenceKey = "[Help " + ( references.size() + 1 ) + "]";
998                 references.put( summary.getReference(), referenceKey );
999             }
1000         }
1001 
1002         String msg = summary.getMessage();
1003 
1004         if ( StringUtils.isNotEmpty( referenceKey ) )
1005         {
1006             if ( msg.indexOf( '\n' ) < 0 )
1007             {
1008                 msg += " -> " + buffer().strong( referenceKey );
1009             }
1010             else
1011             {
1012                 msg += "\n-> " + buffer().strong( referenceKey );
1013             }
1014         }
1015 
1016         String[] lines = msg.split( "(\r\n)|(\r)|(\n)" );
1017         String currentColor = "";
1018 
1019         for ( int i = 0; i < lines.length; i++ )
1020         {
1021             // add eventual current color inherited from previous line 
1022             String line = currentColor + lines[i];
1023 
1024             // look for last ANSI escape sequence to check if nextColor
1025             Matcher matcher = LAST_ANSI_SEQUENCE.matcher( line );
1026             String nextColor = "";
1027             if ( matcher.find() )
1028             {
1029                 nextColor = matcher.group( 1 );
1030                 if ( ANSI_RESET.equals( nextColor ) )
1031                 {
1032                     // last ANSI escape code is reset: no next color
1033                     nextColor = "";
1034                 }
1035             }
1036 
1037             // effective line, with indent and reset if end is colored
1038             line = indent + line + ( "".equals( nextColor ) ? "" : ANSI_RESET );
1039 
1040             if ( ( i == lines.length - 1 ) && ( showErrors
1041                 || ( summary.getException() instanceof InternalErrorException ) ) )
1042             {
1043                 slf4jLogger.error( line, summary.getException() );
1044             }
1045             else
1046             {
1047                 slf4jLogger.error( line );
1048             }
1049 
1050             currentColor = nextColor;
1051         }
1052 
1053         indent += "  ";
1054 
1055         for ( ExceptionSummary child : summary.getChildren() )
1056         {
1057             logSummary( child, references, indent, showErrors );
1058         }
1059     }
1060 
1061     private static final Pattern LAST_ANSI_SEQUENCE = Pattern.compile( "(\u001B\\[[;\\d]*[ -/]*[@-~])[^\u001B]*$" );
1062 
1063     private static final String ANSI_RESET = "\u001B\u005Bm";
1064 
1065     private void configure( CliRequest cliRequest )
1066         throws Exception
1067     {
1068         //
1069         // This is not ideal but there are events specifically for configuration from the CLI which I don't
1070         // believe are really valid but there are ITs which assert the right events are published so this
1071         // needs to be supported so the EventSpyDispatcher needs to be put in the CliRequest so that
1072         // it can be accessed by configuration processors.
1073         //
1074         cliRequest.request.setEventSpyDispatcher( eventSpyDispatcher );
1075 
1076         //
1077         // We expect at most 2 implementations to be available. The SettingsXmlConfigurationProcessor implementation
1078         // is always available in the core and likely always will be, but we may have another ConfigurationProcessor
1079         // present supplied by the user. The rule is that we only allow the execution of one ConfigurationProcessor.
1080         // If there is more than one then we execute the one supplied by the user, otherwise we execute the
1081         // the default SettingsXmlConfigurationProcessor.
1082         //
1083         int userSuppliedConfigurationProcessorCount = configurationProcessors.size() - 1;
1084 
1085         if ( userSuppliedConfigurationProcessorCount == 0 )
1086         {
1087             //
1088             // Our settings.xml source is historically how we have configured Maven from the CLI so we are going to
1089             // have to honour its existence forever. So let's run it.
1090             //
1091             configurationProcessors.get( SettingsXmlConfigurationProcessor.HINT ).process( cliRequest );
1092         }
1093         else if ( userSuppliedConfigurationProcessorCount == 1 )
1094         {
1095             //
1096             // Run the user supplied ConfigurationProcessor
1097             //
1098             for ( Entry<String, ConfigurationProcessor> entry : configurationProcessors.entrySet() )
1099             {
1100                 String hint = entry.getKey();
1101                 if ( !hint.equals( SettingsXmlConfigurationProcessor.HINT ) )
1102                 {
1103                     ConfigurationProcessor configurationProcessor = entry.getValue();
1104                     configurationProcessor.process( cliRequest );
1105                 }
1106             }
1107         }
1108         else if ( userSuppliedConfigurationProcessorCount > 1 )
1109         {
1110             //
1111             // There are too many ConfigurationProcessors so we don't know which one to run so report the error.
1112             //
1113             StringBuilder sb = new StringBuilder(
1114                 String.format( "\nThere can only be one user supplied ConfigurationProcessor, there are %s:\n\n",
1115                                userSuppliedConfigurationProcessorCount ) );
1116             for ( Entry<String, ConfigurationProcessor> entry : configurationProcessors.entrySet() )
1117             {
1118                 String hint = entry.getKey();
1119                 if ( !hint.equals( SettingsXmlConfigurationProcessor.HINT ) )
1120                 {
1121                     ConfigurationProcessor configurationProcessor = entry.getValue();
1122                     sb.append( String.format( "%s\n", configurationProcessor.getClass().getName() ) );
1123                 }
1124             }
1125             sb.append( String.format( "\n" ) );
1126             throw new Exception( sb.toString() );
1127         }
1128     }
1129 
1130     private void toolchains( CliRequest cliRequest )
1131         throws Exception
1132     {
1133         File userToolchainsFile;
1134 
1135         if ( cliRequest.commandLine.hasOption( CLIManager.ALTERNATE_USER_TOOLCHAINS ) )
1136         {
1137             userToolchainsFile =
1138                 new File( cliRequest.commandLine.getOptionValue( CLIManager.ALTERNATE_USER_TOOLCHAINS ) );
1139             userToolchainsFile = resolveFile( userToolchainsFile, cliRequest.workingDirectory );
1140 
1141             if ( !userToolchainsFile.isFile() )
1142             {
1143                 throw new FileNotFoundException(
1144                     "The specified user toolchains file does not exist: " + userToolchainsFile );
1145             }
1146         }
1147         else
1148         {
1149             userToolchainsFile = DEFAULT_USER_TOOLCHAINS_FILE;
1150         }
1151 
1152         File globalToolchainsFile;
1153 
1154         if ( cliRequest.commandLine.hasOption( CLIManager.ALTERNATE_GLOBAL_TOOLCHAINS ) )
1155         {
1156             globalToolchainsFile =
1157                 new File( cliRequest.commandLine.getOptionValue( CLIManager.ALTERNATE_GLOBAL_TOOLCHAINS ) );
1158             globalToolchainsFile = resolveFile( globalToolchainsFile, cliRequest.workingDirectory );
1159 
1160             if ( !globalToolchainsFile.isFile() )
1161             {
1162                 throw new FileNotFoundException(
1163                     "The specified global toolchains file does not exist: " + globalToolchainsFile );
1164             }
1165         }
1166         else
1167         {
1168             globalToolchainsFile = DEFAULT_GLOBAL_TOOLCHAINS_FILE;
1169         }
1170 
1171         cliRequest.request.setGlobalToolchainsFile( globalToolchainsFile );
1172         cliRequest.request.setUserToolchainsFile( userToolchainsFile );
1173 
1174         DefaultToolchainsBuildingRequest toolchainsRequest = new DefaultToolchainsBuildingRequest();
1175         if ( globalToolchainsFile.isFile() )
1176         {
1177             toolchainsRequest.setGlobalToolchainsSource( new FileSource( globalToolchainsFile ) );
1178         }
1179         if ( userToolchainsFile.isFile() )
1180         {
1181             toolchainsRequest.setUserToolchainsSource( new FileSource( userToolchainsFile ) );
1182         }
1183 
1184         eventSpyDispatcher.onEvent( toolchainsRequest );
1185 
1186         slf4jLogger.debug(
1187             "Reading global toolchains from " + getLocation( toolchainsRequest.getGlobalToolchainsSource(),
1188                                                              globalToolchainsFile ) );
1189         slf4jLogger.debug( "Reading user toolchains from " + getLocation( toolchainsRequest.getUserToolchainsSource(),
1190                                                                           userToolchainsFile ) );
1191 
1192         ToolchainsBuildingResult toolchainsResult = toolchainsBuilder.build( toolchainsRequest );
1193 
1194         eventSpyDispatcher.onEvent( toolchainsRequest );
1195 
1196         executionRequestPopulator.populateFromToolchains( cliRequest.request,
1197                                                           toolchainsResult.getEffectiveToolchains() );
1198 
1199         if ( !toolchainsResult.getProblems().isEmpty() && slf4jLogger.isWarnEnabled() )
1200         {
1201             slf4jLogger.warn( "" );
1202             slf4jLogger.warn( "Some problems were encountered while building the effective toolchains" );
1203 
1204             for ( Problem problem : toolchainsResult.getProblems() )
1205             {
1206                 slf4jLogger.warn( problem.getMessage() + " @ " + problem.getLocation() );
1207             }
1208 
1209             slf4jLogger.warn( "" );
1210         }
1211     }
1212 
1213     private Object getLocation( Source source, File defaultLocation )
1214     {
1215         if ( source != null )
1216         {
1217             return source.getLocation();
1218         }
1219         return defaultLocation;
1220     }
1221 
1222     private MavenExecutionRequest populateRequest( CliRequest cliRequest )
1223     {
1224         return populateRequest( cliRequest, cliRequest.request );
1225     }
1226 
1227     private MavenExecutionRequest populateRequest( CliRequest cliRequest, MavenExecutionRequest request )
1228     {
1229         CommandLine commandLine = cliRequest.commandLine;
1230         String workingDirectory = cliRequest.workingDirectory;
1231         boolean quiet = cliRequest.quiet;
1232         boolean showErrors = cliRequest.showErrors;
1233 
1234         String[] deprecatedOptions = { "up", "npu", "cpu", "npr" };
1235         for ( String deprecatedOption : deprecatedOptions )
1236         {
1237             if ( commandLine.hasOption( deprecatedOption ) )
1238             {
1239                 slf4jLogger.warn( "Command line option -" + deprecatedOption
1240                                       + " is deprecated and will be removed in future Maven versions." );
1241             }
1242         }
1243 
1244         // ----------------------------------------------------------------------
1245         // Now that we have everything that we need we will fire up plexus and
1246         // bring the maven component to life for use.
1247         // ----------------------------------------------------------------------
1248 
1249         if ( commandLine.hasOption( CLIManager.BATCH_MODE ) )
1250         {
1251             request.setInteractiveMode( false );
1252         }
1253 
1254         boolean noSnapshotUpdates = false;
1255         if ( commandLine.hasOption( CLIManager.SUPRESS_SNAPSHOT_UPDATES ) )
1256         {
1257             noSnapshotUpdates = true;
1258         }
1259 
1260         // ----------------------------------------------------------------------
1261         //
1262         // ----------------------------------------------------------------------
1263 
1264         @SuppressWarnings( "unchecked" ) List<String> goals = commandLine.getArgList();
1265 
1266         boolean recursive = true;
1267 
1268         // this is the default behavior.
1269         String reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_FAST;
1270 
1271         if ( commandLine.hasOption( CLIManager.NON_RECURSIVE ) )
1272         {
1273             recursive = false;
1274         }
1275 
1276         if ( commandLine.hasOption( CLIManager.FAIL_FAST ) )
1277         {
1278             reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_FAST;
1279         }
1280         else if ( commandLine.hasOption( CLIManager.FAIL_AT_END ) )
1281         {
1282             reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_AT_END;
1283         }
1284         else if ( commandLine.hasOption( CLIManager.FAIL_NEVER ) )
1285         {
1286             reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_NEVER;
1287         }
1288 
1289         if ( commandLine.hasOption( CLIManager.OFFLINE ) )
1290         {
1291             request.setOffline( true );
1292         }
1293 
1294         boolean updateSnapshots = false;
1295 
1296         if ( commandLine.hasOption( CLIManager.UPDATE_SNAPSHOTS ) )
1297         {
1298             updateSnapshots = true;
1299         }
1300 
1301         String globalChecksumPolicy = null;
1302 
1303         if ( commandLine.hasOption( CLIManager.CHECKSUM_FAILURE_POLICY ) )
1304         {
1305             globalChecksumPolicy = MavenExecutionRequest.CHECKSUM_POLICY_FAIL;
1306         }
1307         else if ( commandLine.hasOption( CLIManager.CHECKSUM_WARNING_POLICY ) )
1308         {
1309             globalChecksumPolicy = MavenExecutionRequest.CHECKSUM_POLICY_WARN;
1310         }
1311 
1312         File baseDirectory = new File( workingDirectory, "" ).getAbsoluteFile();
1313 
1314         // ----------------------------------------------------------------------
1315         // Profile Activation
1316         // ----------------------------------------------------------------------
1317 
1318         List<String> activeProfiles = new ArrayList<>();
1319 
1320         List<String> inactiveProfiles = new ArrayList<>();
1321 
1322         if ( commandLine.hasOption( CLIManager.ACTIVATE_PROFILES ) )
1323         {
1324             String[] profileOptionValues = commandLine.getOptionValues( CLIManager.ACTIVATE_PROFILES );
1325             if ( profileOptionValues != null )
1326             {
1327                 for ( String profileOptionValue : profileOptionValues )
1328                 {
1329                     StringTokenizer profileTokens = new StringTokenizer( profileOptionValue, "," );
1330 
1331                     while ( profileTokens.hasMoreTokens() )
1332                     {
1333                         String profileAction = profileTokens.nextToken().trim();
1334 
1335                         if ( profileAction.startsWith( "-" ) || profileAction.startsWith( "!" ) )
1336                         {
1337                             inactiveProfiles.add( profileAction.substring( 1 ) );
1338                         }
1339                         else if ( profileAction.startsWith( "+" ) )
1340                         {
1341                             activeProfiles.add( profileAction.substring( 1 ) );
1342                         }
1343                         else
1344                         {
1345                             activeProfiles.add( profileAction );
1346                         }
1347                     }
1348                 }
1349             }
1350         }
1351 
1352         TransferListener transferListener;
1353 
1354         if ( quiet )
1355         {
1356             transferListener = new QuietMavenTransferListener();
1357         }
1358         else if ( request.isInteractiveMode() && !cliRequest.commandLine.hasOption( CLIManager.LOG_FILE ) )
1359         {
1360             //
1361             // If we're logging to a file then we don't want the console transfer listener as it will spew
1362             // download progress all over the place
1363             //
1364             transferListener = getConsoleTransferListener( cliRequest.commandLine.hasOption( CLIManager.DEBUG ) );
1365         }
1366         else
1367         {
1368             transferListener = getBatchTransferListener();
1369         }
1370 
1371         ExecutionListener executionListener = new ExecutionEventLogger();
1372         if ( eventSpyDispatcher != null )
1373         {
1374             executionListener = eventSpyDispatcher.chainListener( executionListener );
1375         }
1376 
1377         String alternatePomFile = null;
1378         if ( commandLine.hasOption( CLIManager.ALTERNATE_POM_FILE ) )
1379         {
1380             alternatePomFile = commandLine.getOptionValue( CLIManager.ALTERNATE_POM_FILE );
1381         }
1382 
1383         File userToolchainsFile;
1384         if ( commandLine.hasOption( CLIManager.ALTERNATE_USER_TOOLCHAINS ) )
1385         {
1386             userToolchainsFile = new File( commandLine.getOptionValue( CLIManager.ALTERNATE_USER_TOOLCHAINS ) );
1387             userToolchainsFile = resolveFile( userToolchainsFile, workingDirectory );
1388         }
1389         else
1390         {
1391             userToolchainsFile = MavenCli.DEFAULT_USER_TOOLCHAINS_FILE;
1392         }
1393 
1394         request.setBaseDirectory( baseDirectory ).setGoals( goals ).setSystemProperties(
1395             cliRequest.systemProperties ).setUserProperties( cliRequest.userProperties ).setReactorFailureBehavior(
1396             reactorFailureBehaviour ) // default: fail fast
1397             .setRecursive( recursive ) // default: true
1398             .setShowErrors( showErrors ) // default: false
1399             .addActiveProfiles( activeProfiles ) // optional
1400             .addInactiveProfiles( inactiveProfiles ) // optional
1401             .setExecutionListener( executionListener ).setTransferListener(
1402             transferListener ) // default: batch mode which goes along with interactive
1403             .setUpdateSnapshots( updateSnapshots ) // default: false
1404             .setNoSnapshotUpdates( noSnapshotUpdates ) // default: false
1405             .setGlobalChecksumPolicy( globalChecksumPolicy ) // default: warn
1406             .setMultiModuleProjectDirectory( cliRequest.multiModuleProjectDirectory );
1407 
1408         if ( alternatePomFile != null )
1409         {
1410             File pom = resolveFile( new File( alternatePomFile ), workingDirectory );
1411             if ( pom.isDirectory() )
1412             {
1413                 pom = new File( pom, "pom.xml" );
1414             }
1415 
1416             request.setPom( pom );
1417         }
1418         else if ( modelProcessor != null )
1419         {
1420             File pom = modelProcessor.locatePom( baseDirectory );
1421 
1422             if ( pom.isFile() )
1423             {
1424                 request.setPom( pom );
1425             }
1426         }
1427 
1428         if ( ( request.getPom() != null ) && ( request.getPom().getParentFile() != null ) )
1429         {
1430             request.setBaseDirectory( request.getPom().getParentFile() );
1431         }
1432 
1433         if ( commandLine.hasOption( CLIManager.RESUME_FROM ) )
1434         {
1435             request.setResumeFrom( commandLine.getOptionValue( CLIManager.RESUME_FROM ) );
1436         }
1437 
1438         if ( commandLine.hasOption( CLIManager.PROJECT_LIST ) )
1439         {
1440             String[] projectOptionValues = commandLine.getOptionValues( CLIManager.PROJECT_LIST );
1441 
1442             List<String> inclProjects = new ArrayList<>();
1443             List<String> exclProjects = new ArrayList<>();
1444 
1445             if ( projectOptionValues != null )
1446             {
1447                 for ( String projectOptionValue : projectOptionValues )
1448                 {
1449                     StringTokenizer projectTokens = new StringTokenizer( projectOptionValue, "," );
1450 
1451                     while ( projectTokens.hasMoreTokens() )
1452                     {
1453                         String projectAction = projectTokens.nextToken().trim();
1454 
1455                         if ( projectAction.startsWith( "-" ) || projectAction.startsWith( "!" ) )
1456                         {
1457                             exclProjects.add( projectAction.substring( 1 ) );
1458                         }
1459                         else if ( projectAction.startsWith( "+" ) )
1460                         {
1461                             inclProjects.add( projectAction.substring( 1 ) );
1462                         }
1463                         else
1464                         {
1465                             inclProjects.add( projectAction );
1466                         }
1467                     }
1468                 }
1469             }
1470 
1471             request.setSelectedProjects( inclProjects );
1472             request.setExcludedProjects( exclProjects );
1473         }
1474 
1475         if ( commandLine.hasOption( CLIManager.ALSO_MAKE ) && !commandLine.hasOption(
1476             CLIManager.ALSO_MAKE_DEPENDENTS ) )
1477         {
1478             request.setMakeBehavior( MavenExecutionRequest.REACTOR_MAKE_UPSTREAM );
1479         }
1480         else if ( !commandLine.hasOption( CLIManager.ALSO_MAKE ) && commandLine.hasOption(
1481             CLIManager.ALSO_MAKE_DEPENDENTS ) )
1482         {
1483             request.setMakeBehavior( MavenExecutionRequest.REACTOR_MAKE_DOWNSTREAM );
1484         }
1485         else if ( commandLine.hasOption( CLIManager.ALSO_MAKE ) && commandLine.hasOption(
1486             CLIManager.ALSO_MAKE_DEPENDENTS ) )
1487         {
1488             request.setMakeBehavior( MavenExecutionRequest.REACTOR_MAKE_BOTH );
1489         }
1490 
1491         String localRepoProperty = request.getUserProperties().getProperty( MavenCli.LOCAL_REPO_PROPERTY );
1492 
1493         if ( localRepoProperty == null )
1494         {
1495             localRepoProperty = request.getSystemProperties().getProperty( MavenCli.LOCAL_REPO_PROPERTY );
1496         }
1497 
1498         if ( localRepoProperty != null )
1499         {
1500             request.setLocalRepositoryPath( localRepoProperty );
1501         }
1502 
1503         request.setCacheNotFound( true );
1504         request.setCacheTransferError( false );
1505 
1506         //
1507         // Builder, concurrency and parallelism
1508         //
1509         // We preserve the existing methods for builder selection which is to look for various inputs in the threading
1510         // configuration. We don't have an easy way to allow a pluggable builder to provide its own configuration
1511         // parameters but this is sufficient for now. Ultimately we want components like Builders to provide a way to
1512         // extend the command line to accept its own configuration parameters.
1513         //
1514         final String threadConfiguration = commandLine.hasOption( CLIManager.THREADS )
1515             ? commandLine.getOptionValue( CLIManager.THREADS )
1516             : request.getSystemProperties().getProperty(
1517                 MavenCli.THREADS_DEPRECATED ); // TODO Remove this setting. Note that the int-tests use it
1518 
1519         if ( threadConfiguration != null )
1520         {
1521             //
1522             // Default to the standard multithreaded builder
1523             //
1524             request.setBuilderId( "multithreaded" );
1525 
1526             if ( threadConfiguration.contains( "C" ) )
1527             {
1528                 request.setDegreeOfConcurrency( calculateDegreeOfConcurrencyWithCoreMultiplier( threadConfiguration ) );
1529             }
1530             else
1531             {
1532                 request.setDegreeOfConcurrency( Integer.valueOf( threadConfiguration ) );
1533             }
1534         }
1535 
1536         //
1537         // Allow the builder to be overridden by the user if requested. The builders are now pluggable.
1538         //
1539         if ( commandLine.hasOption( CLIManager.BUILDER ) )
1540         {
1541             request.setBuilderId( commandLine.getOptionValue( CLIManager.BUILDER ) );
1542         }
1543 
1544         return request;
1545     }
1546 
1547     int calculateDegreeOfConcurrencyWithCoreMultiplier( String threadConfiguration )
1548     {
1549         int procs = Runtime.getRuntime().availableProcessors();
1550         return (int) ( Float.valueOf( threadConfiguration.replace( "C", "" ) ) * procs );
1551     }
1552 
1553     static File resolveFile( File file, String workingDirectory )
1554     {
1555         if ( file == null )
1556         {
1557             return null;
1558         }
1559         else if ( file.isAbsolute() )
1560         {
1561             return file;
1562         }
1563         else if ( file.getPath().startsWith( File.separator ) )
1564         {
1565             // drive-relative Windows path
1566             return file.getAbsoluteFile();
1567         }
1568         else
1569         {
1570             return new File( workingDirectory, file.getPath() ).getAbsoluteFile();
1571         }
1572     }
1573 
1574     // ----------------------------------------------------------------------
1575     // System properties handling
1576     // ----------------------------------------------------------------------
1577 
1578     static void populateProperties( CommandLine commandLine, Properties systemProperties, Properties userProperties )
1579     {
1580         EnvironmentUtils.addEnvVars( systemProperties );
1581 
1582         // ----------------------------------------------------------------------
1583         // Options that are set on the command line become system properties
1584         // and therefore are set in the session properties. System properties
1585         // are most dominant.
1586         // ----------------------------------------------------------------------
1587 
1588         if ( commandLine.hasOption( CLIManager.SET_SYSTEM_PROPERTY ) )
1589         {
1590             String[] defStrs = commandLine.getOptionValues( CLIManager.SET_SYSTEM_PROPERTY );
1591             
1592             if ( defStrs != null )
1593             {
1594                 for ( String defStr : defStrs )
1595                 {
1596                     setCliProperty( defStr, userProperties );
1597                 }
1598             }
1599         }
1600 
1601         SystemProperties.addSystemProperties( systemProperties );
1602 
1603         // ----------------------------------------------------------------------
1604         // Properties containing info about the currently running version of Maven
1605         // These override any corresponding properties set on the command line
1606         // ----------------------------------------------------------------------
1607 
1608         Properties buildProperties = CLIReportingUtils.getBuildProperties();
1609 
1610         String mavenVersion = buildProperties.getProperty( CLIReportingUtils.BUILD_VERSION_PROPERTY );
1611         systemProperties.setProperty( "maven.version", mavenVersion );
1612 
1613         String mavenBuildVersion = CLIReportingUtils.createMavenVersionString( buildProperties );
1614         systemProperties.setProperty( "maven.build.version", mavenBuildVersion );
1615     }
1616 
1617     private static void setCliProperty( String property, Properties properties )
1618     {
1619         String name;
1620 
1621         String value;
1622 
1623         int i = property.indexOf( '=' );
1624 
1625         if ( i <= 0 )
1626         {
1627             name = property.trim();
1628 
1629             value = "true";
1630         }
1631         else
1632         {
1633             name = property.substring( 0, i ).trim();
1634 
1635             value = property.substring( i + 1 );
1636         }
1637 
1638         properties.setProperty( name, value );
1639 
1640         // ----------------------------------------------------------------------
1641         // I'm leaving the setting of system properties here as not to break
1642         // the SystemPropertyProfileActivator. This won't harm embedding. jvz.
1643         // ----------------------------------------------------------------------
1644 
1645         System.setProperty( name, value );
1646     }
1647 
1648     static class ExitException
1649         extends Exception
1650     {
1651         @SuppressWarnings( "checkstyle:visibilitymodifier" )
1652         public int exitCode;
1653 
1654         public ExitException( int exitCode )
1655         {
1656             this.exitCode = exitCode;
1657         }
1658     }
1659 
1660     //
1661     // Customizations available via the CLI
1662     //
1663 
1664     protected TransferListener getConsoleTransferListener( boolean printResourceNames )
1665     {
1666         return new ConsoleMavenTransferListener( System.out, printResourceNames );
1667     }
1668 
1669     protected TransferListener getBatchTransferListener()
1670     {
1671         return new Slf4jMavenTransferListener();
1672     }
1673 
1674     protected void customizeContainer( PlexusContainer container )
1675     {
1676     }
1677 
1678     protected ModelProcessor createModelProcessor( PlexusContainer container )
1679         throws ComponentLookupException
1680     {
1681         return container.lookup( ModelProcessor.class );
1682     }
1683 }