diff --git a/client/src/main/java/org/mvndaemon/mvnd/client/DefaultClient.java b/client/src/main/java/org/mvndaemon/mvnd/client/DefaultClient.java index fc5a6438..b2b728b1 100644 --- a/client/src/main/java/org/mvndaemon/mvnd/client/DefaultClient.java +++ b/client/src/main/java/org/mvndaemon/mvnd/client/DefaultClient.java @@ -33,6 +33,7 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import org.fusesource.jansi.Ansi; +import org.fusesource.jansi.internal.CLibrary; import org.jline.utils.AttributedString; import org.jline.utils.AttributedStyle; import org.mvndaemon.mvnd.common.BuildProperties; @@ -72,21 +73,36 @@ public class DefaultClient implements Client { } } - // Color - String color = Environment.COLOR.removeCommandLineOption(args); - if (color == null) { - color = Environment.COLOR.getDefault(); - } - // Serial if (Environment.SERIAL.removeCommandLineOption(args) != null) { System.setProperty(Environment.SERIAL.getProperty(), Boolean.toString(true)); } // Batch mode - boolean batchMode = Environment.MAVEN_BATCH_MODE.hasCommandLineOption(args) + final boolean batchMode = Environment.MAVEN_BATCH_MODE.hasCommandLineOption(args) || Environment.COMPLETION.hasCommandLineOption(args); + // Color + String styleColor = Environment.MAVEN_COLOR.getCommandLineOption(args); + if ("always".equals(styleColor) || "never".equals(styleColor)) { + /* If the user knows what he wants, pass his preference unchanged to the daemon */ + } else if ("auto".equals(styleColor)) { + /* Do not pass auto to daemon, we handle it below */ + Environment.MAVEN_COLOR.removeCommandLineOption(args); + styleColor = null; + } else if (styleColor != null) { + throw new IllegalArgumentException("Invalid color configuration option [" + styleColor + + "]. Supported values are (auto|always|never)."); + } + + /* stdout is not a terminal e.g. when stdout is redirected to a file */ + final boolean stdoutIsTerminal = CLibrary.isatty(1) != 0; + if (styleColor == null) { + Environment.MAVEN_COLOR.addCommandLineOption( + args, + (batchMode || logFile != null || !stdoutIsTerminal) ? "never" : "always"); + } + // System properties setSystemPropertiesFromCommandLine(args); @@ -98,8 +114,8 @@ public class DefaultClient implements Client { } int exitCode = 0; - boolean noBuffering = batchMode || parameters.noBuffering(); - try (TerminalOutput output = new TerminalOutput(noBuffering, parameters.rollingWindowSize(), logFile, color)) { + boolean noBuffering = batchMode || parameters.noBuffering() || !stdoutIsTerminal; + try (TerminalOutput output = new TerminalOutput(noBuffering, parameters.rollingWindowSize(), logFile)) { try { final ExecutionResult result = new DefaultClient(parameters).execute(output, args); exitCode = result.getExitCode(); @@ -149,7 +165,6 @@ public class DefaultClient implements Client { boolean version = Environment.MAVEN_VERSION.hasCommandLineOption(args); boolean showVersion = Environment.MAVEN_SHOW_VERSION.hasCommandLineOption(args); boolean debug = Environment.MAVEN_DEBUG.hasCommandLineOption(args); - boolean batchMode = Environment.MAVEN_BATCH_MODE.hasCommandLineOption(args); // Print version if needed if (version || showVersion || debug) { @@ -162,7 +177,8 @@ public class DefaultClient implements Client { + "-" + buildProperties.getOsArch() + " (" + buildProperties.getRevision() + ")"; - final String v = batchMode + boolean isColored = !"never".equals(Environment.MAVEN_COLOR.getCommandLineOption(args)); + final String v = isColored ? mvndVersionString : Ansi.ansi().bold().a(mvndVersionString).reset().toString(); output.accept(Message.log(v)); diff --git a/common/src/main/java/org/mvndaemon/mvnd/common/Environment.java b/common/src/main/java/org/mvndaemon/mvnd/common/Environment.java index 2bac9690..3e2d9cce 100644 --- a/common/src/main/java/org/mvndaemon/mvnd/common/Environment.java +++ b/common/src/main/java/org/mvndaemon/mvnd/common/Environment.java @@ -58,8 +58,6 @@ public enum Environment { STOP(null, null, null, OptionType.VOID, Flags.OPTIONAL, "mvnd:--stop"), /** Use one thread, no log buffering and the default project builder to behave like a standard maven */ SERIAL("mvnd.serial", null, Boolean.FALSE, OptionType.VOID, Flags.OPTIONAL, "mvnd:-1", "mvnd:--serial"), - /** Manage color output, can be either auto (the default), always or never */ - COLOR(null, null, "auto", OptionType.STRING, Flags.OPTIONAL, "mvnd:--color"), // // Log properties @@ -108,6 +106,8 @@ public enum Environment { MAVEN_SHOW_VERSION(null, null, null, OptionType.BOOLEAN, Flags.INTERNAL, "mvn:-V", "mvn:--show-version"), /** Define */ MAVEN_DEFINE(null, null, null, OptionType.STRING, Flags.INTERNAL, "mvn:-D", "mvn:--define"), + /** Whether the output should be styled using ANSI color codes; possible values: auto, always, never */ + MAVEN_COLOR("style.color", null, "auto", OptionType.STRING, Flags.OPTIONAL), // // mvnd properties @@ -379,13 +379,23 @@ public enum Environment { return args.stream().anyMatch(arg -> Stream.of(prefixes).anyMatch(arg::startsWith)); } + public String getCommandLineOption(Collection args) { + return getCommandLineOption(args, false); + } + public String removeCommandLineOption(Collection args) { + return getCommandLineOption(args, true); + } + + String getCommandLineOption(Collection args, boolean remove) { final String[] prefixes = getPrefixes(); String value = null; for (Iterator it = args.iterator(); it.hasNext();) { String arg = it.next(); if (Stream.of(prefixes).anyMatch(arg::startsWith)) { - it.remove(); + if (remove) { + it.remove(); + } if (type == OptionType.VOID) { value = ""; } else { @@ -395,7 +405,9 @@ public enum Environment { if (value.isEmpty()) { if (it.hasNext()) { value = it.next(); - it.remove(); + if (remove) { + it.remove(); + } } } else { if (value.charAt(0) == '=') { diff --git a/common/src/main/java/org/mvndaemon/mvnd/common/logging/TerminalOutput.java b/common/src/main/java/org/mvndaemon/mvnd/common/logging/TerminalOutput.java index 1294aac6..d6899ba4 100644 --- a/common/src/main/java/org/mvndaemon/mvnd/common/logging/TerminalOutput.java +++ b/common/src/main/java/org/mvndaemon/mvnd/common/logging/TerminalOutput.java @@ -35,12 +35,10 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Consumer; import java.util.stream.Collector; import java.util.stream.Collectors; -import org.fusesource.jansi.internal.CLibrary; import org.jline.terminal.Size; import org.jline.terminal.Terminal; import org.jline.terminal.TerminalBuilder; import org.jline.terminal.impl.AbstractPosixTerminal; -import org.jline.utils.AnsiWriter; import org.jline.utils.AttributedString; import org.jline.utils.AttributedStringBuilder; import org.jline.utils.AttributedStyle; @@ -88,10 +86,6 @@ public class TerminalOutput implements ClientOutput { */ public static final int KEY_CTRL_M = 'M' & 0x1f; - public static final String COLOR_AUTO = "auto"; - public static final String COLOR_ALWAYS = "always"; - public static final String COLOR_NEVER = "never"; - private static final AttributedStyle GREEN_FOREGROUND = new AttributedStyle().foreground(AttributedStyle.GREEN); private static final AttributedStyle CYAN_FOREGROUND = new AttributedStyle().foreground(AttributedStyle.CYAN); @@ -149,7 +143,7 @@ public class TerminalOutput implements ClientOutput { } } - public TerminalOutput(boolean noBuffering, int rollingWindowSize, Path logFile, String color) throws IOException { + public TerminalOutput(boolean noBuffering, int rollingWindowSize, Path logFile) throws IOException { this.start = System.currentTimeMillis(); this.terminal = TerminalBuilder.terminal(); this.dumb = terminal.getType().startsWith("dumb"); @@ -165,7 +159,7 @@ public class TerminalOutput implements ClientOutput { this.previousIntHandler = terminal.handle(Terminal.Signal.INT, sig -> daemonDispatch.accept(Message.BareMessage.CANCEL_BUILD_SINGLETON)); this.display = new Display(terminal, false); - this.log = logFile == null ? new MessageCollector(color) : new FileLog(logFile, color); + this.log = logFile == null ? new MessageCollector() : new FileLog(logFile); if (!dumb) { final Thread r = new Thread(this::readInputLoop); r.start(); @@ -771,15 +765,10 @@ public class TerminalOutput implements ClientOutput { private final Writer out; private final Path logFile; - private boolean closed; - public FileLog(Path logFile, String color) throws IOException { + public FileLog(Path logFile) throws IOException { super(); - if (COLOR_ALWAYS.equalsIgnoreCase(color)) { - this.out = Files.newBufferedWriter(logFile, StandardCharsets.UTF_8); - } else { - this.out = new AnsiWriter(Files.newBufferedWriter(logFile, StandardCharsets.UTF_8)); - } + this.out = Files.newBufferedWriter(logFile, StandardCharsets.UTF_8); this.logFile = logFile; } @@ -800,10 +789,7 @@ public class TerminalOutput implements ClientOutput { @Override public void close() throws IOException { - if (!closed) { - out.close(); - closed = true; - } + out.close(); } } @@ -815,23 +801,6 @@ public class TerminalOutput implements ClientOutput { class MessageCollector implements ClientLog { private final List messages = new ArrayList<>(); - private final boolean strip; - - public MessageCollector(String color) { - if (COLOR_NEVER.equalsIgnoreCase(color)) { - this.strip = true; - } else if (COLOR_ALWAYS.equalsIgnoreCase(color)) { - this.strip = false; - } else { - boolean strip; - try { - strip = CLibrary.isatty(1) == 0; - } catch (Throwable t) { - strip = true; - } - this.strip = strip; - } - } @Override public void accept(String message) { @@ -841,7 +810,7 @@ public class TerminalOutput implements ClientOutput { @Override public void flush() { clearDisplay(); - messages.forEach(s -> terminal.writer().println(strip ? AttributedString.stripAnsi(s) : s)); + messages.forEach(terminal.writer()::println); messages.clear(); terminal.flush(); } diff --git a/dist/src/main/distro/bin/mvnd-bash-completion.bash b/dist/src/main/distro/bin/mvnd-bash-completion.bash index 5a3967ec..2247c301 100644 --- a/dist/src/main/distro/bin/mvnd-bash-completion.bash +++ b/dist/src/main/distro/bin/mvnd-bash-completion.bash @@ -146,8 +146,8 @@ _mvnd() _get_comp_words_by_ref -n : cur prev local mvnd_opts="-1" - local mvnd_long_opts="--color|--completion|--purge|--serial|--status|--stop" - local mvnd_properties="-Djava.home|-Dmaven.multiModuleProjectDirectory|-Dmaven.repo.local|-Dmaven.settings|-Dmvnd.builder|-Dmvnd.daemonStorage|-Dmvnd.debug|-Dmvnd.duplicateDaemonGracePeriod|-Dmvnd.enableAssertions|-Dmvnd.expirationCheckDelay|-Dmvnd.home|-Dmvnd.idleTimeout|-Dmvnd.jvmArgs|-Dmvnd.keepAlive|-Dmvnd.logPurgePeriod|-Dmvnd.logback|-Dmvnd.maxHeapSize|-Dmvnd.maxLostKeepAlive|-Dmvnd.minHeapSize|-Dmvnd.minThreads|-Dmvnd.noBuffering|-Dmvnd.noDaemon|-Dmvnd.propertiesPath|-Dmvnd.registry|-Dmvnd.rollingWindowSize|-Dmvnd.serial|-Dmvnd.threads|-Duser.dir|-Duser.home" + local mvnd_long_opts="--completion|--purge|--serial|--status|--stop" + local mvnd_properties="-Djava.home|-Dmaven.multiModuleProjectDirectory|-Dmaven.repo.local|-Dmaven.settings|-Dmvnd.builder|-Dmvnd.daemonStorage|-Dmvnd.debug|-Dmvnd.duplicateDaemonGracePeriod|-Dmvnd.enableAssertions|-Dmvnd.expirationCheckDelay|-Dmvnd.home|-Dmvnd.idleTimeout|-Dmvnd.jvmArgs|-Dmvnd.keepAlive|-Dmvnd.logPurgePeriod|-Dmvnd.logback|-Dmvnd.maxHeapSize|-Dmvnd.maxLostKeepAlive|-Dmvnd.minHeapSize|-Dmvnd.minThreads|-Dmvnd.noBuffering|-Dmvnd.noDaemon|-Dmvnd.propertiesPath|-Dmvnd.registry|-Dmvnd.rollingWindowSize|-Dmvnd.serial|-Dmvnd.threads|-Dstyle.color|-Duser.dir|-Duser.home" local opts="-am|-amd|-B|-C|-c|-cpu|-D|-e|-emp|-ep|-f|-fae|-ff|-fn|-gs|-h|-l|-N|-npr|-npu|-nsu|-o|-P|-pl|-q|-rf|-s|-T|-t|-U|-up|-V|-v|-X|${mvnd_opts}" local long_opts="--also-make|--also-make-dependents|--batch-mode|--strict-checksums|--lax-checksums|--check-plugin-updates|--define|--errors|--encrypt-master-password|--encrypt-password|--file|--fail-at-end|--fail-fast|--fail-never|--global-settings|--help|--log-file|--non-recursive|--no-plugin-registry|--no-plugin-updates|--no-snapshot-updates|--offline|--activate-profiles|--projects|--quiet|--resume-from|--settings|--threads|--toolchains|--update-snapshots|--update-plugins|--show-version|--version|--debug|${mvnd_long_opts}"