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