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