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