diff --git a/daemon/src/main/java/org/apache/maven/cli/DaemonMavenCli.java b/daemon/src/main/java/org/apache/maven/cli/DaemonMavenCli.java index 0f4eb824b..e08831648 100644 --- a/daemon/src/main/java/org/apache/maven/cli/DaemonMavenCli.java +++ b/daemon/src/main/java/org/apache/maven/cli/DaemonMavenCli.java @@ -35,7 +35,7 @@ import java.util.Map.Entry; import java.util.Properties; import java.util.Set; -import java.util.StringTokenizer; +import java.util.function.Consumer; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -43,6 +43,7 @@ import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; import org.apache.commons.cli.ParseException; +import org.apache.commons.lang3.math.NumberUtils; import org.apache.maven.InternalErrorException; import org.apache.maven.Maven; import org.apache.maven.building.FileSource; @@ -59,10 +60,7 @@ import org.apache.maven.exception.DefaultExceptionHandler; import org.apache.maven.exception.ExceptionHandler; import org.apache.maven.exception.ExceptionSummary; -import org.apache.maven.execution.MavenExecutionRequest; -import org.apache.maven.execution.MavenExecutionRequestPopulationException; -import org.apache.maven.execution.MavenExecutionRequestPopulator; -import org.apache.maven.execution.MavenExecutionResult; +import org.apache.maven.execution.*; import org.apache.maven.execution.scope.internal.MojoExecutionScopeModule; import org.apache.maven.extension.internal.CoreExports; import org.apache.maven.extension.internal.CoreExtensionEntry; @@ -172,7 +170,9 @@ public class DaemonMavenCli { private final LoggingExecutionListener executionListener; - /** Non-volatile, assuming that it is accessed only from the main thread */ + /** + * Non-volatile, assuming that it is accessed only from the main thread + */ private BuildEventListener buildEventListener = BuildEventListener.dummy(); public DaemonMavenCli() throws Exception { @@ -349,7 +349,7 @@ private CommandLine cliMerge(CommandLine mavenArgs, CommandLine mavenConfig) { */ void logging(CliRequest cliRequest) { // LOG LEVEL - cliRequest.verbose = cliRequest.commandLine.hasOption(CLIManager.DEBUG); + cliRequest.verbose = cliRequest.commandLine.hasOption(CLIManager.VERBOSE); cliRequest.quiet = !cliRequest.verbose && cliRequest.commandLine.hasOption(CLIManager.QUIET); cliRequest.showErrors = cliRequest.verbose || cliRequest.commandLine.hasOption(CLIManager.ERRORS); @@ -571,7 +571,7 @@ protected void configure() { properties(cliRequest); configure(cliRequest, eventSpyDispatcher, configurationProcessors); LoggingExecutionListener executionListener = container.lookup(LoggingExecutionListener.class); - populateRequest(cliRequest, cliRequest.request, slf4jLogger, eventSpyDispatcher, + populateRequest(cliRequest, cliRequest.request, eventSpyDispatcher, container.lookup(ModelProcessor.class), createTransferListener(cliRequest), buildEventListener, executionListener); executionRequestPopulator.populateDefaults(cliRequest.request); @@ -981,14 +981,13 @@ private Object getLocation(Source source, File defaultLocation) { } private void populateRequest(CliRequest cliRequest) { - populateRequest(cliRequest, cliRequest.request, slf4jLogger, eventSpyDispatcher, modelProcessor, + populateRequest(cliRequest, cliRequest.request, eventSpyDispatcher, modelProcessor, createTransferListener(cliRequest), buildEventListener, executionListener); } - private static void populateRequest( + private void populateRequest( CliRequest cliRequest, MavenExecutionRequest request, - Logger slf4jLogger, EventSpyDispatcher eventSpyDispatcher, ModelProcessor modelProcessor, TransferListener transferListener, @@ -996,243 +995,285 @@ private static void populateRequest( LoggingExecutionListener executionListener) { CommandLine commandLine = cliRequest.commandLine; String workingDirectory = cliRequest.workingDirectory; - boolean showErrors = cliRequest.showErrors; - - String[] deprecatedOptions = { "up", "npu", "cpu", "npr" }; - for (String deprecatedOption : deprecatedOptions) { - if (commandLine.hasOption(deprecatedOption)) { - slf4jLogger.warn("Command line option -{} is deprecated and will be removed in future Maven versions.", - deprecatedOption); - } - } - - // ---------------------------------------------------------------------- - // Now that we have everything that we need we will fire up plexus and - // bring the maven component to life for use. - // ---------------------------------------------------------------------- - - if (commandLine.hasOption(CLIManager.BATCH_MODE)) { - request.setInteractiveMode(false); - } - - boolean noSnapshotUpdates = false; - if (commandLine.hasOption(CLIManager.SUPPRESS_SNAPSHOT_UPDATES)) { - noSnapshotUpdates = true; - } - - // ---------------------------------------------------------------------- - // - // ---------------------------------------------------------------------- - - List goals = commandLine.getArgList(); - - boolean recursive = true; + boolean quiet = cliRequest.quiet; + boolean verbose = cliRequest.verbose; + request.setShowErrors(cliRequest.showErrors); // default: false + File baseDirectory = new File(workingDirectory, "").getAbsoluteFile(); - // this is the default behavior. - String reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_FAST; + disableOnPresentOption(commandLine, CLIManager.BATCH_MODE, request::setInteractiveMode); + enableOnPresentOption(commandLine, CLIManager.SUPPRESS_SNAPSHOT_UPDATES, request::setNoSnapshotUpdates); + request.setGoals(commandLine.getArgList()); + request.setReactorFailureBehavior(determineReactorFailureBehaviour(commandLine)); + disableOnPresentOption(commandLine, CLIManager.NON_RECURSIVE, request::setRecursive); + enableOnPresentOption(commandLine, CLIManager.OFFLINE, request::setOffline); + enableOnPresentOption(commandLine, CLIManager.UPDATE_SNAPSHOTS, request::setUpdateSnapshots); + request.setGlobalChecksumPolicy(determineGlobalCheckPolicy(commandLine)); + request.setBaseDirectory(baseDirectory); + request.setSystemProperties(cliRequest.systemProperties); + request.setUserProperties(cliRequest.userProperties); + request.setMultiModuleProjectDirectory(cliRequest.multiModuleProjectDirectory); + request.setPom(determinePom(modelProcessor, commandLine, workingDirectory, baseDirectory)); + request.setTransferListener(transferListener); + request.setExecutionListener(executionListener); - if (commandLine.hasOption(CLIManager.NON_RECURSIVE)) { - recursive = false; - } + ExecutionEventLogger executionEventLogger = new ExecutionEventLogger(); + executionListener.init( + eventSpyDispatcher.chainListener(executionEventLogger), + buildEventListener); - if (commandLine.hasOption(CLIManager.FAIL_FAST)) { - reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_FAST; - } else if (commandLine.hasOption(CLIManager.FAIL_AT_END)) { - reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_AT_END; - } else if (commandLine.hasOption(CLIManager.FAIL_NEVER)) { - reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_NEVER; + if ((request.getPom() != null) && (request.getPom().getParentFile() != null)) { + request.setBaseDirectory(request.getPom().getParentFile()); } - if (commandLine.hasOption(CLIManager.OFFLINE)) { - request.setOffline(true); - } + request.setResumeFrom(commandLine.getOptionValue(CLIManager.RESUME_FROM)); + enableOnPresentOption(commandLine, CLIManager.RESUME, request::setResume); + request.setMakeBehavior(determineMakeBehavior(commandLine)); + request.setCacheNotFound(true); + request.setCacheTransferError(false); - boolean updateSnapshots = false; + performProjectActivation(commandLine, request.getProjectActivation()); + performProfileActivation(commandLine, request.getProfileActivation()); - if (commandLine.hasOption(CLIManager.UPDATE_SNAPSHOTS)) { - updateSnapshots = true; + final String localRepositoryPath = determineLocalRepositoryPath(request); + if (localRepositoryPath != null) { + request.setLocalRepositoryPath(localRepositoryPath); } - String globalChecksumPolicy = null; + // + // Builder, concurrency and parallelism + // + // We preserve the existing methods for builder selection which is to look for various inputs in the threading + // configuration. We don't have an easy way to allow a pluggable builder to provide its own configuration + // parameters but this is sufficient for now. Ultimately we want components like Builders to provide a way to + // extend the command line to accept its own configuration parameters. + // + final String threadConfiguration = commandLine.getOptionValue(CLIManager.THREADS); - if (commandLine.hasOption(CLIManager.CHECKSUM_FAILURE_POLICY)) { - globalChecksumPolicy = MavenExecutionRequest.CHECKSUM_POLICY_FAIL; - } else if (commandLine.hasOption(CLIManager.CHECKSUM_WARNING_POLICY)) { - globalChecksumPolicy = MavenExecutionRequest.CHECKSUM_POLICY_WARN; + if (threadConfiguration != null) { + int degreeOfConcurrency = calculateDegreeOfConcurrency(threadConfiguration); + if (degreeOfConcurrency > 1) { + request.setBuilderId("multithreaded"); + request.setDegreeOfConcurrency(degreeOfConcurrency); + } } - File baseDirectory = new File(workingDirectory, "").getAbsoluteFile(); - - // ---------------------------------------------------------------------- - // Profile Activation - // ---------------------------------------------------------------------- - - List activeProfiles = new ArrayList<>(); - - List inactiveProfiles = new ArrayList<>(); + // + // Allow the builder to be overridden by the user if requested. The builders are now pluggable. + // + request.setBuilderId(commandLine.getOptionValue(CLIManager.BUILDER, request.getBuilderId())); + } - if (commandLine.hasOption(CLIManager.ACTIVATE_PROFILES)) { - String[] profileOptionValues = commandLine.getOptionValues(CLIManager.ACTIVATE_PROFILES); - if (profileOptionValues != null) { - for (String profileOptionValue : profileOptionValues) { - StringTokenizer profileTokens = new StringTokenizer(profileOptionValue, ","); - - while (profileTokens.hasMoreTokens()) { - String profileAction = profileTokens.nextToken().trim(); - - if (profileAction.startsWith("-") || profileAction.startsWith("!")) { - inactiveProfiles.add(profileAction.substring(1)); - } else if (profileAction.startsWith("+")) { - activeProfiles.add(profileAction.substring(1)); - } else { - activeProfiles.add(profileAction); - } - } - } - } + private String determineLocalRepositoryPath(final MavenExecutionRequest request) { + String userDefinedLocalRepo = request.getUserProperties().getProperty(MavenCli.LOCAL_REPO_PROPERTY); + if (userDefinedLocalRepo != null) { + return userDefinedLocalRepo; } - ExecutionEventLogger executionEventLogger = new ExecutionEventLogger(); - executionListener.init( - eventSpyDispatcher.chainListener(executionEventLogger), - buildEventListener); + return request.getSystemProperties().getProperty(MavenCli.LOCAL_REPO_PROPERTY); + } + private File determinePom(ModelProcessor modelProcessor, final CommandLine commandLine, final String workingDirectory, + final File baseDirectory) { String alternatePomFile = null; if (commandLine.hasOption(CLIManager.ALTERNATE_POM_FILE)) { alternatePomFile = commandLine.getOptionValue(CLIManager.ALTERNATE_POM_FILE); } - request.setBaseDirectory(baseDirectory) - .setGoals(goals) - .setSystemProperties(cliRequest.systemProperties) - .setUserProperties(cliRequest.userProperties) - .setReactorFailureBehavior(reactorFailureBehaviour) // default: fail fast - .setRecursive(recursive) // default: true - .setShowErrors(showErrors) // default: false - .addActiveProfiles(activeProfiles) // optional - .addInactiveProfiles(inactiveProfiles) // optional - .setExecutionListener(executionListener) - .setTransferListener(transferListener) // default: batch mode which goes along with interactive - .setUpdateSnapshots(updateSnapshots) // default: false - .setNoSnapshotUpdates(noSnapshotUpdates) // default: false - .setGlobalChecksumPolicy(globalChecksumPolicy) // default: warn - .setMultiModuleProjectDirectory(cliRequest.getMultiModuleProjectDirectory()); - if (alternatePomFile != null) { File pom = resolveFile(new File(alternatePomFile), workingDirectory); if (pom.isDirectory()) { pom = new File(pom, "pom.xml"); } - request.setPom(pom); + return pom; } else if (modelProcessor != null) { File pom = modelProcessor.locatePom(baseDirectory); if (pom.isFile()) { - request.setPom(pom); + return pom; } } - if ((request.getPom() != null) && (request.getPom().getParentFile() != null)) { - request.setBaseDirectory(request.getPom().getParentFile()); - } + return null; + } - if (commandLine.hasOption(CLIManager.RESUME)) { - request.setResume(true); - } + // Visible for testing + static void performProjectActivation(final CommandLine commandLine, final ProjectActivation projectActivation) { + if (commandLine.hasOption(CLIManager.PROJECT_LIST)) { + final String[] optionValues = commandLine.getOptionValues(CLIManager.PROJECT_LIST); - if (commandLine.hasOption(CLIManager.RESUME_FROM)) { - request.setResumeFrom(commandLine.getOptionValue(CLIManager.RESUME_FROM)); - } + if (optionValues == null || optionValues.length == 0) { + return; + } - if (commandLine.hasOption(CLIManager.PROJECT_LIST)) { - String[] projectOptionValues = commandLine.getOptionValues(CLIManager.PROJECT_LIST); + for (final String optionValue : optionValues) { + for (String token : optionValue.split(",")) { + String selector = token.trim(); + boolean active = true; + if (selector.charAt(0) == '-' || selector.charAt(0) == '!') { + active = false; + selector = selector.substring(1); + } else if (token.charAt(0) == '+') { + selector = selector.substring(1); + } - List inclProjects = new ArrayList<>(); - List exclProjects = new ArrayList<>(); + boolean optional = selector.charAt(0) == '?'; + selector = selector.substring(optional ? 1 : 0); - if (projectOptionValues != null) { - for (String projectOptionValue : projectOptionValues) { - StringTokenizer projectTokens = new StringTokenizer(projectOptionValue, ","); + projectActivation.addProjectActivation(selector, active, optional); + } + } + } + } + + // Visible for testing + static void performProfileActivation(final CommandLine commandLine, final ProfileActivation profileActivation) { + if (commandLine.hasOption(CLIManager.ACTIVATE_PROFILES)) { + final String[] optionValues = commandLine.getOptionValues(CLIManager.ACTIVATE_PROFILES); - while (projectTokens.hasMoreTokens()) { - String projectAction = projectTokens.nextToken().trim(); + if (optionValues == null || optionValues.length == 0) { + return; + } - if (projectAction.startsWith("-") || projectAction.startsWith("!")) { - exclProjects.add(projectAction.substring(1)); - } else if (projectAction.startsWith("+")) { - inclProjects.add(projectAction.substring(1)); - } else { - inclProjects.add(projectAction); - } + for (final String optionValue : optionValues) { + for (String token : optionValue.split(",")) { + String profileId = token.trim(); + boolean active = true; + if (profileId.charAt(0) == '-' || profileId.charAt(0) == '!') { + active = false; + profileId = profileId.substring(1); + } else if (token.charAt(0) == '+') { + profileId = profileId.substring(1); } + + boolean optional = profileId.charAt(0) == '?'; + profileId = profileId.substring(optional ? 1 : 0); + + profileActivation.addProfileActivation(profileId, active, optional); } } - - request.setSelectedProjects(inclProjects); - request.setExcludedProjects(exclProjects); } + } - if (commandLine.hasOption(CLIManager.ALSO_MAKE) && !commandLine.hasOption( - CLIManager.ALSO_MAKE_DEPENDENTS)) { - request.setMakeBehavior(MavenExecutionRequest.REACTOR_MAKE_UPSTREAM); - } else if (!commandLine.hasOption(CLIManager.ALSO_MAKE) && commandLine.hasOption( - CLIManager.ALSO_MAKE_DEPENDENTS)) { - request.setMakeBehavior(MavenExecutionRequest.REACTOR_MAKE_DOWNSTREAM); - } else if (commandLine.hasOption(CLIManager.ALSO_MAKE) && commandLine.hasOption( - CLIManager.ALSO_MAKE_DEPENDENTS)) { - request.setMakeBehavior(MavenExecutionRequest.REACTOR_MAKE_BOTH); + private ExecutionListener determineExecutionListener(EventSpyDispatcher eventSpyDispatcher) { + ExecutionListener executionListener = new ExecutionEventLogger(); + if (eventSpyDispatcher != null) { + return eventSpyDispatcher.chainListener(executionListener); + } else { + return executionListener; } + } - String localRepoProperty = request.getUserProperties().getProperty(MavenCli.LOCAL_REPO_PROPERTY); - - if (localRepoProperty == null) { - localRepoProperty = request.getSystemProperties().getProperty(MavenCli.LOCAL_REPO_PROPERTY); + private String determineReactorFailureBehaviour(final CommandLine commandLine) { + if (commandLine.hasOption(CLIManager.FAIL_FAST)) { + return MavenExecutionRequest.REACTOR_FAIL_FAST; + } else if (commandLine.hasOption(CLIManager.FAIL_AT_END)) { + return MavenExecutionRequest.REACTOR_FAIL_AT_END; + } else if (commandLine.hasOption(CLIManager.FAIL_NEVER)) { + return MavenExecutionRequest.REACTOR_FAIL_NEVER; + } else { + // this is the default behavior. + return MavenExecutionRequest.REACTOR_FAIL_FAST; } + } - if (localRepoProperty != null) { - request.setLocalRepositoryPath(localRepoProperty); + private String determineMakeBehavior(final CommandLine cl) { + if (cl.hasOption(CLIManager.ALSO_MAKE) && !cl.hasOption(CLIManager.ALSO_MAKE_DEPENDENTS)) { + return MavenExecutionRequest.REACTOR_MAKE_UPSTREAM; + } else if (!cl.hasOption(CLIManager.ALSO_MAKE) && cl.hasOption(CLIManager.ALSO_MAKE_DEPENDENTS)) { + return MavenExecutionRequest.REACTOR_MAKE_DOWNSTREAM; + } else if (cl.hasOption(CLIManager.ALSO_MAKE) && cl.hasOption(CLIManager.ALSO_MAKE_DEPENDENTS)) { + return MavenExecutionRequest.REACTOR_MAKE_BOTH; + } else { + return null; } + } - request.setCacheNotFound(true); - request.setCacheTransferError(false); + private String determineGlobalCheckPolicy(final CommandLine commandLine) { + if (commandLine.hasOption(CLIManager.CHECKSUM_FAILURE_POLICY)) { + return MavenExecutionRequest.CHECKSUM_POLICY_FAIL; + } else if (commandLine.hasOption(CLIManager.CHECKSUM_WARNING_POLICY)) { + return MavenExecutionRequest.CHECKSUM_POLICY_WARN; + } else { + return null; + } + } - // - // Builder, concurrency and parallelism - // - // We preserve the existing methods for builder selection which is to look for various inputs in the threading - // configuration. We don't have an easy way to allow a pluggable builder to provide its own configuration - // parameters but this is sufficient for now. Ultimately we want components like Builders to provide a way to - // extend the command line to accept its own configuration parameters. - // - final String threadConfiguration = commandLine.hasOption(CLIManager.THREADS) - ? commandLine.getOptionValue(CLIManager.THREADS) - : null; + private void disableOnPresentOption(final CommandLine commandLine, + final String option, + final Consumer setting) { + if (commandLine.hasOption(option)) { + setting.accept(false); + } + } - if (threadConfiguration != null) { - // - // Default to the standard multithreaded builder - // - request.setBuilderId("multithreaded"); + private void disableOnPresentOption(final CommandLine commandLine, + final char option, + final Consumer setting) { + disableOnPresentOption(commandLine, String.valueOf(option), setting); + } - if (threadConfiguration.contains("C")) { - request.setDegreeOfConcurrency(calculateDegreeOfConcurrencyWithCoreMultiplier(threadConfiguration)); - } else { - request.setDegreeOfConcurrency(Integer.parseInt(threadConfiguration)); - } + private void enableOnPresentOption(final CommandLine commandLine, + final String option, + final Consumer setting) { + if (commandLine.hasOption(option)) { + setting.accept(true); } + } - // - // Allow the builder to be overridden by the user if requested. The builders are now pluggable. - // - if (commandLine.hasOption(CLIManager.BUILDER)) { - request.setBuilderId(commandLine.getOptionValue(CLIManager.BUILDER)); + private void enableOnPresentOption(final CommandLine commandLine, + final char option, + final Consumer setting) { + enableOnPresentOption(commandLine, String.valueOf(option), setting); + } + + private void enableOnAbsentOption(final CommandLine commandLine, + final char option, + final Consumer setting) { + if (!commandLine.hasOption(option)) { + setting.accept(true); } } - static int calculateDegreeOfConcurrencyWithCoreMultiplier(String threadConfiguration) { - int procs = Runtime.getRuntime().availableProcessors(); - return (int) (Float.parseFloat(threadConfiguration.replace("C", "")) * procs); + int calculateDegreeOfConcurrency(String threadConfiguration) { + if (threadConfiguration.endsWith("C")) { + threadConfiguration = threadConfiguration.substring(0, threadConfiguration.length() - 1); + + if (!NumberUtils.isParsable(threadConfiguration)) { + throw new IllegalArgumentException("Invalid threads core multiplier value: '" + threadConfiguration + + "C'. Supported are int and float values ending with C."); + } + + float coreMultiplier = Float.parseFloat(threadConfiguration); + + if (coreMultiplier <= 0.0f) { + throw new IllegalArgumentException("Invalid threads core multiplier value: '" + threadConfiguration + + "C'. Value must be positive."); + } + + int procs = Runtime.getRuntime().availableProcessors(); + int threads = (int) (coreMultiplier * procs); + return threads == 0 ? 1 : threads; + } else { + if (!NumberUtils.isParsable(threadConfiguration)) { + throw new IllegalArgumentException("Invalid threads value: '" + threadConfiguration + + "'. Supported are int values."); + } + + try { + int threads = Integer.parseInt(threadConfiguration); + + if (threads <= 0) { + throw new IllegalArgumentException("Invalid threads value: '" + threadConfiguration + + "'. Value must be positive."); + } + + return threads; + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Invalid threads value: '" + threadConfiguration + + "'. Supported are integer values."); + } + } } static File resolveFile(File file, String workingDirectory) { diff --git a/pom.xml b/pom.xml index 4314fdf3b..cb9a83a84 100644 --- a/pom.xml +++ b/pom.xml @@ -419,6 +419,13 @@ limitations under the License. ${format.skip} ${project.build.directory}/cache + + + org.apache.maven + plexus-utils + ${maven.version} + + org.apache.maven.plugins