Upgrade to 4.0.0-rc-1 (fixes #1203 and #1204)

# Conflicts:
#	pom.xml
This commit is contained in:
Guillaume Nodet
2024-11-07 15:32:34 +01:00
parent 514cf3de02
commit dade78541c
15 changed files with 79 additions and 276 deletions

View File

@@ -18,35 +18,25 @@
*/
package org.apache.maven.cli;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.maven.cling.invoker.mvn.CommonsCliMavenOptions;
import org.apache.maven.jline.MessageUtils;
import org.codehaus.plexus.interpolation.BasicInterpolator;
import org.codehaus.plexus.interpolation.InterpolationException;
import org.mvndaemon.mvnd.common.Environment;
import org.mvndaemon.mvnd.common.OptionType;
import static org.apache.maven.cling.invoker.Utils.createInterpolator;
public class CommonsCliDaemonMavenOptions extends CommonsCliMavenOptions implements DaemonMavenOptions {
public class CommonsCliDaemonMavenOptions extends CommonsCliMavenOptions {
public static CommonsCliDaemonMavenOptions parse(String source, String[] args) throws ParseException {
CLIManager cliManager = new CLIManager();
return new CommonsCliDaemonMavenOptions(source, cliManager, cliManager.parse(args));
@@ -56,44 +46,7 @@ public class CommonsCliDaemonMavenOptions extends CommonsCliMavenOptions impleme
super(source, cliManager, commandLine);
}
public org.apache.commons.cli.Options getOptions() {
return this.cliManager.getOptions();
}
private static CommonsCliDaemonMavenOptions interpolate(
CommonsCliDaemonMavenOptions options, Collection<Map<String, String>> properties) {
try {
// now that we have properties, interpolate all arguments
BasicInterpolator interpolator = createInterpolator(properties);
CommandLine.Builder commandLineBuilder = new CommandLine.Builder();
commandLineBuilder.setDeprecatedHandler(o -> {});
for (Option option : options.commandLine.getOptions()) {
if (!CLIManager.USER_PROPERTY.equals(option.getOpt())) {
List<String> values = option.getValuesList();
for (ListIterator<String> it = values.listIterator(); it.hasNext(); ) {
it.set(interpolator.interpolate(it.next()));
}
}
commandLineBuilder.addOption(option);
}
for (String arg : options.commandLine.getArgList()) {
commandLineBuilder.addArg(interpolator.interpolate(arg));
}
return new CommonsCliDaemonMavenOptions(
options.source, (CLIManager) options.cliManager, commandLineBuilder.build());
} catch (InterpolationException e) {
throw new IllegalArgumentException("Could not interpolate CommonsCliOptions", e);
}
}
@Override
public DaemonMavenOptions interpolate(Collection<Map<String, String>> properties) {
return interpolate(this, properties);
}
protected static class CLIManager extends CommonsCliMavenOptions.CLIManager {
public static final String RAW_STREAMS = "ras";
private static final Pattern HTML_TAGS_PATTERN = Pattern.compile("<[^>]*>");
private static final Pattern COLUMNS_DETECTOR_PATTERN = Pattern.compile("^[ ]+[^s]");
private static final Pattern WS_PATTERN = Pattern.compile("\\s+");
@@ -103,28 +56,15 @@ public class CommonsCliDaemonMavenOptions extends CommonsCliMavenOptions impleme
}
@Override
protected void prepareOptions(Options options) {
super.prepareOptions(options);
options.addOption(Option.builder(RAW_STREAMS)
.longOpt("raw-streams")
.desc("Use raw-streams for daemon communication")
.build());
}
@Override
public void displayHelp(String command, PrintWriter pw) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (PrintWriter pww = new PrintWriter(new PrintStream(baos), true, StandardCharsets.UTF_8)) {
super.displayHelp(command, pww);
}
final String mvnHelp = baos.toString(StandardCharsets.UTF_8);
final Matcher m = COLUMNS_DETECTOR_PATTERN.matcher(mvnHelp);
public void displayHelp(String command, Consumer<String> pw) {
List<String> mvnHelp = new ArrayList<>();
super.displayHelp(command, mvnHelp::add);
final Matcher m = COLUMNS_DETECTOR_PATTERN.matcher(String.join("\n", mvnHelp));
final String indent = m.find() ? m.group() : " ";
final int terminalWidth = getTerminalWidth();
int terminalWidth = getTerminalWidth() <= 0 ? 74 : getTerminalWidth();
final String lineSeparator = System.lineSeparator();
final StringBuilder help =
new StringBuilder(mvnHelp).append(lineSeparator).append("mvnd specific options:");
final StringBuilder help = new StringBuilder().append(lineSeparator).append("mvnd specific options:");
Environment.documentedEntries().forEach(entry -> {
final Environment env = entry.getEntry();
@@ -190,9 +130,8 @@ public class CommonsCliDaemonMavenOptions extends CommonsCliMavenOptions impleme
spaces(help, indentPos - help.length());
wrap(help, toPlainText(entry.getJavaDoc()), terminalWidth, lineEnd, indent);
});
pw.print(help);
pw.flush();
Stream.concat(mvnHelp.stream(), Stream.of(help.toString().split(lineSeparator)))
.forEach(pw);
}
private static int getTerminalWidth() {

View File

@@ -72,7 +72,7 @@ public class DaemonMavenCling implements DaemonCli {
EnvHelper.environment(workingDir, env);
System.setProperty("maven.multiModuleProjectDirectory", projectDir);
return invoker.invoke(parser.parse(ParserRequest.builder(
return invoker.invoke(parser.parseInvocation(ParserRequest.builder(
"mvnd", "Maven Daemon", args, new ProtoLogger(), new DaemonMessageBuilderFactory())
.cwd(Paths.get(workingDir))
.in(in)

View File

@@ -18,68 +18,60 @@
*/
package org.apache.maven.cli;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
import org.apache.maven.api.cli.InvokerException;
import org.apache.maven.api.cli.InvokerRequest;
import org.apache.maven.api.cli.Options;
import org.apache.maven.api.cli.mvn.MavenInvokerRequest;
import org.apache.maven.api.cli.mvn.MavenOptions;
import org.apache.maven.api.services.MavenException;
import org.apache.maven.cling.invoker.ContainerCapsuleFactory;
import org.apache.maven.cling.invoker.ProtoLookup;
import org.apache.maven.cling.invoker.mvn.resident.DefaultResidentMavenInvoker;
import org.apache.maven.cling.invoker.mvn.resident.ResidentMavenContext;
import org.apache.maven.cling.invoker.mvn.resident.ResidentMavenInvoker;
import org.apache.maven.cling.utils.CLIReportingUtils;
import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.jline.MessageUtils;
import org.apache.maven.logging.BuildEventListener;
import org.apache.maven.logging.LoggingOutputStream;
import org.jline.terminal.Terminal;
import org.jline.terminal.impl.ExternalTerminal;
import org.jline.terminal.TerminalBuilder;
import org.mvndaemon.mvnd.common.Environment;
public class DaemonMavenInvoker extends DefaultResidentMavenInvoker {
public class DaemonMavenInvoker extends ResidentMavenInvoker {
public DaemonMavenInvoker(ProtoLookup protoLookup) {
super(protoLookup);
}
// TODO: this is a hack, and fixes issue in DefaultResidentMavenInvoker that does not copy TCCL
private ClassLoader tccl;
protected int doInvoke(LocalContext context) throws Exception {
try {
if (tccl != null) {
context.currentThreadContextClassLoader = tccl;
Thread.currentThread().setContextClassLoader(context.currentThreadContextClassLoader);
}
return super.doInvoke(context);
} finally {
this.tccl = context.currentThreadContextClassLoader;
@Override
protected void createTerminal(ResidentMavenContext context) {
MessageUtils.systemInstall(
builder -> {
builder.streams(
context.invokerRequest.in().orElseThrow(),
context.invokerRequest.out().orElseThrow());
builder.systemOutput(TerminalBuilder.SystemOutput.ForcedSysOut);
builder.provider(TerminalBuilder.PROP_PROVIDER_EXEC);
if (context.coloredOutput != null) {
builder.color(context.coloredOutput);
}
},
terminal -> doConfigureWithTerminal(context, terminal));
context.terminal = MessageUtils.getTerminal();
context.closeables.add(MessageUtils::systemUninstall);
MessageUtils.registerShutdownHook();
if (context.coloredOutput != null) {
MessageUtils.setColorEnabled(context.coloredOutput);
}
}
@Override
protected Terminal createTerminal(LocalContext context) {
try {
Terminal terminal = new ExternalTerminal(
"Maven",
"ansi",
context.invokerRequest.in().get(),
context.invokerRequest.out().get(),
StandardCharsets.UTF_8);
doConfigureWithTerminal(context, terminal);
// If raw-streams options has been set, we need to decorate to push back to the client
if (context.invokerRequest.options().rawStreams().orElse(false)) {
BuildEventListener bel = determineBuildEventListener(context);
OutputStream out = context.invokerRequest.out().orElse(null);
System.setOut(out != null ? printStream(out) : new LoggingOutputStream(bel::log).printStream());
OutputStream err = context.invokerRequest.err().orElse(null);
System.setErr(err != null ? printStream(err) : new LoggingOutputStream(bel::log).printStream());
}
return terminal;
} catch (IOException e) {
throw new MavenException("Error creating terminal", e);
protected void doConfigureWithTerminal(ResidentMavenContext context, Terminal terminal) {
super.doConfigureWithTerminal(context, terminal);
Optional<Boolean> rawStreams = context.invokerRequest.options().rawStreams();
if (rawStreams.orElse(false)) {
System.setOut(printStream(context.invokerRequest.out().orElseThrow()));
System.setErr(printStream(context.invokerRequest.err().orElseThrow()));
}
}
@@ -94,22 +86,18 @@ public class DaemonMavenInvoker extends DefaultResidentMavenInvoker {
}
@Override
protected org.apache.maven.logging.BuildEventListener doDetermineBuildEventListener(LocalContext context) {
protected org.apache.maven.logging.BuildEventListener doDetermineBuildEventListener(ResidentMavenContext context) {
return context.invokerRequest.lookup().lookup(BuildEventListener.class);
}
@Override
protected void helpOrVersionAndMayExit(LocalContext context) throws Exception {
MavenInvokerRequest<MavenOptions> invokerRequest = context.invokerRequest;
protected void helpOrVersionAndMayExit(ResidentMavenContext context) throws Exception {
InvokerRequest invokerRequest = context.invokerRequest;
BuildEventListener buildEventListener =
context.invokerRequest.parserRequest().lookup().lookup(BuildEventListener.class);
if (invokerRequest.options().help().isPresent()) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (PrintWriter pw = new PrintWriter(new PrintStream(baos), true, StandardCharsets.UTF_8)) {
context.invokerRequest.options().displayHelp(invokerRequest.parserRequest(), pw);
}
buildEventListener.log(baos.toString(StandardCharsets.UTF_8));
throw new ExitException(0);
context.invokerRequest.options().displayHelp(invokerRequest.parserRequest(), buildEventListener::log);
throw new InvokerException.ExitException(0);
}
if (invokerRequest.options().showVersionAndExit().isPresent()) {
if (invokerRequest.options().quiet().orElse(false)) {
@@ -117,12 +105,12 @@ public class DaemonMavenInvoker extends DefaultResidentMavenInvoker {
} else {
buildEventListener.log(CLIReportingUtils.showVersion());
}
throw new ExitException(0);
throw new InvokerException.ExitException(0);
}
}
@Override
protected void preCommands(LocalContext context) throws Exception {
protected void preCommands(ResidentMavenContext context) throws Exception {
Options mavenOptions = context.invokerRequest.options();
if (mavenOptions.verbose().orElse(false) || mavenOptions.showVersion().orElse(false)) {
context.invokerRequest
@@ -134,26 +122,25 @@ public class DaemonMavenInvoker extends DefaultResidentMavenInvoker {
}
@Override
protected ContainerCapsuleFactory<MavenOptions, MavenInvokerRequest<MavenOptions>, LocalContext>
createContainerCapsuleFactory() {
protected ContainerCapsuleFactory<ResidentMavenContext> createContainerCapsuleFactory() {
return new DaemonPlexusContainerCapsuleFactory();
}
@Override
protected int doExecute(LocalContext context) throws Exception {
protected int doExecute(ResidentMavenContext context, MavenExecutionRequest request) throws Exception {
context.logger.info(MessageUtils.builder()
.a("Processing build on daemon ")
.strong(Environment.MVND_ID.asString())
.toString());
context.logger.info("Daemon status dump:");
context.logger.info("CWD: " + context.invokerRequest.cwd());
context.logger.info("MAVEN_HOME: " + context.invokerRequest.installationDirectory());
context.logger.info("USER_HOME: " + context.invokerRequest.userHomeDirectory());
context.logger.info("topDirectory: " + context.invokerRequest.topDirectory());
context.logger.info("rootDirectory: " + context.invokerRequest.rootDirectory());
context.logger.debug("Daemon status dump:");
context.logger.debug("CWD: " + context.invokerRequest.cwd());
context.logger.debug("MAVEN_HOME: " + context.invokerRequest.installationDirectory());
context.logger.debug("USER_HOME: " + context.invokerRequest.userHomeDirectory());
context.logger.debug("topDirectory: " + context.invokerRequest.topDirectory());
context.logger.debug("rootDirectory: " + context.invokerRequest.rootDirectory());
try {
return super.doExecute(context);
return super.doExecute(context, request);
} finally {
LoggingOutputStream.forceFlush(System.out);
LoggingOutputStream.forceFlush(System.err);

View File

@@ -1,30 +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.cli;
import java.util.Collection;
import java.util.Map;
import org.apache.maven.api.cli.mvn.MavenOptions;
public interface DaemonMavenOptions extends MavenOptions {
@Override
DaemonMavenOptions interpolate(Collection<Map<String, String>> properties);
}

View File

@@ -26,31 +26,11 @@ import java.util.stream.Stream;
import org.apache.commons.cli.ParseException;
import org.apache.maven.api.cli.ParserException;
import org.apache.maven.api.cli.extensions.CoreExtension;
import org.apache.maven.api.cli.mvn.MavenInvokerRequest;
import org.apache.maven.api.cli.mvn.MavenOptions;
import org.apache.maven.cling.invoker.mvn.BaseMavenParser;
import org.apache.maven.cling.invoker.mvn.DefaultMavenInvokerRequest;
import org.apache.maven.cling.invoker.mvn.MavenParser;
import org.mvndaemon.mvnd.common.Environment;
public class DaemonMavenParser extends BaseMavenParser<MavenOptions, MavenInvokerRequest<MavenOptions>> {
@Override
protected DefaultMavenInvokerRequest<MavenOptions> getInvokerRequest(LocalContext context) {
return new DefaultMavenInvokerRequest<>(
context.parserRequest,
context.cwd,
context.installationDirectory,
context.userHomeDirectory,
context.userProperties,
context.systemProperties,
context.topDirectory,
context.rootDirectory,
context.parserRequest.in(),
context.parserRequest.out(),
context.parserRequest.err(),
context.extensions,
(DaemonMavenOptions) context.options);
}
public class DaemonMavenParser extends MavenParser {
@Override
protected MavenOptions parseArgs(String source, List<String> args) throws ParserException {
try {
@@ -60,12 +40,6 @@ public class DaemonMavenParser extends BaseMavenParser<MavenOptions, MavenInvoke
}
}
@Override
protected MavenOptions assembleOptions(List<MavenOptions> parsedOptions) {
return LayeredDaemonMavenOptions.layerDaemonMavenOptions(
parsedOptions.stream().map(o -> (DaemonMavenOptions) o).toList());
}
@Override
protected Map<String, String> populateSystemProperties(LocalContext context) throws ParserException {
HashMap<String, String> systemProperties = new HashMap<>(super.populateSystemProperties(context));

View File

@@ -25,16 +25,12 @@ import java.util.List;
import java.util.Set;
import java.util.stream.Stream;
import org.apache.maven.api.cli.mvn.MavenInvokerRequest;
import org.apache.maven.api.cli.mvn.MavenOptions;
import org.apache.maven.cling.invoker.PlexusContainerCapsuleFactory;
import org.apache.maven.cling.invoker.mvn.resident.DefaultResidentMavenInvoker;
import org.apache.maven.cling.invoker.mvn.resident.ResidentMavenContext;
import org.apache.maven.extension.internal.CoreExtensionEntry;
import org.mvndaemon.mvnd.common.Environment;
public class DaemonPlexusContainerCapsuleFactory
extends PlexusContainerCapsuleFactory<
MavenOptions, MavenInvokerRequest<MavenOptions>, DefaultResidentMavenInvoker.LocalContext> {
public class DaemonPlexusContainerCapsuleFactory extends PlexusContainerCapsuleFactory<ResidentMavenContext> {
@Override
protected Set<String> collectExportedArtifacts(
@@ -54,7 +50,7 @@ public class DaemonPlexusContainerCapsuleFactory
}
@Override
protected List<Path> parseExtClasspath(DefaultResidentMavenInvoker.LocalContext context) throws Exception {
protected List<Path> parseExtClasspath(ResidentMavenContext context) throws Exception {
return Stream.of(Environment.MVND_EXT_CLASSPATH.asString().split(","))
.filter(s -> s != null && !s.isEmpty())
.map(Paths::get)

View File

@@ -1,59 +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.cli;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import org.apache.maven.cling.invoker.mvn.LayeredMavenOptions;
public class LayeredDaemonMavenOptions extends LayeredMavenOptions<DaemonMavenOptions> implements DaemonMavenOptions {
public static DaemonMavenOptions layerDaemonMavenOptions(Collection<DaemonMavenOptions> options) {
List<DaemonMavenOptions> o = options.stream().filter(Objects::nonNull).toList();
if (o.isEmpty()) {
throw new IllegalArgumentException("No options specified (or all were null)");
} else if (o.size() == 1) {
return o.get(0);
} else {
return new LayeredDaemonMavenOptions(o);
}
}
private LayeredDaemonMavenOptions(List<DaemonMavenOptions> options) {
super(options);
}
@Override
public Optional<Boolean> rawStreams() {
return returnFirstPresentOrEmpty(DaemonMavenOptions::rawStreams);
}
@Override
public DaemonMavenOptions interpolate(Collection<Map<String, String>> properties) {
ArrayList<DaemonMavenOptions> interpolatedOptions = new ArrayList<>(options.size());
for (DaemonMavenOptions o : options) {
interpolatedOptions.add(o.interpolate(properties));
}
return layerDaemonMavenOptions(interpolatedOptions);
}
}