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