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