Let -h/--help display also mvnd specific options #243

This commit is contained in:
Peter Palaga
2020-12-08 09:59:05 +01:00
parent 234f59ecb1
commit 3a4d86bc64
15 changed files with 843 additions and 182 deletions

View File

@@ -19,12 +19,10 @@
package org.apache.maven.cli;
import com.google.inject.AbstractModule;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collections;
@@ -96,6 +94,7 @@ import org.mvndaemon.mvnd.common.Environment;
import org.mvndaemon.mvnd.logging.internal.Slf4jLoggerManager;
import org.mvndaemon.mvnd.logging.smart.AbstractLoggingSpy;
import org.mvndaemon.mvnd.logging.smart.LoggingExecutionListener;
import org.mvndaemon.mvnd.logging.smart.LoggingOutputStream;
import org.slf4j.ILoggerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -252,7 +251,7 @@ public class DaemonMavenCli {
}
} catch (ParseException e) {
System.err.println("Unable to parse maven.config: " + e.getMessage());
cliManager.displayHelp(System.out);
AbstractLoggingSpy.instance().append(MvndHelpFormatter.displayHelp(cliManager));
throw e;
}
@@ -264,16 +263,12 @@ public class DaemonMavenCli {
}
} catch (ParseException e) {
System.err.println("Unable to parse command line options: " + e.getMessage());
cliManager.displayHelp(System.out);
AbstractLoggingSpy.instance().append(MvndHelpFormatter.displayHelp(cliManager));
throw e;
}
if (cliRequest.commandLine.hasOption(CLIManager.HELP)) {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (PrintStream out = new PrintStream(baos, false, StandardCharsets.UTF_8.name())) {
cliManager.displayHelp(out);
}
AbstractLoggingSpy.instance().append(null, new String(baos.toByteArray(), StandardCharsets.UTF_8));
AbstractLoggingSpy.instance().append(MvndHelpFormatter.displayHelp(cliManager));
throw new ExitException(0);
}
@@ -342,6 +337,11 @@ public class DaemonMavenCli {
MessageUtils.setColorEnabled(false);
}
// Workaround for https://github.com/mvndaemon/mvnd/issues/39
final ch.qos.logback.classic.Logger mvndLogger = (ch.qos.logback.classic.Logger) slf4jLoggerFactory
.getLogger("org.mvndaemon.mvnd");
mvndLogger.setLevel(ch.qos.logback.classic.Level.toLevel(System.getProperty("mvnd.log.level", "INFO")));
// LOG STREAMS
if (cliRequest.commandLine.hasOption(CLIManager.LOG_FILE)) {
File logFile = new File(cliRequest.commandLine.getOptionValue(CLIManager.LOG_FILE));
@@ -357,17 +357,16 @@ public class DaemonMavenCli {
// Ignore
//
}
} else {
System.setOut(new LoggingOutputStream(s -> mvndLogger.info("[stdout] " + s)).printStream());
System.setErr(new LoggingOutputStream(s -> mvndLogger.error("[stderr] " + s)).printStream());
}
// Workaround for https://github.com/mvndaemon/mvnd/issues/39
ch.qos.logback.classic.Logger mvndLogger = (ch.qos.logback.classic.Logger) slf4jLoggerFactory
.getLogger("org.mvndaemon.mvnd");
mvndLogger.setLevel(ch.qos.logback.classic.Level.toLevel(System.getProperty("mvnd.log.level", "INFO")));
}
private void version(CliRequest cliRequest) throws ExitException {
if (cliRequest.debug || cliRequest.commandLine.hasOption(CLIManager.VERSION)) {
AbstractLoggingSpy.instance().append(null, CLIReportingUtils.showVersion());
AbstractLoggingSpy.instance().append(CLIReportingUtils.showVersion());
if (cliRequest.commandLine.hasOption(CLIManager.VERSION)) {
throw new ExitException(0);
}

View File

@@ -0,0 +1,183 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed 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.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.cli.HelpFormatter;
import org.mvndaemon.mvnd.common.Environment;
import org.mvndaemon.mvnd.common.OptionType;
/**
* Combines the help message from the stock Maven with {@code mvnd}'s help message.
*/
public class MvndHelpFormatter {
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+");
static String toPlainText(String javadocText) {
return HTML_TAGS_PATTERN.matcher(javadocText).replaceAll("");
}
/**
* Returns Maven option descriptions combined with mvnd options descriptions
*
* @param cliManager
* @return the string containing the help message
*/
public static String displayHelp(CLIManager cliManager) {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (PrintStream out = new PrintStream(baos, false, StandardCharsets.UTF_8.name())) {
cliManager.displayHelp(out);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
final String mvnHelp = new String(baos.toByteArray(), StandardCharsets.UTF_8);
final Matcher m = COLUMNS_DETECTOR_PATTERN.matcher(mvnHelp);
final String indent = m.find() ? m.group() : " ";
final String lineSeparator = System.lineSeparator();
final StringBuilder help = new StringBuilder(mvnHelp)
.append(lineSeparator)
.append("mvnd specific options:")
.append(lineSeparator);
Environment.documentedEntries()
.forEach(entry -> {
final Environment env = entry.getEntry();
help.append(lineSeparator);
int indentPos = help.length() + indent.length();
int lineEnd = help.length() + HelpFormatter.DEFAULT_WIDTH;
spaces(help, HelpFormatter.DEFAULT_LEFT_PAD);
help
.append("-D")
.append(env.getProperty())
.append("=<")
.append(env.getType().name().toLowerCase(Locale.ROOT))
.append('>');
final List<String> opts = env.getOptions();
if (!opts.isEmpty()) {
for (String opt : opts) {
help
.append(',')
.append(opt);
}
help
.append(" <")
.append(env.getType().name().toLowerCase(Locale.ROOT))
.append('>');
}
help.append(' ');
spaces(help, indentPos - help.length());
wrap(help, toPlainText(entry.getJavaDoc()), HelpFormatter.DEFAULT_WIDTH, lineEnd, indent);
indentedLine(help, "Default", env.getDefault(), indent);
indentedLine(help, "Env. variable", env.getEnvironmentVariable(), indent);
});
help
.append(lineSeparator)
.append(lineSeparator)
.append("mvnd value types:")
.append(lineSeparator);
OptionType.documentedEntries()
.forEach(entry -> {
final OptionType type = entry.getEntry();
help.append(lineSeparator);
int indentPos = help.length() + indent.length();
int lineEnd = help.length() + HelpFormatter.DEFAULT_WIDTH;
spaces(help, HelpFormatter.DEFAULT_LEFT_PAD);
help.append(type.name().toLowerCase(Locale.ROOT));
spaces(help, indentPos - help.length());
wrap(help, toPlainText(entry.getJavaDoc()), HelpFormatter.DEFAULT_WIDTH, lineEnd, indent);
});
return help.toString();
}
private static void indentedLine(final StringBuilder stringBuilder, String key, final String value, final String indent) {
int lineEnd;
if (value != null) {
lineEnd = stringBuilder.length() + HelpFormatter.DEFAULT_WIDTH;
stringBuilder
.append(System.lineSeparator())
.append(indent);
wrap(stringBuilder, key + ": " + value, HelpFormatter.DEFAULT_WIDTH, lineEnd, indent);
}
}
/**
* Word-wrap the given {@code text} to the given {@link StringBuilder}
*
* @param stringBuilder the {@link StringBuilder} to append to
* @param text the text to wrap and append
* @param lineLength the preferred line length
* @param nextLineEnd the length of the {@code stringBuilder} at which the current line should end
* @param indent the indentation string
*/
static void wrap(StringBuilder stringBuilder, String text, int lineLength, int nextLineEnd, String indent) {
final StringTokenizer st = new StringTokenizer(text, " \t\n\r", true);
String lastWs = null;
while (st.hasMoreTokens()) {
final String token = st.nextToken();
if (WS_PATTERN.matcher(token).matches()) {
lastWs = token;
} else {
if (stringBuilder.length() + token.length() + (lastWs != null ? lastWs.length() : 0) < nextLineEnd) {
if (lastWs != null) {
stringBuilder.append(lastWs);
}
stringBuilder.append(token);
} else {
nextLineEnd = stringBuilder.length() + lineLength;
stringBuilder
.append(System.lineSeparator())
.append(indent)
.append(token);
}
lastWs = null;
}
}
}
/**
* Append {@code count} spaces to the given {@code stringBuilder}
*
* @param stringBuilder the {@link StringBuilder} to append to
* @param count the number of spaces to append
* @return the given {@code stringBuilder}
*/
static StringBuilder spaces(StringBuilder stringBuilder, int count) {
for (int i = 0; i < count; i++) {
stringBuilder.append(' ');
}
return stringBuilder;
}
}

View File

@@ -121,8 +121,7 @@ public class Server implements AutoCloseable, Runnable {
List<String> opts = new ArrayList<>();
Arrays.stream(Environment.values())
.filter(Environment::isDiscriminating)
.map(v -> v.getProperty() + "=" + v.asString())
.forEach(opts::add);
.forEach(envKey -> envKey.asOptional().ifPresent(val -> opts.add(envKey.getProperty() + "=" + val)));
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(opts.stream().collect(Collectors.joining(
"\n ", "Initializing daemon with properties:\n ", "\n")));
@@ -638,11 +637,13 @@ public class Server implements AutoCloseable, Runnable {
this.queue = queue;
}
@Override
public void finish(int exitCode) throws Exception {
queue.add(new Message.BuildFinished(exitCode));
queue.add(Message.STOP_SINGLETON);
}
@Override
public void fail(Throwable t) throws Exception {
queue.add(new BuildException(t));
queue.add(Message.STOP_SINGLETON);

View File

@@ -34,11 +34,21 @@ public abstract class AbstractLoggingSpy {
AbstractLoggingSpy.instance = instance;
}
public void append(String event) {
append(null, event);
}
public void append(String projectId, String event) {
String msg = event.endsWith("\n") ? event.substring(0, event.length() - 1) : event;
onProjectLog(projectId, msg);
}
public void finish(int exitCode) throws Exception {
}
public void fail(Throwable t) throws Exception {
}
protected void notifySessionStart(ExecutionEvent event) {
onStartSession(event.getSession());
}

View File

@@ -17,20 +17,12 @@ package org.mvndaemon.mvnd.logging.smart;
import org.apache.maven.execution.ExecutionEvent;
import org.apache.maven.execution.ExecutionListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
public class LoggingExecutionListener implements ExecutionListener {
private final ExecutionListener delegate;
static {
Logger logger = LoggerFactory.getLogger("org.mvndaemon.mvnd");
System.setOut(new LoggingOutputStream(s -> logger.info("[stdout] " + s)).printStream());
System.setErr(new LoggingOutputStream(s -> logger.info("[stderr] " + s)).printStream());
}
public LoggingExecutionListener(ExecutionListener delegate) {
this.delegate = delegate;
}