diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/InvokerRequest.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/InvokerRequest.java index e3f2a9f7df8c..34418a5fcc2c 100644 --- a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/InvokerRequest.java +++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/InvokerRequest.java @@ -26,8 +26,10 @@ 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 @@ * * @since 4.0.0 */ +@Immutable @Experimental public interface InvokerRequest { /** - * 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 { @Nonnull Map 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. diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Options.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Options.java index 7209cec47d34..5a486bf2152f 100644 --- a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Options.java +++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Options.java @@ -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); } diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Parser.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Parser.java index 182cb298e40c..a47a76f4e79c 100644 --- a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Parser.java +++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Parser.java @@ -36,10 +36,10 @@ @Experimental public interface Parser> { /** - * 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> { * @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()); } /** diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/ParserRequest.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/ParserRequest.java index edcccaadad11..ae5f83ec4b6b 100644 --- a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/ParserRequest.java +++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/ParserRequest.java @@ -21,10 +21,17 @@ 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 @@ * * @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 + List args(); + + /** + * Per-request {@link Lookup} for customization. + * + * @return a lookup possibly with custom components */ @Nonnull - String[] args(); + 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 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 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 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 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 @@ class Builder { 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 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 Builder err(OutputStream err) { 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 args; + private final Lookup lookup; private final Path cwd; private final Path mavenHome; private final Path userHome; @@ -211,19 +325,23 @@ private static class ParserRequestImpl implements ParserRequest { private ParserRequestImpl( String command, - String[] args, + String commandName, + List 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 String command() { return command; } + @Override + public String commandName() { + return commandName; + } + @Override public Logger logger() { return logger; @@ -248,10 +371,15 @@ public MessageBuilderFactory messageBuilderFactory() { } @Override - public String[] args() { + public List args() { return args; } + @Override + public Lookup lookup() { + return lookup; + } + @Override public Path cwd() { return cwd; @@ -282,5 +410,37 @@ public OutputStream err() { return err; } } + + private static final Lookup EMPTY_LOOKUP = new Lookup() { + @Override + public T lookup(Class type) { + throw new LookupException("empty lookup"); + } + + @Override + public T lookup(Class type, String name) { + throw new LookupException("empty lookup"); + } + + @Override + public Optional lookupOptional(Class type) { + return Optional.empty(); + } + + @Override + public Optional lookupOptional(Class type, String name) { + return Optional.empty(); + } + + @Override + public List lookupList(Class type) { + return List.of(); + } + + @Override + public Map lookupMap(Class type) { + return Map.of(); + } + }; } } diff --git a/maven-cli/src/main/java/org/apache/maven/cling/ClingSupport.java b/maven-cli/src/main/java/org/apache/maven/cling/ClingSupport.java index 2eb512f28372..5ea9ec72dbae 100644 --- a/maven-cli/src/main/java/org/apache/maven/cling/ClingSupport.java +++ b/maven-cli/src/main/java/org/apache/maven/cling/ClingSupport.java @@ -23,13 +23,8 @@ 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 @@ public abstract class ClingSupport> { 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 int run(String[] args) throws IOException { MessageUtils.systemInstall(); MessageUtils.registerShutdownHook(); try (Invoker 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 int run(String[] args) throws IOException { protected abstract Invoker createInvoker(); - protected abstract Parser createParser(); - - protected Logger createLogger() { - return new ProtoLogger(); - } - - protected MessageBuilderFactory createMessageBuilderFactory() { - return new JLineMessageBuilderFactory(); - } + protected abstract R parseArguments(String[] args) throws ParserException, IOException; } diff --git a/maven-cli/src/main/java/org/apache/maven/cling/MavenCling.java b/maven-cli/src/main/java/org/apache/maven/cling/MavenCling.java index 60be0e4fa5cd..ac9130bd31b9 100644 --- a/maven-cli/src/main/java/org/apache/maven/cling/MavenCling.java +++ b/maven-cli/src/main/java/org/apache/maven/cling/MavenCling.java @@ -21,26 +21,26 @@ 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> { - 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 static void main(String[] args) throws IOException { * 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 @@ protected Invoker> createInvoker() { } @Override - protected Parser> createParser() { - return new DefaultLocalMavenParser(); + protected MavenInvokerRequest parseArguments(String[] args) throws ParserException, IOException { + return new DefaultLocalMavenParser().mvn(args, new ProtoLogger(), new JLineMessageBuilderFactory()); } } diff --git a/maven-cli/src/main/java/org/apache/maven/cling/MavenEncCling.java b/maven-cli/src/main/java/org/apache/maven/cling/MavenEncCling.java index 4b90eb9ce690..488eb9eae44a 100644 --- a/maven-cli/src/main/java/org/apache/maven/cling/MavenEncCling.java +++ b/maven-cli/src/main/java/org/apache/maven/cling/MavenEncCling.java @@ -21,26 +21,27 @@ 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 { - 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 static void main(String[] args) throws IOException { * 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 @@ protected Invoker createInvoker() { } @Override - protected Parser createParser() { - return new DefaultEncryptParser(); + protected EncryptInvokerRequest parseArguments(String[] args) throws ParserException, IOException { + return new DefaultEncryptParser() + .parse(ParserRequest.mvnenc(args, new ProtoLogger(), new JLineMessageBuilderFactory()) + .build()); } } diff --git a/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseInvokerRequest.java b/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseInvokerRequest.java index 612b4b9fcbfd..1aa26ca7be1d 100644 --- a/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseInvokerRequest.java +++ b/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseInvokerRequest.java @@ -28,22 +28,19 @@ 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 implements InvokerRequest { - private final String command; + private final ParserRequest parserRequest; private final Path cwd; private final Path installationDirectory; private final Path userHomeDirectory; private final Map userProperties; private final Map 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 implements InvokerRe @SuppressWarnings("ParameterNumber") public BaseInvokerRequest( - @Nonnull String command, + @Nonnull ParserRequest parserRequest, @Nonnull Path cwd, @Nonnull Path installationDirectory, @Nonnull Path userHomeDirectory, @Nonnull Map userProperties, @Nonnull Map systemProperties, - @Nonnull Logger logger, - @Nonnull MessageBuilderFactory messageBuilderFactory, @Nonnull Path topDirectory, @Nullable Path rootDirectory, @Nullable InputStream in, @Nullable OutputStream out, @Nullable OutputStream err, @Nullable List 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 BaseInvokerRequest( } @Override - public String command() { - return command; + public ParserRequest parserRequest() { + return parserRequest; } @Override @@ -115,16 +108,6 @@ public Map systemProperties() { return systemProperties; } - @Override - public Logger logger() { - return logger; - } - - @Override - public MessageBuilderFactory messageBuilderFactory() { - return messageBuilderFactory; - } - @Override public Path topDirectory() { return topDirectory; diff --git a/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseParser.java b/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseParser.java index 842d2005aa9a..e6fde1833710 100644 --- a/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseParser.java +++ b/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseParser.java @@ -23,10 +23,9 @@ 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.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 R parse(ParserRequest parserRequest) throws ParserException, IOException // the basics HashMap 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 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 R parse(ParserRequest parserRequest) throws ParserException, IOException Map paths = new HashMap<>(); paths.put("session.topDirectory", topDirectory.toString()); paths.put("session.rootDirectory", rootDirectory.toString()); - Map userProperties = populateUserProperties(systemProperties, fileSystem, paths, options); + Map userProperties = + populateUserProperties(systemProperties, installationDirectory, paths, options); // options: interpolate Options interpolatedOptions = options.interpolate(Arrays.asList(paths, systemProperties, userProperties)); @@ -121,13 +99,13 @@ public R parse(ParserRequest parserRequest) throws ParserException, IOException // core extensions ArrayList 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 @@ protected abstract R getInvokerRequest( ArrayList 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 overrides) - throws ParserException { + protected Path getCwd(ParserRequest parserRequest, Map 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 overrides) throws ParserException { + protected Path getInstallationDirectory(ParserRequest parserRequest, Map overrides) + throws ParserException { Path result; if (parserRequest.mavenHome() != null) { result = getCanonicalPath(parserRequest.mavenHome()); @@ -181,21 +154,21 @@ protected Path getInstallationDirectory( 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 overrides) throws ParserException { + protected Path getUserHomeDirectory(ParserRequest parserRequest, Map 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 @@ protected Path getTopDirectory(ParserRequest parserRequest, Path cwd) throws Par 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 populateSystemProperties(Map overrides) throws ParserException { @@ -260,7 +258,10 @@ protected Map populateSystemProperties(Map overr } protected Map populateUserProperties( - Map systemProperties, FileSystem fileSystem, Map paths, Options options) + Map systemProperties, + Path installationDirectory, + Map paths, + Options options) throws ParserException, IOException { Properties userProperties = new Properties(); @@ -281,13 +282,15 @@ protected Map populateUserProperties( 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 @@ protected Map populateUserProperties( return toMap(userProperties); } - protected abstract List parseCliOptions(Path rootDirectory, String[] args) throws ParserException, IOException; + protected abstract List parseCliOptions(Path rootDirectory, List args) + throws ParserException, IOException; protected abstract O assembleOptions(List parsedOptions); - protected List readCoreExtensionsDescriptor(String extensionsFile, FileSystem fileSystem) + protected List 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(); diff --git a/maven-cli/src/main/java/org/apache/maven/cling/invoker/CommonsCliOptions.java b/maven-cli/src/main/java/org/apache/maven/cling/invoker/CommonsCliOptions.java index f1e99259b3cd..ecd98216b03c 100644 --- a/maven-cli/src/main/java/org/apache/maven/cling/invoker/CommonsCliOptions.java +++ b/maven-cli/src/main/java/org/apache/maven/cling/invoker/CommonsCliOptions.java @@ -31,6 +31,7 @@ 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 Optional help() { } @Override - public void warnAboutDeprecatedOptions(PrintWriter printWriter) { + public void warnAboutDeprecatedOptions(ParserRequest request, PrintWriter printWriter) { if (cliManager.getUsedDeprecatedOptions().isEmpty()) { return; } @@ -217,15 +218,18 @@ public void warnAboutDeprecatedOptions(PrintWriter printWriter) { 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 { diff --git a/maven-cli/src/main/java/org/apache/maven/cling/invoker/LayeredOptions.java b/maven-cli/src/main/java/org/apache/maven/cling/invoker/LayeredOptions.java index f8ef3f62ecec..e702c76dde69 100644 --- a/maven-cli/src/main/java/org/apache/maven/cling/invoker/LayeredOptions.java +++ b/maven-cli/src/main/java/org/apache/maven/cling/invoker/LayeredOptions.java @@ -27,6 +27,7 @@ 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 Optional help() { } @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 Optional returnFirstPresentOrEmpty(Function> getter) { diff --git a/maven-cli/src/main/java/org/apache/maven/cling/invoker/LookupInvoker.java b/maven-cli/src/main/java/org/apache/maven/cling/invoker/LookupInvoker.java index 616ff89e05f6..9c426776c3e6 100644 --- a/maven-cli/src/main/java/org/apache/maven/cling/invoker/LookupInvoker.java +++ b/maven-cli/src/main/java/org/apache/maven/cling/invoker/LookupInvoker.java @@ -95,6 +95,8 @@ public static class LookupInvokerContext< public final ProtoLookup protoLookup; public final R invokerRequest; public final Function cwdResolver; + public final Function installationResolver; + public final Function userResolver; public final InputStream stdIn; public final PrintWriter stdOut; public final PrintWriter stdErr; @@ -104,10 +106,17 @@ protected LookupInvokerContext(LookupInvoker invoker, R invokerRequest) 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 int invoke(R invokerRequest) throws InvokerException { 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 @@ protected InvokerException handleException(LookupInvokerContext context 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 @@ protected void settings(C context, SettingsBuilder settingsBuilder) throws Excep } 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 @@ protected void settings(C context, SettingsBuilder settingsBuilder) throws Excep 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 @@ protected Path localRepositoryPath(C context) { + "usually located at ${session.rootDirectory}/.mvn/maven.properties."); } } + if (userDefinedLocalRepo != null) { + return context.cwdResolver.apply(userDefinedLocalRepo); + } // settings - if (userDefinedLocalRepo == null) { - userDefinedLocalRepo = context.effectiveSettings.getLocalRepository(); + userDefinedLocalRepo = context.effectiveSettings.getLocalRepository(); + if (userDefinedLocalRepo != null) { + return context.userResolver.apply(userDefinedLocalRepo); } // defaults - if (userDefinedLocalRepo == null) { - userDefinedLocalRepo = context.cwdResolver - .apply(context.invokerRequest.userProperties().get(Constants.MAVEN_USER_CONF)) - .resolve("repository") - .toString(); - } - return context.cwdResolver.apply(userDefinedLocalRepo); + return context.userResolver + .apply(context.invokerRequest.userProperties().get(Constants.MAVEN_USER_CONF)) + .resolve("repository"); } protected void populateRequest(C context, MavenExecutionRequest request) throws Exception { diff --git a/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/DefaultMavenInvoker.java b/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/DefaultMavenInvoker.java index 55b3ad528075..d8a6b982f4a6 100644 --- a/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/DefaultMavenInvoker.java +++ b/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/DefaultMavenInvoker.java @@ -105,7 +105,7 @@ protected int execute(C context) throws Exception { } @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 @@ protected void prepare(C localContext) throws Exception { mavenExecutionRequest.setDegreeOfConcurrency(1); mavenExecutionRequest.setBuilderId("singlethreaded"); - localContext.mavenExecutionRequest = mavenExecutionRequest; + context.mavenExecutionRequest = mavenExecutionRequest; } @Override @@ -146,11 +146,11 @@ protected void init(C context) throws Exception { } @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 @@ protected void customizeSettingsResult(C context, SettingsBuildingResult setting } } - 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 @@ protected void toolchains(C localContext) throws Exception { } } 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 @@ protected void toolchains(C localContext) throws Exception { } } 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 @@ protected void toolchains(C localContext) throws Exception { 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 @@ protected void populateRequest(C context, MavenExecutionRequest request) throws } } - 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 @@ protected String determineReactorFailureBehaviour(C localContext) { } } - 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 @@ protected String determineGlobalChecksumPolicy(C localContext) { } } - 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 @@ protected String determineMakeBehavior(C localContext) { } } - 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 optionValues = mavenOptions.projects().get(); @@ -416,8 +412,8 @@ protected void performProjectActivation(C localContext, ProjectActivation projec } } - 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 optionValues = mavenOptions.activatedProfiles().get(); @@ -444,62 +440,61 @@ protected void performProfileActivation(C localContext, ProfileActivation profil } } - 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); - - MavenExecutionResult result = localContext.maven.execute(request); + context.eventSpyDispatcher.onEvent(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 references = new LinkedHashMap<>(); - List 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 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 sortedProjects = result.getTopologicallySortedProjects(); @@ -509,12 +504,12 @@ protected int doExecute(C localContext) throws Exception { 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 @@ protected int doExecute(C localContext) throws Exception { } } - 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 @@ protected String getResumeFromSelector(List mavenProjects, MavenPr protected static final String ANSI_RESET = "\u001B\u005Bm"; - protected void logSummary(C localContext, ExceptionSummary summary, Map references, String indent) { + protected void logSummary(C context, ExceptionSummary summary, Map references, String indent) { String referenceKey = ""; if (summary.getReference() != null && !summary.getReference().isEmpty()) { @@ -607,11 +602,11 @@ protected void logSummary(C localContext, ExceptionSummary summary, Map extends BaseInvo @SuppressWarnings("ParameterNumber") public DefaultMavenInvokerRequest( - String command, + ParserRequest parserRequest, Path cwd, Path installationDirectory, Path userHomeDirectory, Map userProperties, Map systemProperties, - Logger logger, - MessageBuilderFactory messageBuilderFactory, Path topDirectory, Path rootDirectory, InputStream in, @@ -61,14 +58,12 @@ public DefaultMavenInvokerRequest( List coreExtensions, O options) { super( - command, + parserRequest, cwd, installationDirectory, userHomeDirectory, userProperties, systemProperties, - logger, - messageBuilderFactory, topDirectory, rootDirectory, in, diff --git a/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/DefaultMavenParser.java b/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/DefaultMavenParser.java index 45f673c5e05b..49f3f5674775 100644 --- a/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/DefaultMavenParser.java +++ b/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/DefaultMavenParser.java @@ -53,7 +53,7 @@ protected abstract R getInvokerRequest( Options options); @Override - protected List parseCliOptions(Path rootDirectory, String[] args) throws ParserException, IOException { + protected List parseCliOptions(Path rootDirectory, List args) throws ParserException, IOException { ArrayList result = new ArrayList<>(); // CLI args result.add(parseMavenCliOptions(args)); @@ -65,14 +65,14 @@ protected List parseCliOptions(Path rootDirectory, String[] args) throws Pars return result; } - protected O parseMavenCliOptions(String[] args) throws ParserException { + protected O parseMavenCliOptions(List args) throws ParserException { return parseArgs(Options.SOURCE_CLI, args); } protected O parseMavenConfigOptions(Path configFile) throws ParserException, IOException { try (Stream lines = Files.lines(configFile, Charset.defaultCharset())) { - String[] args = - lines.filter(arg -> !arg.isEmpty() && !arg.startsWith("#")).toArray(String[]::new); + List 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 @@ protected O parseMavenConfigOptions(Path configFile) throws ParserException, IOE } } - protected abstract O parseArgs(String source, String[] args) throws ParserException; + protected abstract O parseArgs(String source, List args) throws ParserException; } diff --git a/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/forked/DefaultForkedMavenInvoker.java b/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/forked/DefaultForkedMavenInvoker.java index 2ec1e556a567..bd303fa6736f 100644 --- a/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/forked/DefaultForkedMavenInvoker.java +++ b/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/forked/DefaultForkedMavenInvoker.java @@ -35,6 +35,7 @@ * 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 int invoke(ForkedMavenInvokerRequest invokerRequest) throws InvokerExcept 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 int invoke(ForkedMavenInvokerRequest invokerRequest) throws InvokerExcept cmdAndArguments.add("-D" + entry.getKey() + "=" + entry.getValue()); } } - if (mavenOptions.alternatePomFile().isPresent()) { - cmdAndArguments.add("-f"); - cmdAndArguments.add(mavenOptions.alternatePomFile().get()); - } - if (mavenOptions.offline().orElse(false)) { - cmdAndArguments.add("-o"); - } if (mavenOptions.showVersionAndExit().orElse(false)) { - cmdAndArguments.add("-v"); + cmdAndArguments.add("--version"); } if (mavenOptions.showVersion().orElse(false)) { - cmdAndArguments.add("-V"); + cmdAndArguments.add("--show-version"); } if (mavenOptions.quiet().orElse(false)) { - cmdAndArguments.add("-q"); + cmdAndArguments.add("--quiet"); } if (mavenOptions.verbose().orElse(false)) { - cmdAndArguments.add("-X"); + cmdAndArguments.add("--verbose"); } if (mavenOptions.showErrors().orElse(false)) { - cmdAndArguments.add("-e"); + cmdAndArguments.add("--errors"); } - if (mavenOptions.nonRecursive().orElse(false)) { - cmdAndArguments.add("-N"); - } - if (mavenOptions.updateSnapshots().orElse(false)) { - cmdAndArguments.add("-U"); + if (mavenOptions.failOnSeverity().isPresent()) { + cmdAndArguments.add("--fail-on-severity"); + cmdAndArguments.add(mavenOptions.failOnSeverity().get()); } if (mavenOptions.nonInteractive().orElse(false)) { - cmdAndArguments.add("-B"); + 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("-l"); + cmdAndArguments.add("--log-file"); cmdAndArguments.add(mavenOptions.logFile().get()); } - // TODO: etc + 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("--file"); + cmdAndArguments.add(mavenOptions.alternatePomFile().get()); + } + if (mavenOptions.offline().orElse(false)) { + cmdAndArguments.add("--offline"); + } + if (mavenOptions.nonRecursive().orElse(false)) { + cmdAndArguments.add("--non-recursive"); + } + if (mavenOptions.updateSnapshots().orElse(false)) { + cmdAndArguments.add("--update-snapshots"); + } + if (mavenOptions.activatedProfiles().isPresent()) { + cmdAndArguments.add("--activate-profiles"); + cmdAndArguments.add( + String.join(",", mavenOptions.activatedProfiles().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"); + } // last the goals cmdAndArguments.addAll(mavenOptions.goals().orElse(Collections.emptyList())); diff --git a/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/forked/DefaultForkedMavenInvokerRequest.java b/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/forked/DefaultForkedMavenInvokerRequest.java index b08bcd7ab5f0..d62c2b77b4e2 100644 --- a/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/forked/DefaultForkedMavenInvokerRequest.java +++ b/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/forked/DefaultForkedMavenInvokerRequest.java @@ -25,11 +25,10 @@ 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 userProperties, Map systemProperties, - Logger logger, - MessageBuilderFactory messageBuilderFactory, Path topDirectory, Path rootDirectory, InputStream in, @@ -58,14 +55,12 @@ public DefaultForkedMavenInvokerRequest( List jvmArguments, MavenOptions options) { super( - command, + parserRequest, cwd, installationDirectory, userHomeDirectory, userProperties, systemProperties, - logger, - messageBuilderFactory, topDirectory, rootDirectory, in, @@ -78,6 +73,6 @@ public DefaultForkedMavenInvokerRequest( @Override public Optional> jvmArguments() { - return Optional.of(jvmArguments); + return Optional.ofNullable(jvmArguments); } } diff --git a/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/forked/DefaultForkedMavenParser.java b/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/forked/DefaultForkedMavenParser.java index c1d873f6b10d..194e6841b4e8 100644 --- a/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/forked/DefaultForkedMavenParser.java +++ b/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/forked/DefaultForkedMavenParser.java @@ -20,7 +20,6 @@ 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 @@ protected ForkedMavenInvokerRequest getInvokerRequest( ArrayList 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 @@ protected ForkedMavenInvokerRequest getInvokerRequest( protected List 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 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()); } diff --git a/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/local/DefaultLocalMavenParser.java b/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/local/DefaultLocalMavenParser.java index 028f124f9bfb..d3fde3193c7d 100644 --- a/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/local/DefaultLocalMavenParser.java +++ b/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/local/DefaultLocalMavenParser.java @@ -52,14 +52,12 @@ protected DefaultMavenInvokerRequest getInvokerRequest( ArrayList 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 @@ protected DefaultMavenInvokerRequest getInvokerRequest( } @Override - protected MavenOptions parseArgs(String source, String[] args) throws ParserException { + protected MavenOptions parseArgs(String source, List 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()); } diff --git a/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/resident/DefaultResidentMavenInvoker.java b/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/resident/DefaultResidentMavenInvoker.java index 66f7f852be80..4063d0de833c 100644 --- a/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/resident/DefaultResidentMavenInvoker.java +++ b/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/resident/DefaultResidentMavenInvoker.java @@ -54,7 +54,7 @@ public void shutDown() throws InvokerException { super.close(); } - public LocalContext createShadow() { + public LocalContext copy(ResidentMavenInvokerRequest invokerRequest) { LocalContext shadow = new LocalContext((DefaultResidentMavenInvoker) invoker, invokerRequest); shadow.logger = logger; @@ -122,10 +122,12 @@ protected LocalContext createContext(ResidentMavenInvokerRequest invokerRequest) 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"; } diff --git a/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/resident/DefaultResidentMavenInvokerRequest.java b/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/resident/DefaultResidentMavenInvokerRequest.java index 744003467d09..5d2dc60bbc13 100644 --- a/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/resident/DefaultResidentMavenInvokerRequest.java +++ b/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/resident/DefaultResidentMavenInvokerRequest.java @@ -25,11 +25,10 @@ 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 userProperties, Map systemProperties, - Logger logger, - MessageBuilderFactory messageBuilderFactory, Path topDirectory, Path rootDirectory, InputStream in, @@ -58,14 +55,12 @@ public DefaultResidentMavenInvokerRequest( List jvmArguments, ResidentMavenOptions options) { super( - command, + parserRequest, cwd, installationDirectory, userHomeDirectory, userProperties, systemProperties, - logger, - messageBuilderFactory, topDirectory, rootDirectory, in, diff --git a/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/resident/DefaultResidentMavenParser.java b/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/resident/DefaultResidentMavenParser.java index c9876dcf208d..affe9c32c442 100644 --- a/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/resident/DefaultResidentMavenParser.java +++ b/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/resident/DefaultResidentMavenParser.java @@ -51,14 +51,12 @@ protected DefaultResidentMavenInvokerRequest getInvokerRequest( ArrayList 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 @@ protected List getJvmArguments(Path rootDirectory) { } @Override - protected ResidentMavenOptions parseArgs(String source, String[] args) throws ParserException { + protected ResidentMavenOptions parseArgs(String source, List 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()); } diff --git a/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/DefaultEncryptInvokerRequest.java b/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/DefaultEncryptInvokerRequest.java index 2ecddac8ca3d..de8b52eb6f7c 100644 --- a/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/DefaultEncryptInvokerRequest.java +++ b/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/DefaultEncryptInvokerRequest.java @@ -25,12 +25,11 @@ 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 userProperties, Map systemProperties, - Logger logger, - MessageBuilderFactory messageBuilderFactory, Path topDirectory, Path rootDirectory, InputStream in, @@ -56,14 +53,12 @@ public DefaultEncryptInvokerRequest( List coreExtensions, Options options) { super( - command, + parserRequest, cwd, installationDirectory, userHomeDirectory, userProperties, systemProperties, - logger, - messageBuilderFactory, topDirectory, rootDirectory, in, diff --git a/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/DefaultEncryptParser.java b/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/DefaultEncryptParser.java index bcaeb48fe427..4032bc756866 100644 --- a/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/DefaultEncryptParser.java +++ b/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/DefaultEncryptParser.java @@ -49,14 +49,12 @@ protected EncryptInvokerRequest getInvokerRequest( ArrayList 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 @@ protected EncryptInvokerRequest getInvokerRequest( } @Override - protected List parseCliOptions(Path rootDirectory, String[] args) throws ParserException { + protected List parseCliOptions(Path rootDirectory, List args) throws ParserException { return Collections.singletonList(parseEncryptCliOptions(args)); } - protected CommonsCliEncryptOptions parseEncryptCliOptions(String[] args) throws ParserException { + protected CommonsCliEncryptOptions parseEncryptCliOptions(List 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); } diff --git a/maven-cli/src/test/java/org/apache/maven/cling/invoker/forked/DefaultForkedMavenInvokerTest.java b/maven-cli/src/test/java/org/apache/maven/cling/invoker/forked/DefaultForkedMavenInvokerTest.java deleted file mode 100644 index f4c4dc585c7c..000000000000 --- a/maven-cli/src/test/java/org/apache/maven/cling/invoker/forked/DefaultForkedMavenInvokerTest.java +++ /dev/null @@ -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)); - } - } -} diff --git a/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTestSupport.java b/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTestSupport.java new file mode 100644 index 000000000000..da6b4deb0be4 --- /dev/null +++ b/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTestSupport.java @@ -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> { + + protected void invoke(Path cwd, Collection 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 = + """ + + + + 4.0.0 + + org.apache.maven.samples + sample + 1.0.0 + + + + + org.junit + junit-bom + 5.11.1 + pom + import + + + + + + + org.junit.jupiter + junit-jupiter-api + test + + + + + """; + 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 parser = createParser(); + try (Invoker 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 createInvoker(); + + protected abstract Parser createParser(); +} diff --git a/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/forked/DefaultForkedMavenInvokerTest.java b/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/forked/DefaultForkedMavenInvokerTest.java new file mode 100644 index 000000000000..f03c161d4c9e --- /dev/null +++ b/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/forked/DefaultForkedMavenInvokerTest.java @@ -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 { + + @Override + protected Invoker createInvoker() { + return new DefaultForkedMavenInvoker(); + } + + @Override + protected Parser createParser() { + return new DefaultForkedMavenParser(); + } + + @Test + void defaultFs(@TempDir(cleanup = CleanupMode.ON_SUCCESS) Path tempDir) throws Exception { + invoke(tempDir, Arrays.asList("clean", "verify")); + } +} diff --git a/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/local/DefaultLocalMavenInvokerTest.java b/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/local/DefaultLocalMavenInvokerTest.java new file mode 100644 index 000000000000..96fed74d35fc --- /dev/null +++ b/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/local/DefaultLocalMavenInvokerTest.java @@ -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> { + @Override + protected Invoker> createInvoker() { + return new DefaultLocalMavenInvoker(ProtoLookup.builder() + .addMapping(ClassWorld.class, new ClassWorld("plexus.core", ClassLoader.getSystemClassLoader())) + .build()); + } + + @Override + protected Parser> 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")); + } + } +} diff --git a/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/resident/DefaultResidentMavenInvokerTest.java b/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/resident/DefaultResidentMavenInvokerTest.java index feb1ecea7089..f90794a66ab0 100644 --- a/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/resident/DefaultResidentMavenInvokerTest.java +++ b/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/resident/DefaultResidentMavenInvokerTest.java @@ -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 { - @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(); - - String pomString = - """ - - - - 4.0.0 - - org.apache.maven.samples - resident-invoker - 1.0.0 - - - - - org.junit - junit-bom - 5.11.1 - pom - import - - - - - - - - org.junit.jupiter - junit-jupiter-api - test - - - +/** + * Resident UT. + */ +public class DefaultResidentMavenInvokerTest + extends MavenInvokerTestSupport { - - """; - Path pom = tempDir.resolve("pom.xml").toAbsolutePath(); - Files.writeString(pom, pomString); + @Override + protected Invoker createInvoker() { + return new DefaultResidentMavenInvoker(ProtoLookup.builder() + .addMapping(ClassWorld.class, new ClassWorld("plexus.core", ClassLoader.getSystemClassLoader())) + .build()); + } - int exitCode; + @Override + protected Parser createParser() { + return new DefaultResidentMavenParser(); + } - 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)); + @Test + void defaultFs(@TempDir(cleanup = CleanupMode.ON_SUCCESS) Path tempDir) throws Exception { + invoke(tempDir, Arrays.asList("clean", "verify")); + } - 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")); } } }