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