[MNG-8334] Fix output redirection (#1826)

This commit is contained in:
Guillaume Nodet 2024-10-22 16:53:58 +02:00 committed by GitHub
parent 867641c9ea
commit 3425b4dd08
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 110 additions and 65 deletions

View File

@ -18,10 +18,10 @@
*/
package org.apache.maven.api.cli;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;
import org.apache.maven.api.annotations.Experimental;
import org.apache.maven.api.annotations.Nonnull;
@ -202,14 +202,14 @@ public interface Options {
/**
* Emits warning messages if deprecated options are used.
*
* @param printWriter the PrintWriter to use for output
* @param printWriter the string consumer to use for output
*/
default void warnAboutDeprecatedOptions(@Nonnull ParserRequest request, @Nonnull PrintWriter printWriter) {}
default void warnAboutDeprecatedOptions(@Nonnull ParserRequest request, @Nonnull Consumer<String> printWriter) {}
/**
* Displays help information for these options.
*
* @param printWriter the PrintWriter to use for output
* @param printWriter the string consumer to use for output
*/
void displayHelp(@Nonnull ParserRequest request, @Nonnull PrintWriter printWriter);
void displayHelp(@Nonnull ParserRequest request, @Nonnull Consumer<String> printWriter);
}

View File

@ -111,8 +111,8 @@ public abstract class BaseParser<O extends Options, R extends InvokerRequest<O>>
List<O> parsedOptions = parseCliOptions(context);
// warn about deprecated options
parsedOptions.forEach(o -> o.warnAboutDeprecatedOptions(
parserRequest, new PrintWriter(parserRequest.out() != null ? parserRequest.out() : System.out, true)));
PrintWriter printWriter = new PrintWriter(parserRequest.out() != null ? parserRequest.out() : System.out, true);
parsedOptions.forEach(o -> o.warnAboutDeprecatedOptions(parserRequest, printWriter::println));
// assemble options if needed
context.options = assembleOptions(parsedOptions);

View File

@ -19,10 +19,12 @@
package org.apache.maven.cling.invoker;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.DefaultParser;
@ -89,7 +91,7 @@ public abstract class CommonsCliOptions implements Options {
@Override
public Optional<Boolean> verbose() {
if (commandLine.hasOption(CLIManager.VERBOSE) || commandLine.hasOption(CLIManager.DEBUG)) {
if (commandLine.hasOption(CLIManager.VERBOSE)) {
return Optional.of(Boolean.TRUE);
}
return Optional.empty();
@ -210,11 +212,11 @@ public abstract class CommonsCliOptions implements Options {
}
@Override
public void warnAboutDeprecatedOptions(ParserRequest request, PrintWriter printWriter) {
public void warnAboutDeprecatedOptions(ParserRequest request, Consumer<String> printWriter) {
if (cliManager.getUsedDeprecatedOptions().isEmpty()) {
return;
}
printWriter.println("Detected deprecated option use in " + source);
printWriter.accept("Detected deprecated option use in " + source);
for (Option option : cliManager.getUsedDeprecatedOptions()) {
StringBuilder sb = new StringBuilder();
sb.append("The option -").append(option.getOpt());
@ -231,12 +233,12 @@ public abstract class CommonsCliOptions implements Options {
.append(" ")
.append(option.getDeprecated().getSince());
}
printWriter.println(sb);
printWriter.accept(sb.toString());
}
}
@Override
public void displayHelp(ParserRequest request, PrintWriter printStream) {
public void displayHelp(ParserRequest request, Consumer<String> printStream) {
cliManager.displayHelp(request.command(), printStream);
}
@ -429,7 +431,7 @@ public abstract class CommonsCliOptions implements Options {
return usedDeprecatedOptions;
}
public void displayHelp(String command, PrintWriter pw) {
public void displayHelp(String command, Consumer<String> pw) {
HelpFormatter formatter = new HelpFormatter();
int width = MessageUtils.getTerminalWidth();
@ -437,10 +439,12 @@ public abstract class CommonsCliOptions implements Options {
width = HelpFormatter.DEFAULT_WIDTH;
}
pw.println();
pw.accept("");
StringWriter sw = new StringWriter();
PrintWriter pw2 = new PrintWriter(sw);
formatter.printHelp(
pw,
pw2,
width,
commandLineSyntax(command),
System.lineSeparator() + "Options:",
@ -449,8 +453,10 @@ public abstract class CommonsCliOptions implements Options {
HelpFormatter.DEFAULT_DESC_PAD,
System.lineSeparator(),
false);
pw.flush();
pw2.flush();
for (String s : sw.toString().split(System.lineSeparator())) {
pw.accept(s);
}
}
protected String commandLineSyntax(String command) {

View File

@ -18,12 +18,12 @@
*/
package org.apache.maven.cling.invoker;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import org.apache.maven.api.cli.Options;
@ -138,10 +138,10 @@ public abstract class LayeredOptions<O extends Options> implements Options {
}
@Override
public void warnAboutDeprecatedOptions(ParserRequest request, PrintWriter printWriter) {}
public void warnAboutDeprecatedOptions(ParserRequest request, Consumer<String> printWriter) {}
@Override
public void displayHelp(ParserRequest request, PrintWriter printWriter) {
public void displayHelp(ParserRequest request, Consumer<String> printWriter) {
options.get(0).displayHelp(request, printWriter);
}

View File

@ -31,6 +31,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.function.Consumer;
import java.util.function.Function;
import org.apache.maven.api.Constants;
@ -71,16 +72,11 @@ import org.apache.maven.cli.transfer.Slf4jMavenTransferListener;
import org.apache.maven.cling.invoker.mvn.ProtoSession;
import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.internal.impl.SettingsUtilsV4;
import org.apache.maven.jline.FastTerminal;
import org.apache.maven.jline.MessageUtils;
import org.apache.maven.logging.BuildEventListener;
import org.apache.maven.logging.LoggingOutputStream;
import org.apache.maven.logging.ProjectBuildLogAppender;
import org.apache.maven.logging.SimpleBuildEventListener;
import org.apache.maven.logging.api.LogLevelRecorder;
import org.apache.maven.slf4j.MavenSimpleLogger;
import org.eclipse.aether.transfer.TransferListener;
import org.jline.jansi.AnsiConsole;
import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;
import org.slf4j.ILoggerFactory;
@ -152,8 +148,9 @@ public abstract class LookupInvoker<
public ILoggerFactory loggerFactory;
public Slf4jConfiguration slf4jConfiguration;
public Slf4jConfiguration.Level loggerLevel;
public Boolean coloredOutput;
public Terminal terminal;
public BuildEventListener buildEventListener;
public Consumer<String> writer;
public ClassLoader currentThreadContextClassLoader;
public ContainerCapsule containerCapsule;
public Lookup lookup;
@ -281,9 +278,9 @@ public abstract class LookupInvoker<
.orElse(userProperties.getOrDefault(
Constants.MAVEN_STYLE_COLOR_PROPERTY, userProperties.getOrDefault("style.color", "auto")));
if ("always".equals(styleColor) || "yes".equals(styleColor) || "force".equals(styleColor)) {
MessageUtils.setColorEnabled(true);
context.coloredOutput = true;
} else if ("never".equals(styleColor) || "no".equals(styleColor) || "none".equals(styleColor)) {
MessageUtils.setColorEnabled(false);
context.coloredOutput = false;
} else if (!"auto".equals(styleColor) && !"tty".equals(styleColor) && !"if-tty".equals(styleColor)) {
throw new IllegalArgumentException(
"Invalid color configuration value '" + styleColor + "'. Supported are 'auto', 'always', 'never'.");
@ -291,7 +288,7 @@ public abstract class LookupInvoker<
boolean isBatchMode = !mavenOptions.forceInteractive().orElse(false)
&& mavenOptions.nonInteractive().orElse(false);
if (isBatchMode || mavenOptions.logFile().isPresent()) {
MessageUtils.setColorEnabled(false);
context.coloredOutput = false;
}
}
@ -312,31 +309,32 @@ public abstract class LookupInvoker<
// so boot it asynchronously
context.terminal = createTerminal(context);
context.closeables.add(MessageUtils::systemUninstall);
// Create the build log appender
ProjectBuildLogAppender projectBuildLogAppender =
new ProjectBuildLogAppender(determineBuildEventListener(context));
context.closeables.add(projectBuildLogAppender);
MessageUtils.registerShutdownHook(); // safety belt
if (context.coloredOutput != null) {
MessageUtils.setColorEnabled(context.coloredOutput);
}
}
protected Terminal createTerminal(C context) {
return new FastTerminal(
() -> TerminalBuilder.builder()
.name("Maven")
.streams(
context.invokerRequest.in().orElse(null),
context.invokerRequest.out().orElse(null))
.dumb(true)
.build(),
MessageUtils.systemInstall(
builder -> {
builder.streams(
context.invokerRequest.in().orElse(null),
context.invokerRequest.out().orElse(null));
builder.systemOutput(TerminalBuilder.SystemOutput.ForcedSysOut);
// The exec builder suffers from https://github.com/jline/jline3/issues/1098
// We could re-enable it when fixed to provide support for non-standard architectures,
// for which JLine does not provide any native library.
builder.exec(false);
if (context.coloredOutput != null) {
builder.color(context.coloredOutput);
}
},
terminal -> doConfigureWithTerminal(context, terminal));
return MessageUtils.getTerminal();
}
protected void doConfigureWithTerminal(C context, Terminal terminal) {
MessageUtils.systemInstall(terminal);
AnsiConsole.setTerminal(terminal);
AnsiConsole.systemInstall();
MessageUtils.registerShutdownHook(); // safety belt
O options = context.invokerRequest.options();
if (options.rawStreams().isEmpty() || !options.rawStreams().get()) {
MavenSimpleLogger stdout = (MavenSimpleLogger) context.loggerFactory.getLogger("stdout");
@ -349,34 +347,33 @@ public abstract class LookupInvoker<
}
}
protected BuildEventListener determineBuildEventListener(C context) {
if (context.buildEventListener == null) {
context.buildEventListener = doDetermineBuildEventListener(context);
protected Consumer<String> determineWriter(C context) {
if (context.writer == null) {
context.writer = doDetermineWriter(context);
}
return context.buildEventListener;
return context.writer;
}
protected BuildEventListener doDetermineBuildEventListener(C context) {
BuildEventListener bel;
protected Consumer<String> doDetermineWriter(C context) {
O options = context.invokerRequest.options();
if (options.logFile().isPresent()) {
Path logFile = context.cwdResolver.apply(options.logFile().get());
try {
PrintWriter printWriter = new PrintWriter(Files.newBufferedWriter(logFile));
bel = new SimpleBuildEventListener(printWriter::println);
context.closeables.add(printWriter);
return printWriter::println;
} catch (IOException e) {
throw new MavenException("Unable to redirect logging to " + logFile, e);
}
} else {
// Given the terminal creation has been offloaded to a different thread,
// do not pass directory the terminal writer
bel = new SimpleBuildEventListener(msg -> {
// do not pass directly the terminal writer
return msg -> {
PrintWriter pw = context.terminal.writer();
pw.println(msg);
pw.flush();
});
};
}
return bel;
}
protected void activateLogging(C context) throws Exception {
@ -412,19 +409,18 @@ public abstract class LookupInvoker<
}
protected void helpOrVersionAndMayExit(C context) throws Exception {
Consumer<String> writer = determineWriter(context);
R invokerRequest = context.invokerRequest;
if (invokerRequest.options().help().isPresent()) {
invokerRequest.options().displayHelp(context.invokerRequest.parserRequest(), context.terminal.writer());
context.terminal.writer().flush();
invokerRequest.options().displayHelp(context.invokerRequest.parserRequest(), writer);
throw new ExitException(0);
}
if (invokerRequest.options().showVersionAndExit().isPresent()) {
if (invokerRequest.options().quiet().orElse(false)) {
context.terminal.writer().println(CLIReportingUtils.showVersionMinimal());
writer.accept(CLIReportingUtils.showVersionMinimal());
} else {
context.terminal.writer().println(CLIReportingUtils.showVersion());
writer.accept(CLIReportingUtils.showVersion());
}
context.terminal.writer().flush();
throw new ExitException(0);
}
}
@ -432,7 +428,7 @@ public abstract class LookupInvoker<
protected void preCommands(C context) throws Exception {
Options mavenOptions = context.invokerRequest.options();
if (mavenOptions.verbose().orElse(false) || mavenOptions.showVersion().orElse(false)) {
context.terminal.writer().println(CLIReportingUtils.showVersion());
determineWriter(context).accept(CLIReportingUtils.showVersion());
}
}

View File

@ -27,6 +27,7 @@ import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -63,8 +64,11 @@ import org.apache.maven.execution.ProfileActivation;
import org.apache.maven.execution.ProjectActivation;
import org.apache.maven.jline.MessageUtils;
import org.apache.maven.lifecycle.LifecycleExecutionException;
import org.apache.maven.logging.BuildEventListener;
import org.apache.maven.logging.LoggingExecutionListener;
import org.apache.maven.logging.MavenTransferListener;
import org.apache.maven.logging.ProjectBuildLogAppender;
import org.apache.maven.logging.SimpleBuildEventListener;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.PlexusContainer;
import org.eclipse.aether.DefaultRepositoryCache;
@ -89,6 +93,7 @@ public abstract class DefaultMavenInvoker<
super(invoker, invokerRequest);
}
public BuildEventListener buildEventListener;
public MavenExecutionRequest mavenExecutionRequest;
public EventSpyDispatcher eventSpyDispatcher;
public MavenExecutionRequestPopulator mavenExecutionRequestPopulator;
@ -161,6 +166,27 @@ public abstract class DefaultMavenInvoker<
}
}
@Override
protected void configureLogging(C context) throws Exception {
super.configureLogging(context);
// Create the build log appender
ProjectBuildLogAppender projectBuildLogAppender =
new ProjectBuildLogAppender(determineBuildEventListener(context));
context.closeables.add(projectBuildLogAppender);
}
protected BuildEventListener determineBuildEventListener(C context) {
if (context.buildEventListener == null) {
context.buildEventListener = doDetermineBuildEventListener(context);
}
return context.buildEventListener;
}
protected BuildEventListener doDetermineBuildEventListener(C context) {
Consumer<String> writer = determineWriter(context);
return new SimpleBuildEventListener(writer);
}
@Override
protected void customizeSettingsRequest(C context, SettingsBuilderRequest settingsBuilderRequest) {
if (context.eventSpyDispatcher != null) {

View File

@ -18,6 +18,8 @@
*/
package org.apache.maven.jline;
import java.util.function.Consumer;
import org.apache.maven.api.services.MessageBuilder;
import org.apache.maven.api.services.MessageBuilderFactory;
import org.jline.jansi.AnsiConsole;
@ -41,11 +43,26 @@ public class MessageUtils {
}
public static void systemInstall() {
systemInstall(null, null);
}
public static void systemInstall(Consumer<TerminalBuilder> builderConsumer, Consumer<Terminal> terminalConsumer) {
MessageUtils.terminal = new FastTerminal(
() -> TerminalBuilder.builder().name("Maven").dumb(true).build(), terminal -> {
() -> {
TerminalBuilder builder =
TerminalBuilder.builder().name("Maven").dumb(true);
if (builderConsumer != null) {
builderConsumer.accept(builder);
}
return builder.build();
},
terminal -> {
MessageUtils.reader = createReader(terminal);
AnsiConsole.setTerminal(terminal);
AnsiConsole.systemInstall();
if (terminalConsumer != null) {
terminalConsumer.accept(terminal);
}
});
}