mirror of https://github.com/apache/maven.git
[MNG-8386] Pull out executor (#1932)
Yet another CLIng cleanup. Changes: * pull out Executor, it does not belong to CLIng (new maven-executor module created with no deps) * resident and maven invoker fixes (proper handling of resources now), no more SO/OOMs * enabled UTs in maven-cli (that revealed the issues) * small bug fixes discovered in cli, improved executor to reveal maven version --- https://issues.apache.org/jira/browse/MNG-8386
This commit is contained in:
parent
8a88a40c8d
commit
7ad2578e77
|
@ -1,82 +0,0 @@
|
|||
/*
|
||||
* 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.api.cli;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.maven.api.annotations.Experimental;
|
||||
import org.apache.maven.api.annotations.Immutable;
|
||||
import org.apache.maven.api.annotations.Nonnull;
|
||||
|
||||
/**
|
||||
* Represents a request to execute Maven with command-line arguments.
|
||||
* This interface encapsulates all the necessary information needed to execute
|
||||
* Maven command with arguments. The arguments were not parsed, they are just passed over
|
||||
* to executed tool.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@Immutable
|
||||
@Experimental
|
||||
public interface ExecutorRequest {
|
||||
/**
|
||||
* The parser request this instance was created from.
|
||||
*/
|
||||
@Nonnull
|
||||
ParserRequest parserRequest();
|
||||
|
||||
/**
|
||||
* Returns the current working directory for the Maven execution.
|
||||
* This is typically the directory from which Maven was invoked.
|
||||
*
|
||||
* @return the current working directory path
|
||||
*/
|
||||
@Nonnull
|
||||
Path cwd();
|
||||
|
||||
/**
|
||||
* Returns the Maven installation directory.
|
||||
* This is usually set by the Maven launcher script using the "maven.home" system property.
|
||||
*
|
||||
* @return the Maven installation directory path
|
||||
*/
|
||||
@Nonnull
|
||||
Path installationDirectory();
|
||||
|
||||
/**
|
||||
* Returns the user's home directory.
|
||||
* This is typically obtained from the "user.home" system property.
|
||||
*
|
||||
* @return the user's home directory path
|
||||
*/
|
||||
@Nonnull
|
||||
Path userHomeDirectory();
|
||||
|
||||
/**
|
||||
* Returns the list of extra JVM arguments to be passed to the forked process.
|
||||
* These arguments allow for customization of the JVM environment in which tool will run.
|
||||
* This property is used ONLY by executors and invokers that spawn a new JVM.
|
||||
*
|
||||
* @return an Optional containing the list of extra JVM arguments, or empty if not specified
|
||||
*/
|
||||
@Nonnull
|
||||
Optional<List<String>> jvmArguments();
|
||||
}
|
|
@ -40,7 +40,50 @@ import org.apache.maven.api.services.MessageBuilderFactory;
|
|||
*/
|
||||
@Immutable
|
||||
@Experimental
|
||||
public interface InvokerRequest extends ExecutorRequest {
|
||||
public interface InvokerRequest {
|
||||
/**
|
||||
* The parser request this instance was created from.
|
||||
*/
|
||||
@Nonnull
|
||||
ParserRequest parserRequest();
|
||||
|
||||
/**
|
||||
* Returns the current working directory for the Maven execution.
|
||||
* This is typically the directory from which Maven was invoked.
|
||||
*
|
||||
* @return the current working directory path
|
||||
*/
|
||||
@Nonnull
|
||||
Path cwd();
|
||||
|
||||
/**
|
||||
* Returns the Maven installation directory.
|
||||
* This is usually set by the Maven launcher script using the "maven.home" system property.
|
||||
*
|
||||
* @return the Maven installation directory path
|
||||
*/
|
||||
@Nonnull
|
||||
Path installationDirectory();
|
||||
|
||||
/**
|
||||
* Returns the user's home directory.
|
||||
* This is typically obtained from the "user.home" system property.
|
||||
*
|
||||
* @return the user's home directory path
|
||||
*/
|
||||
@Nonnull
|
||||
Path userHomeDirectory();
|
||||
|
||||
/**
|
||||
* Returns the list of extra JVM arguments to be passed to the forked process.
|
||||
* These arguments allow for customization of the JVM environment in which tool will run.
|
||||
* This property is used ONLY by executors and invokers that spawn a new JVM.
|
||||
*
|
||||
* @return an Optional containing the list of extra JVM arguments, or empty if not specified
|
||||
*/
|
||||
@Nonnull
|
||||
Optional<List<String>> jvmArguments();
|
||||
|
||||
/**
|
||||
* Shorthand for {@link Logger} to use.
|
||||
*/
|
||||
|
|
|
@ -30,18 +30,6 @@ import org.apache.maven.api.annotations.Nonnull;
|
|||
*/
|
||||
@Experimental
|
||||
public interface Parser {
|
||||
/**
|
||||
* Parses the given ParserRequest to create an {@link ExecutorRequest}.
|
||||
* This method does not interpret tool arguments.
|
||||
*
|
||||
* @param parserRequest the request containing all necessary information for parsing
|
||||
* @return the parsed executor request
|
||||
* @throws ParserException if there's an error during parsing of the request
|
||||
* @throws IOException if there's an I/O error during the parsing process
|
||||
*/
|
||||
@Nonnull
|
||||
ExecutorRequest parseExecution(@Nonnull ParserRequest parserRequest) throws ParserException, IOException;
|
||||
|
||||
/**
|
||||
* Parses the given ParserRequest to create an {@link InvokerRequest}.
|
||||
* This method does interpret tool arguments.
|
||||
|
|
|
@ -95,6 +95,21 @@ under the License.
|
|||
<version>1.3.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.resolver</groupId>
|
||||
<artifactId>maven-resolver-connector-basic</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.resolver</groupId>
|
||||
<artifactId>maven-resolver-transport-file</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.resolver</groupId>
|
||||
<artifactId>maven-resolver-transport-jdk</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
@ -145,6 +160,10 @@ under the License.
|
|||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<properties>
|
||||
<configurationParameters>junit.jupiter.testclass.order.default = org.junit.jupiter.api.ClassOrderer$OrderAnnotation</configurationParameters>
|
||||
</properties>
|
||||
<promoteUserPropertiesToSystemProperties>false</promoteUserPropertiesToSystemProperties>
|
||||
<systemPropertyVariables>
|
||||
<maven.home>${maven.home}</maven.home>
|
||||
</systemPropertyVariables>
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
/*
|
||||
* 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.cling.invoker;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.maven.api.annotations.Nonnull;
|
||||
import org.apache.maven.api.annotations.Nullable;
|
||||
import org.apache.maven.api.cli.ExecutorRequest;
|
||||
import org.apache.maven.api.cli.ParserRequest;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
public class BaseExecutorRequest implements ExecutorRequest {
|
||||
private final ParserRequest parserRequest;
|
||||
private final Path cwd;
|
||||
private final Path installationDirectory;
|
||||
private final Path userHomeDirectory;
|
||||
private final List<String> jvmArguments;
|
||||
|
||||
@SuppressWarnings("ParameterNumber")
|
||||
public BaseExecutorRequest(
|
||||
@Nonnull ParserRequest parserRequest,
|
||||
@Nonnull Path cwd,
|
||||
@Nonnull Path installationDirectory,
|
||||
@Nonnull Path userHomeDirectory,
|
||||
@Nullable List<String> jvmArguments) {
|
||||
this.parserRequest = requireNonNull(parserRequest);
|
||||
this.cwd = requireNonNull(cwd);
|
||||
this.installationDirectory = requireNonNull(installationDirectory);
|
||||
this.userHomeDirectory = requireNonNull(userHomeDirectory);
|
||||
this.jvmArguments = jvmArguments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParserRequest parserRequest() {
|
||||
return parserRequest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path cwd() {
|
||||
return cwd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path installationDirectory() {
|
||||
return installationDirectory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path userHomeDirectory() {
|
||||
return userHomeDirectory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<List<String>> jvmArguments() {
|
||||
return Optional.ofNullable(jvmArguments);
|
||||
}
|
||||
}
|
|
@ -33,7 +33,12 @@ import org.apache.maven.api.cli.extensions.CoreExtension;
|
|||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
public abstract class BaseInvokerRequest extends BaseExecutorRequest implements InvokerRequest {
|
||||
public abstract class BaseInvokerRequest implements InvokerRequest {
|
||||
private final ParserRequest parserRequest;
|
||||
private final Path cwd;
|
||||
private final Path installationDirectory;
|
||||
private final Path userHomeDirectory;
|
||||
private final List<String> jvmArguments;
|
||||
private final Map<String, String> userProperties;
|
||||
private final Map<String, String> systemProperties;
|
||||
private final Path topDirectory;
|
||||
|
@ -58,7 +63,12 @@ public abstract class BaseInvokerRequest extends BaseExecutorRequest implements
|
|||
@Nullable OutputStream err,
|
||||
@Nullable List<CoreExtension> coreExtensions,
|
||||
@Nullable List<String> jvmArguments) {
|
||||
super(parserRequest, cwd, installationDirectory, userHomeDirectory, jvmArguments);
|
||||
this.parserRequest = requireNonNull(parserRequest);
|
||||
this.cwd = requireNonNull(cwd);
|
||||
this.installationDirectory = requireNonNull(installationDirectory);
|
||||
this.userHomeDirectory = requireNonNull(userHomeDirectory);
|
||||
this.jvmArguments = jvmArguments;
|
||||
|
||||
this.userProperties = requireNonNull(userProperties);
|
||||
this.systemProperties = requireNonNull(systemProperties);
|
||||
this.topDirectory = requireNonNull(topDirectory);
|
||||
|
@ -70,6 +80,31 @@ public abstract class BaseInvokerRequest extends BaseExecutorRequest implements
|
|||
this.err = err;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParserRequest parserRequest() {
|
||||
return parserRequest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path cwd() {
|
||||
return cwd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path installationDirectory() {
|
||||
return installationDirectory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path userHomeDirectory() {
|
||||
return userHomeDirectory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<List<String>> jvmArguments() {
|
||||
return Optional.ofNullable(jvmArguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> userProperties() {
|
||||
return userProperties;
|
||||
|
|
|
@ -37,7 +37,6 @@ import java.util.stream.Collectors;
|
|||
|
||||
import org.apache.maven.api.Constants;
|
||||
import org.apache.maven.api.annotations.Nullable;
|
||||
import org.apache.maven.api.cli.ExecutorRequest;
|
||||
import org.apache.maven.api.cli.InvokerRequest;
|
||||
import org.apache.maven.api.cli.Options;
|
||||
import org.apache.maven.api.cli.Parser;
|
||||
|
@ -94,25 +93,6 @@ public abstract class BaseParser implements Parser {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutorRequest parseExecution(ParserRequest parserRequest) throws ParserException, IOException {
|
||||
requireNonNull(parserRequest);
|
||||
|
||||
LocalContext context = new LocalContext(parserRequest);
|
||||
|
||||
// the basics
|
||||
context.cwd = requireNonNull(getCwd(context));
|
||||
context.installationDirectory = requireNonNull(getInstallationDirectory(context));
|
||||
context.userHomeDirectory = requireNonNull(getUserHomeDirectory(context));
|
||||
|
||||
return getExecutionRequest(context);
|
||||
}
|
||||
|
||||
protected ExecutorRequest getExecutionRequest(LocalContext context) {
|
||||
return new BaseExecutorRequest(
|
||||
context.parserRequest, context.cwd, context.installationDirectory, context.userHomeDirectory, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InvokerRequest parseInvocation(ParserRequest parserRequest) throws ParserException, IOException {
|
||||
requireNonNull(parserRequest);
|
||||
|
|
|
@ -57,7 +57,7 @@ public class LookupContext implements AutoCloseable {
|
|||
|
||||
Map<String, String> user = new HashMap<>(invokerRequest.userProperties());
|
||||
user.put("session.topDirectory", invokerRequest.topDirectory().toString());
|
||||
if (invokerRequest.rootDirectory().isEmpty()) {
|
||||
if (invokerRequest.rootDirectory().isPresent()) {
|
||||
user.put(
|
||||
"session.rootDirectory",
|
||||
invokerRequest.rootDirectory().get().toString());
|
||||
|
@ -112,4 +112,15 @@ public class LookupContext implements AutoCloseable {
|
|||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
protected void closeContainer() {
|
||||
if (containerCapsule != null) {
|
||||
try {
|
||||
containerCapsule.close();
|
||||
} finally {
|
||||
lookup = null;
|
||||
containerCapsule = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,10 +64,6 @@ import org.apache.maven.bridge.MavenRepositorySystem;
|
|||
import org.apache.maven.cling.invoker.spi.PropertyContributorsHolder;
|
||||
import org.apache.maven.cling.logging.Slf4jConfiguration;
|
||||
import org.apache.maven.cling.logging.Slf4jConfigurationFactory;
|
||||
import org.apache.maven.cling.transfer.ConsoleMavenTransferListener;
|
||||
import org.apache.maven.cling.transfer.QuietMavenTransferListener;
|
||||
import org.apache.maven.cling.transfer.SimplexTransferListener;
|
||||
import org.apache.maven.cling.transfer.Slf4jMavenTransferListener;
|
||||
import org.apache.maven.cling.utils.CLIReportingUtils;
|
||||
import org.apache.maven.execution.MavenExecutionRequest;
|
||||
import org.apache.maven.internal.impl.SettingsUtilsV4;
|
||||
|
@ -76,7 +72,6 @@ import org.apache.maven.jline.MessageUtils;
|
|||
import org.apache.maven.logging.LoggingOutputStream;
|
||||
import org.apache.maven.logging.api.LogLevelRecorder;
|
||||
import org.apache.maven.slf4j.MavenSimpleLogger;
|
||||
import org.eclipse.aether.transfer.TransferListener;
|
||||
import org.jline.terminal.Terminal;
|
||||
import org.jline.terminal.TerminalBuilder;
|
||||
import org.jline.terminal.impl.AbstractPosixTerminal;
|
||||
|
@ -132,6 +127,7 @@ public abstract class LookupInvoker<C extends LookupContext> implements Invoker
|
|||
validate(context);
|
||||
prepare(context);
|
||||
configureLogging(context);
|
||||
createTerminal(context);
|
||||
activateLogging(context);
|
||||
helpOrVersionAndMayExit(context);
|
||||
preCommands(context);
|
||||
|
@ -216,18 +212,9 @@ public abstract class LookupInvoker<C extends LookupContext> implements Invoker
|
|||
context.slf4jConfiguration.setRootLoggerLevel(context.loggerLevel);
|
||||
// else fall back to default log level specified in conf
|
||||
// see https://issues.apache.org/jira/browse/MNG-2570
|
||||
|
||||
// JLine is quite slow to start due to the native library unpacking and loading
|
||||
// so boot it asynchronously
|
||||
context.terminal = createTerminal(context);
|
||||
context.closeables.add(MessageUtils::systemUninstall);
|
||||
MessageUtils.registerShutdownHook(); // safety belt
|
||||
if (context.coloredOutput != null) {
|
||||
MessageUtils.setColorEnabled(context.coloredOutput);
|
||||
}
|
||||
}
|
||||
|
||||
protected Terminal createTerminal(C context) {
|
||||
protected void createTerminal(C context) {
|
||||
MessageUtils.systemInstall(
|
||||
builder -> {
|
||||
builder.streams(
|
||||
|
@ -243,7 +230,15 @@ public abstract class LookupInvoker<C extends LookupContext> implements Invoker
|
|||
}
|
||||
},
|
||||
terminal -> doConfigureWithTerminal(context, terminal));
|
||||
return MessageUtils.getTerminal();
|
||||
|
||||
context.terminal = MessageUtils.getTerminal();
|
||||
// JLine is quite slow to start due to the native library unpacking and loading
|
||||
// so boot it asynchronously
|
||||
context.closeables.add(MessageUtils::systemUninstall);
|
||||
MessageUtils.registerShutdownHook(); // safety belt
|
||||
if (context.coloredOutput != null) {
|
||||
MessageUtils.setColorEnabled(context.coloredOutput);
|
||||
}
|
||||
}
|
||||
|
||||
protected void doConfigureWithTerminal(C context, Terminal terminal) {
|
||||
|
@ -271,7 +266,7 @@ public abstract class LookupInvoker<C extends LookupContext> implements Invoker
|
|||
if (options.logFile().isPresent()) {
|
||||
Path logFile = context.cwdResolver.apply(options.logFile().get());
|
||||
try {
|
||||
PrintWriter printWriter = new PrintWriter(Files.newBufferedWriter(logFile));
|
||||
PrintWriter printWriter = new PrintWriter(Files.newBufferedWriter(logFile), true);
|
||||
context.closeables.add(printWriter);
|
||||
return printWriter::println;
|
||||
} catch (IOException e) {
|
||||
|
@ -376,7 +371,7 @@ public abstract class LookupInvoker<C extends LookupContext> implements Invoker
|
|||
|
||||
protected void container(C context) throws Exception {
|
||||
context.containerCapsule = createContainerCapsuleFactory().createContainerCapsule(this, context);
|
||||
context.closeables.add(context.containerCapsule);
|
||||
context.closeables.add(context::closeContainer);
|
||||
context.lookup = context.containerCapsule.getLookup();
|
||||
|
||||
// refresh logger in case container got customized by spy
|
||||
|
@ -743,24 +738,5 @@ public abstract class LookupInvoker<C extends LookupContext> implements Invoker
|
|||
return ciEnv != null && !"false".equals(ciEnv);
|
||||
}
|
||||
|
||||
protected TransferListener determineTransferListener(C context, boolean noTransferProgress) {
|
||||
boolean quiet = context.invokerRequest.options().quiet().orElse(false);
|
||||
boolean logFile = context.invokerRequest.options().logFile().isPresent();
|
||||
boolean runningOnCI = isRunningOnCI(context);
|
||||
boolean quietCI = runningOnCI
|
||||
&& !context.invokerRequest.options().forceInteractive().orElse(false);
|
||||
|
||||
if (quiet || noTransferProgress || quietCI) {
|
||||
return new QuietMavenTransferListener();
|
||||
} else if (context.interactive && !logFile) {
|
||||
return new SimplexTransferListener(new ConsoleMavenTransferListener(
|
||||
context.invokerRequest.messageBuilderFactory(),
|
||||
context.terminal.writer(),
|
||||
context.invokerRequest.options().verbose().orElse(false)));
|
||||
} else {
|
||||
return new Slf4jMavenTransferListener();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract int execute(C context) throws Exception;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
*/
|
||||
package org.apache.maven.cling.invoker;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
|
@ -47,14 +46,6 @@ import static java.util.Objects.requireNonNull;
|
|||
public final class Utils {
|
||||
private Utils() {}
|
||||
|
||||
@Nullable
|
||||
public static File toFile(Path path) {
|
||||
if (path != null) {
|
||||
return path.toFile();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static String stripLeadingAndTrailingQuotes(String str) {
|
||||
requireNonNull(str, "str");
|
||||
|
|
|
@ -32,6 +32,12 @@ public class MavenContext extends LookupContext {
|
|||
|
||||
public BuildEventListener buildEventListener;
|
||||
public EventSpyDispatcher eventSpyDispatcher;
|
||||
|
||||
public Maven maven;
|
||||
|
||||
@Override
|
||||
protected void closeContainer() {
|
||||
eventSpyDispatcher = null;
|
||||
maven = null;
|
||||
super.closeContainer();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,6 +50,10 @@ import org.apache.maven.cling.event.ExecutionEventLogger;
|
|||
import org.apache.maven.cling.invoker.LookupInvoker;
|
||||
import org.apache.maven.cling.invoker.ProtoLookup;
|
||||
import org.apache.maven.cling.invoker.Utils;
|
||||
import org.apache.maven.cling.transfer.ConsoleMavenTransferListener;
|
||||
import org.apache.maven.cling.transfer.QuietMavenTransferListener;
|
||||
import org.apache.maven.cling.transfer.SimplexTransferListener;
|
||||
import org.apache.maven.cling.transfer.Slf4jMavenTransferListener;
|
||||
import org.apache.maven.cling.utils.CLIReportingUtils;
|
||||
import org.apache.maven.eventspy.internal.EventSpyDispatcher;
|
||||
import org.apache.maven.exception.DefaultExceptionHandler;
|
||||
|
@ -389,12 +393,27 @@ public abstract class MavenInvoker<C extends MavenContext> extends LookupInvoker
|
|||
if (context.eventSpyDispatcher != null) {
|
||||
listener = context.eventSpyDispatcher.chainListener(listener);
|
||||
}
|
||||
listener = new LoggingExecutionListener(listener, determineBuildEventListener(context));
|
||||
return listener;
|
||||
return new LoggingExecutionListener(listener, determineBuildEventListener(context));
|
||||
}
|
||||
|
||||
protected TransferListener determineTransferListener(C context, boolean noTransferProgress) {
|
||||
TransferListener delegate = super.determineTransferListener(context, noTransferProgress);
|
||||
boolean quiet = context.invokerRequest.options().quiet().orElse(false);
|
||||
boolean logFile = context.invokerRequest.options().logFile().isPresent();
|
||||
boolean runningOnCI = isRunningOnCI(context);
|
||||
boolean quietCI = runningOnCI
|
||||
&& !context.invokerRequest.options().forceInteractive().orElse(false);
|
||||
|
||||
TransferListener delegate;
|
||||
if (quiet || noTransferProgress || quietCI) {
|
||||
delegate = new QuietMavenTransferListener();
|
||||
} else if (context.interactive && !logFile) {
|
||||
delegate = new SimplexTransferListener(new ConsoleMavenTransferListener(
|
||||
context.invokerRequest.messageBuilderFactory(),
|
||||
context.terminal.writer(),
|
||||
context.invokerRequest.options().verbose().orElse(false)));
|
||||
} else {
|
||||
delegate = new Slf4jMavenTransferListener();
|
||||
}
|
||||
return new MavenTransferListener(delegate, determineBuildEventListener(context));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
* 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.cling.invoker.mvn.forked;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.apache.maven.api.cli.Executor;
|
||||
import org.apache.maven.api.cli.ExecutorException;
|
||||
import org.apache.maven.api.cli.ExecutorRequest;
|
||||
import org.apache.maven.internal.impl.model.profile.Os;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* Forked executor implementation, that spawns a subprocess with Maven from the installation directory.
|
||||
*/
|
||||
public class ForkedMavenExecutor implements Executor {
|
||||
@Override
|
||||
public int execute(ExecutorRequest executorRequest) throws ExecutorException {
|
||||
requireNonNull(executorRequest);
|
||||
validate(executorRequest);
|
||||
|
||||
ArrayList<String> cmdAndArguments = new ArrayList<>();
|
||||
cmdAndArguments.add(executorRequest
|
||||
.installationDirectory()
|
||||
.resolve("bin")
|
||||
.resolve(
|
||||
Os.IS_WINDOWS
|
||||
? executorRequest.parserRequest().command() + ".cmd"
|
||||
: executorRequest.parserRequest().command())
|
||||
.toString());
|
||||
|
||||
cmdAndArguments.addAll(executorRequest.parserRequest().args());
|
||||
|
||||
try {
|
||||
ProcessBuilder pb = new ProcessBuilder()
|
||||
.directory(executorRequest.cwd().toFile())
|
||||
.command(cmdAndArguments);
|
||||
|
||||
if (executorRequest.jvmArguments().isPresent()) {
|
||||
pb.environment()
|
||||
.put(
|
||||
"MAVEN_OPTS",
|
||||
String.join(" ", executorRequest.jvmArguments().get()));
|
||||
}
|
||||
|
||||
return pb.start().waitFor();
|
||||
} catch (IOException e) {
|
||||
throw new ExecutorException("IO problem while executing command: " + cmdAndArguments, e);
|
||||
} catch (InterruptedException e) {
|
||||
throw new ExecutorException("Interrupted while executing command: " + cmdAndArguments, e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void validate(ExecutorRequest executorRequest) throws ExecutorException {}
|
||||
}
|
|
@ -29,12 +29,12 @@ public class ResidentMavenContext extends MavenContext {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void close() throws InvokerException {
|
||||
// we are resident, we do not shut down here
|
||||
protected void closeContainer() {
|
||||
// we are resident; we do not shut down here
|
||||
}
|
||||
|
||||
public void shutDown() throws InvokerException {
|
||||
super.close();
|
||||
super.closeContainer();
|
||||
}
|
||||
|
||||
public ResidentMavenContext copy(InvokerRequest invokerRequest) {
|
||||
|
@ -43,16 +43,9 @@ public class ResidentMavenContext extends MavenContext {
|
|||
}
|
||||
ResidentMavenContext shadow = new ResidentMavenContext(invokerRequest);
|
||||
|
||||
shadow.logger = logger;
|
||||
shadow.loggerFactory = loggerFactory;
|
||||
shadow.loggerLevel = loggerLevel;
|
||||
// we carry over only "resident" things
|
||||
shadow.containerCapsule = containerCapsule;
|
||||
shadow.lookup = lookup;
|
||||
|
||||
shadow.interactive = interactive;
|
||||
shadow.localRepositoryPath = localRepositoryPath;
|
||||
shadow.effectiveSettings = effectiveSettings;
|
||||
|
||||
shadow.eventSpyDispatcher = eventSpyDispatcher;
|
||||
shadow.maven = maven;
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
*/
|
||||
package org.apache.maven.cling.transfer;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import org.apache.maven.api.services.MessageBuilder;
|
||||
|
@ -37,10 +36,6 @@ public abstract class AbstractMavenTransferListener extends AbstractTransferList
|
|||
protected final MessageBuilderFactory messageBuilderFactory;
|
||||
protected final PrintWriter out;
|
||||
|
||||
protected AbstractMavenTransferListener(MessageBuilderFactory messageBuilderFactory, PrintStream out) {
|
||||
this(messageBuilderFactory, new PrintWriter(out));
|
||||
}
|
||||
|
||||
protected AbstractMavenTransferListener(MessageBuilderFactory messageBuilderFactory, PrintWriter out) {
|
||||
this.messageBuilderFactory = messageBuilderFactory;
|
||||
this.out = out;
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
*/
|
||||
package org.apache.maven.cling.transfer;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
|
@ -43,11 +42,6 @@ public class ConsoleMavenTransferListener extends AbstractMavenTransferListener
|
|||
private final boolean printResourceNames;
|
||||
private int lastLength;
|
||||
|
||||
public ConsoleMavenTransferListener(
|
||||
MessageBuilderFactory messageBuilderFactory, PrintStream out, boolean printResourceNames) {
|
||||
this(messageBuilderFactory, new PrintWriter(out), printResourceNames);
|
||||
}
|
||||
|
||||
public ConsoleMavenTransferListener(
|
||||
MessageBuilderFactory messageBuilderFactory, PrintWriter out, boolean printResourceNames) {
|
||||
super(messageBuilderFactory, out);
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* 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.cling.invoker.mvn;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.maven.api.cli.Executor;
|
||||
import org.apache.maven.api.cli.Parser;
|
||||
import org.apache.maven.api.cli.ParserRequest;
|
||||
import org.apache.maven.cling.invoker.ProtoLogger;
|
||||
import org.apache.maven.jline.JLineMessageBuilderFactory;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public abstract class MavenExecutorTestSupport {
|
||||
|
||||
protected void execute(Path cwd, Collection<String> goals) throws Exception {
|
||||
Files.createDirectory(cwd.resolve(".mvn"));
|
||||
Path pom = cwd.resolve("pom.xml").toAbsolutePath();
|
||||
Files.writeString(pom, MavenTestSupport.POM_STRING);
|
||||
Path appJava = cwd.resolve("src/main/java/org/apache/maven/samples/sample/App.java");
|
||||
Files.createDirectories(appJava.getParent());
|
||||
Files.writeString(appJava, MavenTestSupport.APP_JAVA_STRING);
|
||||
|
||||
Parser parser = createParser();
|
||||
try (Executor invoker = createExecutor()) {
|
||||
for (String goal : goals) {
|
||||
Path logFile = cwd.resolve(goal + "-build.log").toAbsolutePath();
|
||||
int exitCode = invoker.execute(parser.parseExecution(ParserRequest.mvn(
|
||||
List.of("-l", logFile.toString(), goal),
|
||||
new ProtoLogger(),
|
||||
new JLineMessageBuilderFactory())
|
||||
.cwd(cwd)
|
||||
.build()));
|
||||
String log = Files.readString(logFile);
|
||||
System.out.println(log);
|
||||
assertEquals(0, exitCode, log);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract Executor createExecutor();
|
||||
|
||||
protected abstract Parser createParser();
|
||||
}
|
|
@ -34,6 +34,51 @@ import org.junit.jupiter.api.Assumptions;
|
|||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public abstract class MavenInvokerTestSupport {
|
||||
public static final String POM_STRING =
|
||||
"""
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.apache.maven.samples</groupId>
|
||||
<artifactId>sample</artifactId>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.junit</groupId>
|
||||
<artifactId>junit-bom</artifactId>
|
||||
<version>5.11.1</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
""";
|
||||
|
||||
public static final String APP_JAVA_STRING =
|
||||
"""
|
||||
package org.apache.maven.samples.sample;
|
||||
|
||||
public class App {
|
||||
public static void main(String... args) {
|
||||
System.out.println("Hello World!");
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
protected void invoke(Path cwd, Collection<String> goals) throws Exception {
|
||||
// works only in recent Maven4
|
||||
|
@ -45,10 +90,10 @@ public abstract class MavenInvokerTestSupport {
|
|||
|
||||
Files.createDirectory(cwd.resolve(".mvn"));
|
||||
Path pom = cwd.resolve("pom.xml").toAbsolutePath();
|
||||
Files.writeString(pom, MavenTestSupport.POM_STRING);
|
||||
Files.writeString(pom, POM_STRING);
|
||||
Path appJava = cwd.resolve("src/main/java/org/apache/maven/samples/sample/App.java");
|
||||
Files.createDirectories(appJava.getParent());
|
||||
Files.writeString(appJava, MavenTestSupport.APP_JAVA_STRING);
|
||||
Files.writeString(appJava, APP_JAVA_STRING);
|
||||
|
||||
Parser parser = createParser();
|
||||
try (Invoker invoker = createInvoker()) {
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
* 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.cling.invoker.mvn;
|
||||
|
||||
public final class MavenTestSupport {
|
||||
private MavenTestSupport() {}
|
||||
|
||||
public static final String POM_STRING =
|
||||
"""
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.apache.maven.samples</groupId>
|
||||
<artifactId>sample</artifactId>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.junit</groupId>
|
||||
<artifactId>junit-bom</artifactId>
|
||||
<version>5.11.1</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
""";
|
||||
|
||||
public static final String APP_JAVA_STRING =
|
||||
"""
|
||||
package org.apache.maven.samples.sample;
|
||||
|
||||
public class App {
|
||||
public static void main(String... args) {
|
||||
System.out.println("Hello World!");
|
||||
}
|
||||
}
|
||||
""";
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
/*
|
||||
* 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.cling.invoker.mvn.embedded;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.maven.api.cli.Executor;
|
||||
import org.apache.maven.api.cli.Parser;
|
||||
import org.apache.maven.cling.invoker.mvn.MavenExecutorTestSupport;
|
||||
import org.apache.maven.cling.invoker.mvn.MavenParser;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.CleanupMode;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
/**
|
||||
* Forked UT: it cannot use jimFS as it runs in child process.
|
||||
*/
|
||||
@Disabled(
|
||||
"The tests reuse properties from the JVM being launched, thus may lead to failures depending on which options are used")
|
||||
public class EmbeddedMavenExecutorTest extends MavenExecutorTestSupport {
|
||||
|
||||
@Override
|
||||
protected Executor createExecutor() {
|
||||
return new EmbeddedMavenExecutor();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Parser createParser() {
|
||||
return new MavenParser();
|
||||
}
|
||||
|
||||
@Test
|
||||
void defaultFs(@TempDir(cleanup = CleanupMode.ON_SUCCESS) Path tempDir) throws Exception {
|
||||
System.setProperty("maven.home", "/home/cstamas/Tools/maven/apache-maven-4.0.0-beta-6-SNAPSHOT");
|
||||
execute(tempDir, List.of("verify"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void defaultFs3x(@TempDir(cleanup = CleanupMode.ON_SUCCESS) Path tempDir) throws Exception {
|
||||
System.setProperty("maven.home", "/home/cstamas/.sdkman/candidates/maven/3.9.9");
|
||||
execute(tempDir, List.of("verify"));
|
||||
}
|
||||
}
|
|
@ -25,7 +25,7 @@ import org.apache.maven.api.cli.Invoker;
|
|||
import org.apache.maven.api.cli.Parser;
|
||||
import org.apache.maven.cling.invoker.mvn.MavenInvokerTestSupport;
|
||||
import org.apache.maven.cling.invoker.mvn.MavenParser;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.CleanupMode;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
@ -33,8 +33,7 @@ import org.junit.jupiter.api.io.TempDir;
|
|||
/**
|
||||
* Forked UT: it cannot use jimFS as it runs in child process.
|
||||
*/
|
||||
@Disabled(
|
||||
"The tests reuse properties from the JVM being launched, thus may lead to failures depending on which options are used")
|
||||
@Order(300)
|
||||
public class ForkedMavenInvokerTest extends MavenInvokerTestSupport {
|
||||
|
||||
@Override
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.apache.maven.cling.invoker.mvn.MavenInvokerTestSupport;
|
|||
import org.apache.maven.cling.invoker.mvn.MavenParser;
|
||||
import org.codehaus.plexus.classworlds.ClassWorld;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.CleanupMode;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
@ -38,9 +39,8 @@ import org.junit.jupiter.api.io.TempDir;
|
|||
/**
|
||||
* Local UT.
|
||||
*/
|
||||
@Disabled(
|
||||
"The tests reuse properties from the JVM being launched, thus may lead to failures depending on which options are used")
|
||||
public class DefaultLocalMavenInvokerTest extends MavenInvokerTestSupport {
|
||||
@Order(200)
|
||||
public class LocalMavenInvokerTest extends MavenInvokerTestSupport {
|
||||
@Override
|
||||
protected Invoker createInvoker() {
|
||||
return new LocalMavenInvoker(ProtoLookup.builder()
|
|
@ -31,6 +31,7 @@ import org.apache.maven.cling.invoker.mvn.MavenInvokerTestSupport;
|
|||
import org.apache.maven.cling.invoker.mvn.MavenParser;
|
||||
import org.codehaus.plexus.classworlds.ClassWorld;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.CleanupMode;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
@ -38,9 +39,8 @@ import org.junit.jupiter.api.io.TempDir;
|
|||
/**
|
||||
* Resident UT.
|
||||
*/
|
||||
@Disabled(
|
||||
"The tests reuse properties from the JVM being launched, thus may lead to failures depending on which options are used")
|
||||
public class DefaultResidentMavenInvokerTest extends MavenInvokerTestSupport {
|
||||
@Order(100)
|
||||
public class ResidentMavenInvokerTest extends MavenInvokerTestSupport {
|
||||
|
||||
@Override
|
||||
protected Invoker createInvoker() {
|
|
@ -0,0 +1,103 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven</artifactId>
|
||||
<version>4.0.0-beta-6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../../</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>maven-executor</artifactId>
|
||||
|
||||
<name>Maven 4 Executor</name>
|
||||
<description>Maven 4 Executor, for executing Maven 3/4.</description>
|
||||
|
||||
<properties>
|
||||
<maven3version>3.9.9</maven3version>
|
||||
<maven4version>4.0.0-beta-5</maven4version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-api-meta</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>prepare-maven-distros</id>
|
||||
<goals>
|
||||
<goal>unpack</goal>
|
||||
</goals>
|
||||
<phase>generate-test-resources</phase>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>apache-maven</artifactId>
|
||||
<version>${maven3version}</version>
|
||||
<classifier>bin</classifier>
|
||||
<type>zip</type>
|
||||
</artifactItem>
|
||||
<artifactItem>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>apache-maven</artifactId>
|
||||
<version>${maven4version}</version>
|
||||
<classifier>bin</classifier>
|
||||
<type>zip</type>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<promoteUserPropertiesToSystemProperties>false</promoteUserPropertiesToSystemProperties>
|
||||
<systemPropertyVariables>
|
||||
<maven3version>${maven3version}</maven3version>
|
||||
<maven4version>${maven4version}</maven4version>
|
||||
<maven3home>${project.build.directory}/dependency/apache-maven-${maven3version}</maven3home>
|
||||
<maven4home>${project.build.directory}/dependency/apache-maven-${maven4version}</maven4home>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -30,6 +30,14 @@ import org.apache.maven.api.annotations.Nonnull;
|
|||
*/
|
||||
@Experimental
|
||||
public interface Executor extends AutoCloseable {
|
||||
// Logic borrowed from Commons-Lang3
|
||||
boolean IS_WINDOWS = System.getProperty("os.name", "unknown").startsWith("Windows");
|
||||
|
||||
/**
|
||||
* Maven version string returned when the actual version of Maven cannot be determinet.
|
||||
*/
|
||||
String UNKNOWN_VERSION = "unknown";
|
||||
|
||||
/**
|
||||
* Invokes the tool application using the provided {@link ExecutorRequest}.
|
||||
* This method is responsible for executing the command or build
|
||||
|
@ -41,6 +49,18 @@ public interface Executor extends AutoCloseable {
|
|||
*/
|
||||
int execute(@Nonnull ExecutorRequest executorRequest) throws ExecutorException;
|
||||
|
||||
/**
|
||||
* Returns the Maven version that provided {@link ExecutorRequest} point at (would use). Please not, that this
|
||||
* operation, depending on underlying implementation may be costly. If caller use this method often, it is
|
||||
* caller responsibility to properly cache returned values (key can be {@link ExecutorRequest#installationDirectory()}.
|
||||
*
|
||||
* @param executorRequest the request containing all necessary information for the execution
|
||||
* @return a string representing the Maven version or {@link #UNKNOWN_VERSION}
|
||||
* @throws ExecutorException if an error occurs during the execution process
|
||||
*/
|
||||
@Nonnull
|
||||
String mavenVersion(@Nonnull ExecutorRequest executorRequest) throws ExecutorException;
|
||||
|
||||
/**
|
||||
* Closes and disposes of this {@link Executor} instance, releasing any resources it may hold.
|
||||
* This method is called automatically when using try-with-resources statements.
|
|
@ -20,7 +20,6 @@ package org.apache.maven.api.cli;
|
|||
|
||||
import org.apache.maven.api.annotations.Experimental;
|
||||
import org.apache.maven.api.annotations.Nullable;
|
||||
import org.apache.maven.api.services.MavenException;
|
||||
|
||||
/**
|
||||
* Represents an exception that occurs during the execution of a Maven build or command.
|
||||
|
@ -30,7 +29,7 @@ import org.apache.maven.api.services.MavenException;
|
|||
* @since 4.0.0
|
||||
*/
|
||||
@Experimental
|
||||
public class ExecutorException extends MavenException {
|
||||
public class ExecutorException extends RuntimeException {
|
||||
/**
|
||||
* Constructs a new {@code InvokerException} with the specified detail message.
|
||||
*
|
|
@ -0,0 +1,299 @@
|
|||
/*
|
||||
* 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.api.cli;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.maven.api.annotations.Experimental;
|
||||
import org.apache.maven.api.annotations.Immutable;
|
||||
import org.apache.maven.api.annotations.Nonnull;
|
||||
import org.apache.maven.api.annotations.Nullable;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* Represents a request to execute Maven with command-line arguments.
|
||||
* This interface encapsulates all the necessary information needed to execute
|
||||
* Maven command with arguments. The arguments were not parsed, they are just passed over
|
||||
* to executed tool.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@Immutable
|
||||
@Experimental
|
||||
public interface ExecutorRequest {
|
||||
/**
|
||||
* The command to execute, ie "mvn".
|
||||
*/
|
||||
@Nonnull
|
||||
String command();
|
||||
|
||||
/**
|
||||
* The immutable list of arguments to pass to the command.
|
||||
*/
|
||||
@Nonnull
|
||||
List<String> arguments();
|
||||
|
||||
/**
|
||||
* Returns the current working directory for the Maven execution.
|
||||
* This is typically the directory from which Maven was invoked.
|
||||
*
|
||||
* @return the current working directory path
|
||||
*/
|
||||
@Nonnull
|
||||
Path cwd();
|
||||
|
||||
/**
|
||||
* Returns the Maven installation directory.
|
||||
* This is usually set by the Maven launcher script using the "maven.home" system property.
|
||||
*
|
||||
* @return the Maven installation directory path
|
||||
*/
|
||||
@Nonnull
|
||||
Path installationDirectory();
|
||||
|
||||
/**
|
||||
* Returns the user's home directory.
|
||||
* This is typically obtained from the "user.home" system property.
|
||||
*
|
||||
* @return the user's home directory path
|
||||
*/
|
||||
@Nonnull
|
||||
Path userHomeDirectory();
|
||||
|
||||
/**
|
||||
* Returns the list of extra JVM arguments to be passed to the forked process.
|
||||
* These arguments allow for customization of the JVM environment in which tool will run.
|
||||
* This property is used ONLY by executors and invokers that spawn a new JVM.
|
||||
*
|
||||
* @return an Optional containing the list of extra JVM arguments, or empty if not specified
|
||||
*/
|
||||
@Nonnull
|
||||
Optional<List<String>> jvmArguments();
|
||||
|
||||
/**
|
||||
* Returns {@link Builder} for this instance.
|
||||
*/
|
||||
@Nonnull
|
||||
default Builder toBuilder() {
|
||||
return new Builder(
|
||||
command(),
|
||||
arguments(),
|
||||
cwd(),
|
||||
installationDirectory(),
|
||||
userHomeDirectory(),
|
||||
jvmArguments().orElse(null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns new empty builder.
|
||||
*/
|
||||
@Nonnull
|
||||
static Builder empyBuilder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns new builder pre-set to run Maven. The discovery of maven home is attempted.
|
||||
*/
|
||||
@Nonnull
|
||||
static Builder mavenBuilder(@Nullable Path installationDirectory) {
|
||||
return new Builder(
|
||||
"mvn",
|
||||
null,
|
||||
getCanonicalPath(Paths.get(System.getProperty("user.dir"))),
|
||||
installationDirectory != null ? getCanonicalPath(installationDirectory) : discoverMavenHome(),
|
||||
getCanonicalPath(Paths.get(System.getProperty("user.home"))),
|
||||
null);
|
||||
}
|
||||
|
||||
class Builder {
|
||||
private String command;
|
||||
private List<String> arguments;
|
||||
private Path cwd;
|
||||
private Path installationDirectory;
|
||||
private Path userHomeDirectory;
|
||||
private List<String> jvmArguments;
|
||||
|
||||
private Builder() {}
|
||||
|
||||
private Builder(
|
||||
String command,
|
||||
List<String> arguments,
|
||||
Path cwd,
|
||||
Path installationDirectory,
|
||||
Path userHomeDirectory,
|
||||
List<String> jvmArguments) {
|
||||
this.command = command;
|
||||
this.arguments = arguments;
|
||||
this.cwd = cwd;
|
||||
this.installationDirectory = installationDirectory;
|
||||
this.userHomeDirectory = userHomeDirectory;
|
||||
this.jvmArguments = jvmArguments;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Builder command(String command) {
|
||||
this.command = requireNonNull(command, "command");
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Builder arguments(List<String> arguments) {
|
||||
this.arguments = requireNonNull(arguments, "arguments");
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Builder argument(String argument) {
|
||||
if (arguments == null) {
|
||||
arguments = new ArrayList<>();
|
||||
}
|
||||
this.arguments.add(requireNonNull(argument, "argument"));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Builder cwd(Path cwd) {
|
||||
this.cwd = requireNonNull(cwd, "cwd");
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Builder installationDirectory(Path installationDirectory) {
|
||||
this.installationDirectory = requireNonNull(installationDirectory, "installationDirectory");
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Builder userHomeDirectory(Path userHomeDirectory) {
|
||||
this.userHomeDirectory = requireNonNull(userHomeDirectory, "userHomeDirectory");
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Builder jvmArguments(List<String> jvmArguments) {
|
||||
this.jvmArguments = jvmArguments;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Builder jvmArgument(String jvmArgument) {
|
||||
if (jvmArguments == null) {
|
||||
jvmArguments = new ArrayList<>();
|
||||
}
|
||||
this.jvmArguments.add(requireNonNull(jvmArgument, "jvmArgument"));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public ExecutorRequest build() {
|
||||
return new Impl(command, arguments, cwd, installationDirectory, userHomeDirectory, jvmArguments);
|
||||
}
|
||||
|
||||
private static class Impl implements ExecutorRequest {
|
||||
private final String command;
|
||||
private final List<String> arguments;
|
||||
private final Path cwd;
|
||||
private final Path installationDirectory;
|
||||
private final Path userHomeDirectory;
|
||||
private final List<String> jvmArguments;
|
||||
|
||||
private Impl(
|
||||
String command,
|
||||
List<String> arguments,
|
||||
Path cwd,
|
||||
Path installationDirectory,
|
||||
Path userHomeDirectory,
|
||||
List<String> jvmArguments) {
|
||||
this.command = requireNonNull(command);
|
||||
this.arguments = arguments == null ? List.of() : List.copyOf(arguments);
|
||||
this.cwd = requireNonNull(cwd);
|
||||
this.installationDirectory = requireNonNull(installationDirectory);
|
||||
this.userHomeDirectory = requireNonNull(userHomeDirectory);
|
||||
this.jvmArguments = jvmArguments != null ? List.copyOf(jvmArguments) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String command() {
|
||||
return command;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> arguments() {
|
||||
return arguments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path cwd() {
|
||||
return cwd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path installationDirectory() {
|
||||
return installationDirectory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path userHomeDirectory() {
|
||||
return userHomeDirectory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<List<String>> jvmArguments() {
|
||||
return Optional.ofNullable(jvmArguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ExecutionRequest{" + "command='"
|
||||
+ command + '\'' + ", arguments="
|
||||
+ arguments + ", cwd="
|
||||
+ cwd + ", installationDirectory="
|
||||
+ installationDirectory + ", userHomeDirectory="
|
||||
+ userHomeDirectory + ", jvmArguments="
|
||||
+ jvmArguments + '}';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
static Path discoverMavenHome() {
|
||||
String mavenHome = System.getProperty("maven.home");
|
||||
if (mavenHome == null) {
|
||||
throw new ExecutorException("requires maven.home Java System Property set");
|
||||
}
|
||||
return getCanonicalPath(Paths.get(mavenHome));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
static Path getCanonicalPath(Path path) {
|
||||
requireNonNull(path, "path");
|
||||
try {
|
||||
return path.toRealPath();
|
||||
} catch (IOException e) {
|
||||
return getCanonicalPath(path.getParent()).resolve(path.getFileName());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.maven.cling.invoker.mvn.embedded;
|
||||
package org.apache.maven.cling.executor.embedded;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
|
@ -102,6 +102,13 @@ public class EmbeddedMavenExecutor implements Executor {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mavenVersion(ExecutorRequest executorRequest) throws ExecutorException {
|
||||
requireNonNull(executorRequest);
|
||||
validate(executorRequest);
|
||||
return mayCreate(executorRequest).version;
|
||||
}
|
||||
|
||||
protected Context mayCreate(ExecutorRequest executorRequest) {
|
||||
Path installation = executorRequest.installationDirectory();
|
||||
if (!Files.isDirectory(installation)) {
|
||||
|
@ -148,7 +155,7 @@ public class EmbeddedMavenExecutor implements Executor {
|
|||
Object classWorld = launcherClass.getMethod("getWorld").invoke(launcher);
|
||||
Class<?> cliClass =
|
||||
(Class<?>) launcherClass.getMethod("getMainClass").invoke(launcher);
|
||||
String version = getMavenVersion(cliClass.getClassLoader());
|
||||
String version = getMavenVersion(cliClass);
|
||||
Function<ExecutorRequest, Integer> exec;
|
||||
|
||||
if (version.startsWith("3.")) {
|
||||
|
@ -160,10 +167,7 @@ public class EmbeddedMavenExecutor implements Executor {
|
|||
exec = r -> {
|
||||
try {
|
||||
return (int) doMain.invoke(mavenCli, new Object[] {
|
||||
r.parserRequest().args().toArray(new String[0]),
|
||||
r.cwd().toString(),
|
||||
null,
|
||||
null
|
||||
r.arguments().toArray(new String[0]), r.cwd().toString(), null, null
|
||||
});
|
||||
} catch (Exception e) {
|
||||
throw new ExecutorException("Failed to execute", e);
|
||||
|
@ -174,8 +178,7 @@ public class EmbeddedMavenExecutor implements Executor {
|
|||
Method mainMethod = cliClass.getMethod("main", String[].class, classWorld.getClass());
|
||||
exec = r -> {
|
||||
try {
|
||||
return (int) mainMethod.invoke(
|
||||
null, r.parserRequest().args().toArray(new String[0]), classWorld);
|
||||
return (int) mainMethod.invoke(null, r.arguments().toArray(new String[0]), classWorld);
|
||||
} catch (Exception e) {
|
||||
throw new ExecutorException("Failed to execute", e);
|
||||
}
|
||||
|
@ -250,10 +253,9 @@ public class EmbeddedMavenExecutor implements Executor {
|
|||
urls.toArray(new URL[0]), ClassLoader.getSystemClassLoader().getParent());
|
||||
}
|
||||
|
||||
public String getMavenVersion(ClassLoader classLoader) throws IOException {
|
||||
protected String getMavenVersion(Class<?> clazz) throws IOException {
|
||||
Properties props = new Properties();
|
||||
try (InputStream is =
|
||||
classLoader.getResourceAsStream("/META-INF/maven/org.apache.maven/maven-core/pom.properties")) {
|
||||
try (InputStream is = clazz.getResourceAsStream("/META-INF/maven/org.apache.maven/maven-core/pom.properties")) {
|
||||
if (is != null) {
|
||||
props.load(is);
|
||||
}
|
||||
|
@ -261,7 +263,7 @@ public class EmbeddedMavenExecutor implements Executor {
|
|||
if (version != null) {
|
||||
return version;
|
||||
}
|
||||
return "unknown";
|
||||
return UNKNOWN_VERSION;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* 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.cling.executor.forked;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.apache.maven.api.cli.Executor;
|
||||
import org.apache.maven.api.cli.ExecutorException;
|
||||
import org.apache.maven.api.cli.ExecutorRequest;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* Forked executor implementation, that spawns a subprocess with Maven from the installation directory. Very costly
|
||||
* but provides the best isolation.
|
||||
*/
|
||||
public class ForkedMavenExecutor implements Executor {
|
||||
@Override
|
||||
public int execute(ExecutorRequest executorRequest) throws ExecutorException {
|
||||
requireNonNull(executorRequest);
|
||||
validate(executorRequest);
|
||||
|
||||
return doExecute(executorRequest, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mavenVersion(ExecutorRequest executorRequest) throws ExecutorException {
|
||||
requireNonNull(executorRequest);
|
||||
validate(executorRequest);
|
||||
try {
|
||||
Path cwd = Files.createTempDirectory("forked-executor-maven-version");
|
||||
try {
|
||||
ArrayList<String> stdout = new ArrayList<>();
|
||||
int exitCode = doExecute(
|
||||
executorRequest.toBuilder()
|
||||
.cwd(cwd)
|
||||
.arguments(List.of("--version", "--color", "never"))
|
||||
.build(),
|
||||
p -> {
|
||||
String line;
|
||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
|
||||
while ((line = br.readLine()) != null) {
|
||||
stdout.add(line);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
});
|
||||
if (exitCode == 0) {
|
||||
for (String line : stdout) {
|
||||
if (line.startsWith("Apache Maven ")) {
|
||||
return line.substring(13, line.indexOf("(") - 1);
|
||||
}
|
||||
}
|
||||
return UNKNOWN_VERSION;
|
||||
} else {
|
||||
throw new ExecutorException(
|
||||
"Maven version query unexpected exitCode=" + exitCode + "\nLog: " + stdout);
|
||||
}
|
||||
} finally {
|
||||
Files.deleteIfExists(cwd);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new ExecutorException("Failed to determine maven version", e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void validate(ExecutorRequest executorRequest) throws ExecutorException {}
|
||||
|
||||
protected int doExecute(ExecutorRequest executorRequest, Consumer<Process> processConsumer)
|
||||
throws ExecutorException {
|
||||
ArrayList<String> cmdAndArguments = new ArrayList<>();
|
||||
cmdAndArguments.add(executorRequest
|
||||
.installationDirectory()
|
||||
.resolve("bin")
|
||||
.resolve(IS_WINDOWS ? executorRequest.command() + ".cmd" : executorRequest.command())
|
||||
.toString());
|
||||
|
||||
cmdAndArguments.addAll(executorRequest.arguments());
|
||||
|
||||
try {
|
||||
ProcessBuilder pb = new ProcessBuilder()
|
||||
.directory(executorRequest.cwd().toFile())
|
||||
.command(cmdAndArguments);
|
||||
|
||||
if (executorRequest.jvmArguments().isPresent()) {
|
||||
pb.environment()
|
||||
.put(
|
||||
"MAVEN_OPTS",
|
||||
String.join(" ", executorRequest.jvmArguments().get()));
|
||||
}
|
||||
|
||||
Process process = pb.start();
|
||||
if (processConsumer != null) {
|
||||
processConsumer.accept(process);
|
||||
}
|
||||
return process.waitFor();
|
||||
} catch (IOException e) {
|
||||
throw new ExecutorException("IO problem while executing command: " + cmdAndArguments, e);
|
||||
} catch (InterruptedException e) {
|
||||
throw new ExecutorException("Interrupted while executing command: " + cmdAndArguments, e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
* 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.cling.executor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.maven.api.cli.Executor;
|
||||
import org.apache.maven.api.cli.ExecutorRequest;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.CleanupMode;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public abstract class MavenExecutorTestSupport {
|
||||
@Test
|
||||
void defaultFs(@TempDir(cleanup = CleanupMode.ON_SUCCESS) Path tempDir) throws Exception {
|
||||
layDownFiles(tempDir);
|
||||
String logfile = "m4.log";
|
||||
execute(
|
||||
tempDir.resolve(logfile),
|
||||
List.of(mvn4ExecutorRequestBuilder()
|
||||
.cwd(tempDir)
|
||||
.argument("verify")
|
||||
.argument("-l")
|
||||
.argument(logfile)
|
||||
.build()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void version() throws Exception {
|
||||
assertEquals(
|
||||
System.getProperty("maven4version"),
|
||||
mavenVersion(mvn4ExecutorRequestBuilder().build()));
|
||||
}
|
||||
|
||||
@Disabled("JUnit on Windows fails to clean up as mvn3 seems does not close log file properly")
|
||||
@Test
|
||||
void defaultFs3x(@TempDir(cleanup = CleanupMode.ON_SUCCESS) Path tempDir) throws Exception {
|
||||
layDownFiles(tempDir);
|
||||
String logfile = "m3.log";
|
||||
execute(
|
||||
tempDir.resolve(logfile),
|
||||
List.of(mvn3ExecutorRequestBuilder()
|
||||
.cwd(tempDir)
|
||||
.argument("verify")
|
||||
.argument("-l")
|
||||
.argument(logfile)
|
||||
.build()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void version3x() throws Exception {
|
||||
assertEquals(
|
||||
System.getProperty("maven3version"),
|
||||
mavenVersion(mvn3ExecutorRequestBuilder().build()));
|
||||
}
|
||||
|
||||
public static final String POM_STRING =
|
||||
"""
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.apache.maven.samples</groupId>
|
||||
<artifactId>sample</artifactId>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.junit</groupId>
|
||||
<artifactId>junit-bom</artifactId>
|
||||
<version>5.11.1</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
""";
|
||||
|
||||
public static final String APP_JAVA_STRING =
|
||||
"""
|
||||
package org.apache.maven.samples.sample;
|
||||
|
||||
public class App {
|
||||
public static void main(String... args) {
|
||||
System.out.println("Hello World!");
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
protected void execute(Path logFile, Collection<ExecutorRequest> requests) throws Exception {
|
||||
try (Executor invoker = createExecutor()) {
|
||||
for (ExecutorRequest request : requests) {
|
||||
int exitCode = invoker.execute(request);
|
||||
if (exitCode != 0) {
|
||||
throw new FailedExecution(request, exitCode, Files.readString(logFile));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected String mavenVersion(ExecutorRequest request) throws Exception {
|
||||
try (Executor invoker = createExecutor()) {
|
||||
return invoker.mavenVersion(request);
|
||||
}
|
||||
}
|
||||
|
||||
protected ExecutorRequest.Builder mvn3ExecutorRequestBuilder() {
|
||||
return ExecutorRequest.mavenBuilder(Paths.get(System.getProperty("maven3home")));
|
||||
}
|
||||
|
||||
protected ExecutorRequest.Builder mvn4ExecutorRequestBuilder() {
|
||||
return ExecutorRequest.mavenBuilder(Paths.get(System.getProperty("maven4home")));
|
||||
}
|
||||
|
||||
protected void layDownFiles(Path cwd) throws IOException {
|
||||
Files.createDirectory(cwd.resolve(".mvn"));
|
||||
Path pom = cwd.resolve("pom.xml").toAbsolutePath();
|
||||
Files.writeString(pom, POM_STRING);
|
||||
Path appJava = cwd.resolve("src/main/java/org/apache/maven/samples/sample/App.java");
|
||||
Files.createDirectories(appJava.getParent());
|
||||
Files.writeString(appJava, APP_JAVA_STRING);
|
||||
}
|
||||
|
||||
protected static class FailedExecution extends Exception {
|
||||
private final ExecutorRequest request;
|
||||
private final int exitCode;
|
||||
private final String log;
|
||||
|
||||
public FailedExecution(ExecutorRequest request, int exitCode, String log) {
|
||||
super(request.toString() + " => " + exitCode + "\n" + log);
|
||||
this.request = request;
|
||||
this.exitCode = exitCode;
|
||||
this.log = log;
|
||||
}
|
||||
|
||||
public ExecutorRequest getRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
public int getExitCode() {
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
public String getLog() {
|
||||
return log;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract Executor createExecutor();
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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.cling.executor.embedded;
|
||||
|
||||
import org.apache.maven.api.cli.Executor;
|
||||
import org.apache.maven.cling.executor.MavenExecutorTestSupport;
|
||||
|
||||
/**
|
||||
* Embedded executor UT
|
||||
*/
|
||||
public class EmbeddedMavenExecutorTest extends MavenExecutorTestSupport {
|
||||
|
||||
@Override
|
||||
protected Executor createExecutor() {
|
||||
return new EmbeddedMavenExecutor();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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.cling.executor.forked;
|
||||
|
||||
import org.apache.maven.api.cli.Executor;
|
||||
import org.apache.maven.cling.executor.MavenExecutorTestSupport;
|
||||
|
||||
/**
|
||||
* Forked executor UT
|
||||
*/
|
||||
public class ForkedMavenExecutorTest extends MavenExecutorTestSupport {
|
||||
|
||||
@Override
|
||||
protected Executor createExecutor() {
|
||||
return new ForkedMavenExecutor();
|
||||
}
|
||||
}
|
|
@ -38,5 +38,6 @@ under the License.
|
|||
<module>maven-logging</module>
|
||||
<module>maven-core</module>
|
||||
<module>maven-cli</module>
|
||||
<module>maven-executor</module>
|
||||
</modules>
|
||||
</project>
|
||||
|
|
1
pom.xml
1
pom.xml
|
@ -675,6 +675,7 @@ under the License.
|
|||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.5.2</version>
|
||||
<configuration>
|
||||
<argLine>-Xmx256m</argLine>
|
||||
</configuration>
|
||||
|
|
|
@ -42,7 +42,7 @@ public class ReactorGraph {
|
|||
CLUSTER_PATTERNS.put("JLine", Pattern.compile("^org\\.jline:.*"));
|
||||
CLUSTER_PATTERNS.put("Maven API", Pattern.compile("^org\\.apache\\.maven:maven-api-(?!impl).*"));
|
||||
CLUSTER_PATTERNS.put("Maven Resolver", Pattern.compile("^org\\.apache\\.maven\\.resolver:.*"));
|
||||
CLUSTER_PATTERNS.put("Maven Implementation", Pattern.compile("^org\\.apache\\.maven:maven-(impl|di|core|cli|xml|jline|logging):.*"));
|
||||
CLUSTER_PATTERNS.put("Maven Implementation", Pattern.compile("^org\\.apache\\.maven:maven-(impl|di|core|cli|xml|jline|logging|executor):.*"));
|
||||
CLUSTER_PATTERNS.put("Maven Compatibility", Pattern.compile("^org\\.apache\\.maven:maven-(artifact|builder-support|compat|embedder|model|model-builder|plugin-api|repository-metadata|resolver-provider|settings|settings-builder|toolchain-builder|toolchain-model):.*"));
|
||||
CLUSTER_PATTERNS.put("Sisu", Pattern.compile("(^org\\.eclipse\\.sisu:.*)|(.*:guice:.*)|(.*:javax.inject:.*)|(.*:javax.annotation-api:.*)"));
|
||||
CLUSTER_PATTERNS.put("Plexus", Pattern.compile("^org\\.codehaus\\.plexus:.*"));
|
||||
|
|
Loading…
Reference in New Issue