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