[MNG-8283] Maven CLIng smaller bugfixes and improvements (#1772)

This PR adopts CLIng for use in mvnd, and adds several improvements to CLIng overall.

Major topics:
* ability to pass in per-request Lookup for customization
* makes parser request creation a bit friendlier
* removes a log of redundancy (same stuff copied over)
* ability to alter rootDirectory detection in parsers
* resident invoker bugfix
* adds UTs for 3 invoker implementations

---

https://issues.apache.org/jira/browse/MNG-8283
This commit is contained in:
Tamas Cservenak 2024-10-04 14:23:10 +02:00 committed by GitHub
parent 1f1a0f9a72
commit 533790bb4a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 863 additions and 500 deletions

View File

@ -26,8 +26,10 @@ import java.util.Map;
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.cli.extensions.CoreExtension;
import org.apache.maven.api.services.Lookup;
import org.apache.maven.api.services.MessageBuilderFactory;
/**
@ -38,15 +40,35 @@ import org.apache.maven.api.services.MessageBuilderFactory;
*
* @since 4.0.0
*/
@Immutable
@Experimental
public interface InvokerRequest<O extends Options> {
/**
* Returns the command to be executed.
*
* @return the command string
* The parser request this instance was created from.
*/
@Nonnull
String command();
ParserRequest parserRequest();
/**
* Shorthand for {@link Logger} to use.
*/
default Logger logger() {
return parserRequest().logger();
}
/**
* Shorthand for {@link MessageBuilderFactory}.
*/
default MessageBuilderFactory messageBuilderFactory() {
return parserRequest().messageBuilderFactory();
}
/**
* Shorthand for {@link Lookup}.
*/
default Lookup lookup() {
return parserRequest().lookup();
}
/**
* Returns the current working directory for the Maven execution.
@ -93,24 +115,6 @@ public interface InvokerRequest<O extends Options> {
@Nonnull
Map<String, String> systemProperties();
/**
* Returns the logger to be used during the early phases of Maven execution,
* before the main Maven logger is initialized.
*
* @return the early-phase logger
*/
@Nonnull
Logger logger();
/**
* Returns the factory for creating message builders.
* Message builders are used for constructing formatted log messages.
*
* @return the message builder factory
*/
@Nonnull
MessageBuilderFactory messageBuilderFactory();
/**
* Returns the top-level directory of the Maven invocation.
* This is typically the directory containing the POM file being executed.

View File

@ -196,12 +196,12 @@ public interface Options {
*
* @param printWriter the PrintWriter to use for output
*/
default void warnAboutDeprecatedOptions(@Nonnull PrintWriter printWriter) {}
default void warnAboutDeprecatedOptions(@Nonnull ParserRequest request, @Nonnull PrintWriter printWriter) {}
/**
* Displays help information for these options.
*
* @param printWriter the PrintWriter to use for output
*/
void displayHelp(@Nonnull String command, @Nonnull PrintWriter printWriter);
void displayHelp(@Nonnull ParserRequest request, @Nonnull PrintWriter printWriter);
}

View File

@ -36,10 +36,10 @@ import org.apache.maven.api.services.MessageBuilderFactory;
@Experimental
public interface Parser<R extends InvokerRequest<? extends Options>> {
/**
* Parses the given command and arguments to create an InvokerRequest.
* This is a convenience method that internally creates a ParserRequest.
* Parses the given Maven arguments to create an InvokerRequest.
* This is a convenience method that internally creates a ParserRequest using
* {@link ParserRequest#mvn(String[], Logger, MessageBuilderFactory)}.
*
* @param command the Maven command to execute
* @param args the command-line arguments
* @param logger the logger to use during parsing
* @param messageBuilderFactory the factory for creating message builders
@ -48,14 +48,9 @@ public interface Parser<R extends InvokerRequest<? extends Options>> {
* @throws IOException if there's an I/O error during the parsing process
*/
@Nonnull
default R parse(
@Nonnull String command,
@Nonnull String[] args,
@Nonnull Logger logger,
@Nonnull MessageBuilderFactory messageBuilderFactory)
default R mvn(@Nonnull String[] args, @Nonnull Logger logger, @Nonnull MessageBuilderFactory messageBuilderFactory)
throws ParserException, IOException {
return parse(ParserRequest.builder(command, args, logger, messageBuilderFactory)
.build());
return parse(ParserRequest.mvn(args, logger, messageBuilderFactory).build());
}
/**

View File

@ -21,10 +21,17 @@ package org.apache.maven.api.cli;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
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 org.apache.maven.api.services.Lookup;
import org.apache.maven.api.services.LookupException;
import org.apache.maven.api.services.MessageBuilderFactory;
import static java.util.Objects.requireNonNull;
@ -36,16 +43,31 @@ import static java.util.Objects.requireNonNull;
*
* @since 4.0.0
*/
@Immutable
@Experimental
public interface ParserRequest {
String MVN_CMD = "mvn";
String MVN_NAME = "Maven";
String MVNENC_CMD = "mvnenc";
String MVNENC_NAME = "Maven Password Encrypting Tool";
/**
* Returns the Maven command to be executed.
* Returns the Maven command to be executed. This command is used in some invokers (ie forked) but also to
* present help to user.
*
* @return the command string
*/
@Nonnull
String command();
/**
* Returns the Maven command name (ie "Maven"). This string is used in some invokers to complete error messages.
*
* @return the command (human) name
*/
String commandName();
/**
* Returns the logger to be used during the parsing process.
*
@ -65,10 +87,18 @@ public interface ParserRequest {
/**
* Returns the command-line arguments to be parsed.
*
* @return an array of argument strings
* @return a list of argument strings
*/
@Nonnull
String[] args();
List<String> args();
/**
* Per-request {@link Lookup} for customization.
*
* @return a lookup possibly with custom components
*/
@Nonnull
Lookup lookup();
/**
* Returns the current working directory for the Maven execution.
@ -124,10 +154,67 @@ public interface ParserRequest {
@Nullable
OutputStream err();
/**
* Creates a new Builder instance for constructing a Maven ParserRequest.
*
* @param args the command-line arguments
* @param logger the logger to be used during parsing
* @param messageBuilderFactory the factory for creating message builders
* @return a new Builder instance
*/
@Nonnull
static Builder mvn(
@Nonnull String[] args, @Nonnull Logger logger, @Nonnull MessageBuilderFactory messageBuilderFactory) {
return mvn(Arrays.asList(args), logger, messageBuilderFactory);
}
/**
* Creates a new Builder instance for constructing a Maven ParserRequest.
*
* @param args the command-line arguments
* @param logger the logger to be used during parsing
* @param messageBuilderFactory the factory for creating message builders
* @return a new Builder instance
*/
@Nonnull
static Builder mvn(
@Nonnull List<String> args, @Nonnull Logger logger, @Nonnull MessageBuilderFactory messageBuilderFactory) {
return builder(MVN_CMD, MVN_NAME, args, logger, messageBuilderFactory);
}
/**
* Creates a new Builder instance for constructing a Maven Encrypting Tool ParserRequest.
*
* @param args the command-line arguments
* @param logger the logger to be used during parsing
* @param messageBuilderFactory the factory for creating message builders
* @return a new Builder instance
*/
@Nonnull
static Builder mvnenc(
@Nonnull String[] args, @Nonnull Logger logger, @Nonnull MessageBuilderFactory messageBuilderFactory) {
return mvnenc(Arrays.asList(args), logger, messageBuilderFactory);
}
/**
* Creates a new Builder instance for constructing a Maven Encrypting Tool ParserRequest.
*
* @param args the command-line arguments
* @param logger the logger to be used during parsing
* @param messageBuilderFactory the factory for creating message builders
* @return a new Builder instance
*/
@Nonnull
static Builder mvnenc(
@Nonnull List<String> args, @Nonnull Logger logger, @Nonnull MessageBuilderFactory messageBuilderFactory) {
return builder(MVNENC_CMD, MVNENC_NAME, args, logger, messageBuilderFactory);
}
/**
* Creates a new Builder instance for constructing a ParserRequest.
*
* @param command the Maven command to be executed
* @param commandName the Maven command Name to be executed
* @param args the command-line arguments
* @param logger the logger to be used during parsing
* @param messageBuilderFactory the factory for creating message builders
@ -136,17 +223,20 @@ public interface ParserRequest {
@Nonnull
static Builder builder(
@Nonnull String command,
@Nonnull String[] args,
@Nonnull String commandName,
@Nonnull List<String> args,
@Nonnull Logger logger,
@Nonnull MessageBuilderFactory messageBuilderFactory) {
return new Builder(command, args, logger, messageBuilderFactory);
return new Builder(command, commandName, args, logger, messageBuilderFactory);
}
class Builder {
private final String command;
private final String[] args;
private final String commandName;
private final List<String> args;
private final Logger logger;
private final MessageBuilderFactory messageBuilderFactory;
private Lookup lookup = EMPTY_LOOKUP;
private Path cwd;
private Path mavenHome;
private Path userHome;
@ -154,13 +244,24 @@ public interface ParserRequest {
private OutputStream out;
private OutputStream err;
private Builder(String command, String[] args, Logger logger, MessageBuilderFactory messageBuilderFactory) {
this.command = requireNonNull(command, "appName");
private Builder(
String command,
String commandName,
List<String> args,
Logger logger,
MessageBuilderFactory messageBuilderFactory) {
this.command = requireNonNull(command, "command");
this.commandName = requireNonNull(commandName, "commandName");
this.args = requireNonNull(args, "args");
this.logger = requireNonNull(logger, "logger");
this.messageBuilderFactory = requireNonNull(messageBuilderFactory, "messageBuilderFactory");
}
public Builder lookup(@Nonnull Lookup lookup) {
this.lookup = requireNonNull(lookup);
return this;
}
public Builder cwd(Path cwd) {
this.cwd = cwd;
return this;
@ -193,15 +294,28 @@ public interface ParserRequest {
public ParserRequest build() {
return new ParserRequestImpl(
command, args, logger, messageBuilderFactory, cwd, mavenHome, userHome, in, out, err);
command,
commandName,
args,
logger,
messageBuilderFactory,
lookup,
cwd,
mavenHome,
userHome,
in,
out,
err);
}
@SuppressWarnings("ParameterNumber")
private static class ParserRequestImpl implements ParserRequest {
private final String command;
private final String commandName;
private final Logger logger;
private final MessageBuilderFactory messageBuilderFactory;
private final String[] args;
private final List<String> args;
private final Lookup lookup;
private final Path cwd;
private final Path mavenHome;
private final Path userHome;
@ -211,19 +325,23 @@ public interface ParserRequest {
private ParserRequestImpl(
String command,
String[] args,
String commandName,
List<String> args,
Logger logger,
MessageBuilderFactory messageBuilderFactory,
Lookup lookup,
Path cwd,
Path mavenHome,
Path userHome,
InputStream in,
OutputStream out,
OutputStream err) {
this.command = requireNonNull(command, "appName");
this.args = requireNonNull(args, "args");
this.command = requireNonNull(command, "command");
this.commandName = requireNonNull(commandName, "commandName");
this.args = List.copyOf(requireNonNull(args, "args"));
this.logger = requireNonNull(logger, "logger");
this.messageBuilderFactory = requireNonNull(messageBuilderFactory, "messageBuilderFactory");
this.lookup = requireNonNull(lookup, "lookup");
this.cwd = cwd;
this.mavenHome = mavenHome;
this.userHome = userHome;
@ -237,6 +355,11 @@ public interface ParserRequest {
return command;
}
@Override
public String commandName() {
return commandName;
}
@Override
public Logger logger() {
return logger;
@ -248,10 +371,15 @@ public interface ParserRequest {
}
@Override
public String[] args() {
public List<String> args() {
return args;
}
@Override
public Lookup lookup() {
return lookup;
}
@Override
public Path cwd() {
return cwd;
@ -282,5 +410,37 @@ public interface ParserRequest {
return err;
}
}
private static final Lookup EMPTY_LOOKUP = new Lookup() {
@Override
public <T> T lookup(Class<T> type) {
throw new LookupException("empty lookup");
}
@Override
public <T> T lookup(Class<T> type, String name) {
throw new LookupException("empty lookup");
}
@Override
public <T> Optional<T> lookupOptional(Class<T> type) {
return Optional.empty();
}
@Override
public <T> Optional<T> lookupOptional(Class<T> type, String name) {
return Optional.empty();
}
@Override
public <T> List<T> lookupList(Class<T> type) {
return List.of();
}
@Override
public <T> Map<String, T> lookupMap(Class<T> type) {
return Map.of();
}
};
}
}

View File

@ -23,13 +23,8 @@ import java.io.IOException;
import org.apache.maven.api.cli.Invoker;
import org.apache.maven.api.cli.InvokerException;
import org.apache.maven.api.cli.InvokerRequest;
import org.apache.maven.api.cli.Logger;
import org.apache.maven.api.cli.Options;
import org.apache.maven.api.cli.Parser;
import org.apache.maven.api.cli.ParserException;
import org.apache.maven.api.services.MessageBuilderFactory;
import org.apache.maven.cling.invoker.ProtoLogger;
import org.apache.maven.jline.JLineMessageBuilderFactory;
import org.apache.maven.jline.MessageUtils;
import org.codehaus.plexus.classworlds.ClassWorld;
@ -44,26 +39,24 @@ import static java.util.Objects.requireNonNull;
public abstract class ClingSupport<O extends Options, R extends InvokerRequest<O>> {
static final String CORE_CLASS_REALM_ID = "plexus.core";
protected final String command;
protected final ClassWorld classWorld;
protected final boolean classWorldManaged;
/**
* Ctor that creates "managed" ClassWorld. This constructor is not used in "normal" circumstances.
*/
public ClingSupport(String command) {
this(command, new ClassWorld(CORE_CLASS_REALM_ID, Thread.currentThread().getContextClassLoader()), true);
public ClingSupport() {
this(new ClassWorld(CORE_CLASS_REALM_ID, Thread.currentThread().getContextClassLoader()), true);
}
/**
* Ctor to be used when running in ClassWorlds Launcher.
*/
public ClingSupport(String command, ClassWorld classWorld) {
this(command, classWorld, false);
public ClingSupport(ClassWorld classWorld) {
this(classWorld, false);
}
protected ClingSupport(String command, ClassWorld classWorld, boolean classWorldManaged) {
this.command = requireNonNull(command, "command");
private ClingSupport(ClassWorld classWorld, boolean classWorldManaged) {
this.classWorld = requireNonNull(classWorld);
this.classWorldManaged = classWorldManaged;
}
@ -75,7 +68,7 @@ public abstract class ClingSupport<O extends Options, R extends InvokerRequest<O
MessageUtils.systemInstall();
MessageUtils.registerShutdownHook();
try (Invoker<R> invoker = createInvoker()) {
return invoker.invoke(createParser().parse(command, args, createLogger(), createMessageBuilderFactory()));
return invoker.invoke(parseArguments(args));
} catch (ParserException e) {
System.err.println(e.getMessage());
return 1;
@ -94,13 +87,5 @@ public abstract class ClingSupport<O extends Options, R extends InvokerRequest<O
protected abstract Invoker<R> createInvoker();
protected abstract Parser<R> createParser();
protected Logger createLogger() {
return new ProtoLogger();
}
protected MessageBuilderFactory createMessageBuilderFactory() {
return new JLineMessageBuilderFactory();
}
protected abstract R parseArguments(String[] args) throws ParserException, IOException;
}

View File

@ -21,26 +21,26 @@ package org.apache.maven.cling;
import java.io.IOException;
import org.apache.maven.api.cli.Invoker;
import org.apache.maven.api.cli.Parser;
import org.apache.maven.api.cli.ParserException;
import org.apache.maven.api.cli.mvn.MavenInvokerRequest;
import org.apache.maven.api.cli.mvn.MavenOptions;
import org.apache.maven.cling.invoker.ProtoLogger;
import org.apache.maven.cling.invoker.ProtoLookup;
import org.apache.maven.cling.invoker.mvn.local.DefaultLocalMavenInvoker;
import org.apache.maven.cling.invoker.mvn.local.DefaultLocalMavenParser;
import org.apache.maven.jline.JLineMessageBuilderFactory;
import org.codehaus.plexus.classworlds.ClassWorld;
/**
* Maven CLI "new-gen".
*/
public class MavenCling extends ClingSupport<MavenOptions, MavenInvokerRequest<MavenOptions>> {
public static final String NAME = "mvn";
/**
* "Normal" Java entry point. Note: Maven uses ClassWorld Launcher and this entry point is NOT used under normal
* circumstances.
*/
public static void main(String[] args) throws IOException {
int exitCode = new MavenCling(NAME).run(args);
int exitCode = new MavenCling().run(args);
System.exit(exitCode);
}
@ -48,15 +48,15 @@ public class MavenCling extends ClingSupport<MavenOptions, MavenInvokerRequest<M
* ClassWorld Launcher "enhanced" entry point: returning exitCode and accepts Class World.
*/
public static int main(String[] args, ClassWorld world) throws IOException {
return new MavenCling(NAME, world).run(args);
return new MavenCling(world).run(args);
}
public MavenCling(String command) {
super(command);
public MavenCling() {
super();
}
public MavenCling(String command, ClassWorld classWorld) {
super(command, classWorld);
public MavenCling(ClassWorld classWorld) {
super(classWorld);
}
@Override
@ -66,7 +66,7 @@ public class MavenCling extends ClingSupport<MavenOptions, MavenInvokerRequest<M
}
@Override
protected Parser<MavenInvokerRequest<MavenOptions>> createParser() {
return new DefaultLocalMavenParser();
protected MavenInvokerRequest<MavenOptions> parseArguments(String[] args) throws ParserException, IOException {
return new DefaultLocalMavenParser().mvn(args, new ProtoLogger(), new JLineMessageBuilderFactory());
}
}

View File

@ -21,26 +21,27 @@ package org.apache.maven.cling;
import java.io.IOException;
import org.apache.maven.api.cli.Invoker;
import org.apache.maven.api.cli.Parser;
import org.apache.maven.api.cli.ParserException;
import org.apache.maven.api.cli.ParserRequest;
import org.apache.maven.api.cli.mvnenc.EncryptInvokerRequest;
import org.apache.maven.api.cli.mvnenc.EncryptOptions;
import org.apache.maven.cling.invoker.ProtoLogger;
import org.apache.maven.cling.invoker.ProtoLookup;
import org.apache.maven.cling.invoker.mvnenc.DefaultEncryptInvoker;
import org.apache.maven.cling.invoker.mvnenc.DefaultEncryptParser;
import org.apache.maven.jline.JLineMessageBuilderFactory;
import org.codehaus.plexus.classworlds.ClassWorld;
/**
* Maven encrypt CLI "new-gen".
*/
public class MavenEncCling extends ClingSupport<EncryptOptions, EncryptInvokerRequest> {
public static final String NAME = "mvnenc";
/**
* "Normal" Java entry point. Note: Maven uses ClassWorld Launcher and this entry point is NOT used under normal
* circumstances.
*/
public static void main(String[] args) throws IOException {
int exitCode = new MavenEncCling(NAME).run(args);
int exitCode = new MavenEncCling().run(args);
System.exit(exitCode);
}
@ -48,15 +49,15 @@ public class MavenEncCling extends ClingSupport<EncryptOptions, EncryptInvokerRe
* ClassWorld Launcher "enhanced" entry point: returning exitCode and accepts Class World.
*/
public static int main(String[] args, ClassWorld world) throws IOException {
return new MavenEncCling(NAME, world).run(args);
return new MavenEncCling(world).run(args);
}
public MavenEncCling(String command) {
super(command);
public MavenEncCling() {
super();
}
public MavenEncCling(String command, ClassWorld classWorld) {
super(command, classWorld);
public MavenEncCling(ClassWorld classWorld) {
super(classWorld);
}
@Override
@ -66,7 +67,9 @@ public class MavenEncCling extends ClingSupport<EncryptOptions, EncryptInvokerRe
}
@Override
protected Parser<EncryptInvokerRequest> createParser() {
return new DefaultEncryptParser();
protected EncryptInvokerRequest parseArguments(String[] args) throws ParserException, IOException {
return new DefaultEncryptParser()
.parse(ParserRequest.mvnenc(args, new ProtoLogger(), new JLineMessageBuilderFactory())
.build());
}
}

View File

@ -28,22 +28,19 @@ import java.util.Optional;
import org.apache.maven.api.annotations.Nonnull;
import org.apache.maven.api.annotations.Nullable;
import org.apache.maven.api.cli.InvokerRequest;
import org.apache.maven.api.cli.Logger;
import org.apache.maven.api.cli.Options;
import org.apache.maven.api.cli.ParserRequest;
import org.apache.maven.api.cli.extensions.CoreExtension;
import org.apache.maven.api.services.MessageBuilderFactory;
import static java.util.Objects.requireNonNull;
public abstract class BaseInvokerRequest<T extends Options> implements InvokerRequest<T> {
private final String command;
private final ParserRequest parserRequest;
private final Path cwd;
private final Path installationDirectory;
private final Path userHomeDirectory;
private final Map<String, String> userProperties;
private final Map<String, String> systemProperties;
private final Logger logger;
private final MessageBuilderFactory messageBuilderFactory;
private final Path topDirectory;
private final Path rootDirectory;
@ -54,28 +51,24 @@ public abstract class BaseInvokerRequest<T extends Options> implements InvokerRe
@SuppressWarnings("ParameterNumber")
public BaseInvokerRequest(
@Nonnull String command,
@Nonnull ParserRequest parserRequest,
@Nonnull Path cwd,
@Nonnull Path installationDirectory,
@Nonnull Path userHomeDirectory,
@Nonnull Map<String, String> userProperties,
@Nonnull Map<String, String> systemProperties,
@Nonnull Logger logger,
@Nonnull MessageBuilderFactory messageBuilderFactory,
@Nonnull Path topDirectory,
@Nullable Path rootDirectory,
@Nullable InputStream in,
@Nullable OutputStream out,
@Nullable OutputStream err,
@Nullable List<CoreExtension> coreExtensions) {
this.command = requireNonNull(command);
this.parserRequest = requireNonNull(parserRequest);
this.cwd = requireNonNull(cwd);
this.installationDirectory = requireNonNull(installationDirectory);
this.userHomeDirectory = requireNonNull(userHomeDirectory);
this.userProperties = requireNonNull(userProperties);
this.systemProperties = requireNonNull(systemProperties);
this.logger = requireNonNull(logger);
this.messageBuilderFactory = requireNonNull(messageBuilderFactory);
this.topDirectory = requireNonNull(topDirectory);
this.rootDirectory = rootDirectory;
@ -86,8 +79,8 @@ public abstract class BaseInvokerRequest<T extends Options> implements InvokerRe
}
@Override
public String command() {
return command;
public ParserRequest parserRequest() {
return parserRequest;
}
@Override
@ -115,16 +108,6 @@ public abstract class BaseInvokerRequest<T extends Options> implements InvokerRe
return systemProperties;
}
@Override
public Logger logger() {
return logger;
}
@Override
public MessageBuilderFactory messageBuilderFactory() {
return messageBuilderFactory;
}
@Override
public Path topDirectory() {
return topDirectory;

View File

@ -23,10 +23,9 @@ import javax.xml.stream.XMLStreamException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@ -39,7 +38,6 @@ import java.util.Set;
import java.util.function.Function;
import org.apache.maven.api.Constants;
import org.apache.maven.api.annotations.Nullable;
import org.apache.maven.api.cli.InvokerRequest;
import org.apache.maven.api.cli.Options;
import org.apache.maven.api.cli.Parser;
@ -69,41 +67,20 @@ public abstract class BaseParser<O extends Options, R extends InvokerRequest<O>>
// the basics
HashMap<String, String> overrides = new HashMap<>();
FileSystem fileSystem = requireNonNull(getFileSystem(parserRequest));
Path cwd = requireNonNull(getCwd(parserRequest, fileSystem, overrides));
Path installationDirectory = requireNonNull(getInstallationDirectory(parserRequest, fileSystem, overrides));
Path userHomeDirectory = requireNonNull(getUserHomeDirectory(parserRequest, fileSystem, overrides));
Path cwd = requireNonNull(getCwd(parserRequest, overrides));
Path installationDirectory = requireNonNull(getInstallationDirectory(parserRequest, overrides));
Path userHomeDirectory = requireNonNull(getUserHomeDirectory(parserRequest, overrides));
// top/root
Path topDirectory = getCanonicalPath(requireNonNull(getTopDirectory(parserRequest, cwd)));
RootLocator rootLocator =
ServiceLoader.load(RootLocator.class).iterator().next();
@Nullable Path rootDirectory = rootLocator.findRoot(topDirectory);
// TODO: multiModuleProjectDirectory vs rootDirectory?
// fallback if no root? otherwise make sure they are same?
Path mmpd = System.getProperty("maven.multiModuleProjectDirectory") == null
? null
: getCanonicalPath(cwd.resolve(requireNonNull(
System.getProperty("maven.multiModuleProjectDirectory"),
"maven.multiModuleProjectDirectory is not set")));
if (rootDirectory == null) {
parserRequest.logger().warn(rootLocator.getNoRootMessage());
rootDirectory = requireNonNull(
mmpd, "maven.multiModuleProjectDirectory is not set and rootDirectory was not discovered");
} else {
rootDirectory = getCanonicalPath(rootDirectory);
if (mmpd != null && !Objects.equals(rootDirectory, mmpd)) {
parserRequest.logger().warn("Project root directory and multiModuleProjectDirectory are not aligned");
}
}
Path topDirectory = requireNonNull(getTopDirectory(parserRequest, cwd));
Path rootDirectory = requireNonNull(getRootDirectory(parserRequest, cwd, topDirectory));
// options
List<O> parsedOptions = parseCliOptions(rootDirectory, parserRequest.args());
// warn about deprecated options
parsedOptions.forEach(o -> o.warnAboutDeprecatedOptions(
new PrintWriter(parserRequest.out() != null ? parserRequest.out() : System.out, true)));
parserRequest, new PrintWriter(parserRequest.out() != null ? parserRequest.out() : System.out, true)));
// assemble options if needed
O options = assembleOptions(parsedOptions);
@ -113,7 +90,8 @@ public abstract class BaseParser<O extends Options, R extends InvokerRequest<O>>
Map<String, String> paths = new HashMap<>();
paths.put("session.topDirectory", topDirectory.toString());
paths.put("session.rootDirectory", rootDirectory.toString());
Map<String, String> userProperties = populateUserProperties(systemProperties, fileSystem, paths, options);
Map<String, String> userProperties =
populateUserProperties(systemProperties, installationDirectory, paths, options);
// options: interpolate
Options interpolatedOptions = options.interpolate(Arrays.asList(paths, systemProperties, userProperties));
@ -121,13 +99,13 @@ public abstract class BaseParser<O extends Options, R extends InvokerRequest<O>>
// core extensions
ArrayList<CoreExtension> extensions = new ArrayList<>();
String installationExtensionsFile = userProperties.get(Constants.MAVEN_INSTALLATION_EXTENSIONS);
extensions.addAll(readCoreExtensionsDescriptor(installationExtensionsFile, fileSystem));
extensions.addAll(readCoreExtensionsDescriptor(installationExtensionsFile, installationDirectory));
String projectExtensionsFile = userProperties.get(Constants.MAVEN_PROJECT_EXTENSIONS);
extensions.addAll(readCoreExtensionsDescriptor(projectExtensionsFile, fileSystem));
extensions.addAll(readCoreExtensionsDescriptor(projectExtensionsFile, cwd));
String userExtensionsFile = userProperties.get(Constants.MAVEN_USER_EXTENSIONS);
extensions.addAll(readCoreExtensionsDescriptor(userExtensionsFile, fileSystem));
extensions.addAll(readCoreExtensionsDescriptor(userExtensionsFile, userHomeDirectory));
return getInvokerRequest(
parserRequest,
@ -155,23 +133,18 @@ public abstract class BaseParser<O extends Options, R extends InvokerRequest<O>>
ArrayList<CoreExtension> extensions,
Options options);
protected FileSystem getFileSystem(ParserRequest parserRequest) throws ParserException, IOException {
return parserRequest.cwd() != null ? parserRequest.cwd().getFileSystem() : FileSystems.getDefault();
}
protected Path getCwd(ParserRequest parserRequest, FileSystem fileSystem, Map<String, String> overrides)
throws ParserException {
protected Path getCwd(ParserRequest parserRequest, Map<String, String> overrides) throws ParserException {
if (parserRequest.cwd() != null) {
Path result = getCanonicalPath(parserRequest.cwd());
overrides.put("user.dir", result.toString());
return result;
} else {
return getCanonicalPath(fileSystem.getPath(System.getProperty("user.dir")));
return getCanonicalPath(Paths.get(System.getProperty("user.dir")));
}
}
protected Path getInstallationDirectory(
ParserRequest parserRequest, FileSystem fileSystem, Map<String, String> overrides) throws ParserException {
protected Path getInstallationDirectory(ParserRequest parserRequest, Map<String, String> overrides)
throws ParserException {
Path result;
if (parserRequest.mavenHome() != null) {
result = getCanonicalPath(parserRequest.mavenHome());
@ -181,21 +154,21 @@ public abstract class BaseParser<O extends Options, R extends InvokerRequest<O>>
if (mavenHome == null) {
throw new ParserException("local mode requires " + Constants.MAVEN_HOME + " Java System Property set");
}
result = getCanonicalPath(fileSystem.getPath(mavenHome));
result = getCanonicalPath(Paths.get(mavenHome));
}
// TODO: we still do this but would be cool if this becomes unneeded
System.setProperty(Constants.MAVEN_HOME, result.toString());
return result;
}
protected Path getUserHomeDirectory(
ParserRequest parserRequest, FileSystem fileSystem, Map<String, String> overrides) throws ParserException {
protected Path getUserHomeDirectory(ParserRequest parserRequest, Map<String, String> overrides)
throws ParserException {
if (parserRequest.userHome() != null) {
Path result = getCanonicalPath(parserRequest.userHome());
overrides.put("user.home", result.toString());
return result;
} else {
return getCanonicalPath(fileSystem.getPath(System.getProperty("user.home")));
return getCanonicalPath(Paths.get(System.getProperty("user.home")));
}
}
@ -226,7 +199,32 @@ public abstract class BaseParser<O extends Options, R extends InvokerRequest<O>>
isAltFile = arg.equals("-f") || arg.equals("--file");
}
}
return topDirectory;
return getCanonicalPath(topDirectory);
}
protected Path getRootDirectory(ParserRequest parserRequest, Path cwd, Path topDirectory) throws ParserException {
RootLocator rootLocator =
ServiceLoader.load(RootLocator.class).iterator().next();
Path rootDirectory = rootLocator.findRoot(topDirectory);
// TODO: multiModuleProjectDirectory vs rootDirectory?
// fallback if no root? otherwise make sure they are same?
Path mmpd = System.getProperty("maven.multiModuleProjectDirectory") == null
? null
: getCanonicalPath(cwd.resolve(requireNonNull(
System.getProperty("maven.multiModuleProjectDirectory"),
"maven.multiModuleProjectDirectory is not set")));
if (rootDirectory == null) {
parserRequest.logger().warn(rootLocator.getNoRootMessage());
rootDirectory = requireNonNull(
mmpd, "maven.multiModuleProjectDirectory is not set and rootDirectory was not discovered");
} else {
rootDirectory = getCanonicalPath(rootDirectory);
if (mmpd != null && !Objects.equals(rootDirectory, mmpd)) {
parserRequest.logger().warn("Project root directory and multiModuleProjectDirectory are not aligned");
}
}
return getCanonicalPath(rootDirectory);
}
protected Map<String, String> populateSystemProperties(Map<String, String> overrides) throws ParserException {
@ -260,7 +258,10 @@ public abstract class BaseParser<O extends Options, R extends InvokerRequest<O>>
}
protected Map<String, String> populateUserProperties(
Map<String, String> systemProperties, FileSystem fileSystem, Map<String, String> paths, Options options)
Map<String, String> systemProperties,
Path installationDirectory,
Map<String, String> paths,
Options options)
throws ParserException, IOException {
Properties userProperties = new Properties();
@ -281,13 +282,15 @@ public abstract class BaseParser<O extends Options, R extends InvokerRequest<O>>
Path mavenConf;
if (systemProperties.get(MAVEN_INSTALLATION_CONF) != null) {
mavenConf = fileSystem.getPath(systemProperties.get(MAVEN_INSTALLATION_CONF));
mavenConf = installationDirectory.resolve(systemProperties.get(MAVEN_INSTALLATION_CONF));
} else if (systemProperties.get("maven.conf") != null) {
mavenConf = fileSystem.getPath(systemProperties.get("maven.conf"));
mavenConf = installationDirectory.resolve(systemProperties.get("maven.conf"));
} else if (systemProperties.get(MAVEN_HOME) != null) {
mavenConf = fileSystem.getPath(systemProperties.get(MAVEN_HOME), "conf");
mavenConf = installationDirectory
.resolve(systemProperties.get(MAVEN_HOME))
.resolve("conf");
} else {
mavenConf = fileSystem.getPath("");
mavenConf = installationDirectory.resolve("");
}
Path propertiesFile = mavenConf.resolve("maven.properties");
MavenPropertiesLoader.loadProperties(userProperties, propertiesFile, callback, false);
@ -304,15 +307,16 @@ public abstract class BaseParser<O extends Options, R extends InvokerRequest<O>>
return toMap(userProperties);
}
protected abstract List<O> parseCliOptions(Path rootDirectory, String[] args) throws ParserException, IOException;
protected abstract List<O> parseCliOptions(Path rootDirectory, List<String> args)
throws ParserException, IOException;
protected abstract O assembleOptions(List<O> parsedOptions);
protected List<CoreExtension> readCoreExtensionsDescriptor(String extensionsFile, FileSystem fileSystem)
protected List<CoreExtension> readCoreExtensionsDescriptor(String extensionsFile, Path cwd)
throws ParserException, IOException {
try {
if (extensionsFile != null) {
Path extensionsPath = fileSystem.getPath(extensionsFile);
Path extensionsPath = cwd.resolve(extensionsFile);
if (Files.exists(extensionsPath)) {
try (InputStream is = Files.newInputStream(extensionsPath)) {
return new CoreExtensionsStaxReader().read(is, true).getExtensions();

View File

@ -31,6 +31,7 @@ import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.ParseException;
import org.apache.maven.api.cli.Options;
import org.apache.maven.api.cli.ParserRequest;
import org.apache.maven.cli.CleanArgument;
import org.apache.maven.jline.MessageUtils;
@ -201,7 +202,7 @@ public abstract class CommonsCliOptions implements Options {
}
@Override
public void warnAboutDeprecatedOptions(PrintWriter printWriter) {
public void warnAboutDeprecatedOptions(ParserRequest request, PrintWriter printWriter) {
if (cliManager.getUsedDeprecatedOptions().isEmpty()) {
return;
}
@ -217,15 +218,18 @@ public abstract class CommonsCliOptions implements Options {
sb.append("and will be removed in a future version");
}
if (option.getDeprecated().getSince() != null) {
sb.append("since Maven ").append(option.getDeprecated().getSince());
sb.append("since ")
.append(request.commandName())
.append(" ")
.append(option.getDeprecated().getSince());
}
printWriter.println(sb);
}
}
@Override
public void displayHelp(String command, PrintWriter printStream) {
cliManager.displayHelp(command, printStream);
public void displayHelp(ParserRequest request, PrintWriter printStream) {
cliManager.displayHelp(request.command(), printStream);
}
protected static class CLIManager {

View File

@ -27,6 +27,7 @@ import java.util.Optional;
import java.util.function.Function;
import org.apache.maven.api.cli.Options;
import org.apache.maven.api.cli.ParserRequest;
/**
* Options that are "layered" by precedence order.
@ -132,11 +133,11 @@ public abstract class LayeredOptions<O extends Options> implements Options {
}
@Override
public void warnAboutDeprecatedOptions(PrintWriter printWriter) {}
public void warnAboutDeprecatedOptions(ParserRequest request, PrintWriter printWriter) {}
@Override
public void displayHelp(String command, PrintWriter printWriter) {
options.get(0).displayHelp(command, printWriter);
public void displayHelp(ParserRequest request, PrintWriter printWriter) {
options.get(0).displayHelp(request, printWriter);
}
protected <T> Optional<T> returnFirstPresentOrEmpty(Function<O, Optional<T>> getter) {

View File

@ -95,6 +95,8 @@ public abstract class LookupInvoker<
public final ProtoLookup protoLookup;
public final R invokerRequest;
public final Function<String, Path> cwdResolver;
public final Function<String, Path> installationResolver;
public final Function<String, Path> userResolver;
public final InputStream stdIn;
public final PrintWriter stdOut;
public final PrintWriter stdErr;
@ -104,10 +106,17 @@ public abstract class LookupInvoker<
this.protoLookup = invoker.protoLookup;
this.invokerRequest = requireNonNull(invokerRequest);
this.cwdResolver = s -> invokerRequest.cwd().resolve(s).normalize().toAbsolutePath();
this.installationResolver = s -> invokerRequest
.installationDirectory()
.resolve(s)
.normalize()
.toAbsolutePath();
this.userResolver = s ->
invokerRequest.userHomeDirectory().resolve(s).normalize().toAbsolutePath();
this.stdIn = invokerRequest.in().orElse(System.in);
this.stdOut = new PrintWriter(invokerRequest.out().orElse(System.out), true);
this.stdErr = new PrintWriter(invokerRequest.err().orElse(System.err), true);
this.logger = invokerRequest.logger();
this.logger = invokerRequest.parserRequest().logger();
}
public Logger logger;
@ -149,7 +158,7 @@ public abstract class LookupInvoker<
logging(context);
if (invokerRequest.options().help().isPresent()) {
invokerRequest.options().displayHelp(context.invokerRequest.command(), context.stdOut);
invokerRequest.options().displayHelp(context.invokerRequest.parserRequest(), context.stdOut);
return 0;
}
if (invokerRequest.options().showVersionAndExit().isPresent()) {
@ -178,9 +187,11 @@ public abstract class LookupInvoker<
throws InvokerException {
boolean showStackTrace = context.invokerRequest.options().showErrors().orElse(false);
if (showStackTrace) {
context.logger.error("Error executing Maven.", e);
context.logger.error(
"Error executing " + context.invokerRequest.parserRequest().commandName() + ".", e);
} else {
context.logger.error("Error executing Maven.");
context.logger.error(
"Error executing " + context.invokerRequest.parserRequest().commandName() + ".");
context.logger.error(e.getMessage());
for (Throwable cause = e.getCause(); cause != null && cause != cause.getCause(); cause = cause.getCause()) {
context.logger.error("Caused by: " + cause.getMessage());
@ -338,7 +349,7 @@ public abstract class LookupInvoker<
} else {
String userSettingsFileStr = context.invokerRequest.userProperties().get(Constants.MAVEN_USER_SETTINGS);
if (userSettingsFileStr != null) {
userSettingsFile = context.cwdResolver.apply(userSettingsFileStr);
userSettingsFile = context.userResolver.apply(userSettingsFileStr);
}
}
@ -374,7 +385,7 @@ public abstract class LookupInvoker<
String installationSettingsFileStr =
context.invokerRequest.userProperties().get(Constants.MAVEN_INSTALLATION_SETTINGS);
if (installationSettingsFileStr != null) {
installationSettingsFile = context.cwdResolver.apply(installationSettingsFileStr);
installationSettingsFile = context.installationResolver.apply(installationSettingsFileStr);
}
}
@ -463,18 +474,18 @@ public abstract class LookupInvoker<
+ "usually located at ${session.rootDirectory}/.mvn/maven.properties.");
}
}
if (userDefinedLocalRepo != null) {
return context.cwdResolver.apply(userDefinedLocalRepo);
}
// settings
if (userDefinedLocalRepo == null) {
userDefinedLocalRepo = context.effectiveSettings.getLocalRepository();
if (userDefinedLocalRepo != null) {
return context.userResolver.apply(userDefinedLocalRepo);
}
// defaults
if (userDefinedLocalRepo == null) {
userDefinedLocalRepo = context.cwdResolver
return context.userResolver
.apply(context.invokerRequest.userProperties().get(Constants.MAVEN_USER_CONF))
.resolve("repository")
.toString();
}
return context.cwdResolver.apply(userDefinedLocalRepo);
.resolve("repository");
}
protected void populateRequest(C context, MavenExecutionRequest request) throws Exception {

View File

@ -105,7 +105,7 @@ public abstract class DefaultMavenInvoker<
}
@Override
protected void prepare(C localContext) throws Exception {
protected void prepare(C context) throws Exception {
// explicitly fill in "defaults"?
DefaultMavenExecutionRequest mavenExecutionRequest = new DefaultMavenExecutionRequest();
mavenExecutionRequest.setRepositoryCache(new DefaultRepositoryCache());
@ -120,7 +120,7 @@ public abstract class DefaultMavenInvoker<
mavenExecutionRequest.setDegreeOfConcurrency(1);
mavenExecutionRequest.setBuilderId("singlethreaded");
localContext.mavenExecutionRequest = mavenExecutionRequest;
context.mavenExecutionRequest = mavenExecutionRequest;
}
@Override
@ -146,11 +146,11 @@ public abstract class DefaultMavenInvoker<
}
@Override
protected void postCommands(C localContext) throws Exception {
super.postCommands(localContext);
protected void postCommands(C context) throws Exception {
super.postCommands(context);
R invokerRequest = localContext.invokerRequest;
Logger logger = localContext.logger;
R invokerRequest = context.invokerRequest;
Logger logger = context.logger;
if (invokerRequest.options().relaxedChecksums().orElse(false)) {
logger.info("Disabling strict checksum verification on all artifact downloads.");
} else if (invokerRequest.options().strictChecksums().orElse(false)) {
@ -172,12 +172,12 @@ public abstract class DefaultMavenInvoker<
}
}
protected void toolchains(C localContext) throws Exception {
protected void toolchains(C context) throws Exception {
Path userToolchainsFile = null;
if (localContext.invokerRequest.options().altUserToolchains().isPresent()) {
userToolchainsFile = localContext.cwdResolver.apply(
localContext.invokerRequest.options().altUserToolchains().get());
if (context.invokerRequest.options().altUserToolchains().isPresent()) {
userToolchainsFile = context.cwdResolver.apply(
context.invokerRequest.options().altUserToolchains().get());
if (!Files.isRegularFile(userToolchainsFile)) {
throw new FileNotFoundException(
@ -185,20 +185,17 @@ public abstract class DefaultMavenInvoker<
}
} else {
String userToolchainsFileStr =
localContext.invokerRequest.userProperties().get(Constants.MAVEN_USER_TOOLCHAINS);
context.invokerRequest.userProperties().get(Constants.MAVEN_USER_TOOLCHAINS);
if (userToolchainsFileStr != null) {
userToolchainsFile = localContext.cwdResolver.apply(userToolchainsFileStr);
userToolchainsFile = context.cwdResolver.apply(userToolchainsFileStr);
}
}
Path installationToolchainsFile = null;
if (localContext.invokerRequest.options().altInstallationToolchains().isPresent()) {
installationToolchainsFile = localContext.cwdResolver.apply(localContext
.invokerRequest
.options()
.altInstallationToolchains()
.get());
if (context.invokerRequest.options().altInstallationToolchains().isPresent()) {
installationToolchainsFile = context.cwdResolver.apply(
context.invokerRequest.options().altInstallationToolchains().get());
if (!Files.isRegularFile(installationToolchainsFile)) {
throw new FileNotFoundException(
@ -206,15 +203,15 @@ public abstract class DefaultMavenInvoker<
}
} else {
String installationToolchainsFileStr =
localContext.invokerRequest.userProperties().get(Constants.MAVEN_INSTALLATION_TOOLCHAINS);
context.invokerRequest.userProperties().get(Constants.MAVEN_INSTALLATION_TOOLCHAINS);
if (installationToolchainsFileStr != null) {
installationToolchainsFile = localContext.cwdResolver.apply(installationToolchainsFileStr);
installationToolchainsFile = context.cwdResolver.apply(installationToolchainsFileStr);
}
}
localContext.mavenExecutionRequest.setInstallationToolchainsFile(
context.mavenExecutionRequest.setInstallationToolchainsFile(
installationToolchainsFile != null ? installationToolchainsFile.toFile() : null);
localContext.mavenExecutionRequest.setUserToolchainsFile(
context.mavenExecutionRequest.setUserToolchainsFile(
userToolchainsFile != null ? userToolchainsFile.toFile() : null);
DefaultToolchainsBuildingRequest toolchainsRequest = new DefaultToolchainsBuildingRequest();
@ -225,35 +222,35 @@ public abstract class DefaultMavenInvoker<
toolchainsRequest.setUserToolchainsSource(new FileSource(userToolchainsFile));
}
localContext.eventSpyDispatcher.onEvent(toolchainsRequest);
context.eventSpyDispatcher.onEvent(toolchainsRequest);
localContext.logger.debug("Reading installation toolchains from '"
context.logger.debug("Reading installation toolchains from '"
+ (toolchainsRequest.getGlobalToolchainsSource() != null
? toolchainsRequest.getGlobalToolchainsSource().getLocation()
: installationToolchainsFile)
+ "'");
localContext.logger.debug("Reading user toolchains from '"
context.logger.debug("Reading user toolchains from '"
+ (toolchainsRequest.getUserToolchainsSource() != null
? toolchainsRequest.getUserToolchainsSource().getLocation()
: userToolchainsFile)
+ "'");
ToolchainsBuildingResult toolchainsResult = localContext.toolchainsBuilder.build(toolchainsRequest);
ToolchainsBuildingResult toolchainsResult = context.toolchainsBuilder.build(toolchainsRequest);
localContext.eventSpyDispatcher.onEvent(toolchainsResult);
context.eventSpyDispatcher.onEvent(toolchainsResult);
localContext.mavenExecutionRequestPopulator.populateFromToolchains(
localContext.mavenExecutionRequest, toolchainsResult.getEffectiveToolchains());
context.mavenExecutionRequestPopulator.populateFromToolchains(
context.mavenExecutionRequest, toolchainsResult.getEffectiveToolchains());
if (!toolchainsResult.getProblems().isEmpty()) {
localContext.logger.warn("");
localContext.logger.warn("Some problems were encountered while building the effective toolchains");
context.logger.warn("");
context.logger.warn("Some problems were encountered while building the effective toolchains");
for (Problem problem : toolchainsResult.getProblems()) {
localContext.logger.warn(problem.getMessage() + " @ " + problem.getLocation());
context.logger.warn(problem.getMessage() + " @ " + problem.getLocation());
}
localContext.logger.warn("");
context.logger.warn("");
}
}
@ -325,21 +322,21 @@ public abstract class DefaultMavenInvoker<
}
}
protected Path determinePom(C localContext) {
Path current = localContext.invokerRequest.cwd();
if (localContext.invokerRequest.options().alternatePomFile().isPresent()) {
current = localContext.cwdResolver.apply(
localContext.invokerRequest.options().alternatePomFile().get());
protected Path determinePom(C context) {
Path current = context.invokerRequest.cwd();
if (context.invokerRequest.options().alternatePomFile().isPresent()) {
current = context.cwdResolver.apply(
context.invokerRequest.options().alternatePomFile().get());
}
if (localContext.modelProcessor != null) {
return localContext.modelProcessor.locateExistingPom(current);
if (context.modelProcessor != null) {
return context.modelProcessor.locateExistingPom(current);
} else {
return Files.isRegularFile(current) ? current : null;
}
}
protected String determineReactorFailureBehaviour(C localContext) {
MavenOptions mavenOptions = localContext.invokerRequest.options();
protected String determineReactorFailureBehaviour(C context) {
MavenOptions mavenOptions = context.invokerRequest.options();
if (mavenOptions.failFast().isPresent()) {
return MavenExecutionRequest.REACTOR_FAIL_FAST;
} else if (mavenOptions.failAtEnd().isPresent()) {
@ -351,8 +348,8 @@ public abstract class DefaultMavenInvoker<
}
}
protected String determineGlobalChecksumPolicy(C localContext) {
MavenOptions mavenOptions = localContext.invokerRequest.options();
protected String determineGlobalChecksumPolicy(C context) {
MavenOptions mavenOptions = context.invokerRequest.options();
if (mavenOptions.strictChecksums().orElse(false)) {
return MavenExecutionRequest.CHECKSUM_POLICY_FAIL;
} else if (mavenOptions.relaxedChecksums().orElse(false)) {
@ -362,18 +359,17 @@ public abstract class DefaultMavenInvoker<
}
}
protected ExecutionListener determineExecutionListener(C localContext) {
ExecutionListener executionListener =
new ExecutionEventLogger(localContext.invokerRequest.messageBuilderFactory());
if (localContext.eventSpyDispatcher != null) {
return localContext.eventSpyDispatcher.chainListener(executionListener);
protected ExecutionListener determineExecutionListener(C context) {
ExecutionListener executionListener = new ExecutionEventLogger(context.invokerRequest.messageBuilderFactory());
if (context.eventSpyDispatcher != null) {
return context.eventSpyDispatcher.chainListener(executionListener);
} else {
return executionListener;
}
}
protected String determineMakeBehavior(C localContext) {
MavenOptions mavenOptions = localContext.invokerRequest.options();
protected String determineMakeBehavior(C context) {
MavenOptions mavenOptions = context.invokerRequest.options();
if (mavenOptions.alsoMake().isPresent()
&& mavenOptions.alsoMakeDependents().isEmpty()) {
return MavenExecutionRequest.REACTOR_MAKE_UPSTREAM;
@ -388,8 +384,8 @@ public abstract class DefaultMavenInvoker<
}
}
protected void performProjectActivation(C localContext, ProjectActivation projectActivation) {
MavenOptions mavenOptions = localContext.invokerRequest.options();
protected void performProjectActivation(C context, ProjectActivation projectActivation) {
MavenOptions mavenOptions = context.invokerRequest.options();
if (mavenOptions.projects().isPresent()
&& !mavenOptions.projects().get().isEmpty()) {
List<String> optionValues = mavenOptions.projects().get();
@ -416,8 +412,8 @@ public abstract class DefaultMavenInvoker<
}
}
protected void performProfileActivation(C localContext, ProfileActivation profileActivation) {
MavenOptions mavenOptions = localContext.invokerRequest.options();
protected void performProfileActivation(C context, ProfileActivation profileActivation) {
MavenOptions mavenOptions = context.invokerRequest.options();
if (mavenOptions.activatedProfiles().isPresent()
&& !mavenOptions.activatedProfiles().get().isEmpty()) {
List<String> optionValues = mavenOptions.activatedProfiles().get();
@ -444,62 +440,61 @@ public abstract class DefaultMavenInvoker<
}
}
protected int doExecute(C localContext) throws Exception {
MavenExecutionRequest request = localContext.mavenExecutionRequest;
protected int doExecute(C context) throws Exception {
MavenExecutionRequest request = context.mavenExecutionRequest;
// why? No way to disable caching?
if (localContext.mavenExecutionRequest.getRepositoryCache() == null) {
localContext.mavenExecutionRequest.setRepositoryCache(new DefaultRepositoryCache());
if (context.mavenExecutionRequest.getRepositoryCache() == null) {
context.mavenExecutionRequest.setRepositoryCache(new DefaultRepositoryCache());
}
localContext.eventSpyDispatcher.onEvent(request);
context.eventSpyDispatcher.onEvent(request);
MavenExecutionResult result = localContext.maven.execute(request);
localContext.eventSpyDispatcher.onEvent(result);
localContext.eventSpyDispatcher.close();
MavenExecutionResult result;
try {
result = context.maven.execute(request);
context.eventSpyDispatcher.onEvent(result);
} finally {
context.eventSpyDispatcher.close();
}
if (result.hasExceptions()) {
ExceptionHandler handler = new DefaultExceptionHandler();
Map<String, String> references = new LinkedHashMap<>();
List<MavenProject> failedProjects = new ArrayList<>();
for (Throwable exception : result.getExceptions()) {
ExceptionSummary summary = handler.handleException(exception);
logSummary(localContext, summary, references, "");
logSummary(context, summary, references, "");
if (exception instanceof LifecycleExecutionException) {
failedProjects.add(((LifecycleExecutionException) exception).getProject());
}
}
localContext.logger.error("");
context.logger.error("");
if (!localContext.invokerRequest.options().showErrors().orElse(false)) {
localContext.logger.error("To see the full stack trace of the errors, re-run Maven with the '"
if (!context.invokerRequest.options().showErrors().orElse(false)) {
context.logger.error("To see the full stack trace of the errors, re-run Maven with the '"
+ MessageUtils.builder().strong("-e") + "' switch");
}
if (!localContext.invokerRequest.options().verbose().orElse(false)) {
localContext.logger.error("Re-run Maven using the '"
if (!context.invokerRequest.options().verbose().orElse(false)) {
context.logger.error("Re-run Maven using the '"
+ MessageUtils.builder().strong("-X") + "' switch to enable verbose output");
}
if (!references.isEmpty()) {
localContext.logger.error("");
localContext.logger.error("For more information about the errors and possible solutions"
context.logger.error("");
context.logger.error("For more information about the errors and possible solutions"
+ ", please read the following articles:");
for (Map.Entry<String, String> entry : references.entrySet()) {
localContext.logger.error(MessageUtils.builder().strong(entry.getValue()) + " " + entry.getKey());
context.logger.error(MessageUtils.builder().strong(entry.getValue()) + " " + entry.getKey());
}
}
if (result.canResume()) {
logBuildResumeHint(localContext, "mvn [args] -r");
logBuildResumeHint(context, "mvn [args] -r");
} else if (!failedProjects.isEmpty()) {
List<MavenProject> sortedProjects = result.getTopologicallySortedProjects();
@ -509,12 +504,12 @@ public abstract class DefaultMavenInvoker<
MavenProject firstFailedProject = failedProjects.get(0);
if (!firstFailedProject.equals(sortedProjects.get(0))) {
String resumeFromSelector = getResumeFromSelector(sortedProjects, firstFailedProject);
logBuildResumeHint(localContext, "mvn [args] -rf " + resumeFromSelector);
logBuildResumeHint(context, "mvn [args] -rf " + resumeFromSelector);
}
}
if (localContext.invokerRequest.options().failNever().orElse(false)) {
localContext.logger.info("Build failures were ignored.");
if (context.invokerRequest.options().failNever().orElse(false)) {
context.logger.info("Build failures were ignored.");
return 0;
} else {
return 1;
@ -524,10 +519,10 @@ public abstract class DefaultMavenInvoker<
}
}
protected void logBuildResumeHint(C localContext, String resumeBuildHint) {
localContext.logger.error("");
localContext.logger.error("After correcting the problems, you can resume the build with the command");
localContext.logger.error(
protected void logBuildResumeHint(C context, String resumeBuildHint) {
context.logger.error("");
context.logger.error("After correcting the problems, you can resume the build with the command");
context.logger.error(
MessageUtils.builder().a(" ").strong(resumeBuildHint).toString());
}
@ -567,7 +562,7 @@ public abstract class DefaultMavenInvoker<
protected static final String ANSI_RESET = "\u001B\u005Bm";
protected void logSummary(C localContext, ExceptionSummary summary, Map<String, String> references, String indent) {
protected void logSummary(C context, ExceptionSummary summary, Map<String, String> references, String indent) {
String referenceKey = "";
if (summary.getReference() != null && !summary.getReference().isEmpty()) {
@ -607,11 +602,11 @@ public abstract class DefaultMavenInvoker<
line = indent + line + ("".equals(nextColor) ? "" : ANSI_RESET);
if ((i == lines.length - 1)
&& (localContext.invokerRequest.options().showErrors().orElse(false)
&& (context.invokerRequest.options().showErrors().orElse(false)
|| (summary.getException() instanceof InternalErrorException))) {
localContext.logger.error(line, summary.getException());
context.logger.error(line, summary.getException());
} else {
localContext.logger.error(line);
context.logger.error(line);
}
currentColor = nextColor;
@ -620,7 +615,7 @@ public abstract class DefaultMavenInvoker<
indent += " ";
for (ExceptionSummary child : summary.getChildren()) {
logSummary(localContext, child, references, indent);
logSummary(context, child, references, indent);
}
}
}

View File

@ -25,11 +25,10 @@ import java.util.List;
import java.util.Map;
import org.apache.maven.api.annotations.Nonnull;
import org.apache.maven.api.cli.Logger;
import org.apache.maven.api.cli.ParserRequest;
import org.apache.maven.api.cli.extensions.CoreExtension;
import org.apache.maven.api.cli.mvn.MavenInvokerRequest;
import org.apache.maven.api.cli.mvn.MavenOptions;
import org.apache.maven.api.services.MessageBuilderFactory;
import org.apache.maven.cling.invoker.BaseInvokerRequest;
import static java.util.Objects.requireNonNull;
@ -45,14 +44,12 @@ public class DefaultMavenInvokerRequest<O extends MavenOptions> extends BaseInvo
@SuppressWarnings("ParameterNumber")
public DefaultMavenInvokerRequest(
String command,
ParserRequest parserRequest,
Path cwd,
Path installationDirectory,
Path userHomeDirectory,
Map<String, String> userProperties,
Map<String, String> systemProperties,
Logger logger,
MessageBuilderFactory messageBuilderFactory,
Path topDirectory,
Path rootDirectory,
InputStream in,
@ -61,14 +58,12 @@ public class DefaultMavenInvokerRequest<O extends MavenOptions> extends BaseInvo
List<CoreExtension> coreExtensions,
O options) {
super(
command,
parserRequest,
cwd,
installationDirectory,
userHomeDirectory,
userProperties,
systemProperties,
logger,
messageBuilderFactory,
topDirectory,
rootDirectory,
in,

View File

@ -53,7 +53,7 @@ public abstract class DefaultMavenParser<O extends MavenOptions, R extends Maven
Options options);
@Override
protected List<O> parseCliOptions(Path rootDirectory, String[] args) throws ParserException, IOException {
protected List<O> parseCliOptions(Path rootDirectory, List<String> args) throws ParserException, IOException {
ArrayList<O> result = new ArrayList<>();
// CLI args
result.add(parseMavenCliOptions(args));
@ -65,14 +65,14 @@ public abstract class DefaultMavenParser<O extends MavenOptions, R extends Maven
return result;
}
protected O parseMavenCliOptions(String[] args) throws ParserException {
protected O parseMavenCliOptions(List<String> args) throws ParserException {
return parseArgs(Options.SOURCE_CLI, args);
}
protected O parseMavenConfigOptions(Path configFile) throws ParserException, IOException {
try (Stream<String> lines = Files.lines(configFile, Charset.defaultCharset())) {
String[] args =
lines.filter(arg -> !arg.isEmpty() && !arg.startsWith("#")).toArray(String[]::new);
List<String> args =
lines.filter(arg -> !arg.isEmpty() && !arg.startsWith("#")).toList();
O options = parseArgs("maven.config", args);
if (options.goals().isPresent()) {
// This file can only contain options, not args (goals or phases)
@ -83,5 +83,5 @@ public abstract class DefaultMavenParser<O extends MavenOptions, R extends Maven
}
}
protected abstract O parseArgs(String source, String[] args) throws ParserException;
protected abstract O parseArgs(String source, List<String> args) throws ParserException;
}

View File

@ -35,6 +35,7 @@ import static java.util.Objects.requireNonNull;
* Forked invoker implementation, it spawns a subprocess with Maven.
*/
public class DefaultForkedMavenInvoker implements ForkedMavenInvoker {
@SuppressWarnings("MethodLength")
@Override
public int invoke(ForkedMavenInvokerRequest invokerRequest) throws InvokerException {
requireNonNull(invokerRequest);
@ -44,7 +45,10 @@ public class DefaultForkedMavenInvoker implements ForkedMavenInvoker {
cmdAndArguments.add(invokerRequest
.installationDirectory()
.resolve("bin")
.resolve(Os.IS_WINDOWS ? invokerRequest.command() + ".cmd" : invokerRequest.command())
.resolve(
Os.IS_WINDOWS
? invokerRequest.parserRequest().command() + ".cmd"
: invokerRequest.parserRequest().command())
.toString());
MavenOptions mavenOptions = invokerRequest.options();
@ -54,42 +58,138 @@ public class DefaultForkedMavenInvoker implements ForkedMavenInvoker {
cmdAndArguments.add("-D" + entry.getKey() + "=" + entry.getValue());
}
}
if (mavenOptions.showVersionAndExit().orElse(false)) {
cmdAndArguments.add("--version");
}
if (mavenOptions.showVersion().orElse(false)) {
cmdAndArguments.add("--show-version");
}
if (mavenOptions.quiet().orElse(false)) {
cmdAndArguments.add("--quiet");
}
if (mavenOptions.verbose().orElse(false)) {
cmdAndArguments.add("--verbose");
}
if (mavenOptions.showErrors().orElse(false)) {
cmdAndArguments.add("--errors");
}
if (mavenOptions.failOnSeverity().isPresent()) {
cmdAndArguments.add("--fail-on-severity");
cmdAndArguments.add(mavenOptions.failOnSeverity().get());
}
if (mavenOptions.nonInteractive().orElse(false)) {
cmdAndArguments.add("--non-interactive");
}
if (mavenOptions.forceInteractive().orElse(false)) {
cmdAndArguments.add("--force-interactive");
}
if (mavenOptions.altUserSettings().isPresent()) {
cmdAndArguments.add("--settings");
cmdAndArguments.add(mavenOptions.altUserSettings().get());
}
if (mavenOptions.altProjectSettings().isPresent()) {
cmdAndArguments.add("--project-settings");
cmdAndArguments.add(mavenOptions.altProjectSettings().get());
}
if (mavenOptions.altInstallationSettings().isPresent()) {
cmdAndArguments.add("--install-settings");
cmdAndArguments.add(mavenOptions.altInstallationSettings().get());
}
if (mavenOptions.altUserToolchains().isPresent()) {
cmdAndArguments.add("--toolchains");
cmdAndArguments.add(mavenOptions.altUserToolchains().get());
}
if (mavenOptions.altInstallationToolchains().isPresent()) {
cmdAndArguments.add("--install-toolchains");
cmdAndArguments.add(mavenOptions.altInstallationToolchains().get());
}
if (mavenOptions.logFile().isPresent()) {
cmdAndArguments.add("--log-file");
cmdAndArguments.add(mavenOptions.logFile().get());
}
if (mavenOptions.color().isPresent()) {
cmdAndArguments.add("--color");
cmdAndArguments.add(mavenOptions.color().get());
}
if (mavenOptions.help().orElse(false)) {
cmdAndArguments.add("--help");
}
if (mavenOptions.alternatePomFile().isPresent()) {
cmdAndArguments.add("-f");
cmdAndArguments.add("--file");
cmdAndArguments.add(mavenOptions.alternatePomFile().get());
}
if (mavenOptions.offline().orElse(false)) {
cmdAndArguments.add("-o");
}
if (mavenOptions.showVersionAndExit().orElse(false)) {
cmdAndArguments.add("-v");
}
if (mavenOptions.showVersion().orElse(false)) {
cmdAndArguments.add("-V");
}
if (mavenOptions.quiet().orElse(false)) {
cmdAndArguments.add("-q");
}
if (mavenOptions.verbose().orElse(false)) {
cmdAndArguments.add("-X");
}
if (mavenOptions.showErrors().orElse(false)) {
cmdAndArguments.add("-e");
cmdAndArguments.add("--offline");
}
if (mavenOptions.nonRecursive().orElse(false)) {
cmdAndArguments.add("-N");
cmdAndArguments.add("--non-recursive");
}
if (mavenOptions.updateSnapshots().orElse(false)) {
cmdAndArguments.add("-U");
cmdAndArguments.add("--update-snapshots");
}
if (mavenOptions.nonInteractive().orElse(false)) {
cmdAndArguments.add("-B");
if (mavenOptions.activatedProfiles().isPresent()) {
cmdAndArguments.add("--activate-profiles");
cmdAndArguments.add(
String.join(",", mavenOptions.activatedProfiles().get()));
}
if (mavenOptions.logFile().isPresent()) {
cmdAndArguments.add("-l");
cmdAndArguments.add(mavenOptions.logFile().get());
if (mavenOptions.suppressSnapshotUpdates().orElse(false)) {
cmdAndArguments.add("--no-snapshot-updates");
}
if (mavenOptions.strictChecksums().orElse(false)) {
cmdAndArguments.add("--strict-checksums");
}
if (mavenOptions.relaxedChecksums().orElse(false)) {
cmdAndArguments.add("--lax-checksums");
}
if (mavenOptions.failFast().orElse(false)) {
cmdAndArguments.add("--fail-fast");
}
if (mavenOptions.failAtEnd().orElse(false)) {
cmdAndArguments.add("--fail-at-end");
}
if (mavenOptions.failNever().orElse(false)) {
cmdAndArguments.add("--fail-never");
}
if (mavenOptions.resume().orElse(false)) {
cmdAndArguments.add("--resume");
}
if (mavenOptions.resumeFrom().isPresent()) {
cmdAndArguments.add("--resume-from");
cmdAndArguments.add(mavenOptions.resumeFrom().get());
}
if (mavenOptions.projects().isPresent()) {
cmdAndArguments.add("--projects");
cmdAndArguments.add(String.join(",", mavenOptions.projects().get()));
}
if (mavenOptions.alsoMake().orElse(false)) {
cmdAndArguments.add("--also-make");
}
if (mavenOptions.alsoMakeDependents().orElse(false)) {
cmdAndArguments.add("--also-make-dependents");
}
if (mavenOptions.threads().isPresent()) {
cmdAndArguments.add("--threads");
cmdAndArguments.add(mavenOptions.threads().get());
}
if (mavenOptions.builder().isPresent()) {
cmdAndArguments.add("--builder");
cmdAndArguments.add(mavenOptions.builder().get());
}
if (mavenOptions.noTransferProgress().orElse(false)) {
cmdAndArguments.add("--no-transfer-progress");
}
if (mavenOptions.cacheArtifactNotFound().isPresent()) {
cmdAndArguments.add("--cache-artifact-not-found");
cmdAndArguments.add(mavenOptions.cacheArtifactNotFound().get().toString());
}
if (mavenOptions.strictArtifactDescriptorPolicy().isPresent()) {
cmdAndArguments.add("--strict-artifact-descriptor-policy");
cmdAndArguments.add(
mavenOptions.strictArtifactDescriptorPolicy().get().toString());
}
if (mavenOptions.ignoreTransitiveRepositories().isPresent()) {
cmdAndArguments.add("--ignore-transitive-repositories");
}
// TODO: etc
// last the goals
cmdAndArguments.addAll(mavenOptions.goals().orElse(Collections.emptyList()));

View File

@ -25,11 +25,10 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.apache.maven.api.cli.Logger;
import org.apache.maven.api.cli.ParserRequest;
import org.apache.maven.api.cli.extensions.CoreExtension;
import org.apache.maven.api.cli.mvn.MavenOptions;
import org.apache.maven.api.cli.mvn.forked.ForkedMavenInvokerRequest;
import org.apache.maven.api.services.MessageBuilderFactory;
import org.apache.maven.cling.invoker.mvn.DefaultMavenInvokerRequest;
/**
@ -41,14 +40,12 @@ public class DefaultForkedMavenInvokerRequest extends DefaultMavenInvokerRequest
@SuppressWarnings("ParameterNumber")
public DefaultForkedMavenInvokerRequest(
String command,
ParserRequest parserRequest,
Path cwd,
Path installationDirectory,
Path userHomeDirectory,
Map<String, String> userProperties,
Map<String, String> systemProperties,
Logger logger,
MessageBuilderFactory messageBuilderFactory,
Path topDirectory,
Path rootDirectory,
InputStream in,
@ -58,14 +55,12 @@ public class DefaultForkedMavenInvokerRequest extends DefaultMavenInvokerRequest
List<String> jvmArguments,
MavenOptions options) {
super(
command,
parserRequest,
cwd,
installationDirectory,
userHomeDirectory,
userProperties,
systemProperties,
logger,
messageBuilderFactory,
topDirectory,
rootDirectory,
in,
@ -78,6 +73,6 @@ public class DefaultForkedMavenInvokerRequest extends DefaultMavenInvokerRequest
@Override
public Optional<List<String>> jvmArguments() {
return Optional.of(jvmArguments);
return Optional.ofNullable(jvmArguments);
}
}

View File

@ -20,7 +20,6 @@ package org.apache.maven.cling.invoker.mvn.forked;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@ -53,14 +52,12 @@ public class DefaultForkedMavenParser extends DefaultMavenParser<MavenOptions, F
ArrayList<CoreExtension> extensions,
Options options) {
return new DefaultForkedMavenInvokerRequest(
parserRequest.command(),
parserRequest,
cwd,
installationDirectory,
userHomeDirectory,
userProperties,
systemProperties,
parserRequest.logger(),
parserRequest.messageBuilderFactory(),
topDirectory,
rootDirectory,
parserRequest.in(),
@ -74,15 +71,15 @@ public class DefaultForkedMavenParser extends DefaultMavenParser<MavenOptions, F
protected List<String> getJvmArguments(Path rootDirectory) {
if (rootDirectory != null) {
// TODO: do this
return Collections.emptyList();
return null;
}
return null;
}
@Override
protected MavenOptions parseArgs(String source, String[] args) throws ParserException {
protected MavenOptions parseArgs(String source, List<String> args) throws ParserException {
try {
return CommonsCliMavenOptions.parse(source, args);
return CommonsCliMavenOptions.parse(source, args.toArray(new String[0]));
} catch (ParseException e) {
throw new ParserException("Failed to parse source " + source, e.getCause());
}

View File

@ -52,14 +52,12 @@ public class DefaultLocalMavenParser extends DefaultMavenParser<MavenOptions, Ma
ArrayList<CoreExtension> extensions,
Options options) {
return new DefaultMavenInvokerRequest<>(
parserRequest.command(),
parserRequest,
cwd,
installationDirectory,
userHomeDirectory,
userProperties,
systemProperties,
parserRequest.logger(),
parserRequest.messageBuilderFactory(),
topDirectory,
rootDirectory,
parserRequest.in(),
@ -70,9 +68,9 @@ public class DefaultLocalMavenParser extends DefaultMavenParser<MavenOptions, Ma
}
@Override
protected MavenOptions parseArgs(String source, String[] args) throws ParserException {
protected MavenOptions parseArgs(String source, List<String> args) throws ParserException {
try {
return CommonsCliMavenOptions.parse(source, args);
return CommonsCliMavenOptions.parse(source, args.toArray(new String[0]));
} catch (ParseException e) {
throw new ParserException("Failed to parse source " + source, e.getCause());
}

View File

@ -54,7 +54,7 @@ public class DefaultResidentMavenInvoker
super.close();
}
public LocalContext createShadow() {
public LocalContext copy(ResidentMavenInvokerRequest invokerRequest) {
LocalContext shadow = new LocalContext((DefaultResidentMavenInvoker) invoker, invokerRequest);
shadow.logger = logger;
@ -122,10 +122,12 @@ public class DefaultResidentMavenInvoker
throw new InvokerException("Failed to init master context", e);
}
})
.createShadow();
.copy(invokerRequest);
}
protected String getContextId(ResidentMavenInvokerRequest invokerRequest) {
// TODO: in a moment Maven stop pushing user properties to system properties (and maybe something more)
// and allow multiple instances per JVM, this may become a pool?
return "resident";
}

View File

@ -25,11 +25,10 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.apache.maven.api.cli.Logger;
import org.apache.maven.api.cli.ParserRequest;
import org.apache.maven.api.cli.extensions.CoreExtension;
import org.apache.maven.api.cli.mvn.resident.ResidentMavenInvokerRequest;
import org.apache.maven.api.cli.mvn.resident.ResidentMavenOptions;
import org.apache.maven.api.services.MessageBuilderFactory;
import org.apache.maven.cling.invoker.mvn.DefaultMavenInvokerRequest;
/**
@ -41,14 +40,12 @@ public class DefaultResidentMavenInvokerRequest extends DefaultMavenInvokerReque
@SuppressWarnings("ParameterNumber")
public DefaultResidentMavenInvokerRequest(
String command,
ParserRequest parserRequest,
Path cwd,
Path installationDirectory,
Path userHomeDirectory,
Map<String, String> userProperties,
Map<String, String> systemProperties,
Logger logger,
MessageBuilderFactory messageBuilderFactory,
Path topDirectory,
Path rootDirectory,
InputStream in,
@ -58,14 +55,12 @@ public class DefaultResidentMavenInvokerRequest extends DefaultMavenInvokerReque
List<String> jvmArguments,
ResidentMavenOptions options) {
super(
command,
parserRequest,
cwd,
installationDirectory,
userHomeDirectory,
userProperties,
systemProperties,
logger,
messageBuilderFactory,
topDirectory,
rootDirectory,
in,

View File

@ -51,14 +51,12 @@ public class DefaultResidentMavenParser extends DefaultMavenParser<ResidentMaven
ArrayList<CoreExtension> extensions,
Options options) {
return new DefaultResidentMavenInvokerRequest(
parserRequest.command(),
parserRequest,
cwd,
installationDirectory,
userHomeDirectory,
userProperties,
systemProperties,
parserRequest.logger(),
parserRequest.messageBuilderFactory(),
topDirectory,
rootDirectory,
parserRequest.in(),
@ -78,9 +76,9 @@ public class DefaultResidentMavenParser extends DefaultMavenParser<ResidentMaven
}
@Override
protected ResidentMavenOptions parseArgs(String source, String[] args) throws ParserException {
protected ResidentMavenOptions parseArgs(String source, List<String> args) throws ParserException {
try {
return CommonsCliResidentMavenOptions.parse(source, args);
return CommonsCliResidentMavenOptions.parse(source, args.toArray(new String[0]));
} catch (ParseException e) {
throw new ParserException("Failed to parse source " + source, e.getCause());
}

View File

@ -25,12 +25,11 @@ import java.util.List;
import java.util.Map;
import org.apache.maven.api.annotations.Nonnull;
import org.apache.maven.api.cli.Logger;
import org.apache.maven.api.cli.Options;
import org.apache.maven.api.cli.ParserRequest;
import org.apache.maven.api.cli.extensions.CoreExtension;
import org.apache.maven.api.cli.mvnenc.EncryptInvokerRequest;
import org.apache.maven.api.cli.mvnenc.EncryptOptions;
import org.apache.maven.api.services.MessageBuilderFactory;
import org.apache.maven.cling.invoker.BaseInvokerRequest;
import static java.util.Objects.requireNonNull;
@ -40,14 +39,12 @@ public class DefaultEncryptInvokerRequest extends BaseInvokerRequest<EncryptOpti
@SuppressWarnings("ParameterNumber")
public DefaultEncryptInvokerRequest(
String command,
ParserRequest parserRequest,
Path cwd,
Path installationDirectory,
Path userHomeDirectory,
Map<String, String> userProperties,
Map<String, String> systemProperties,
Logger logger,
MessageBuilderFactory messageBuilderFactory,
Path topDirectory,
Path rootDirectory,
InputStream in,
@ -56,14 +53,12 @@ public class DefaultEncryptInvokerRequest extends BaseInvokerRequest<EncryptOpti
List<CoreExtension> coreExtensions,
Options options) {
super(
command,
parserRequest,
cwd,
installationDirectory,
userHomeDirectory,
userProperties,
systemProperties,
logger,
messageBuilderFactory,
topDirectory,
rootDirectory,
in,

View File

@ -49,14 +49,12 @@ public class DefaultEncryptParser extends BaseParser<EncryptOptions, EncryptInvo
ArrayList<CoreExtension> extensions,
Options options) {
return new DefaultEncryptInvokerRequest(
parserRequest.command(),
parserRequest,
cwd,
installationDirectory,
userHomeDirectory,
userProperties,
systemProperties,
parserRequest.logger(),
parserRequest.messageBuilderFactory(),
topDirectory,
rootDirectory,
parserRequest.in(),
@ -67,13 +65,13 @@ public class DefaultEncryptParser extends BaseParser<EncryptOptions, EncryptInvo
}
@Override
protected List<EncryptOptions> parseCliOptions(Path rootDirectory, String[] args) throws ParserException {
protected List<EncryptOptions> parseCliOptions(Path rootDirectory, List<String> args) throws ParserException {
return Collections.singletonList(parseEncryptCliOptions(args));
}
protected CommonsCliEncryptOptions parseEncryptCliOptions(String[] args) throws ParserException {
protected CommonsCliEncryptOptions parseEncryptCliOptions(List<String> args) throws ParserException {
try {
return CommonsCliEncryptOptions.parse(args);
return CommonsCliEncryptOptions.parse(args.toArray(new String[0]));
} catch (ParseException e) {
throw new ParserException("Failed to parse command line options: " + e.getMessage(), e);
}

View File

@ -1,50 +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.forked;
import java.nio.file.Files;
import java.nio.file.Path;
import org.apache.maven.cling.invoker.ProtoLogger;
import org.apache.maven.cling.invoker.mvn.forked.DefaultForkedMavenInvoker;
import org.apache.maven.cling.invoker.mvn.forked.DefaultForkedMavenParser;
import org.apache.maven.jline.JLineMessageBuilderFactory;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
@Disabled("Works ONLY with Maven4!")
public class DefaultForkedMavenInvokerTest {
@Test
void smoke(@TempDir Path tempDir) throws Exception {
try (DefaultForkedMavenInvoker invoker = new DefaultForkedMavenInvoker()) {
Files.createDirectory(tempDir.resolve(".mvn"));
Path log = tempDir.resolve("build.log").toAbsolutePath();
int exitcode = invoker.invoke(new DefaultForkedMavenParser()
.parse(
"mvn",
new String[] {"-l", log.toString(), "clean"},
new ProtoLogger(),
new JLineMessageBuilderFactory()));
System.out.println("exit code: " + exitcode);
System.out.println("log:");
System.out.println(Files.readString(log));
}
}
}

View File

@ -0,0 +1,117 @@
/*
* 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.nio.file.Paths;
import java.util.Collection;
import java.util.List;
import org.apache.maven.api.cli.Invoker;
import org.apache.maven.api.cli.Parser;
import org.apache.maven.api.cli.ParserRequest;
import org.apache.maven.api.cli.mvn.MavenInvokerRequest;
import org.apache.maven.api.cli.mvn.MavenOptions;
import org.apache.maven.cling.invoker.ProtoLogger;
import org.apache.maven.jline.JLineMessageBuilderFactory;
import org.junit.jupiter.api.Assumptions;
import static org.junit.jupiter.api.Assertions.assertEquals;
public abstract class MavenInvokerTestSupport<O extends MavenOptions, R extends MavenInvokerRequest<O>> {
protected void invoke(Path cwd, Collection<String> goals) throws Exception {
// works only in recent Maven4
Assumptions.assumeTrue(Files.isRegularFile(
Paths.get(System.getProperty("maven.home")).resolve("conf").resolve("maven.properties")));
Files.createDirectory(cwd.resolve(".mvn"));
String pomString =
"""
<?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>
""";
Path pom = cwd.resolve("pom.xml").toAbsolutePath();
Files.writeString(pom, pomString);
String appJavaString =
"""
package org.apache.maven.samples.sample;
public class App {
public static void main(String... args) {
System.out.println("Hello World!");
}
}
""";
Path appJava = cwd.resolve("src/main/java/org/apache/maven/samples/sample/App.java");
Files.createDirectories(appJava.getParent());
Files.writeString(appJava, appJavaString);
Parser<R> parser = createParser();
try (Invoker<R> invoker = createInvoker()) {
for (String goal : goals) {
Path logFile = cwd.resolve(goal + "-build.log").toAbsolutePath();
int exitCode = invoker.invoke(parser.parse(ParserRequest.mvn(
List.of("-l", logFile.toString(), goal),
new ProtoLogger(),
new JLineMessageBuilderFactory())
.cwd(cwd)
.build()));
String log = Files.readString(logFile);
assertEquals(0, exitCode, log);
}
}
}
protected abstract Invoker<R> createInvoker();
protected abstract Parser<R> createParser();
}

View File

@ -0,0 +1,52 @@
/*
* 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.nio.file.Path;
import java.util.Arrays;
import org.apache.maven.api.cli.Invoker;
import org.apache.maven.api.cli.Parser;
import org.apache.maven.api.cli.mvn.MavenOptions;
import org.apache.maven.api.cli.mvn.forked.ForkedMavenInvokerRequest;
import org.apache.maven.cling.invoker.mvn.MavenInvokerTestSupport;
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.
*/
public class DefaultForkedMavenInvokerTest extends MavenInvokerTestSupport<MavenOptions, ForkedMavenInvokerRequest> {
@Override
protected Invoker<ForkedMavenInvokerRequest> createInvoker() {
return new DefaultForkedMavenInvoker();
}
@Override
protected Parser<ForkedMavenInvokerRequest> createParser() {
return new DefaultForkedMavenParser();
}
@Test
void defaultFs(@TempDir(cleanup = CleanupMode.ON_SUCCESS) Path tempDir) throws Exception {
invoke(tempDir, Arrays.asList("clean", "verify"));
}
}

View File

@ -0,0 +1,68 @@
/*
* 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.local;
import java.nio.file.FileSystem;
import java.nio.file.Path;
import java.util.Arrays;
import com.google.common.jimfs.Configuration;
import com.google.common.jimfs.Jimfs;
import org.apache.maven.api.cli.Invoker;
import org.apache.maven.api.cli.Parser;
import org.apache.maven.api.cli.mvn.MavenInvokerRequest;
import org.apache.maven.api.cli.mvn.MavenOptions;
import org.apache.maven.cling.invoker.ProtoLookup;
import org.apache.maven.cling.invoker.mvn.MavenInvokerTestSupport;
import org.codehaus.plexus.classworlds.ClassWorld;
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;
/**
* Local UT.
*/
public class DefaultLocalMavenInvokerTest
extends MavenInvokerTestSupport<MavenOptions, MavenInvokerRequest<MavenOptions>> {
@Override
protected Invoker<MavenInvokerRequest<MavenOptions>> createInvoker() {
return new DefaultLocalMavenInvoker(ProtoLookup.builder()
.addMapping(ClassWorld.class, new ClassWorld("plexus.core", ClassLoader.getSystemClassLoader()))
.build());
}
@Override
protected Parser<MavenInvokerRequest<MavenOptions>> createParser() {
return new DefaultLocalMavenParser();
}
@Test
void defaultFs(@TempDir(cleanup = CleanupMode.ON_SUCCESS) Path tempDir) throws Exception {
invoke(tempDir, Arrays.asList("clean", "verify"));
}
@Disabled("Until we move off fully from File")
@Test
void jimFs() throws Exception {
try (FileSystem fs = Jimfs.newFileSystem(Configuration.unix())) {
invoke(fs.getPath("/"), Arrays.asList("clean", "verify"));
}
}
}

View File

@ -18,94 +18,52 @@
*/
package org.apache.maven.cling.invoker.mvn.resident;
import java.nio.file.Files;
import java.nio.file.FileSystem;
import java.nio.file.Path;
import java.util.Arrays;
import org.apache.maven.api.cli.ParserRequest;
import org.apache.maven.api.cli.mvn.resident.ResidentMavenParser;
import org.apache.maven.cling.invoker.ProtoLogger;
import com.google.common.jimfs.Configuration;
import com.google.common.jimfs.Jimfs;
import org.apache.maven.api.cli.Invoker;
import org.apache.maven.api.cli.Parser;
import org.apache.maven.api.cli.mvn.resident.ResidentMavenInvokerRequest;
import org.apache.maven.api.cli.mvn.resident.ResidentMavenOptions;
import org.apache.maven.cling.invoker.ProtoLookup;
import org.apache.maven.jline.JLineMessageBuilderFactory;
import org.apache.maven.cling.invoker.mvn.MavenInvokerTestSupport;
import org.codehaus.plexus.classworlds.ClassWorld;
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;
@Disabled("Works ONLY with Maven4!")
public class DefaultResidentMavenInvokerTest {
/**
* Resident UT.
*/
public class DefaultResidentMavenInvokerTest
extends MavenInvokerTestSupport<ResidentMavenOptions, ResidentMavenInvokerRequest> {
@Override
protected Invoker<ResidentMavenInvokerRequest> createInvoker() {
return new DefaultResidentMavenInvoker(ProtoLookup.builder()
.addMapping(ClassWorld.class, new ClassWorld("plexus.core", ClassLoader.getSystemClassLoader()))
.build());
}
@Override
protected Parser<ResidentMavenInvokerRequest> createParser() {
return new DefaultResidentMavenParser();
}
@Test
void smoke(@TempDir Path tempDir) throws Exception {
try (ClassWorld classWorld =
new ClassWorld("plexus.core", Thread.currentThread().getContextClassLoader());
DefaultResidentMavenInvoker invoker = new DefaultResidentMavenInvoker(ProtoLookup.builder()
.addMapping(ClassWorld.class, classWorld)
.build())) {
ResidentMavenParser parser = new DefaultResidentMavenParser();
Files.createDirectory(tempDir.resolve(".mvn"));
Path log = tempDir.resolve("build.log").toAbsolutePath();
void defaultFs(@TempDir(cleanup = CleanupMode.ON_SUCCESS) Path tempDir) throws Exception {
invoke(tempDir, Arrays.asList("clean", "verify"));
}
String pomString =
"""
<?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>resident-invoker</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>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
</dependency>
</dependencies>
</project>
""";
Path pom = tempDir.resolve("pom.xml").toAbsolutePath();
Files.writeString(pom, pomString);
int exitCode;
exitCode = invoker.invoke(parser.parse(ParserRequest.builder(
"mvn",
new String[] {"-l", log.toString(), "clean"},
new ProtoLogger(),
new JLineMessageBuilderFactory())
.cwd(tempDir)
.build()));
System.out.println("1st exit code: " + exitCode);
System.out.println("log:");
System.out.println(Files.readString(log));
exitCode = invoker.invoke(parser.parse(ParserRequest.builder(
"mvn",
new String[] {"-l", log.toString(), "clean"},
new ProtoLogger(),
new JLineMessageBuilderFactory())
.cwd(tempDir)
.build()));
System.out.println("2nd exit code: " + exitCode);
System.out.println("log:");
System.out.println(Files.readString(log));
@Disabled("Until we move off fully from File")
@Test
void jimFs() throws Exception {
try (FileSystem fs = Jimfs.newFileSystem(Configuration.unix())) {
invoke(fs.getPath("/"), Arrays.asList("clean", "verify"));
}
}
}