Switch to maven 4.0.0-alpha-2 (#718)

This commit is contained in:
Guillaume Nodet
2022-11-19 16:56:26 +01:00
committed by GitHub
parent 5a8f29cf6f
commit 895381b43f
16 changed files with 407 additions and 860 deletions

View File

@@ -24,8 +24,6 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
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;
@@ -37,7 +35,7 @@ import java.util.Map;
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;
@@ -45,6 +43,7 @@ import java.util.stream.Stream;
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;
@@ -61,10 +60,7 @@ 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.*;
import org.apache.maven.execution.scope.internal.MojoExecutionScopeModule;
import org.apache.maven.extension.internal.CoreExports;
import org.apache.maven.extension.internal.CoreExtensionEntry;
@@ -101,9 +97,6 @@ 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;
@@ -177,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 {
@@ -354,12 +349,12 @@ public class DaemonMavenCli {
*/
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);
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);
ch.qos.logback.classic.Level level;
if (cliRequest.debug) {
if (cliRequest.verbose) {
level = ch.qos.logback.classic.Level.DEBUG;
} else if (cliRequest.quiet) {
level = ch.qos.logback.classic.Level.WARN;
@@ -413,7 +408,7 @@ public class DaemonMavenCli {
}
private void version(CliRequest cliRequest) throws ExitException {
if (cliRequest.debug || cliRequest.commandLine.hasOption(CLIManager.VERSION)) {
if (cliRequest.verbose || cliRequest.commandLine.hasOption(CLIManager.VERSION)) {
buildEventListener.log(CLIReportingUtils.showVersion());
if (cliRequest.commandLine.hasOption(CLIManager.VERSION)) {
throw new ExitException(0);
@@ -576,7 +571,7 @@ public class DaemonMavenCli {
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);
@@ -729,18 +724,7 @@ public class DaemonMavenCli {
}
}
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) {
if (result.canResume()) {
logBuildResumeHint("mvn <args> -r");
} else if (!failedProjects.isEmpty()) {
List<MavenProject> sortedProjects = result.getTopologicallySortedProjects();
@@ -763,8 +747,6 @@ public class DaemonMavenCli {
return 1;
}
} else {
Path directory = Paths.get(request.getBaseDirectory()).resolve("target");
new DefaultBuildResumptionDataRepository().removeResumptionData(directory);
return 0;
}
}
@@ -999,14 +981,13 @@ public class DaemonMavenCli {
}
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,
@@ -1014,208 +995,50 @@ public class DaemonMavenCli {
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;
}
boolean quiet = cliRequest.quiet;
boolean verbose = cliRequest.verbose;
request.setShowErrors(cliRequest.showErrors); // default: false
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);
}
}
}
}
}
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);
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);
}
enableOnPresentOption(commandLine, CLIManager.RESUME, request::setResume);
request.setMakeBehavior(determineMakeBehavior(commandLine));
request.setCacheNotFound(true);
request.setCacheTransferError(false);
performProjectActivation(commandLine, request.getProjectActivation());
performProfileActivation(commandLine, request.getProfileActivation());
final String localRepositoryPath = determineLocalRepositoryPath(request);
if (localRepositoryPath != null) {
request.setLocalRepositoryPath(localRepositoryPath);
}
//
// Builder, concurrency and parallelism
//
@@ -1224,34 +1047,233 @@ public class DaemonMavenCli {
// 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;
final String threadConfiguration = commandLine.getOptionValue(CLIManager.THREADS);
if (threadConfiguration != null) {
//
// Default to the standard multithreaded builder
//
int degreeOfConcurrency = calculateDegreeOfConcurrency(threadConfiguration);
if (degreeOfConcurrency > 1) {
request.setBuilderId("multithreaded");
if (threadConfiguration.contains("C")) {
request.setDegreeOfConcurrency(calculateDegreeOfConcurrencyWithCoreMultiplier(threadConfiguration));
} else {
request.setDegreeOfConcurrency(Integer.parseInt(threadConfiguration));
request.setDegreeOfConcurrency(degreeOfConcurrency);
}
}
//
// 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));
request.setBuilderId(commandLine.getOptionValue(CLIManager.BUILDER, request.getBuilderId()));
}
private String determineLocalRepositoryPath(final MavenExecutionRequest request) {
String userDefinedLocalRepo = request.getUserProperties().getProperty(MavenCli.LOCAL_REPO_PROPERTY);
if (userDefinedLocalRepo != null) {
return userDefinedLocalRepo;
}
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);
}
if (alternatePomFile != null) {
File pom = resolveFile(new File(alternatePomFile), workingDirectory);
if (pom.isDirectory()) {
pom = new File(pom, "pom.xml");
}
return pom;
} else if (modelProcessor != null) {
File pom = modelProcessor.locatePom(baseDirectory);
if (pom.isFile()) {
return pom;
}
}
static int calculateDegreeOfConcurrencyWithCoreMultiplier(String threadConfiguration) {
return null;
}
// 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 (optionValues == null || optionValues.length == 0) {
return;
}
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);
}
boolean optional = selector.charAt(0) == '?';
selector = selector.substring(optional ? 1 : 0);
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);
if (optionValues == null || optionValues.length == 0) {
return;
}
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);
}
}
}
}
private ExecutionListener determineExecutionListener(EventSpyDispatcher eventSpyDispatcher) {
ExecutionListener executionListener = new ExecutionEventLogger();
if (eventSpyDispatcher != null) {
return eventSpyDispatcher.chainListener(executionListener);
} else {
return executionListener;
}
}
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;
}
}
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;
}
}
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;
}
}
private void disableOnPresentOption(final CommandLine commandLine,
final String option,
final Consumer<Boolean> setting) {
if (commandLine.hasOption(option)) {
setting.accept(false);
}
}
private void disableOnPresentOption(final CommandLine commandLine,
final char option,
final Consumer<Boolean> setting) {
disableOnPresentOption(commandLine, String.valueOf(option), setting);
}
private void enableOnPresentOption(final CommandLine commandLine,
final String option,
final Consumer<Boolean> setting) {
if (commandLine.hasOption(option)) {
setting.accept(true);
}
}
private void enableOnPresentOption(final CommandLine commandLine,
final char option,
final Consumer<Boolean> setting) {
enableOnPresentOption(commandLine, String.valueOf(option), setting);
}
private void enableOnAbsentOption(final CommandLine commandLine,
final char option,
final Consumer<Boolean> setting) {
if (!commandLine.hasOption(option)) {
setting.accept(true);
}
}
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();
return (int) (Float.parseFloat(threadConfiguration.replace("C", "")) * procs);
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) {
@@ -1280,15 +1302,10 @@ public class DaemonMavenCli {
// 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);
}
}
}
final Properties userSpecifiedProperties = commandLine.getOptionProperties(
String.valueOf(CLIManager.SET_SYSTEM_PROPERTY));
userSpecifiedProperties.forEach(
(prop, value) -> setCliProperty((String) prop, (String) value, userProperties));
SystemProperties.addSystemProperties(systemProperties);
@@ -1316,23 +1333,7 @@ public class DaemonMavenCli {
}
}
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);
}
private static void setCliProperty(String name, String value, Properties properties) {
properties.setProperty(name, value);
// ----------------------------------------------------------------------

View File

@@ -31,6 +31,7 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
@@ -41,6 +42,7 @@ import org.apache.maven.artifact.InvalidRepositoryException;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.repository.LegacyLocalRepositoryManager;
import org.apache.maven.bridge.MavenRepositorySystem;
import org.apache.maven.feature.Features;
import org.apache.maven.model.Build;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.DependencyManagement;
@@ -50,6 +52,7 @@ import org.apache.maven.model.Model;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.Profile;
import org.apache.maven.model.ReportPlugin;
import org.apache.maven.model.building.ArtifactModelSource;
import org.apache.maven.model.building.DefaultModelBuildingRequest;
import org.apache.maven.model.building.DefaultModelProblem;
import org.apache.maven.model.building.FileModelSource;
@@ -62,11 +65,15 @@ import org.apache.maven.model.building.ModelProblem;
import org.apache.maven.model.building.ModelProcessor;
import org.apache.maven.model.building.ModelSource;
import org.apache.maven.model.building.StringModelSource;
import org.apache.maven.model.building.TransformerContext;
import org.apache.maven.model.building.TransformerContextBuilder;
import org.apache.maven.model.resolution.ModelResolver;
import org.apache.maven.repository.internal.ArtifactDescriptorUtils;
import org.apache.maven.repository.internal.DefaultModelCache;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.util.Os;
import org.codehaus.plexus.util.StringUtils;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.RequestTrace;
import org.eclipse.aether.impl.RemoteRepositoryManager;
@@ -76,17 +83,12 @@ import org.eclipse.aether.repository.WorkspaceRepository;
import org.eclipse.aether.resolution.ArtifactRequest;
import org.eclipse.aether.resolution.ArtifactResult;
import org.eclipse.sisu.Priority;
import org.eclipse.sisu.Typed;
/**
* DefaultProjectBuilder
*
* File origin:
* https://github.com/apache/maven/blob/maven-3.6.2/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java
* CachingProjectBuilder
*/
@Named
@Singleton
@Typed(ProjectBuilder.class)
@Priority(10)
public class CachingProjectBuilder
implements ProjectBuilder {
@@ -115,10 +117,7 @@ public class CachingProjectBuilder
@Inject
private ProjectDependenciesResolver dependencyResolver;
private final ModelCache modelCache = new ReactorModelCache();
public CachingProjectBuilder() {
}
private final ModelCache modelCache = DefaultModelCache.newInstance(new DefaultRepositorySystemSession());
// ----------------------------------------------------------------------
// MavenProjectBuilder Implementation
@@ -128,14 +127,14 @@ public class CachingProjectBuilder
public ProjectBuildingResult build(File pomFile, ProjectBuildingRequest request)
throws ProjectBuildingException {
return build(pomFile, new FileModelSource(pomFile),
new InternalConfig(request, null, new SnapshotModelCache(getModelCache())));
new InternalConfig(request, null, null));
}
@Override
public ProjectBuildingResult build(ModelSource modelSource, ProjectBuildingRequest request)
throws ProjectBuildingException {
return build(null, modelSource,
new InternalConfig(request, null, new SnapshotModelCache(getModelCache())));
new InternalConfig(request, null, null));
}
private ProjectBuildingResult build(File pomFile, ModelSource modelSource, InternalConfig config)
@@ -170,8 +169,7 @@ public class CachingProjectBuilder
} catch (ModelBuildingException e) {
result = e.getResult();
if (result == null || result.getEffectiveModel() == null) {
throw (ProjectBuildingException) new ProjectBuildingException(e.getModelId(), e.getMessage(), pomFile)
.initCause(e);
throw new ProjectBuildingException(e.getModelId(), e.getMessage(), pomFile, e);
}
// validation error, continue project building and delay failing to help IDEs
error = e;
@@ -179,8 +177,8 @@ public class CachingProjectBuilder
modelProblems = result.getProblems();
initProject(project, Collections.<String, MavenProject> emptyMap(), true,
result, new HashMap<File, Boolean>(), projectBuildingRequest);
initProject(project, Collections.emptyMap(), true,
result, new HashMap<>(), projectBuildingRequest);
} else if (projectBuildingRequest.isResolveDependencies()) {
projectBuildingHelper.selectProjectRealm(project);
}
@@ -235,13 +233,7 @@ public class CachingProjectBuilder
}
private List<String> getProfileIds(List<Profile> profiles) {
List<String> ids = new ArrayList<>(profiles.size());
for (Profile profile : profiles) {
ids.add(profile.getId());
}
return ids;
return profiles.stream().map(Profile::getId).collect(Collectors.toList());
}
private ModelBuildingRequest getModelBuildingRequest(InternalConfig config) {
@@ -264,7 +256,11 @@ public class CachingProjectBuilder
request.setUserProperties(configuration.getUserProperties());
request.setBuildStartTime(configuration.getBuildStartTime());
request.setModelResolver(resolver);
request.setModelCache(config.modelCache);
// this is a hint that we want to build 1 file, so don't cache. See MNG-7063
if (config.modelPool != null) {
request.setModelCache(new SnapshotModelCache(modelCache, DefaultModelCache.newInstance(config.session)));
}
request.setTransformerContextBuilder(config.transformerContextBuilder);
return request;
}
@@ -281,7 +277,7 @@ public class CachingProjectBuilder
org.eclipse.aether.artifact.Artifact pomArtifact = RepositoryUtils.toArtifact(artifact);
pomArtifact = ArtifactDescriptorUtils.toPomArtifact(pomArtifact);
InternalConfig config = new InternalConfig(request, null, new SnapshotModelCache(getModelCache()));
InternalConfig config = new InternalConfig(request, null, null);
boolean localProject;
@@ -309,7 +305,13 @@ public class CachingProjectBuilder
artifact.setResolved(true);
}
return build(localProject ? pomFile : null, new FileModelSource(pomFile), config);
if (localProject) {
return build(pomFile, new FileModelSource(pomFile), config);
} else {
return build(null, new ArtifactModelSource(pomFile, artifact.getGroupId(), artifact.getArtifactId(),
artifact.getVersion()),
config);
}
}
private ModelSource createStubModelSource(Artifact artifact) {
@@ -334,26 +336,32 @@ public class CachingProjectBuilder
List<InterimResult> interimResults = new ArrayList<>();
ReactorModelPool modelPool = new ReactorModelPool();
ReactorModelPool.Builder poolBuilder = new ReactorModelPool.Builder();
final ReactorModelPool modelPool = poolBuilder.build();
InternalConfig config = new InternalConfig(request, modelPool, new SnapshotModelCache(getModelCache()));
InternalConfig config = new InternalConfig(request, modelPool, modelBuilder.newTransformerContextBuilder());
Map<String, MavenProject> projectIndex = new HashMap<>(256);
Map<File, MavenProject> projectIndex = new HashMap<>(256);
boolean noErrors = build(results, interimResults, projectIndex, pomFiles, new LinkedHashSet<File>(), true, recursive,
config);
populateReactorModelPool(modelPool, interimResults);
// phase 1: get file Models from the reactor.
boolean noErrors = build(results, interimResults, projectIndex, pomFiles, new LinkedHashSet<>(), true, recursive,
config, poolBuilder);
ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
try {
noErrors = build(results, new ArrayList<MavenProject>(), projectIndex, interimResults, request,
new HashMap<File, Boolean>(), config.session) && noErrors;
// Phase 2: get effective models from the reactor
noErrors = build(results, new ArrayList<>(), projectIndex, interimResults, request,
new HashMap<>(), config.session) && noErrors;
} finally {
Thread.currentThread().setContextClassLoader(oldContextClassLoader);
}
if (Features.buildConsumer(request.getUserProperties()).isActive()) {
request.getRepositorySession().getData().set(TransformerContext.KEY,
config.transformerContextBuilder.build());
}
if (!noErrors) {
throw new ProjectBuildingException(results);
}
@@ -363,14 +371,16 @@ public class CachingProjectBuilder
@SuppressWarnings("checkstyle:parameternumber")
private boolean build(List<ProjectBuildingResult> results, List<InterimResult> interimResults,
Map<String, MavenProject> projectIndex, List<File> pomFiles, Set<File> aggregatorFiles,
boolean isRoot, boolean recursive, InternalConfig config) {
Map<File, MavenProject> projectIndex, List<File> pomFiles, Set<File> aggregatorFiles,
boolean root, boolean recursive, InternalConfig config,
ReactorModelPool.Builder poolBuilder) {
boolean noErrors = true;
for (File pomFile : pomFiles) {
aggregatorFiles.add(pomFile);
if (!build(results, interimResults, projectIndex, pomFile, aggregatorFiles, isRoot, recursive, config)) {
if (!build(results, interimResults, projectIndex, pomFile, aggregatorFiles, root, recursive, config,
poolBuilder)) {
noErrors = false;
}
@@ -382,18 +392,18 @@ public class CachingProjectBuilder
@SuppressWarnings("checkstyle:parameternumber")
private boolean build(List<ProjectBuildingResult> results, List<InterimResult> interimResults,
Map<String, MavenProject> projectIndex, File pomFile, Set<File> aggregatorFiles,
boolean isRoot, boolean recursive, InternalConfig config) {
Map<File, MavenProject> projectIndex, File pomFile, Set<File> aggregatorFiles,
boolean isRoot, boolean recursive, InternalConfig config,
ReactorModelPool.Builder poolBuilder) {
boolean noErrors = true;
ModelBuildingRequest request = getModelBuildingRequest(config);
MavenProject project = new MavenProject();
project.setFile(pomFile);
request.setPomFile(pomFile);
request.setTwoPhaseBuilding(true);
request.setLocationTracking(true);
ModelBuildingRequest request = getModelBuildingRequest(config)
.setPomFile(pomFile)
.setTwoPhaseBuilding(true)
.setLocationTracking(true);
DefaultModelBuildingListener listener = new DefaultModelBuildingListener(project, projectBuildingHelper,
config.request);
@@ -404,7 +414,7 @@ public class CachingProjectBuilder
result = modelBuilder.build(request);
} catch (ModelBuildingException e) {
result = e.getResult();
if (result == null || result.getEffectiveModel() == null) {
if (result == null || result.getFileModel() == null) {
results.add(new DefaultProjectBuildingResult(e.getModelId(), pomFile, e.getProblems()));
return false;
@@ -414,25 +424,16 @@ public class CachingProjectBuilder
noErrors = false;
}
Model model = result.getEffectiveModel();
try {
// first pass: build without building parent.
initProject(project, projectIndex, false, result, new HashMap<File, Boolean>(0), config.request);
} catch (InvalidArtifactRTException iarte) {
result.getProblems().add(new DefaultModelProblem(null, ModelProblem.Severity.ERROR, null, model, -1, -1,
iarte));
}
Model model = result.getFileModel().clone();
projectIndex.put(result.getModelIds().get(0), project);
poolBuilder.put(model.getPomFile().toPath(), model);
InterimResult interimResult = new InterimResult(pomFile, request, result, listener, isRoot);
interimResults.add(interimResult);
if (recursive && !model.getModules().isEmpty()) {
if (recursive) {
File basedir = pomFile.getParentFile();
List<File> moduleFiles = new ArrayList<>();
for (String module : model.getModules()) {
if (StringUtils.isEmpty(module)) {
continue;
@@ -491,11 +492,13 @@ public class CachingProjectBuilder
interimResult.modules = new ArrayList<>();
if (!build(results, interimResult.modules, projectIndex, moduleFiles, aggregatorFiles, false,
recursive, config)) {
recursive, config, poolBuilder)) {
noErrors = false;
}
}
projectIndex.put(pomFile, project);
return noErrors;
}
@@ -524,17 +527,8 @@ public class CachingProjectBuilder
}
private void populateReactorModelPool(ReactorModelPool reactorModelPool, List<InterimResult> interimResults) {
for (InterimResult interimResult : interimResults) {
Model model = interimResult.result.getEffectiveModel();
reactorModelPool.put(model.getGroupId(), model.getArtifactId(), model.getVersion(), model.getPomFile());
populateReactorModelPool(reactorModelPool, interimResult.modules);
}
}
private boolean build(List<ProjectBuildingResult> results, List<MavenProject> projects,
Map<String, MavenProject> projectIndex, List<InterimResult> interimResults,
Map<File, MavenProject> projectIndex, List<InterimResult> interimResults,
ProjectBuildingRequest request, Map<File, Boolean> profilesXmls,
RepositorySystemSession session) {
boolean noErrors = true;
@@ -569,9 +563,11 @@ public class CachingProjectBuilder
results.add(new DefaultProjectBuildingResult(project, result.getProblems(), resolutionResult));
} catch (ModelBuildingException e) {
DefaultProjectBuildingResult result = null;
if (project == null) {
if (project == null || interimResult.result.getEffectiveModel() == null) {
result = new DefaultProjectBuildingResult(e.getModelId(), interimResult.pomFile, e.getProblems());
} else {
project.setModel(interimResult.result.getEffectiveModel());
result = new DefaultProjectBuildingResult(project, e.getProblems(), null);
}
results.add(result);
@@ -584,14 +580,13 @@ public class CachingProjectBuilder
}
@SuppressWarnings("checkstyle:methodlength")
private void initProject(MavenProject project, Map<String, MavenProject> projects,
private void initProject(MavenProject project, Map<File, MavenProject> projects,
boolean buildParentIfNotExisting, ModelBuildingResult result,
Map<File, Boolean> profilesXmls, ProjectBuildingRequest projectBuildingRequest) {
Model model = result.getEffectiveModel();
project.setModel(model);
project.setOriginalModel(result.getRawModel());
project.setFile(model.getPomFile());
project.setOriginalModel(result.getFileModel());
initParent(project, projects, buildParentIfNotExisting, result, projectBuildingRequest);
@@ -618,14 +613,6 @@ public class CachingProjectBuilder
project.setInjectedProfileIds(modelId, getProfileIds(result.getActivePomProfiles(modelId)));
}
String modelId = findProfilesXml(result, profilesXmls);
if (modelId != null) {
ModelProblem problem = new DefaultModelProblem("Detected profiles.xml alongside " + modelId
+ ", this file is no longer supported and was ignored" + ", please use the settings.xml instead",
ModelProblem.Severity.WARNING, ModelProblem.Version.V30, model, -1, -1, null);
result.getProblems().add(problem);
}
//
// All the parts that were taken out of MavenProject for Maven 4.0.0
//
@@ -747,7 +734,7 @@ public class CachingProjectBuilder
try {
DeploymentRepository r = project.getDistributionManagement().getRepository();
if (!StringUtils.isEmpty(r.getId()) && !StringUtils.isEmpty(r.getUrl())) {
ArtifactRepository repo = repositorySystem.buildArtifactRepository(r);
ArtifactRepository repo = MavenRepositorySystem.buildArtifactRepository(r);
repositorySystem.injectProxy(projectBuildingRequest.getRepositorySession(),
Arrays.asList(repo));
repositorySystem.injectAuthentication(projectBuildingRequest.getRepositorySession(),
@@ -766,7 +753,7 @@ public class CachingProjectBuilder
try {
DeploymentRepository r = project.getDistributionManagement().getSnapshotRepository();
if (!StringUtils.isEmpty(r.getId()) && !StringUtils.isEmpty(r.getUrl())) {
ArtifactRepository repo = repositorySystem.buildArtifactRepository(r);
ArtifactRepository repo = MavenRepositorySystem.buildArtifactRepository(r);
repositorySystem.injectProxy(projectBuildingRequest.getRepositorySession(),
Arrays.asList(repo));
repositorySystem.injectAuthentication(projectBuildingRequest.getRepositorySession(),
@@ -780,7 +767,7 @@ public class CachingProjectBuilder
}
}
private void initParent(MavenProject project, Map<String, MavenProject> projects, boolean buildParentIfNotExisting,
private void initParent(MavenProject project, Map<File, MavenProject> projects, boolean buildParentIfNotExisting,
ModelBuildingResult result, ProjectBuildingRequest projectBuildingRequest) {
Model parentModel = result.getModelIds().size() > 1 && !result.getModelIds().get(1).isEmpty()
? result.getRawModel(result.getModelIds().get(1))
@@ -797,7 +784,7 @@ public class CachingProjectBuilder
// org.apache.maven.its.mng4834:parent:0.1
String parentModelId = result.getModelIds().get(1);
File parentPomFile = result.getRawModel(parentModelId).getPomFile();
MavenProject parent = projects.get(parentModelId);
MavenProject parent = projects.get(parentPomFile);
if (parent == null && buildParentIfNotExisting) {
//
// At this point the DefaultModelBuildingListener has fired and it populates the
@@ -872,28 +859,6 @@ public class CachingProjectBuilder
return version;
}
private String findProfilesXml(ModelBuildingResult result, Map<File, Boolean> profilesXmls) {
for (String modelId : result.getModelIds()) {
Model model = result.getRawModel(modelId);
File basedir = model.getProjectDirectory();
if (basedir == null) {
break;
}
Boolean profilesXml = profilesXmls.get(basedir);
if (profilesXml == null) {
profilesXml = new File(basedir, "profiles.xml").exists();
profilesXmls.put(basedir, profilesXml);
}
if (profilesXml) {
return modelId;
}
}
return null;
}
/**
* InternalConfig
*/
@@ -907,21 +872,20 @@ public class CachingProjectBuilder
private final ReactorModelPool modelPool;
private final ModelCache modelCache;
private final TransformerContextBuilder transformerContextBuilder;
InternalConfig(ProjectBuildingRequest request, ReactorModelPool modelPool, ModelCache modelCache) {
InternalConfig(ProjectBuildingRequest request, ReactorModelPool modelPool,
TransformerContextBuilder transformerContextBuilder) {
this.request = request;
this.modelPool = modelPool;
this.modelCache = modelCache;
this.transformerContextBuilder = transformerContextBuilder;
session = LegacyLocalRepositoryManager.overlay(request.getLocalRepository(), request.getRepositorySession(),
repoSystem);
repositories = RepositoryUtils.toRepos(request.getRemoteRepositories());
}
}
private ModelCache getModelCache() {
return this.modelCache;
}
}

View File

@@ -22,9 +22,9 @@ public class SnapshotModelCache implements ModelCache {
private final ModelCache globalCache;
private final ModelCache reactorCache;
public SnapshotModelCache(ModelCache globalCache) {
public SnapshotModelCache(ModelCache globalCache, ModelCache reactorCache) {
this.globalCache = globalCache;
this.reactorCache = new ReactorModelCache();
this.reactorCache = reactorCache;
}
@Override

View File

@@ -1,52 +0,0 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed 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.mvndaemon.mvnd.execution;
/*
* 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.
*/
import java.util.Optional;
import org.apache.maven.execution.MavenExecutionResult;
/**
* Instances of this class are responsible for determining whether it makes sense to "resume" a build (i.e., using
* the {@code --resume} flag.
*/
public interface BuildResumptionAnalyzer {
/**
* Construct an instance of {@link BuildResumptionData} based on the outcome of the current Maven build.
*
* @param result Outcome of the current Maven build.
* @return A {@link BuildResumptionData} instance or {@link Optional#empty()} if resuming the build is not
* possible.
*/
Optional<BuildResumptionData> determineBuildResumptionData(final MavenExecutionResult result);
}

View File

@@ -1,60 +0,0 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed 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.mvndaemon.mvnd.execution;
/*
* 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.
*/
import java.util.List;
/**
* This class holds the information required to enable resuming a Maven build with {@code --resume}.
*/
public class BuildResumptionData {
/**
* The list of projects that remain to be built.
*/
private final List<String> remainingProjects;
public BuildResumptionData(final List<String> remainingProjects) {
this.remainingProjects = remainingProjects;
}
/**
* Returns the projects that still need to be built when resuming.
*
* @return A list containing the group and artifact id of the projects.
*/
public List<String> getRemainingProjects() {
return this.remainingProjects;
}
}

View File

@@ -1,72 +0,0 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed 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.mvndaemon.mvnd.execution;
/*
* 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.
*/
import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.project.MavenProject;
/**
* Instances of this interface retrieve and store data for the --resume / -r feature. This data is used to ensure newer
* builds of the same project, that have the -r command-line flag, skip successfully built projects during earlier
* invocations of Maven.
*/
public interface BuildResumptionDataRepository {
/**
* Persists any data needed to resume the build at a later point in time, using a new Maven invocation. This method
* may also decide it is not needed or meaningful to persist such data, and return <code>false</code> to indicate
* so.
*
* @param rootProject The root project that is being built.
* @param buildResumptionData Information needed to resume the build.
* @throws BuildResumptionPersistenceException When an error occurs while persisting data.
*/
void persistResumptionData(final MavenProject rootProject, final BuildResumptionData buildResumptionData)
throws BuildResumptionPersistenceException;
/**
* Uses previously stored resumption data to enrich an existing execution request.
*
* @param request The execution request that will be enriched.
* @param rootProject The root project that is being built.
*/
void applyResumptionData(final MavenExecutionRequest request, final MavenProject rootProject);
/**
* Removes previously stored resumption data.
*
* @param rootProject The root project that is being built.
*/
void removeResumptionData(final MavenProject rootProject);
}

View File

@@ -1,46 +0,0 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed 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.mvndaemon.mvnd.execution;
/*
* 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.
*/
/**
* This exception will be thrown when something fails while persisting build resumption data.
*
* @see BuildResumptionDataRepository#persistResumptionData
*/
public class BuildResumptionPersistenceException extends Exception {
public BuildResumptionPersistenceException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -1,85 +0,0 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed 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.mvndaemon.mvnd.execution;
/*
* 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.
*/
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.inject.Named;
import javax.inject.Singleton;
import org.apache.maven.execution.BuildFailure;
import org.apache.maven.execution.BuildSuccess;
import org.apache.maven.execution.MavenExecutionResult;
import org.apache.maven.project.MavenProject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Default implementation of {@link BuildResumptionAnalyzer}.
*/
@Named
@Singleton
public class DefaultBuildResumptionAnalyzer implements BuildResumptionAnalyzer {
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultBuildResumptionAnalyzer.class);
@Override
public Optional<BuildResumptionData> determineBuildResumptionData(final MavenExecutionResult result) {
if (!result.hasExceptions()) {
return Optional.empty();
}
List<MavenProject> sortedProjects = result.getTopologicallySortedProjects();
boolean hasNoSuccess = sortedProjects.stream()
.noneMatch(project -> result.getBuildSummary(project) instanceof BuildSuccess);
if (hasNoSuccess) {
return Optional.empty();
}
List<String> remainingProjects = sortedProjects.stream()
.filter(project -> result.getBuildSummary(project) == null
|| result.getBuildSummary(project) instanceof BuildFailure)
.map(project -> project.getGroupId() + ":" + project.getArtifactId())
.collect(Collectors.toList());
if (remainingProjects.isEmpty()) {
LOGGER.info("No remaining projects found, resuming the build would not make sense.");
return Optional.empty();
}
return Optional.of(new BuildResumptionData(remainingProjects));
}
}

View File

@@ -1,150 +0,0 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed 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.mvndaemon.mvnd.execution;
/*
* 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.
*/
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Properties;
import java.util.stream.Stream;
import javax.inject.Named;
import javax.inject.Singleton;
import org.apache.commons.lang3.StringUtils;
import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.project.MavenProject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This implementation of {@link BuildResumptionDataRepository} persists information in a properties file. The file is
* stored in the build output directory under the Maven execution root.
*/
@Named
@Singleton
public class DefaultBuildResumptionDataRepository implements BuildResumptionDataRepository {
private static final String RESUME_PROPERTIES_FILENAME = "resume.properties";
private static final String REMAINING_PROJECTS = "remainingProjects";
private static final String PROPERTY_DELIMITER = ", ";
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultBuildResumptionDataRepository.class);
@Override
public void persistResumptionData(MavenProject rootProject, BuildResumptionData buildResumptionData)
throws BuildResumptionPersistenceException {
Path directory = Paths.get(rootProject.getBuild().getDirectory());
persistResumptionData(directory, buildResumptionData);
}
public void persistResumptionData(Path directory, BuildResumptionData buildResumptionData)
throws BuildResumptionPersistenceException {
Properties properties = convertToProperties(buildResumptionData);
Path resumeProperties = directory.resolve(RESUME_PROPERTIES_FILENAME);
try {
Files.createDirectories(resumeProperties.getParent());
try (Writer writer = Files.newBufferedWriter(resumeProperties)) {
properties.store(writer, null);
}
} catch (IOException e) {
String message = "Could not create " + RESUME_PROPERTIES_FILENAME + " file.";
throw new BuildResumptionPersistenceException(message, e);
}
}
private Properties convertToProperties(final BuildResumptionData buildResumptionData) {
Properties properties = new Properties();
String value = String.join(PROPERTY_DELIMITER, buildResumptionData.getRemainingProjects());
properties.setProperty(REMAINING_PROJECTS, value);
return properties;
}
@Override
public void applyResumptionData(MavenExecutionRequest request, MavenProject rootProject) {
Path directory = Paths.get(rootProject.getBuild().getDirectory());
applyResumptionData(request, directory);
}
public void applyResumptionData(MavenExecutionRequest request, Path directory) {
Properties properties = loadResumptionFile(directory);
applyResumptionProperties(request, properties);
}
@Override
public void removeResumptionData(MavenProject rootProject) {
Path directory = Paths.get(rootProject.getBuild().getDirectory());
removeResumptionData(directory);
}
public void removeResumptionData(Path directory) {
Path resumeProperties = directory.resolve(RESUME_PROPERTIES_FILENAME);
try {
Files.deleteIfExists(resumeProperties);
} catch (IOException e) {
LOGGER.warn("Could not delete {} file. ", RESUME_PROPERTIES_FILENAME, e);
}
}
private Properties loadResumptionFile(Path rootBuildDirectory) {
Properties properties = new Properties();
Path path = rootBuildDirectory.resolve(RESUME_PROPERTIES_FILENAME);
if (!Files.exists(path)) {
LOGGER.warn("The {} file does not exist. The --resume / -r feature will not work.", path);
return properties;
}
try (Reader reader = Files.newBufferedReader(path)) {
properties.load(reader);
} catch (IOException e) {
LOGGER.warn("Unable to read {}. The --resume / -r feature will not work.", path);
}
return properties;
}
// This method is made package-private for testing purposes
void applyResumptionProperties(MavenExecutionRequest request, Properties properties) {
if (properties.containsKey(REMAINING_PROJECTS)
&& StringUtils.isEmpty(request.getResumeFrom())) {
String propertyValue = properties.getProperty(REMAINING_PROJECTS);
Stream.of(propertyValue.split(PROPERTY_DELIMITER))
.filter(StringUtils::isNotEmpty)
.forEach(request.getSelectedProjects()::add);
LOGGER.info("Resuming from {} due to the --resume / -r feature.", propertyValue);
}
}
}

View File

@@ -19,15 +19,20 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.apache.maven.artifact.repository.metadata.io.MetadataReader;
import org.apache.maven.plugin.MavenPluginManager;
import org.apache.maven.plugin.version.PluginVersionRequest;
import org.apache.maven.plugin.version.PluginVersionResolutionException;
import org.apache.maven.plugin.version.PluginVersionResolver;
import org.apache.maven.plugin.version.PluginVersionResult;
import org.apache.maven.plugin.version.internal.DefaultPluginVersionResolver;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.SessionData;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.version.VersionScheme;
import org.eclipse.sisu.Priority;
import org.eclipse.sisu.Typed;
@@ -39,6 +44,12 @@ public class CachingPluginVersionResolver extends DefaultPluginVersionResolver {
private static final Object CACHE_KEY = new Object();
@Inject
public CachingPluginVersionResolver(RepositorySystem repositorySystem, MetadataReader metadataReader,
MavenPluginManager pluginManager, VersionScheme versionScheme) {
super(repositorySystem, metadataReader, pluginManager, versionScheme);
}
@Override
public PluginVersionResult resolve(PluginVersionRequest request) throws PluginVersionResolutionException {
Map<String, PluginVersionResult> cache = getCache(request.getRepositorySession().getData());

View File

@@ -47,13 +47,11 @@ import org.apache.maven.classrealm.ClassRealmManager;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.execution.scope.internal.MojoExecutionScopeModule;
import org.apache.maven.model.Plugin;
import org.apache.maven.monitor.logging.DefaultLog;
import org.apache.maven.plugin.ContextEnabled;
import org.apache.maven.plugin.DebugConfigurationListener;
import org.apache.maven.plugin.ExtensionRealmCache;
import org.apache.maven.plugin.InvalidPluginDescriptorException;
import org.apache.maven.plugin.MavenPluginManager;
import org.apache.maven.plugin.MavenPluginValidator;
import org.apache.maven.plugin.Mojo;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.plugin.MojoNotFoundException;
@@ -72,6 +70,7 @@ import org.apache.maven.plugin.descriptor.MojoDescriptor;
import org.apache.maven.plugin.descriptor.Parameter;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
import org.apache.maven.plugin.descriptor.PluginDescriptorBuilder;
import org.apache.maven.plugin.internal.MojoLogWrapper;
import org.apache.maven.plugin.internal.PluginDependenciesResolver;
import org.apache.maven.plugin.version.DefaultPluginVersionRequest;
import org.apache.maven.plugin.version.PluginVersionRequest;
@@ -97,7 +96,6 @@ import org.codehaus.plexus.component.repository.exception.ComponentLookupExcepti
import org.codehaus.plexus.configuration.PlexusConfiguration;
import org.codehaus.plexus.configuration.PlexusConfigurationException;
import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.logging.LoggerManager;
import org.codehaus.plexus.util.ReaderFactory;
import org.codehaus.plexus.util.StringUtils;
@@ -112,6 +110,8 @@ import org.eclipse.sisu.Priority;
import org.eclipse.sisu.Typed;
import org.mvndaemon.mvnd.cache.invalidating.InvalidatingPluginDescriptorCache;
import org.mvndaemon.mvnd.cache.invalidating.InvalidatingPluginRealmCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/*
* gnodet: This file is based on maven DefaultMavenPluginManager and changed in order
@@ -143,8 +143,7 @@ public class CliMavenPluginManager
*/
public static final String KEY_EXTENSIONS_REALMS = CliMavenPluginManager.class.getName() + "/extensionsRealms";
@Inject
private Logger logger;
private final Logger logger = LoggerFactory.getLogger(getClass());
@Inject
private LoggerManager loggerManager;
@@ -236,13 +235,12 @@ public class CliMavenPluginManager
throw new PluginDescriptorParsingException(plugin, pluginFile.getAbsolutePath(), e);
}
MavenPluginValidator validator = new MavenPluginValidator(pluginArtifact);
List<String> errors = new ArrayList<>();
validate(pluginArtifact, pluginDescriptor, errors);
validator.validate(pluginDescriptor);
if (validator.hasErrors()) {
if (!errors.isEmpty()) {
throw new InvalidPluginDescriptorException(
"Invalid plugin descriptor for " + plugin.getId() + " (" + pluginFile + ")", validator.getErrors());
"Invalid plugin descriptor for " + plugin.getId() + " (" + pluginFile + ")", errors);
}
pluginDescriptor.setPluginArtifact(pluginArtifact);
@@ -250,6 +248,20 @@ public class CliMavenPluginManager
return pluginDescriptor;
}
private void validate(Artifact pluginArtifact, PluginDescriptor pluginDescriptor, List<String> errors) {
if (!pluginArtifact.getGroupId().equals(pluginDescriptor.getGroupId())) {
errors.add("Plugin's descriptor contains the wrong group ID: " + pluginDescriptor.getGroupId());
}
if (!pluginArtifact.getArtifactId().equals(pluginDescriptor.getArtifactId())) {
errors.add("Plugin's descriptor contains the wrong artifact ID: " + pluginDescriptor.getArtifactId());
}
if (!pluginArtifact.getBaseVersion().equals(pluginDescriptor.getVersion())) {
errors.add("Plugin's descriptor contains the wrong version: " + pluginDescriptor.getVersion());
}
}
private String getPluginDescriptorLocation() {
return "META-INF/maven/plugin.xml";
}
@@ -516,8 +528,8 @@ public class CliMavenPluginManager
}
if (mojo instanceof Mojo) {
Logger mojoLogger = loggerManager.getLoggerForComponent(mojoDescriptor.getImplementation());
((Mojo) mojo).setLog(new DefaultLog(mojoLogger));
org.slf4j.Logger mojoLogger = LoggerFactory.getLogger(mojoDescriptor.getImplementation());
((Mojo) mojo).setLog(new MojoLogWrapper(mojoLogger));
}
Xpp3Dom dom = mojoExecution.getConfiguration();

View File

@@ -21,5 +21,10 @@ under the License.
<extension>
<exportedPackages>
<exportedPackage>org.apache.commons.logging.*</exportedPackage>
<exportedPackage>org.codehaus.plexus.components.interactivity.*</exportedPackage>
</exportedPackages>
<exportedArtifacts>
<exportedArtifact>org.codehaus.plexus:plexus-interactivity-api</exportedArtifact>
</exportedArtifacts>
</extension>

View File

@@ -20,12 +20,13 @@
<artifactSet to="/mvn">
<artifact id="org.apache.maven:apache-maven:tar.gz:bin">
<unpack useRoot="false"
excludes="lib/*slf4j*,conf/logging/*,lib/maven-slf4j-provider*,bin/mvn*,lib/jansi-*.jar,lib/jansi-native/*,lib/maven-resolver-api-*,lib/maven-resolver-impl-*,lib/maven-resolver-spi-*,lib/maven-resolver-util-*,lib/maven-resolver-connector-*,lib/maven-resolver-transport-*"/>
excludes="conf/logging/*,lib/maven-slf4j-provider*,bin/mvn*,lib/jansi-*.jar,lib/jansi-native/*,lib/plexus-utils-3.*"/>
</artifact>
</artifactSet>
<artifactSet to="/mvn/lib">
<exclusion id="javax.annotation:javax.annotation-api"/>
<exclusion id="org.codehaus.plexus:plexus-utils"/>
<artifact id="org.apache.maven.resolver:maven-resolver-api"/>
<artifact id="org.apache.maven.resolver:maven-resolver-impl"/>
<artifact id="org.apache.maven.resolver:maven-resolver-spi"/>
@@ -38,6 +39,7 @@
<exclusion id="org.slf4j:slf4j-api"/>
<artifact id="org.apache.maven.daemon:mvnd-daemon:${project.version}">
<exclusion id="org.codehaus.plexus:plexus-classworlds"/>
<exclusion id="org.codehaus.plexus:plexus-utils"/>
<exclusion id="*:cdi-api"/>
<exclusion id="*:commons-cli"/>
<exclusion id="*:commons-io"/>
@@ -45,7 +47,10 @@
<exclusion id="*:guava"/>
<exclusion id="*:guice"/>
<exclusion id="*:javax.inject"/>
<exclusion id="*:jcl-over-slf4j"/>
<exclusion id="*:jul-to-slf4j"/>
<exclusion id="*:jsr250-api"/>
<exclusion id="*:log4j-over-slf4j"/>
<exclusion id="*:maven-artifact"/>
<exclusion id="*:maven-builder-support"/>
<exclusion id="*:maven-core"/>
@@ -70,6 +75,7 @@
<exclusion id="*:plexus-sec-dispatcher"/>
<exclusion id="*:plexus-utils"/>
<exclusion id="*:plexus-container-default"/>
<exclusion id="*:slf4j-api"/>
</artifact>
<artifact id="org.apache.maven.daemon:mvnd-client:${project.version}">
<exclusion id="*:*"/>

View File

@@ -19,6 +19,7 @@ import java.io.IOException;
import javax.inject.Inject;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
import org.mvndaemon.mvnd.assertj.TestClientOutput;
import org.mvndaemon.mvnd.client.Client;
import org.mvndaemon.mvnd.client.DaemonParameters;
@@ -27,6 +28,7 @@ import org.mvndaemon.mvnd.common.Message.Prompt;
import org.mvndaemon.mvnd.junit.MvndTest;
@MvndTest(projectDir = "src/test/projects/single-module")
@Timeout(300)
public class InteractiveTest {
@Inject

View File

@@ -61,6 +61,10 @@ public class NewManagedModuleNativeIT {
registry.awaitIdle(d.getId());
/* Do the changes */
System.gc();
Thread.sleep(100);
System.gc();
final Path srcDir = parentDir.resolve("../changes").normalize();
try (Stream<Path> files = Files.walk(srcDir)) {
files.forEach(source -> {

11
pom.xml
View File

@@ -64,8 +64,8 @@
<jline.version>3.21.0</jline.version>
<junit.jupiter.version>5.7.2</junit.jupiter.version>
<logback.version>1.2.10</logback.version>
<maven.version>3.8.6</maven.version>
<maven.resolver.version>1.7.3</maven.resolver.version>
<maven.version>4.0.0-alpha-2</maven.version>
<maven.resolver.version>1.8.2</maven.resolver.version>
<slf4j.version>1.7.35</slf4j.version>
<sisu.version>0.3.5</sisu.version>
@@ -412,6 +412,13 @@ limitations under the License.</inlineHeader>
<skip>${format.skip}</skip>
<cachedir>${project.build.directory}/cache</cachedir>
</configuration>
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>plexus-utils</artifactId>
<version>${maven.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>