blob: 7ddc2515778d9b84e99a4a8396d926a9ec34b8c0 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.cli;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.google.inject.AbstractModule;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.ParseException;
import org.apache.maven.InternalErrorException;
import org.apache.maven.Maven;
import org.apache.maven.building.FileSource;
import org.apache.maven.building.Problem;
import org.apache.maven.building.Source;
import org.apache.maven.cli.configuration.ConfigurationProcessor;
import org.apache.maven.cli.configuration.SettingsXmlConfigurationProcessor;
import org.apache.maven.cli.event.ExecutionEventLogger;
import org.apache.maven.cli.internal.BootstrapCoreExtensionManager;
import org.apache.maven.cli.internal.extension.model.CoreExtension;
import org.apache.maven.cli.transfer.QuietMavenTransferListener;
import org.apache.maven.cli.transfer.Slf4jMavenTransferListener;
import org.apache.maven.eventspy.internal.EventSpyDispatcher;
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.scope.internal.MojoExecutionScopeModule;
import org.apache.maven.extension.internal.CoreExports;
import org.apache.maven.extension.internal.CoreExtensionEntry;
import org.apache.maven.lifecycle.LifecycleExecutionException;
import org.apache.maven.model.building.ModelProcessor;
import org.apache.maven.plugin.ExtensionRealmCache;
import org.apache.maven.plugin.PluginArtifactsCache;
import org.apache.maven.plugin.PluginRealmCache;
import org.apache.maven.plugin.version.PluginVersionResolver;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.artifact.ProjectArtifactsCache;
import org.apache.maven.properties.internal.SystemProperties;
import org.apache.maven.session.scope.internal.SessionScopeModule;
import org.apache.maven.shared.utils.logging.MessageBuilder;
import org.apache.maven.shared.utils.logging.MessageUtils;
import org.apache.maven.toolchain.building.DefaultToolchainsBuildingRequest;
import org.apache.maven.toolchain.building.ToolchainsBuilder;
import org.apache.maven.toolchain.building.ToolchainsBuildingResult;
import org.codehaus.plexus.ContainerConfiguration;
import org.codehaus.plexus.DefaultContainerConfiguration;
import org.codehaus.plexus.DefaultPlexusContainer;
import org.codehaus.plexus.PlexusConstants;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.classworlds.ClassWorld;
import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.eclipse.aether.transfer.TransferListener;
import org.mvndaemon.mvnd.cache.invalidating.InvalidatingExtensionRealmCache;
import org.mvndaemon.mvnd.cache.invalidating.InvalidatingPluginArtifactsCache;
import org.mvndaemon.mvnd.cache.invalidating.InvalidatingPluginRealmCache;
import org.mvndaemon.mvnd.cache.invalidating.InvalidatingProjectArtifactsCache;
import org.mvndaemon.mvnd.cli.EnvHelper;
import org.mvndaemon.mvnd.common.Environment;
import org.mvndaemon.mvnd.common.Os;
import org.mvndaemon.mvnd.execution.BuildResumptionPersistenceException;
import org.mvndaemon.mvnd.execution.DefaultBuildResumptionAnalyzer;
import org.mvndaemon.mvnd.execution.DefaultBuildResumptionDataRepository;
import org.mvndaemon.mvnd.logging.internal.Slf4jLoggerManager;
import org.mvndaemon.mvnd.logging.smart.BuildEventListener;
import org.mvndaemon.mvnd.logging.smart.LoggingExecutionListener;
import org.mvndaemon.mvnd.logging.smart.LoggingOutputStream;
import org.mvndaemon.mvnd.plugin.CachingPluginVersionResolver;
import org.mvndaemon.mvnd.transfer.DaemonMavenTransferListener;
import org.slf4j.ILoggerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonatype.plexus.components.sec.dispatcher.DefaultSecDispatcher;
import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher;
import static java.util.Comparator.comparing;
import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;
/**
* File origin:
* https://github.com/apache/maven/blob/maven-3.6.2/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java
*
* @author Jason van Zyl
*/
public class DaemonMavenCli implements DaemonCli {
public static final String LOCAL_REPO_PROPERTY = "maven.repo.local";
public static final String MULTIMODULE_PROJECT_DIRECTORY = "maven.multiModuleProjectDirectory";
public static final String USER_HOME = System.getProperty("user.home");
public static final File USER_MAVEN_CONFIGURATION_HOME = new File(USER_HOME, ".m2");
public static final File DEFAULT_USER_TOOLCHAINS_FILE = new File(USER_MAVEN_CONFIGURATION_HOME, "toolchains.xml");
public static final File DEFAULT_GLOBAL_TOOLCHAINS_FILE =
new File(System.getProperty("maven.conf"), "toolchains.xml");
private static final String EXT_CLASS_PATH = "maven.ext.class.path";
private static final String EXTENSIONS_FILENAME = ".mvn/extensions.xml";
private static final String MVN_MAVEN_CONFIG = ".mvn/maven.config";
public static final String STYLE_COLOR_PROPERTY = "style.color";
public static final String RESUME = "r";
public static final String RAW_STREAMS = "raw-streams";
private final Slf4jLoggerManager plexusLoggerManager;
private final ILoggerFactory slf4jLoggerFactory;
private final Logger slf4jLogger;
private final ClassWorld classWorld;
private final DefaultPlexusContainer container;
private final EventSpyDispatcher eventSpyDispatcher;
private final ModelProcessor modelProcessor;
private final Maven maven;
private final MavenExecutionRequestPopulator executionRequestPopulator;
private final ToolchainsBuilder toolchainsBuilder;
private final DefaultSecDispatcher dispatcher;
private final Map<String, ConfigurationProcessor> configurationProcessors;
private final LoggingExecutionListener executionListener;
/** Non-volatile, assuming that it is accessed only from the main thread */
private BuildEventListener buildEventListener = BuildEventListener.dummy();
public DaemonMavenCli() throws Exception {
slf4jLoggerFactory = LoggerFactory.getILoggerFactory();
slf4jLogger = slf4jLoggerFactory.getLogger(this.getClass().getName());
plexusLoggerManager = new Slf4jLoggerManager();
this.classWorld = ((ClassRealm) Thread.currentThread().getContextClassLoader()).getWorld();
container = container();
eventSpyDispatcher = container.lookup(EventSpyDispatcher.class);
maven = container.lookup(Maven.class);
executionRequestPopulator = container.lookup(MavenExecutionRequestPopulator.class);
modelProcessor = createModelProcessor(container);
configurationProcessors = container.lookupMap(ConfigurationProcessor.class);
toolchainsBuilder = container.lookup(ToolchainsBuilder.class);
dispatcher = (DefaultSecDispatcher) container.lookup(SecDispatcher.class, "maven");
executionListener = container.lookup(LoggingExecutionListener.class);
}
public int main(
List<String> arguments,
String workingDirectory,
String projectDirectory,
Map<String, String> clientEnv,
BuildEventListener buildEventListener)
throws Exception {
this.buildEventListener = buildEventListener;
try {
CliRequest req = new CliRequest(null, null);
req.args = arguments.toArray(new String[0]);
req.workingDirectory = new File(workingDirectory).getCanonicalPath();
req.multiModuleProjectDirectory = new File(projectDirectory);
return doMain(req, clientEnv);
} finally {
this.buildEventListener = BuildEventListener.dummy();
}
}
public int doMain(CliRequest cliRequest, Map<String, String> clientEnv) throws Exception {
Properties props = (Properties) System.getProperties().clone();
ClassLoader tccl = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(container.getContainerRealm());
initialize(cliRequest);
environment(cliRequest.workingDirectory, clientEnv);
cli(cliRequest);
properties(cliRequest);
logging(cliRequest);
informativeCommands(cliRequest);
version(cliRequest);
container(cliRequest);
configure(cliRequest, eventSpyDispatcher, configurationProcessors);
toolchains(cliRequest);
populateRequest(cliRequest);
encryption(cliRequest);
return execute(cliRequest);
} catch (ExitException e) {
return e.exitCode;
} finally {
eventSpyDispatcher.close();
System.setProperties(props);
Thread.currentThread().setContextClassLoader(tccl);
}
}
void initialize(CliRequest cliRequest) throws ExitException {
cliRequest.classWorld = classWorld;
if (cliRequest.workingDirectory == null) {
cliRequest.workingDirectory = System.getProperty("user.dir");
}
if (cliRequest.multiModuleProjectDirectory == null) {
buildEventListener.log(String.format("-D%s system property is not set.", MULTIMODULE_PROJECT_DIRECTORY));
throw new ExitException(1);
}
System.setProperty("maven.multiModuleProjectDirectory", cliRequest.multiModuleProjectDirectory.toString());
}
void cli(CliRequest cliRequest) throws Exception {
CLIManager cliManager = newCLIManager();
CommandLine mavenConfig = null;
try {
File configFile = new File(cliRequest.multiModuleProjectDirectory, MVN_MAVEN_CONFIG);
if (configFile.isFile()) {
try (Stream<String> lines = Files.lines(configFile.toPath(), Charset.defaultCharset())) {
String[] args = lines.filter(arg -> !arg.isEmpty()).toArray(String[]::new);
mavenConfig = cliManager.parse(args);
List<?> unrecognized = mavenConfig.getArgList();
if (!unrecognized.isEmpty()) {
// This file can only contain options, not args (goals or phases)
throw new ParseException("Unrecognized maven.config file entries: " + unrecognized);
}
}
}
} catch (ParseException e) {
buildEventListener.log("Unable to parse maven.config: " + e.getMessage());
buildEventListener.log("Run 'mvnd --help' for available options.");
throw new ExitException(1);
}
try {
if (mavenConfig == null) {
cliRequest.commandLine = cliManager.parse(cliRequest.args);
} else {
cliRequest.commandLine = cliMerge(cliManager.parse(cliRequest.args), mavenConfig);
}
} catch (ParseException e) {
buildEventListener.log("Unable to parse command line options: " + e.getMessage());
buildEventListener.log("Run 'mvnd --help' for available options.");
throw new ExitException(1);
}
}
private void informativeCommands(CliRequest cliRequest) throws Exception {
if (cliRequest.commandLine.hasOption(CLIManager.HELP)) {
buildEventListener.log(MvndHelpFormatter.displayHelp(newCLIManager()));
throw new ExitException(0);
}
if (cliRequest.commandLine.hasOption(CLIManager.VERSION)) {
if (cliRequest.commandLine.hasOption(CLIManager.QUIET)) {
buildEventListener.log(CLIReportingUtils.showVersionMinimal());
} else {
buildEventListener.log(CLIReportingUtils.showVersion());
}
throw new ExitException(0);
}
}
private CLIManager newCLIManager() {
CLIManager cliManager = new CLIManager();
cliManager.options.addOption(Option.builder(RESUME)
.longOpt("resume")
.desc("Resume reactor from "
+ "the last failed project, using the resume.properties file in the build directory")
.build());
cliManager.options.addOption(Option.builder()
.longOpt(RAW_STREAMS)
.desc("Do not decorate output and error streams")
.build());
return cliManager;
}
private CommandLine cliMerge(CommandLine mavenArgs, CommandLine mavenConfig) {
CommandLine.Builder commandLineBuilder = new CommandLine.Builder();
// the args are easy, cli first then config file
for (String arg : mavenArgs.getArgs()) {
commandLineBuilder.addArg(arg);
}
for (String arg : mavenConfig.getArgs()) {
commandLineBuilder.addArg(arg);
}
// now add all options, except for -D with cli first then config file
List<Option> setPropertyOptions = new ArrayList<>();
for (Option opt : mavenArgs.getOptions()) {
if (String.valueOf(CLIManager.SET_SYSTEM_PROPERTY).equals(opt.getOpt())) {
setPropertyOptions.add(opt);
} else {
commandLineBuilder.addOption(opt);
}
}
for (Option opt : mavenConfig.getOptions()) {
commandLineBuilder.addOption(opt);
}
// finally add the CLI system properties
for (Option opt : setPropertyOptions) {
commandLineBuilder.addOption(opt);
}
return commandLineBuilder.build();
}
/**
* configure logging
*/
void logging(CliRequest cliRequest) {
// LOG LEVEL
cliRequest.debug = cliRequest.commandLine.hasOption(CLIManager.DEBUG);
cliRequest.quiet = !cliRequest.debug && cliRequest.commandLine.hasOption(CLIManager.QUIET);
cliRequest.showErrors = cliRequest.debug || cliRequest.commandLine.hasOption(CLIManager.ERRORS);
ch.qos.logback.classic.Level level;
if (cliRequest.debug) {
level = ch.qos.logback.classic.Level.DEBUG;
} else if (cliRequest.quiet) {
level = ch.qos.logback.classic.Level.WARN;
} else {
level = ch.qos.logback.classic.Level.INFO;
}
((ch.qos.logback.classic.Logger) slf4jLoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME)).setLevel(level);
// LOG COLOR
String styleColor = cliRequest.getUserProperties().getProperty(STYLE_COLOR_PROPERTY, "auto");
if ("always".equals(styleColor)) {
MessageUtils.setColorEnabled(true);
} else if ("never".equals(styleColor)) {
MessageUtils.setColorEnabled(false);
} else if (!"auto".equals(styleColor)) {
throw new IllegalArgumentException("Invalid color configuration option [" + styleColor
+ "]. Supported values are (auto|always|never).");
} else if (cliRequest.commandLine.hasOption(CLIManager.BATCH_MODE)
|| cliRequest.commandLine.hasOption(CLIManager.LOG_FILE)) {
MessageUtils.setColorEnabled(false);
}
// Workaround for https://github.com/apache/maven-mvnd/issues/39
final ch.qos.logback.classic.Logger mvndLogger =
(ch.qos.logback.classic.Logger) slf4jLoggerFactory.getLogger("org.mvndaemon.mvnd");
mvndLogger.setLevel(ch.qos.logback.classic.Level.toLevel(System.getProperty("mvnd.log.level"), null));
// LOG STREAMS
if (cliRequest.commandLine.hasOption(CLIManager.LOG_FILE)) {
File logFile = new File(cliRequest.commandLine.getOptionValue(CLIManager.LOG_FILE));
logFile = resolveFile(logFile, cliRequest.workingDirectory);
// redirect stdout and stderr to file
try {
PrintStream ps = new PrintStream(new FileOutputStream(logFile), true);
System.setOut(ps);
System.setErr(ps);
} catch (FileNotFoundException e) {
//
// Ignore
//
}
} else if (!Environment.MVND_RAW_STREAMS
.asOptional()
.map(Boolean::parseBoolean)
.orElse(Boolean.FALSE)) {
ch.qos.logback.classic.Logger stdout =
(ch.qos.logback.classic.Logger) slf4jLoggerFactory.getLogger("stdout");
ch.qos.logback.classic.Logger stderr =
(ch.qos.logback.classic.Logger) slf4jLoggerFactory.getLogger("stderr");
stdout.setLevel(ch.qos.logback.classic.Level.INFO);
stderr.setLevel(ch.qos.logback.classic.Level.INFO);
System.setOut(new LoggingOutputStream(s -> stdout.info("[stdout] " + s)).printStream());
System.setErr(new LoggingOutputStream(s -> stderr.warn("[stderr] " + s)).printStream());
}
}
private void version(CliRequest cliRequest) throws ExitException {
if (cliRequest.debug || cliRequest.commandLine.hasOption(CLIManager.SHOW_VERSION)) {
buildEventListener.log(CLIReportingUtils.showVersion());
}
}
private void commands(CliRequest cliRequest) {
if (cliRequest.showErrors) {
slf4jLogger.info("Error stacktraces are turned on.");
}
if (MavenExecutionRequest.CHECKSUM_POLICY_WARN.equals(cliRequest.request.getGlobalChecksumPolicy())) {
slf4jLogger.info("Disabling strict checksum verification on all artifact downloads.");
} else if (MavenExecutionRequest.CHECKSUM_POLICY_FAIL.equals(cliRequest.request.getGlobalChecksumPolicy())) {
slf4jLogger.info("Enabling strict checksum verification on all artifact downloads.");
}
if (slf4jLogger.isDebugEnabled()) {
slf4jLogger.debug("Message scheme: {}", (MessageUtils.isColorEnabled() ? "color" : "plain"));
if (MessageUtils.isColorEnabled()) {
MessageBuilder buff = MessageUtils.buffer();
buff.a("Message styles: ");
buff.a(MessageUtils.level().debug("debug")).a(' ');
buff.a(MessageUtils.level().info("info")).a(' ');
buff.a(MessageUtils.level().warning("warning")).a(' ');
buff.a(MessageUtils.level().error("error")).a(' ');
buff.success("success").a(' ');
buff.failure("failure").a(' ');
buff.strong("strong").a(' ');
buff.mojo("mojo").a(' ');
buff.project("project");
slf4jLogger.debug(buff.toString());
}
}
}
// Needed to make this method package visible to make writing a unit test possible
// Maybe it's better to move some of those methods to separate class (SoC).
void properties(CliRequest cliRequest) {
populateProperties(cliRequest.commandLine, cliRequest.systemProperties, cliRequest.userProperties);
}
void container(CliRequest cliRequest) {
Map<String, Object> data = new HashMap<>();
data.put("plexus", container);
data.put("workingDirectory", cliRequest.workingDirectory);
data.put("systemProperties", cliRequest.systemProperties);
data.put("userProperties", cliRequest.userProperties);
data.put("versionProperties", CLIReportingUtils.getBuildProperties());
eventSpyDispatcher.init(() -> data);
}
DefaultPlexusContainer container() throws Exception {
ClassRealm coreRealm = classWorld.getClassRealm("plexus.core");
if (coreRealm == null) {
coreRealm = classWorld.getRealms().iterator().next();
}
List<File> extClassPath = Stream.of(
Environment.MVND_EXT_CLASSPATH.asString().split(","))
.filter(s -> s != null && !s.isEmpty())
.map(File::new)
.collect(Collectors.toList());
CoreExtensionEntry coreEntry = CoreExtensionEntry.discoverFrom(coreRealm);
List<CoreExtension> extensions = Stream.of(
Environment.MVND_CORE_EXTENSIONS.asString().split(";"))
.filter(s -> s != null && !s.isEmpty())
.map(s -> {
String[] parts = s.split(":");
CoreExtension ce = new CoreExtension();
ce.setGroupId(parts[0]);
ce.setArtifactId(parts[1]);
ce.setVersion(parts[2]);
return ce;
})
.collect(Collectors.toList());
List<CoreExtensionEntry> extensionsEntries =
loadCoreExtensions(extensions, coreRealm, coreEntry.getExportedArtifacts());
ClassRealm containerRealm = setupContainerRealm(classWorld, coreRealm, extClassPath, extensionsEntries);
ContainerConfiguration cc = new DefaultContainerConfiguration()
.setClassWorld(classWorld)
.setRealm(containerRealm)
.setClassPathScanning(PlexusConstants.SCANNING_INDEX)
.setAutoWiring(true)
.setJSR250Lifecycle(true)
.setName("maven");
Set<String> exportedArtifacts = new HashSet<>(coreEntry.getExportedArtifacts());
Set<String> exportedPackages = new HashSet<>(coreEntry.getExportedPackages());
for (CoreExtensionEntry extension : extensionsEntries) {
exportedArtifacts.addAll(extension.getExportedArtifacts());
exportedPackages.addAll(extension.getExportedPackages());
}
exportedPackages.add("org.codehaus.plexus.components.interactivity");
exportedPackages.add("org.mvndaemon.mvnd.interactivity");
exportedArtifacts.add("org.codehaus.plexus:plexus-interactivity-api");
final CoreExports exports = new CoreExports(containerRealm, exportedArtifacts, exportedPackages);
final DefaultPlexusContainer container = new DefaultPlexusContainer(cc, new AbstractModule() {
@Override
protected void configure() {
bind(ILoggerFactory.class).toInstance(slf4jLoggerFactory);
bind(CoreExports.class).toInstance(exports);
bind(ExtensionRealmCache.class).to(InvalidatingExtensionRealmCache.class);
bind(PluginArtifactsCache.class).to(InvalidatingPluginArtifactsCache.class);
bind(PluginRealmCache.class).to(InvalidatingPluginRealmCache.class);
bind(ProjectArtifactsCache.class).to(InvalidatingProjectArtifactsCache.class);
bind(PluginVersionResolver.class).to(CachingPluginVersionResolver.class);
}
});
// NOTE: To avoid inconsistencies, we'll use the TCCL exclusively for lookups
container.setLookupRealm(null);
Thread.currentThread().setContextClassLoader(container.getContainerRealm());
container.setLoggerManager(plexusLoggerManager);
for (CoreExtensionEntry extension : extensionsEntries) {
container.discoverComponents(
extension.getClassRealm(),
new SessionScopeModule(container),
new MojoExecutionScopeModule(container));
}
return container;
}
private List<CoreExtensionEntry> loadCoreExtensions(
List<CoreExtension> extensions, ClassRealm containerRealm, Set<String> providedArtifacts) {
try {
if (extensions.isEmpty()) {
return Collections.emptyList();
}
ContainerConfiguration cc = new DefaultContainerConfiguration() //
.setClassWorld(classWorld) //
.setRealm(containerRealm) //
.setClassPathScanning(PlexusConstants.SCANNING_INDEX) //
.setAutoWiring(true) //
.setJSR250Lifecycle(true) //
.setName("maven");
DefaultPlexusContainer container = new DefaultPlexusContainer(cc, new AbstractModule() {
@Override
protected void configure() {
bind(ILoggerFactory.class).toInstance(slf4jLoggerFactory);
}
});
MavenExecutionRequestPopulator executionRequestPopulator = null;
try {
CliRequest cliRequest = new CliRequest(new String[0], classWorld);
cliRequest.commandLine = new CommandLine.Builder().build();
container.setLookupRealm(null);
container.setLoggerManager(plexusLoggerManager);
container.getLoggerManager().setThresholds(cliRequest.request.getLoggingLevel());
Thread.currentThread().setContextClassLoader(container.getContainerRealm());
executionRequestPopulator = container.lookup(MavenExecutionRequestPopulator.class);
final Map<String, ConfigurationProcessor> configurationProcessors =
container.lookupMap(ConfigurationProcessor.class);
final EventSpyDispatcher eventSpyDispatcher = container.lookup(EventSpyDispatcher.class);
properties(cliRequest);
configure(cliRequest, eventSpyDispatcher, configurationProcessors);
LoggingExecutionListener executionListener = container.lookup(LoggingExecutionListener.class);
populateRequest(
cliRequest,
cliRequest.request,
slf4jLogger,
eventSpyDispatcher,
container.lookup(ModelProcessor.class),
createTransferListener(cliRequest),
buildEventListener,
executionListener);
executionRequestPopulator.populateDefaults(cliRequest.request);
BootstrapCoreExtensionManager resolver = container.lookup(BootstrapCoreExtensionManager.class);
return Collections.unmodifiableList(
resolver.loadCoreExtensions(cliRequest.request, providedArtifacts, extensions));
} finally {
executionRequestPopulator = null;
container.dispose();
}
} catch (RuntimeException e) {
// runtime exceptions are most likely bugs in maven, let them bubble up to the user
throw e;
} catch (Exception e) {
slf4jLogger.warn("Failed to load extensions descriptor {}: {}", extensions, e.getMessage());
}
return Collections.emptyList();
}
private ClassRealm setupContainerRealm(
ClassWorld classWorld, ClassRealm coreRealm, List<File> extClassPath, List<CoreExtensionEntry> extensions)
throws Exception {
if (!extClassPath.isEmpty() || !extensions.isEmpty()) {
ClassRealm extRealm = classWorld.newRealm("maven.ext", null);
extRealm.setParentRealm(coreRealm);
slf4jLogger.debug("Populating class realm {}", extRealm.getId());
for (File file : extClassPath) {
extRealm.addURL(file.toURI().toURL());
}
for (CoreExtensionEntry entry : reverse(extensions)) {
Set<String> exportedPackages = entry.getExportedPackages();
ClassRealm realm = entry.getClassRealm();
for (String exportedPackage : exportedPackages) {
extRealm.importFrom(realm, exportedPackage);
}
if (exportedPackages.isEmpty()) {
// sisu uses realm imports to establish component visibility
extRealm.importFrom(realm, realm.getId());
}
}
return extRealm;
}
return coreRealm;
}
private static <T> List<T> reverse(List<T> list) {
List<T> copy = new ArrayList<>(list);
Collections.reverse(copy);
return copy;
}
private List<File> parseExtClasspath(CliRequest cliRequest) {
String extClassPath = cliRequest.userProperties.getProperty(EXT_CLASS_PATH);
if (extClassPath == null) {
extClassPath = cliRequest.systemProperties.getProperty(EXT_CLASS_PATH);
}
List<File> jars = new ArrayList<>();
if (extClassPath != null) {
for (String jar : extClassPath.split(File.pathSeparator)) {
File file = resolveFile(new File(jar), cliRequest.workingDirectory);
slf4jLogger.debug(" Included {}", file);
jars.add(file);
}
}
return jars;
}
//
// This should probably be a separate tool and not be baked into Maven.
//
private void encryption(CliRequest cliRequest) throws Exception {
if (cliRequest.commandLine.hasOption(CLIManager.ENCRYPT_MASTER_PASSWORD)) {
throw new UnsupportedOperationException("Unsupported option: " + CLIManager.ENCRYPT_MASTER_PASSWORD);
} else if (cliRequest.commandLine.hasOption(CLIManager.ENCRYPT_PASSWORD)) {
throw new UnsupportedOperationException("Unsupported option: " + CLIManager.ENCRYPT_PASSWORD);
}
}
private void environment(String workingDir, Map<String, String> clientEnv) {
EnvHelper.environment(workingDir, clientEnv);
}
private int execute(CliRequest cliRequest) throws MavenExecutionRequestPopulationException {
commands(cliRequest);
MavenExecutionRequest request = executionRequestPopulator.populateDefaults(cliRequest.request);
eventSpyDispatcher.onEvent(request);
slf4jLogger.info(buffer().a("Processing build on daemon ")
.strong(Environment.MVND_ID.asString())
.toString());
MavenExecutionResult result = maven.execute(request);
LoggingOutputStream.forceFlush(System.out);
LoggingOutputStream.forceFlush(System.err);
eventSpyDispatcher.onEvent(result);
if (result.hasExceptions()) {
ExceptionHandler handler = new DefaultExceptionHandler();
Map<String, String> references = new LinkedHashMap<>();
List<MavenProject> failedProjects = new ArrayList<>();
for (Throwable exception : result.getExceptions()) {
ExceptionSummary summary = handler.handleException(exception);
logSummary(summary, references, "", cliRequest.showErrors);
if (exception instanceof LifecycleExecutionException) {
MavenProject project = ((LifecycleExecutionException) exception).getProject();
if (project != null) {
failedProjects.add(project);
}
}
}
slf4jLogger.error("");
if (!cliRequest.showErrors) {
slf4jLogger.error(
"To see the full stack trace of the errors, re-run Maven with the {} switch.",
buffer().strong("-e"));
}
if (!slf4jLogger.isDebugEnabled()) {
slf4jLogger.error(
"Re-run Maven using the {} switch to enable full debug logging.", buffer().strong("-X"));
}
if (!references.isEmpty()) {
slf4jLogger.error("");
slf4jLogger.error("For more information about the errors and possible solutions"
+ ", please read the following articles:");
for (Entry<String, String> entry : references.entrySet()) {
slf4jLogger.error("{} {}", buffer().strong(entry.getValue()), entry.getKey());
}
}
boolean canResume = new DefaultBuildResumptionAnalyzer()
.determineBuildResumptionData(result)
.map(resumption -> {
try {
Path directory =
Paths.get(request.getBaseDirectory()).resolve("target");
new DefaultBuildResumptionDataRepository().persistResumptionData(directory, resumption);
return true;
} catch (BuildResumptionPersistenceException e) {
slf4jLogger.warn("Could not persist build resumption data", e);
}
return false;
})
.orElse(false);
if (canResume) {
logBuildResumeHint("mvn <args> -r");
} else if (!failedProjects.isEmpty()) {
List<MavenProject> sortedProjects = result.getTopologicallySortedProjects();
// Sort the failedProjects list in the topologically sorted order.
failedProjects.sort(comparing(sortedProjects::indexOf));
MavenProject firstFailedProject = failedProjects.get(0);
if (!firstFailedProject.equals(sortedProjects.get(0))) {
String resumeFromSelector = getResumeFromSelector(sortedProjects, firstFailedProject);
logBuildResumeHint("mvn <args> -rf " + resumeFromSelector);
}
}
if (MavenExecutionRequest.REACTOR_FAIL_NEVER.equals(cliRequest.request.getReactorFailureBehavior())) {
slf4jLogger.info("Build failures were ignored.");
return 0;
} else {
return 1;
}
} else {
Path directory = Paths.get(request.getBaseDirectory()).resolve("target");
new DefaultBuildResumptionDataRepository().removeResumptionData(directory);
return 0;
}
}
private void logBuildResumeHint(String resumeBuildHint) {
slf4jLogger.error("");
slf4jLogger.error("After correcting the problems, you can resume the build with the command");
slf4jLogger.error(buffer().a(" ").strong(resumeBuildHint).toString());
}
/**
* A helper method to determine the value to resume the build with {@code -rf} taking into account the
* edge case where multiple modules in the reactor have the same artifactId.
* <p>
* {@code -rf :artifactId} will pick up the first module which matches, but when multiple modules in the
* reactor have the same artifactId, effective failed module might be later in build reactor.
* This means that developer will either have to type groupId or wait for build execution of all modules
* which were fine, but they are still before one which reported errors.
* <p>
* Then the returned value is {@code groupId:artifactId} when there is a name clash and
* {@code :artifactId} if there is no conflict.
*
* @param mavenProjects Maven projects which are part of build execution.
* @param failedProject Project which has failed.
* @return Value for -rf flag to resume build exactly from place where it failed ({@code :artifactId} in
* general and {@code groupId:artifactId} when there is a name clash).
*/
private String getResumeFromSelector(List<MavenProject> mavenProjects, MavenProject failedProject) {
for (MavenProject buildProject : mavenProjects) {
if (failedProject.getArtifactId().equals(buildProject.getArtifactId())
&& !failedProject.equals(buildProject)) {
return failedProject.getGroupId() + ":" + failedProject.getArtifactId();
}
}
return ":" + failedProject.getArtifactId();
}
private void logSummary(
ExceptionSummary summary, Map<String, String> references, String indent, boolean showErrors) {
String msg = summary.getMessage();
if (!summary.getReference().isEmpty()) {
String referenceKey =
references.computeIfAbsent(summary.getReference(), k -> "[Help " + (references.size() + 1) + "]");
if (msg.indexOf('\n') < 0) {
msg += " -> " + buffer().strong(referenceKey);
} else {
msg += "\n-> " + buffer().strong(referenceKey);
}
}
String[] lines = msg.split("(\r\n)|(\r)|(\n)");
String currentColor = "";
for (int i = 0; i < lines.length; i++) {
// add eventual current color inherited from previous line
String line = currentColor + lines[i];
// look for last ANSI escape sequence to check if nextColor
Matcher matcher = LAST_ANSI_SEQUENCE.matcher(line);
String nextColor = "";
if (matcher.find()) {
nextColor = matcher.group(1);
if (ANSI_RESET.equals(nextColor)) {
// last ANSI escape code is reset: no next color
nextColor = "";
}
}
// effective line, with indent and reset if end is colored
line = indent + line + ("".equals(nextColor) ? "" : ANSI_RESET);
if ((i == lines.length - 1) && (showErrors || (summary.getException() instanceof InternalErrorException))) {
slf4jLogger.error(line, summary.getException());
} else {
slf4jLogger.error(line);
}
currentColor = nextColor;
}
indent += " ";
for (ExceptionSummary child : summary.getChildren()) {
logSummary(child, references, indent, showErrors);
}
}
private static final Pattern LAST_ANSI_SEQUENCE = Pattern.compile("(\u001B\\[[;\\d]*[ -/]*[@-~])[^\u001B]*$");
private static final String ANSI_RESET = "\u001B\u005Bm";
private static void configure(
CliRequest cliRequest,
EventSpyDispatcher eventSpyDispatcher,
Map<String, ConfigurationProcessor> configurationProcessors)
throws Exception {
//
// This is not ideal but there are events specifically for configuration from the CLI which I don't
// believe are really valid but there are ITs which assert the right events are published so this
// needs to be supported so the EventSpyDispatcher needs to be put in the CliRequest so that
// it can be accessed by configuration processors.
//
cliRequest.request.setEventSpyDispatcher(eventSpyDispatcher);
//
// We expect at most 2 implementations to be available. The SettingsXmlConfigurationProcessor implementation
// is always available in the core and likely always will be, but we may have another ConfigurationProcessor
// present supplied by the user. The rule is that we only allow the execution of one ConfigurationProcessor.
// If there is more than one then we execute the one supplied by the user, otherwise we execute the
// the default SettingsXmlConfigurationProcessor.
//
int userSuppliedConfigurationProcessorCount = configurationProcessors.size() - 1;
if (userSuppliedConfigurationProcessorCount == 0) {
//
// Our settings.xml source is historically how we have configured Maven from the CLI so we are going to
// have to honour its existence forever. So let's run it.
//
configurationProcessors.get(SettingsXmlConfigurationProcessor.HINT).process(cliRequest);
} else if (userSuppliedConfigurationProcessorCount == 1) {
//
// Run the user supplied ConfigurationProcessor
//
for (Entry<String, ConfigurationProcessor> entry : configurationProcessors.entrySet()) {
String hint = entry.getKey();
if (!hint.equals(SettingsXmlConfigurationProcessor.HINT)) {
ConfigurationProcessor configurationProcessor = entry.getValue();
configurationProcessor.process(cliRequest);
}
}
} else if (userSuppliedConfigurationProcessorCount > 1) {
//
// There are too many ConfigurationProcessors so we don't know which one to run so report the error.
//
StringBuilder sb = new StringBuilder(String.format(
"\nThere can only be one user supplied ConfigurationProcessor, there are %s:\n\n",
userSuppliedConfigurationProcessorCount));
for (Entry<String, ConfigurationProcessor> entry : configurationProcessors.entrySet()) {
String hint = entry.getKey();
if (!hint.equals(SettingsXmlConfigurationProcessor.HINT)) {
ConfigurationProcessor configurationProcessor = entry.getValue();
sb.append(String.format(
"%s\n", configurationProcessor.getClass().getName()));
}
}
sb.append("\n");
throw new Exception(sb.toString());
}
}
void toolchains(CliRequest cliRequest) throws Exception {
File userToolchainsFile;
if (cliRequest.commandLine.hasOption(CLIManager.ALTERNATE_USER_TOOLCHAINS)) {
userToolchainsFile = new File(cliRequest.commandLine.getOptionValue(CLIManager.ALTERNATE_USER_TOOLCHAINS));
userToolchainsFile = resolveFile(userToolchainsFile, cliRequest.workingDirectory);
if (!userToolchainsFile.isFile()) {
throw new FileNotFoundException(
"The specified user toolchains file does not exist: " + userToolchainsFile);
}
} else {
userToolchainsFile = DEFAULT_USER_TOOLCHAINS_FILE;
}
File globalToolchainsFile;
if (cliRequest.commandLine.hasOption(CLIManager.ALTERNATE_GLOBAL_TOOLCHAINS)) {
globalToolchainsFile =
new File(cliRequest.commandLine.getOptionValue(CLIManager.ALTERNATE_GLOBAL_TOOLCHAINS));
globalToolchainsFile = resolveFile(globalToolchainsFile, cliRequest.workingDirectory);
if (!globalToolchainsFile.isFile()) {
throw new FileNotFoundException(
"The specified global toolchains file does not exist: " + globalToolchainsFile);
}
} else {
globalToolchainsFile = DEFAULT_GLOBAL_TOOLCHAINS_FILE;
}
cliRequest.request.setGlobalToolchainsFile(globalToolchainsFile);
cliRequest.request.setUserToolchainsFile(userToolchainsFile);
DefaultToolchainsBuildingRequest toolchainsRequest = new DefaultToolchainsBuildingRequest();
if (globalToolchainsFile.isFile()) {
toolchainsRequest.setGlobalToolchainsSource(new FileSource(globalToolchainsFile));
}
if (userToolchainsFile.isFile()) {
toolchainsRequest.setUserToolchainsSource(new FileSource(userToolchainsFile));
}
eventSpyDispatcher.onEvent(toolchainsRequest);
slf4jLogger.debug(
"Reading global toolchains from {}",
getLocation(toolchainsRequest.getGlobalToolchainsSource(), globalToolchainsFile));
slf4jLogger.debug(
"Reading user toolchains from {}",
getLocation(toolchainsRequest.getUserToolchainsSource(), userToolchainsFile));
ToolchainsBuildingResult toolchainsResult = toolchainsBuilder.build(toolchainsRequest);
eventSpyDispatcher.onEvent(toolchainsResult);
executionRequestPopulator.populateFromToolchains(cliRequest.request, toolchainsResult.getEffectiveToolchains());
if (!toolchainsResult.getProblems().isEmpty() && slf4jLogger.isWarnEnabled()) {
slf4jLogger.warn("");
slf4jLogger.warn("Some problems were encountered while building the effective toolchains");
for (Problem problem : toolchainsResult.getProblems()) {
slf4jLogger.warn("{} @ {}", problem.getMessage(), problem.getLocation());
}
slf4jLogger.warn("");
}
}
private Object getLocation(Source source, File defaultLocation) {
if (source != null) {
return source.getLocation();
}
return defaultLocation;
}
private void populateRequest(CliRequest cliRequest) {
populateRequest(
cliRequest,
cliRequest.request,
slf4jLogger,
eventSpyDispatcher,
modelProcessor,
createTransferListener(cliRequest),
buildEventListener,
executionListener);
}
private static void populateRequest(
CliRequest cliRequest,
MavenExecutionRequest request,
Logger slf4jLogger,
EventSpyDispatcher eventSpyDispatcher,
ModelProcessor modelProcessor,
TransferListener transferListener,
BuildEventListener buildEventListener,
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.SUPRESS_SNAPSHOT_UPDATES)) {
noSnapshotUpdates = true;
}
// ----------------------------------------------------------------------
//
// ----------------------------------------------------------------------
List<String> goals = commandLine.getArgList();
boolean recursive = true;
// this is the default behavior.
String reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_FAST;
if (commandLine.hasOption(CLIManager.NON_RECURSIVE)) {
recursive = false;
}
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 (commandLine.hasOption(CLIManager.OFFLINE)) {
request.setOffline(true);
}
boolean updateSnapshots = false;
if (commandLine.hasOption(CLIManager.UPDATE_SNAPSHOTS)) {
updateSnapshots = true;
}
String globalChecksumPolicy = null;
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;
}
File baseDirectory = new File(workingDirectory, "").getAbsoluteFile();
// ----------------------------------------------------------------------
// Profile Activation
// ----------------------------------------------------------------------
List<String> activeProfiles = new ArrayList<>();
List<String> inactiveProfiles = new ArrayList<>();
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);
}
}
}
}
}
ExecutionEventLogger executionEventLogger = new ExecutionEventLogger();
executionListener.init(eventSpyDispatcher.chainListener(executionEventLogger), buildEventListener);
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);
} else if (modelProcessor != null) {
File pom = modelProcessor.locatePom(baseDirectory);
if (pom.isFile()) {
request.setPom(pom);
}
}
if ((request.getPom() != null) && (request.getPom().getParentFile() != null)) {
request.setBaseDirectory(request.getPom().getParentFile());
}
if (commandLine.hasOption(RESUME)) {
new DefaultBuildResumptionDataRepository()
.applyResumptionData(
request, Paths.get(request.getBaseDirectory()).resolve("target"));
}
if (commandLine.hasOption(CLIManager.RESUME_FROM)) {
request.setResumeFrom(commandLine.getOptionValue(CLIManager.RESUME_FROM));
}
if (commandLine.hasOption(CLIManager.PROJECT_LIST)) {
String[] projectOptionValues = commandLine.getOptionValues(CLIManager.PROJECT_LIST);
List<String> inclProjects = new ArrayList<>();
List<String> exclProjects = new ArrayList<>();
if (projectOptionValues != null) {
for (String projectOptionValue : projectOptionValues) {
StringTokenizer projectTokens = new StringTokenizer(projectOptionValue, ",");
while (projectTokens.hasMoreTokens()) {
String projectAction = projectTokens.nextToken().trim();
if (projectAction.startsWith("-") || projectAction.startsWith("!")) {
exclProjects.add(projectAction.substring(1));
} else if (projectAction.startsWith("+")) {
inclProjects.add(projectAction.substring(1));
} else {
inclProjects.add(projectAction);
}
}
}
}
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);
}
String localRepoProperty = request.getUserProperties().getProperty(MavenCli.LOCAL_REPO_PROPERTY);
if (localRepoProperty == null) {
localRepoProperty = request.getSystemProperties().getProperty(MavenCli.LOCAL_REPO_PROPERTY);
}
if (localRepoProperty != null) {
request.setLocalRepositoryPath(localRepoProperty);
}
request.setCacheNotFound(true);
request.setCacheTransferError(false);
//
// 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;
if (threadConfiguration != null) {
//
// Default to the standard multithreaded builder
//
request.setBuilderId("multithreaded");
if (threadConfiguration.contains("C")) {
request.setDegreeOfConcurrency(calculateDegreeOfConcurrencyWithCoreMultiplier(threadConfiguration));
} else {
request.setDegreeOfConcurrency(Integer.parseInt(threadConfiguration));
}
}
//
// 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));
}
}
static int calculateDegreeOfConcurrencyWithCoreMultiplier(String threadConfiguration) {
int procs = Runtime.getRuntime().availableProcessors();
return (int) (Float.parseFloat(threadConfiguration.replace("C", "")) * procs);
}
static File resolveFile(File file, String workingDirectory) {
if (file == null) {
return null;
} else if (file.isAbsolute()) {
return file;
} else if (file.getPath().startsWith(File.separator)) {
// drive-relative Windows path
return file.getAbsoluteFile();
} else {
return new File(workingDirectory, file.getPath()).getAbsoluteFile();
}
}
// ----------------------------------------------------------------------
// System properties handling
// ----------------------------------------------------------------------
static void populateProperties(CommandLine commandLine, Properties systemProperties, Properties userProperties) {
addEnvVars(systemProperties);
// ----------------------------------------------------------------------
// Options that are set on the command line become system properties
// and therefore are set in the session properties. System properties
// are most dominant.
// ----------------------------------------------------------------------
if (commandLine.hasOption(CLIManager.SET_SYSTEM_PROPERTY)) {
String[] defStrs = commandLine.getOptionValues(CLIManager.SET_SYSTEM_PROPERTY);
if (defStrs != null) {
for (String defStr : defStrs) {
setCliProperty(defStr, userProperties);
}
}
}
SystemProperties.addSystemProperties(systemProperties);
// ----------------------------------------------------------------------
// Properties containing info about the currently running version of Maven
// These override any corresponding properties set on the command line
// ----------------------------------------------------------------------
Properties buildProperties = CLIReportingUtils.getBuildProperties();
String mavenVersion = buildProperties.getProperty(CLIReportingUtils.BUILD_VERSION_PROPERTY);
systemProperties.setProperty("maven.version", mavenVersion);
String mavenBuildVersion = CLIReportingUtils.createMavenVersionString(buildProperties);
systemProperties.setProperty("maven.build.version", mavenBuildVersion);
}
public static void addEnvVars(Properties props) {
if (props != null) {
boolean caseSensitive = Os.current() == Os.WINDOWS;
for (Map.Entry<String, String> entry : System.getenv().entrySet()) {
String key = "env."
+ (caseSensitive ? entry.getKey() : entry.getKey().toUpperCase(Locale.ENGLISH));
props.setProperty(key, entry.getValue());
}
}
}
private static void setCliProperty(String property, Properties properties) {
String name;
String value;
int i = property.indexOf('=');
if (i <= 0) {
name = property.trim();
value = "true";
} else {
name = property.substring(0, i).trim();
value = property.substring(i + 1);
}
properties.setProperty(name, value);
// ----------------------------------------------------------------------
// I'm leaving the setting of system properties here as not to break
// the SystemPropertyProfileActivator. This won't harm embedding. jvz.
// ----------------------------------------------------------------------
System.setProperty(name, value);
}
static class ExitException extends Exception {
static final long serialVersionUID = 1L;
int exitCode;
ExitException(int exitCode) {
this.exitCode = exitCode;
}
}
//
// Customizations available via the CLI
//
protected TransferListener createTransferListener(CliRequest cliRequest) {
if (cliRequest.quiet || cliRequest.commandLine.hasOption(CLIManager.NO_TRANSFER_PROGRESS)) {
return new QuietMavenTransferListener();
} else if (cliRequest.request.isInteractiveMode() && !cliRequest.commandLine.hasOption(CLIManager.LOG_FILE)) {
//
// If we're logging to a file then we don't want the console transfer listener as it will spew
// download progress all over the place
//
return getConsoleTransferListener();
} else {
return getBatchTransferListener();
}
}
protected TransferListener getConsoleTransferListener() {
return new DaemonMavenTransferListener(buildEventListener, new Slf4jMavenTransferListener());
}
protected TransferListener getBatchTransferListener() {
return new Slf4jMavenTransferListener();
}
protected ModelProcessor createModelProcessor(PlexusContainer container) throws ComponentLookupException {
return container.lookup(ModelProcessor.class);
}
}