This is an automated email from the ASF dual-hosted git repository. gnodet pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/maven.git
The following commit(s) were added to refs/heads/master by this push: new 3425b4dd08 [MNG-8334] Fix output redirection (#1826) 3425b4dd08 is described below commit 3425b4dd08fa4dd416e0e16c39c5dc8c6d80b6cc Author: Guillaume Nodet <gno...@gmail.com> AuthorDate: Tue Oct 22 16:53:58 2024 +0200 [MNG-8334] Fix output redirection (#1826) --- .../java/org/apache/maven/api/cli/Options.java | 10 +-- .../org/apache/maven/cling/invoker/BaseParser.java | 4 +- .../maven/cling/invoker/CommonsCliOptions.java | 26 ++++--- .../apache/maven/cling/invoker/LayeredOptions.java | 6 +- .../apache/maven/cling/invoker/LookupInvoker.java | 84 +++++++++++----------- .../cling/invoker/mvn/DefaultMavenInvoker.java | 26 +++++++ .../java/org/apache/maven/jline/MessageUtils.java | 19 ++++- 7 files changed, 110 insertions(+), 65 deletions(-) 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 a267623dc4..fe5629d0d7 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 @@ -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); } 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 144ed3d583..cef034fe9a 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 @@ -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); 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 56c3b09c67..471757ca4f 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 @@ -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) { 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 83219eb27b..c5b9a50ffa 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 @@ -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); } 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 2530b1d0cf..6f18d0b365 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 @@ -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()); } } 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 6bfb5121f5..d08056d3e7 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 @@ -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) { diff --git a/maven-jline/src/main/java/org/apache/maven/jline/MessageUtils.java b/maven-jline/src/main/java/org/apache/maven/jline/MessageUtils.java index 43a75aa323..35a5416850 100644 --- a/maven-jline/src/main/java/org/apache/maven/jline/MessageUtils.java +++ b/maven-jline/src/main/java/org/apache/maven/jline/MessageUtils.java @@ -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); + } }); }