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.settings.building.DefaultSettingsBuildingRequest; 063import org.apache.maven.settings.building.SettingsBuilder; 064import org.apache.maven.settings.building.SettingsBuildingRequest; 065import org.apache.maven.settings.building.SettingsBuildingResult; 066import org.apache.maven.settings.building.SettingsProblem; 067import org.apache.maven.settings.building.SettingsSource; 068import org.codehaus.plexus.ContainerConfiguration; 069import org.codehaus.plexus.DefaultContainerConfiguration; 070import org.codehaus.plexus.DefaultPlexusContainer; 071import org.codehaus.plexus.PlexusConstants; 072import org.codehaus.plexus.PlexusContainer; 073import org.codehaus.plexus.classworlds.ClassWorld; 074import org.codehaus.plexus.classworlds.realm.ClassRealm; 075import org.codehaus.plexus.component.repository.exception.ComponentLookupException; 076import org.codehaus.plexus.logging.LoggerManager; 077import org.codehaus.plexus.util.StringUtils; 078import org.eclipse.aether.transfer.TransferListener; 079import org.slf4j.ILoggerFactory; 080import org.slf4j.Logger; 081import org.slf4j.LoggerFactory; 082import org.sonatype.plexus.components.cipher.DefaultPlexusCipher; 083import org.sonatype.plexus.components.sec.dispatcher.DefaultSecDispatcher; 084import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher; 085import org.sonatype.plexus.components.sec.dispatcher.SecUtil; 086import org.sonatype.plexus.components.sec.dispatcher.model.SettingsSecurity; 087 088import com.google.inject.AbstractModule; 089 090// TODO: push all common bits back to plexus cli and prepare for transition to Guice. We don't need 50 ways to make CLIs 091 092/** 093 * @author Jason van Zyl 094 * @noinspection UseOfSystemOutOrSystemErr,ACCESS_STATIC_VIA_INSTANCE 095 */ 096public class MavenCli 097{ 098 public static final String LOCAL_REPO_PROPERTY = "maven.repo.local"; 099 100 public static final String THREADS_DEPRECATED = "maven.threads.experimental"; 101 102 public static final String userHome = System.getProperty( "user.home" ); 103 104 public static final File userMavenConfigurationHome = new File( userHome, ".m2" ); 105 106 public static final File DEFAULT_USER_SETTINGS_FILE = new File( userMavenConfigurationHome, "settings.xml" ); 107 108 public static final File DEFAULT_GLOBAL_SETTINGS_FILE = 109 new File( System.getProperty( "maven.home", System.getProperty( "user.dir", "" ) ), "conf/settings.xml" ); 110 111 public static final File DEFAULT_USER_TOOLCHAINS_FILE = new File( userMavenConfigurationHome, "toolchains.xml" ); 112 113 private static final String EXT_CLASS_PATH = "maven.ext.class.path"; 114 115 private ClassWorld classWorld; 116 117 private LoggerManager plexusLoggerManager; 118 119 private ILoggerFactory slf4jLoggerFactory; 120 121 private Logger slf4jLogger; 122 123 private EventSpyDispatcher eventSpyDispatcher; 124 125 private ModelProcessor modelProcessor; 126 127 private Maven maven; 128 129 private MavenExecutionRequestPopulator executionRequestPopulator; 130 131 private SettingsBuilder settingsBuilder; 132 133 private DefaultSecDispatcher dispatcher; 134 135 public MavenCli() 136 { 137 this( null ); 138 } 139 140 // This supports painless invocation by the Verifier during embedded execution of the core ITs 141 public MavenCli( ClassWorld classWorld ) 142 { 143 this.classWorld = classWorld; 144 } 145 146 public static void main( String[] args ) 147 { 148 int result = main( args, null ); 149 150 System.exit( result ); 151 } 152 153 /** @noinspection ConfusingMainMethod */ 154 public static int main( String[] args, ClassWorld classWorld ) 155 { 156 MavenCli cli = new MavenCli(); 157 return cli.doMain( new CliRequest( args, classWorld ) ); 158 } 159 160 // TODO: need to externalize CliRequest 161 public static int doMain( String[] args, ClassWorld classWorld ) 162 { 163 MavenCli cli = new MavenCli(); 164 return cli.doMain( new CliRequest( args, classWorld ) ); 165 } 166 167 // This supports painless invocation by the Verifier during embedded execution of the core ITs 168 public int doMain( String[] args, String workingDirectory, PrintStream stdout, PrintStream stderr ) 169 { 170 PrintStream oldout = System.out; 171 PrintStream olderr = System.err; 172 173 try 174 { 175 if ( stdout != null ) 176 { 177 System.setOut( stdout ); 178 } 179 if ( stderr != null ) 180 { 181 System.setErr( stderr ); 182 } 183 184 CliRequest cliRequest = new CliRequest( args, classWorld ); 185 cliRequest.workingDirectory = workingDirectory; 186 187 return doMain( cliRequest ); 188 } 189 finally 190 { 191 System.setOut( oldout ); 192 System.setErr( olderr ); 193 } 194 } 195 196 // TODO: need to externalize CliRequest 197 public int doMain( CliRequest cliRequest ) 198 { 199 PlexusContainer localContainer = null; 200 try 201 { 202 initialize( cliRequest ); 203 cli( cliRequest ); 204 logging( cliRequest ); 205 version( cliRequest ); 206 properties( cliRequest ); 207 localContainer = container( cliRequest ); 208 commands( cliRequest ); 209 settings( cliRequest ); 210 populateRequest( cliRequest ); 211 encryption( cliRequest ); 212 repository( cliRequest ); 213 return execute( cliRequest ); 214 } 215 catch ( ExitException e ) 216 { 217 return e.exitCode; 218 } 219 catch ( UnrecognizedOptionException e ) 220 { 221 // pure user error, suppress stack trace 222 return 1; 223 } 224 catch ( BuildAbort e ) 225 { 226 CLIReportingUtils.showError( slf4jLogger, "ABORTED", e, cliRequest.showErrors ); 227 228 return 2; 229 } 230 catch ( Exception e ) 231 { 232 CLIReportingUtils.showError( slf4jLogger, "Error executing Maven.", e, cliRequest.showErrors ); 233 234 return 1; 235 } 236 finally 237 { 238 if ( localContainer != null ) 239 { 240 localContainer.dispose(); 241 } 242 } 243 } 244 245 private void initialize( CliRequest cliRequest ) 246 { 247 if ( cliRequest.workingDirectory == null ) 248 { 249 cliRequest.workingDirectory = System.getProperty( "user.dir" ); 250 } 251 252 // 253 // Make sure the Maven home directory is an absolute path to save us from confusion with say drive-relative 254 // Windows paths. 255 // 256 String mavenHome = System.getProperty( "maven.home" ); 257 258 if ( mavenHome != null ) 259 { 260 System.setProperty( "maven.home", new File( mavenHome ).getAbsolutePath() ); 261 } 262 } 263 264 private void cli( CliRequest cliRequest ) 265 throws Exception 266 { 267 // 268 // Parsing errors can happen during the processing of the arguments and we prefer not having to check if the logger is null 269 // and construct this so we can use an SLF4J logger everywhere. 270 // 271 slf4jLogger = new Slf4jStdoutLogger(); 272 273 CLIManager cliManager = new CLIManager(); 274 275 try 276 { 277 cliRequest.commandLine = cliManager.parse( cliRequest.args ); 278 } 279 catch ( ParseException e ) 280 { 281 System.err.println( "Unable to parse command line options: " + e.getMessage() ); 282 cliManager.displayHelp( System.out ); 283 throw e; 284 } 285 286 if ( cliRequest.commandLine.hasOption( CLIManager.HELP ) ) 287 { 288 cliManager.displayHelp( System.out ); 289 throw new ExitException( 0 ); 290 } 291 292 if ( cliRequest.commandLine.hasOption( CLIManager.VERSION ) ) 293 { 294 System.out.println( CLIReportingUtils.showVersion() ); 295 throw new ExitException( 0 ); 296 } 297 } 298 299 /** 300 * configure logging 301 */ 302 private void logging( CliRequest cliRequest ) 303 { 304 cliRequest.debug = cliRequest.commandLine.hasOption( CLIManager.DEBUG ); 305 cliRequest.quiet = !cliRequest.debug && cliRequest.commandLine.hasOption( CLIManager.QUIET ); 306 cliRequest.showErrors = cliRequest.debug || cliRequest.commandLine.hasOption( CLIManager.ERRORS ); 307 308 slf4jLoggerFactory = LoggerFactory.getILoggerFactory(); 309 Slf4jConfiguration slf4jConfiguration = Slf4jConfigurationFactory.getConfiguration( slf4jLoggerFactory ); 310 311 if ( cliRequest.debug ) 312 { 313 cliRequest.request.setLoggingLevel( MavenExecutionRequest.LOGGING_LEVEL_DEBUG ); 314 slf4jConfiguration.setRootLoggerLevel( Slf4jConfiguration.Level.DEBUG ); 315 } 316 else if ( cliRequest.quiet ) 317 { 318 cliRequest.request.setLoggingLevel( MavenExecutionRequest.LOGGING_LEVEL_ERROR ); 319 slf4jConfiguration.setRootLoggerLevel( Slf4jConfiguration.Level.ERROR ); 320 } 321 else 322 { 323 cliRequest.request.setLoggingLevel( MavenExecutionRequest.LOGGING_LEVEL_INFO ); 324 slf4jConfiguration.setRootLoggerLevel( Slf4jConfiguration.Level.INFO ); 325 } 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; 504 char[] password; 505 if ( ( cons = System.console() ) != null 506 && ( password = cons.readPassword( "Master password: " ) ) != null ) 507 { 508 // Cipher uses Strings 509 passwd = String.copyValueOf( password ); 510 511 // Sun/Oracle advises to empty the char array 512 java.util.Arrays.fill( password, ' ' ); 513 } 514 } 515 516 DefaultPlexusCipher cipher = new DefaultPlexusCipher(); 517 518 System.out.println( cipher.encryptAndDecorate( passwd, 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; 529 char[] password; 530 if ( ( cons = System.console() ) != null 531 && ( password = cons.readPassword( "Password: " ) ) != null ) 532 { 533 // Cipher uses Strings 534 passwd = String.copyValueOf( password ); 535 536 // Sun/Oracle advises to empty the char array 537 java.util.Arrays.fill( password, ' ' ); 538 } 539 } 540 541 String configurationFile = dispatcher.getConfigurationFile(); 542 543 if ( configurationFile.startsWith( "~" ) ) 544 { 545 configurationFile = System.getProperty( "user.home" ) + configurationFile.substring( 1 ); 546 } 547 548 String file = System.getProperty( DefaultSecDispatcher.SYSTEM_PROPERTY_SEC_LOCATION, configurationFile ); 549 550 String master = null; 551 552 SettingsSecurity sec = SecUtil.read( file, true ); 553 if ( sec != null ) 554 { 555 master = sec.getMaster(); 556 } 557 558 if ( master == null ) 559 { 560 throw new IllegalStateException( "Master password is not set in the setting security file: " + file ); 561 } 562 563 DefaultPlexusCipher cipher = new DefaultPlexusCipher(); 564 String masterPasswd = cipher.decryptDecorated( master, DefaultSecDispatcher.SYSTEM_PROPERTY_SEC_LOCATION ); 565 System.out.println( cipher.encryptAndDecorate( passwd, masterPasswd ) ); 566 567 throw new ExitException( 0 ); 568 } 569 } 570 571 private void repository( CliRequest cliRequest ) 572 throws Exception 573 { 574 if ( cliRequest.commandLine.hasOption( CLIManager.LEGACY_LOCAL_REPOSITORY ) || 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 && ( showErrors || ( summary.getException() instanceof InternalErrorException ) ) ) 693 { 694 slf4jLogger.error( line, summary.getException() ); 695 } 696 else 697 { 698 slf4jLogger.error( line ); 699 } 700 } 701 702 indent += " "; 703 704 for ( ExceptionSummary child : summary.getChildren() ) 705 { 706 logSummary( child, references, indent, showErrors ); 707 } 708 } 709 710 private void settings( CliRequest cliRequest ) 711 throws Exception 712 { 713 File userSettingsFile; 714 715 if ( cliRequest.commandLine.hasOption( CLIManager.ALTERNATE_USER_SETTINGS ) ) 716 { 717 userSettingsFile = new File( cliRequest.commandLine.getOptionValue( CLIManager.ALTERNATE_USER_SETTINGS ) ); 718 userSettingsFile = resolveFile( userSettingsFile, cliRequest.workingDirectory ); 719 720 if ( !userSettingsFile.isFile() ) 721 { 722 throw new FileNotFoundException( "The specified user settings file does not exist: " 723 + userSettingsFile ); 724 } 725 } 726 else 727 { 728 userSettingsFile = DEFAULT_USER_SETTINGS_FILE; 729 } 730 731 File globalSettingsFile; 732 733 if ( cliRequest.commandLine.hasOption( CLIManager.ALTERNATE_GLOBAL_SETTINGS ) ) 734 { 735 globalSettingsFile = 736 new File( cliRequest.commandLine.getOptionValue( CLIManager.ALTERNATE_GLOBAL_SETTINGS ) ); 737 globalSettingsFile = resolveFile( globalSettingsFile, cliRequest.workingDirectory ); 738 739 if ( !globalSettingsFile.isFile() ) 740 { 741 throw new FileNotFoundException( "The specified global settings file does not exist: " 742 + globalSettingsFile ); 743 } 744 } 745 else 746 { 747 globalSettingsFile = DEFAULT_GLOBAL_SETTINGS_FILE; 748 } 749 750 cliRequest.request.setGlobalSettingsFile( globalSettingsFile ); 751 cliRequest.request.setUserSettingsFile( userSettingsFile ); 752 753 SettingsBuildingRequest settingsRequest = new DefaultSettingsBuildingRequest(); 754 settingsRequest.setGlobalSettingsFile( globalSettingsFile ); 755 settingsRequest.setUserSettingsFile( userSettingsFile ); 756 settingsRequest.setSystemProperties( cliRequest.systemProperties ); 757 settingsRequest.setUserProperties( cliRequest.userProperties ); 758 759 eventSpyDispatcher.onEvent( settingsRequest ); 760 761 slf4jLogger.debug( "Reading global settings from " 762 + getSettingsLocation( settingsRequest.getGlobalSettingsSource(), settingsRequest.getGlobalSettingsFile() ) ); 763 slf4jLogger.debug( "Reading user settings from " 764 + getSettingsLocation( settingsRequest.getUserSettingsSource(), settingsRequest.getUserSettingsFile() ) ); 765 766 SettingsBuildingResult settingsResult = settingsBuilder.build( settingsRequest ); 767 768 eventSpyDispatcher.onEvent( settingsResult ); 769 770 executionRequestPopulator.populateFromSettings( cliRequest.request, settingsResult.getEffectiveSettings() ); 771 772 if ( !settingsResult.getProblems().isEmpty() && slf4jLogger.isWarnEnabled() ) 773 { 774 slf4jLogger.warn( "" ); 775 slf4jLogger.warn( "Some problems were encountered while building the effective settings" ); 776 777 for ( SettingsProblem problem : settingsResult.getProblems() ) 778 { 779 slf4jLogger.warn( problem.getMessage() + " @ " + problem.getLocation() ); 780 } 781 782 slf4jLogger.warn( "" ); 783 } 784 } 785 786 private Object getSettingsLocation( SettingsSource source, File file ) 787 { 788 if ( source != null ) 789 { 790 return source.getLocation(); 791 } 792 return file; 793 } 794 795 private MavenExecutionRequest populateRequest( CliRequest cliRequest ) 796 { 797 MavenExecutionRequest request = cliRequest.request; 798 CommandLine commandLine = cliRequest.commandLine; 799 String workingDirectory = cliRequest.workingDirectory; 800 boolean quiet = cliRequest.quiet; 801 boolean showErrors = cliRequest.showErrors; 802 803 String[] deprecatedOptions = { "up", "npu", "cpu", "npr" }; 804 for ( String deprecatedOption : deprecatedOptions ) 805 { 806 if ( commandLine.hasOption( deprecatedOption ) ) 807 { 808 slf4jLogger.warn( "Command line option -" + deprecatedOption 809 + " is deprecated and will be removed in future Maven versions." ); 810 } 811 } 812 813 // ---------------------------------------------------------------------- 814 // Now that we have everything that we need we will fire up plexus and 815 // bring the maven component to life for use. 816 // ---------------------------------------------------------------------- 817 818 if ( commandLine.hasOption( CLIManager.BATCH_MODE ) ) 819 { 820 request.setInteractiveMode( false ); 821 } 822 823 boolean noSnapshotUpdates = false; 824 if ( commandLine.hasOption( CLIManager.SUPRESS_SNAPSHOT_UPDATES ) ) 825 { 826 noSnapshotUpdates = true; 827 } 828 829 // ---------------------------------------------------------------------- 830 // 831 // ---------------------------------------------------------------------- 832 833 @SuppressWarnings( "unchecked" ) 834 List<String> goals = commandLine.getArgList(); 835 836 boolean recursive = true; 837 838 // this is the default behavior. 839 String reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_FAST; 840 841 if ( commandLine.hasOption( CLIManager.NON_RECURSIVE ) ) 842 { 843 recursive = false; 844 } 845 846 if ( commandLine.hasOption( CLIManager.FAIL_FAST ) ) 847 { 848 reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_FAST; 849 } 850 else if ( commandLine.hasOption( CLIManager.FAIL_AT_END ) ) 851 { 852 reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_AT_END; 853 } 854 else if ( commandLine.hasOption( CLIManager.FAIL_NEVER ) ) 855 { 856 reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_NEVER; 857 } 858 859 if ( commandLine.hasOption( CLIManager.OFFLINE ) ) 860 { 861 request.setOffline( true ); 862 } 863 864 boolean updateSnapshots = false; 865 866 if ( commandLine.hasOption( CLIManager.UPDATE_SNAPSHOTS ) ) 867 { 868 updateSnapshots = true; 869 } 870 871 String globalChecksumPolicy = null; 872 873 if ( commandLine.hasOption( CLIManager.CHECKSUM_FAILURE_POLICY ) ) 874 { 875 globalChecksumPolicy = MavenExecutionRequest.CHECKSUM_POLICY_FAIL; 876 } 877 else if ( commandLine.hasOption( CLIManager.CHECKSUM_WARNING_POLICY ) ) 878 { 879 globalChecksumPolicy = MavenExecutionRequest.CHECKSUM_POLICY_WARN; 880 } 881 882 File baseDirectory = new File( workingDirectory, "" ).getAbsoluteFile(); 883 884 // ---------------------------------------------------------------------- 885 // Profile Activation 886 // ---------------------------------------------------------------------- 887 888 List<String> activeProfiles = new ArrayList<String>(); 889 890 List<String> inactiveProfiles = new ArrayList<String>(); 891 892 if ( commandLine.hasOption( CLIManager.ACTIVATE_PROFILES ) ) 893 { 894 String[] profileOptionValues = commandLine.getOptionValues( CLIManager.ACTIVATE_PROFILES ); 895 if ( profileOptionValues != null ) 896 { 897 for ( String profileOptionValue : profileOptionValues ) 898 { 899 StringTokenizer profileTokens = new StringTokenizer( profileOptionValue, "," ); 900 901 while ( profileTokens.hasMoreTokens() ) 902 { 903 String profileAction = profileTokens.nextToken().trim(); 904 905 if ( profileAction.startsWith( "-" ) || profileAction.startsWith( "!" ) ) 906 { 907 inactiveProfiles.add( profileAction.substring( 1 ) ); 908 } 909 else if ( profileAction.startsWith( "+" ) ) 910 { 911 activeProfiles.add( profileAction.substring( 1 ) ); 912 } 913 else 914 { 915 activeProfiles.add( profileAction ); 916 } 917 } 918 } 919 } 920 } 921 922 TransferListener transferListener; 923 924 if ( quiet ) 925 { 926 transferListener = new QuietMavenTransferListener(); 927 } 928 else if ( request.isInteractiveMode() && !cliRequest.commandLine.hasOption( CLIManager.LOG_FILE ) ) 929 { 930 // 931 // If we're logging to a file then we don't want the console transfer listener as it will spew 932 // download progress all over the place 933 // 934 transferListener = getConsoleTransferListener(); 935 } 936 else 937 { 938 transferListener = getBatchTransferListener(); 939 } 940 941 ExecutionListener executionListener = new ExecutionEventLogger(); 942 executionListener = eventSpyDispatcher.chainListener( executionListener ); 943 944 String alternatePomFile = null; 945 if ( commandLine.hasOption( CLIManager.ALTERNATE_POM_FILE ) ) 946 { 947 alternatePomFile = commandLine.getOptionValue( CLIManager.ALTERNATE_POM_FILE ); 948 } 949 950 File userToolchainsFile; 951 if ( commandLine.hasOption( CLIManager.ALTERNATE_USER_TOOLCHAINS ) ) 952 { 953 userToolchainsFile = new File( commandLine.getOptionValue( CLIManager.ALTERNATE_USER_TOOLCHAINS ) ); 954 userToolchainsFile = resolveFile( userToolchainsFile, workingDirectory ); 955 } 956 else 957 { 958 userToolchainsFile = MavenCli.DEFAULT_USER_TOOLCHAINS_FILE; 959 } 960 961 request.setBaseDirectory( baseDirectory ).setGoals( goals ) 962 .setSystemProperties( cliRequest.systemProperties ) 963 .setUserProperties( cliRequest.userProperties ) 964 .setReactorFailureBehavior( reactorFailureBehaviour ) // default: fail fast 965 .setRecursive( recursive ) // default: true 966 .setShowErrors( showErrors ) // default: false 967 .addActiveProfiles( activeProfiles ) // optional 968 .addInactiveProfiles( inactiveProfiles ) // optional 969 .setExecutionListener( executionListener ) 970 .setTransferListener( transferListener ) // default: batch mode which goes along with interactive 971 .setUpdateSnapshots( updateSnapshots ) // default: false 972 .setNoSnapshotUpdates( noSnapshotUpdates ) // default: false 973 .setGlobalChecksumPolicy( globalChecksumPolicy ) // default: warn 974 .setUserToolchainsFile( userToolchainsFile ); 975 976 if ( alternatePomFile != null ) 977 { 978 File pom = resolveFile( new File( alternatePomFile ), workingDirectory ); 979 if ( pom.isDirectory() ) 980 { 981 pom = new File( pom, "pom.xml" ); 982 } 983 984 request.setPom( pom ); 985 } 986 else 987 { 988 File pom = modelProcessor.locatePom( baseDirectory ); 989 990 if ( pom.isFile() ) 991 { 992 request.setPom( pom ); 993 } 994 } 995 996 if ( ( request.getPom() != null ) && ( request.getPom().getParentFile() != null ) ) 997 { 998 request.setBaseDirectory( request.getPom().getParentFile() ); 999 } 1000 1001 if ( commandLine.hasOption( CLIManager.RESUME_FROM ) ) 1002 { 1003 request.setResumeFrom( commandLine.getOptionValue( CLIManager.RESUME_FROM ) ); 1004 } 1005 1006 if ( commandLine.hasOption( CLIManager.PROJECT_LIST ) ) 1007 { 1008 String[] projectOptionValues = commandLine.getOptionValues( CLIManager.PROJECT_LIST ); 1009 1010 List<String> inclProjects = new ArrayList<String>(); 1011 List<String> exclProjects = new ArrayList<String>(); 1012 1013 if ( projectOptionValues != null ) 1014 { 1015 for ( String projectOptionValue : projectOptionValues ) 1016 { 1017 StringTokenizer projectTokens = new StringTokenizer( projectOptionValue, "," ); 1018 1019 while ( projectTokens.hasMoreTokens() ) 1020 { 1021 String projectAction = projectTokens.nextToken().trim(); 1022 1023 if ( projectAction.startsWith( "-" ) || projectAction.startsWith( "!" ) ) 1024 { 1025 exclProjects.add( projectAction.substring( 1 ) ); 1026 } 1027 else if ( projectAction.startsWith( "+" ) ) 1028 { 1029 inclProjects.add( projectAction.substring( 1 ) ); 1030 } 1031 else 1032 { 1033 inclProjects.add( projectAction ); 1034 } 1035 } 1036 } 1037 } 1038 1039 request.setSelectedProjects( inclProjects ); 1040 request.setExcludedProjects( exclProjects ); 1041 } 1042 1043 if ( commandLine.hasOption( CLIManager.ALSO_MAKE ) 1044 && !commandLine.hasOption( CLIManager.ALSO_MAKE_DEPENDENTS ) ) 1045 { 1046 request.setMakeBehavior( MavenExecutionRequest.REACTOR_MAKE_UPSTREAM ); 1047 } 1048 else if ( !commandLine.hasOption( CLIManager.ALSO_MAKE ) 1049 && commandLine.hasOption( CLIManager.ALSO_MAKE_DEPENDENTS ) ) 1050 { 1051 request.setMakeBehavior( MavenExecutionRequest.REACTOR_MAKE_DOWNSTREAM ); 1052 } 1053 else if ( commandLine.hasOption( CLIManager.ALSO_MAKE ) 1054 && commandLine.hasOption( CLIManager.ALSO_MAKE_DEPENDENTS ) ) 1055 { 1056 request.setMakeBehavior( MavenExecutionRequest.REACTOR_MAKE_BOTH ); 1057 } 1058 1059 String localRepoProperty = request.getUserProperties().getProperty( MavenCli.LOCAL_REPO_PROPERTY ); 1060 1061 if ( localRepoProperty == null ) 1062 { 1063 localRepoProperty = request.getSystemProperties().getProperty( MavenCli.LOCAL_REPO_PROPERTY ); 1064 } 1065 1066 if ( localRepoProperty != null ) 1067 { 1068 request.setLocalRepositoryPath( localRepoProperty ); 1069 } 1070 1071 1072 request.setCacheNotFound( true ); 1073 request.setCacheTransferError( false ); 1074 1075 // 1076 // Builder, concurrency and parallelism 1077 // 1078 // We preserve the existing methods for builder selection which is to look for various inputs in the threading 1079 // configuration. We don't have an easy way to allow a pluggable builder to provide its own configuration parameters 1080 // but this is sufficient for now. Ultimately we want components like Builders to provide a way to extend the command 1081 // line to accept its own configuration parameters. 1082 // 1083 final String threadConfiguration = commandLine.hasOption( CLIManager.THREADS ) 1084 ? commandLine.getOptionValue( CLIManager.THREADS ) 1085 : request.getSystemProperties().getProperty( 1086 MavenCli.THREADS_DEPRECATED ); // TODO: Remove this setting. Note that the int-tests use it 1087 1088 if ( threadConfiguration != null ) 1089 { 1090 // 1091 // Default to the standard multithreaded builder 1092 // 1093 request.setBuilderId( "multithreaded" ); 1094 1095 if ( threadConfiguration.contains( "C" ) ) 1096 { 1097 request.setDegreeOfConcurrency( calculateDegreeOfConcurrencyWithCoreMultiplier( threadConfiguration ) ); 1098 } 1099 else 1100 { 1101 request.setDegreeOfConcurrency( Integer.valueOf( threadConfiguration ) ); 1102 } 1103 } 1104 1105 // 1106 // Allow the builder to be overriden by the user if requested. The builders are now pluggable. 1107 // 1108 if ( commandLine.hasOption( CLIManager.BUILDER ) ) 1109 { 1110 request.setBuilderId( commandLine.getOptionValue( CLIManager.BUILDER ) ); 1111 } 1112 1113 return request; 1114 } 1115 1116 int calculateDegreeOfConcurrencyWithCoreMultiplier( String threadConfiguration ) 1117 { 1118 return (int) ( Float.valueOf( threadConfiguration.replace( "C", "" ) ) * Runtime.getRuntime().availableProcessors() ); 1119 } 1120 1121 static File resolveFile( File file, String workingDirectory ) 1122 { 1123 if ( file == null ) 1124 { 1125 return null; 1126 } 1127 else if ( file.isAbsolute() ) 1128 { 1129 return file; 1130 } 1131 else if ( file.getPath().startsWith( File.separator ) ) 1132 { 1133 // drive-relative Windows path 1134 return file.getAbsoluteFile(); 1135 } 1136 else 1137 { 1138 return new File( workingDirectory, file.getPath() ).getAbsoluteFile(); 1139 } 1140 } 1141 1142 // ---------------------------------------------------------------------- 1143 // System properties handling 1144 // ---------------------------------------------------------------------- 1145 1146 static void populateProperties( CommandLine commandLine, Properties systemProperties, Properties userProperties ) 1147 { 1148 EnvironmentUtils.addEnvVars( systemProperties ); 1149 1150 // ---------------------------------------------------------------------- 1151 // Options that are set on the command line become system properties 1152 // and therefore are set in the session properties. System properties 1153 // are most dominant. 1154 // ---------------------------------------------------------------------- 1155 1156 if ( commandLine.hasOption( CLIManager.SET_SYSTEM_PROPERTY ) ) 1157 { 1158 String[] defStrs = commandLine.getOptionValues( CLIManager.SET_SYSTEM_PROPERTY ); 1159 1160 if ( defStrs != null ) 1161 { 1162 for ( String defStr : defStrs ) 1163 { 1164 setCliProperty( defStr, userProperties ); 1165 } 1166 } 1167 } 1168 1169 systemProperties.putAll( System.getProperties() ); 1170 1171 // ---------------------------------------------------------------------- 1172 // Properties containing info about the currently running version of Maven 1173 // These override any corresponding properties set on the command line 1174 // ---------------------------------------------------------------------- 1175 1176 Properties buildProperties = CLIReportingUtils.getBuildProperties(); 1177 1178 String mavenVersion = buildProperties.getProperty( CLIReportingUtils.BUILD_VERSION_PROPERTY ); 1179 systemProperties.setProperty( "maven.version", mavenVersion ); 1180 1181 String mavenBuildVersion = CLIReportingUtils.createMavenVersionString( buildProperties ); 1182 systemProperties.setProperty( "maven.build.version", mavenBuildVersion ); 1183 } 1184 1185 private static void setCliProperty( String property, Properties properties ) 1186 { 1187 String name; 1188 1189 String value; 1190 1191 int i = property.indexOf( "=" ); 1192 1193 if ( i <= 0 ) 1194 { 1195 name = property.trim(); 1196 1197 value = "true"; 1198 } 1199 else 1200 { 1201 name = property.substring( 0, i ).trim(); 1202 1203 value = property.substring( i + 1 ); 1204 } 1205 1206 properties.setProperty( name, value ); 1207 1208 // ---------------------------------------------------------------------- 1209 // I'm leaving the setting of system properties here as not to break 1210 // the SystemPropertyProfileActivator. This won't harm embedding. jvz. 1211 // ---------------------------------------------------------------------- 1212 1213 System.setProperty( name, value ); 1214 } 1215 1216 static class CliRequest 1217 { 1218 String[] args; 1219 CommandLine commandLine; 1220 ClassWorld classWorld; 1221 String workingDirectory; 1222 boolean debug; 1223 boolean quiet; 1224 boolean showErrors = true; 1225 Properties userProperties = new Properties(); 1226 Properties systemProperties = new Properties(); 1227 MavenExecutionRequest request; 1228 1229 CliRequest( String[] args, ClassWorld classWorld ) 1230 { 1231 this.args = args; 1232 this.classWorld = classWorld; 1233 this.request = new DefaultMavenExecutionRequest(); 1234 } 1235 } 1236 1237 static class ExitException 1238 extends Exception 1239 { 1240 1241 public int exitCode; 1242 1243 public ExitException( int exitCode ) 1244 { 1245 this.exitCode = exitCode; 1246 } 1247 1248 } 1249 1250 // 1251 // Customizations available via the CLI 1252 // 1253 1254 protected TransferListener getConsoleTransferListener() 1255 { 1256 return new ConsoleMavenTransferListener( System.out ); 1257 } 1258 1259 protected TransferListener getBatchTransferListener() 1260 { 1261 return new Slf4jMavenTransferListener(); 1262 } 1263 1264 protected void customizeContainer( PlexusContainer container ) 1265 { 1266 } 1267 1268 protected ModelProcessor createModelProcessor( PlexusContainer container ) 1269 throws ComponentLookupException 1270 { 1271 return container.lookup( ModelProcessor.class ); 1272 } 1273}