mirror of
https://github.com/apache/maven-mvnd.git
synced 2026-01-13 07:04:14 +08:00
Fix exports and move help in place (#1166)
Depends on https://github.com/apache/maven/pull/1799 Help output https://gist.github.com/cstamas/b53333945eb2d7721e0aa5f03329c4a6
This commit is contained in:
@@ -18,18 +18,31 @@
|
||||
*/
|
||||
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.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
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;
|
||||
|
||||
@@ -81,6 +94,14 @@ public class CommonsCliDaemonMavenOptions extends CommonsCliMavenOptions impleme
|
||||
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+");
|
||||
|
||||
static String toPlainText(String javadocText) {
|
||||
return HTML_TAGS_PATTERN.matcher(javadocText).replaceAll("");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prepareOptions(Options options) {
|
||||
super.prepareOptions(options);
|
||||
@@ -89,5 +110,149 @@ public class CommonsCliDaemonMavenOptions extends CommonsCliMavenOptions impleme
|
||||
.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);
|
||||
final String indent = m.find() ? m.group() : " ";
|
||||
|
||||
final int terminalWidth = getTerminalWidth();
|
||||
final String lineSeparator = System.lineSeparator();
|
||||
final StringBuilder help =
|
||||
new StringBuilder(mvnHelp).append(lineSeparator).append("mvnd specific options:");
|
||||
|
||||
Environment.documentedEntries().forEach(entry -> {
|
||||
final Environment env = entry.getEntry();
|
||||
help.append(lineSeparator);
|
||||
int indentPos = help.length() + indent.length();
|
||||
int lineEnd = help.length() + terminalWidth;
|
||||
spaces(help, HelpFormatter.DEFAULT_LEFT_PAD);
|
||||
final String property = env.getProperty();
|
||||
if (property != null) {
|
||||
help.append("-D").append(property);
|
||||
if (env.getType() != OptionType.VOID) {
|
||||
help.append("=<")
|
||||
.append(env.getType().name().toLowerCase(Locale.ROOT))
|
||||
.append('>');
|
||||
}
|
||||
}
|
||||
|
||||
final Set<String> opts = env.getOptions();
|
||||
if (!opts.isEmpty()) {
|
||||
if (property != null) {
|
||||
help.append(';');
|
||||
}
|
||||
boolean first = true;
|
||||
for (String opt : opts) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
help.append(',');
|
||||
}
|
||||
help.append(opt);
|
||||
}
|
||||
if (env.getType() != OptionType.VOID) {
|
||||
help.append(" <")
|
||||
.append(env.getType().name().toLowerCase(Locale.ROOT))
|
||||
.append('>');
|
||||
}
|
||||
}
|
||||
help.append(' ');
|
||||
|
||||
spaces(help, indentPos - help.length());
|
||||
wrap(help, toPlainText(entry.getJavaDoc()), terminalWidth, lineEnd, indent);
|
||||
|
||||
if (env.isDocumentedAsDiscriminating()) {
|
||||
indentedLine(help, terminalWidth, "This is a discriminating start parameter.", indent);
|
||||
}
|
||||
if (env.getDefault() != null) {
|
||||
indentedLine(help, terminalWidth, "Default: " + env.getDefault(), indent);
|
||||
}
|
||||
if (env.getEnvironmentVariable() != null) {
|
||||
indentedLine(help, terminalWidth, "Env. variable: " + env.getEnvironmentVariable(), indent);
|
||||
}
|
||||
});
|
||||
|
||||
help.append(lineSeparator).append(lineSeparator).append("mvnd value types:");
|
||||
|
||||
OptionType.documentedEntries().forEach(entry -> {
|
||||
final OptionType type = entry.getEntry();
|
||||
help.append(lineSeparator);
|
||||
int indentPos = help.length() + indent.length();
|
||||
int lineEnd = help.length() + terminalWidth;
|
||||
spaces(help, HelpFormatter.DEFAULT_LEFT_PAD);
|
||||
help.append(type.name().toLowerCase(Locale.ROOT));
|
||||
spaces(help, indentPos - help.length());
|
||||
wrap(help, toPlainText(entry.getJavaDoc()), terminalWidth, lineEnd, indent);
|
||||
});
|
||||
|
||||
pw.print(help);
|
||||
pw.flush();
|
||||
}
|
||||
|
||||
private static int getTerminalWidth() {
|
||||
int terminalWidth = MessageUtils.getTerminalWidth();
|
||||
if (terminalWidth <= 0) {
|
||||
terminalWidth = HelpFormatter.DEFAULT_WIDTH;
|
||||
}
|
||||
return terminalWidth;
|
||||
}
|
||||
|
||||
private static void indentedLine(StringBuilder stringBuilder, int terminalWidth, String text, String indent) {
|
||||
final int lineEnd = stringBuilder.length() + terminalWidth;
|
||||
stringBuilder.append(System.lineSeparator()).append(indent);
|
||||
wrap(stringBuilder, text, terminalWidth, 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
|
||||
*/
|
||||
static void spaces(StringBuilder stringBuilder, int count) {
|
||||
stringBuilder.append(" ".repeat(Math.max(0, count)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,11 @@
|
||||
*/
|
||||
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 org.apache.maven.api.cli.Options;
|
||||
@@ -92,9 +94,11 @@ public class DaemonMavenInvoker extends DefaultResidentMavenInvoker {
|
||||
BuildEventListener buildEventListener =
|
||||
context.invokerRequest.parserRequest().lookup().lookup(BuildEventListener.class);
|
||||
if (invokerRequest.options().help().isPresent()) {
|
||||
// TODO: ugly, cleanup
|
||||
buildEventListener.log(
|
||||
MvndHelpFormatter.displayHelp((CommonsCliDaemonMavenOptions) context.invokerRequest.options()));
|
||||
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);
|
||||
}
|
||||
if (invokerRequest.options().showVersionAndExit().isPresent()) {
|
||||
|
||||
@@ -20,19 +20,39 @@ package org.apache.maven.cli;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.HashSet;
|
||||
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.extension.internal.CoreExtensionEntry;
|
||||
import org.mvndaemon.mvnd.common.Environment;
|
||||
|
||||
public class DaemonPlexusContainerCapsuleFactory
|
||||
extends PlexusContainerCapsuleFactory<
|
||||
MavenOptions, MavenInvokerRequest<MavenOptions>, DefaultResidentMavenInvoker.LocalContext> {
|
||||
|
||||
@Override
|
||||
protected Set<String> collectExportedArtifacts(
|
||||
CoreExtensionEntry coreEntry, List<CoreExtensionEntry> extensionEntries) {
|
||||
HashSet<String> result = new HashSet<>(super.collectExportedArtifacts(coreEntry, extensionEntries));
|
||||
result.add("org.codehaus.plexus:plexus-interactivity-api");
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<String> collectExportedPackages(
|
||||
CoreExtensionEntry coreEntry, List<CoreExtensionEntry> extensionEntries) {
|
||||
HashSet<String> result = new HashSet<>(super.collectExportedPackages(coreEntry, extensionEntries));
|
||||
result.add("org.mvndaemon.mvnd.interactivity");
|
||||
result.add("org.codehaus.plexus.components.interactivity");
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Path> parseExtClasspath(DefaultResidentMavenInvoker.LocalContext context) throws Exception {
|
||||
return Stream.of(Environment.MVND_EXT_CLASSPATH.asString().split(","))
|
||||
|
||||
@@ -1,205 +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.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
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 The cli manager
|
||||
* @return the string containing the help message
|
||||
*/
|
||||
public static String displayHelp(CommonsCliDaemonMavenOptions cliManager) {
|
||||
int terminalWidth = getTerminalWidth();
|
||||
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
try (PrintStream out = new PrintStream(baos, false, StandardCharsets.UTF_8)) {
|
||||
out.println();
|
||||
PrintWriter pw = new PrintWriter(out);
|
||||
HelpFormatter formatter = new HelpFormatter();
|
||||
formatter.printHelp(
|
||||
pw,
|
||||
terminalWidth,
|
||||
"mvnd [options] [<goal(s)>] [<phase(s)>]",
|
||||
"\nOptions:",
|
||||
cliManager.getOptions(),
|
||||
1,
|
||||
3,
|
||||
"\n",
|
||||
false);
|
||||
pw.flush();
|
||||
}
|
||||
final String mvnHelp = baos.toString(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:");
|
||||
|
||||
Environment.documentedEntries().forEach(entry -> {
|
||||
final Environment env = entry.getEntry();
|
||||
help.append(lineSeparator);
|
||||
int indentPos = help.length() + indent.length();
|
||||
int lineEnd = help.length() + terminalWidth;
|
||||
spaces(help, HelpFormatter.DEFAULT_LEFT_PAD);
|
||||
final String property = env.getProperty();
|
||||
if (property != null) {
|
||||
help.append("-D").append(property);
|
||||
if (env.getType() != OptionType.VOID) {
|
||||
help.append("=<")
|
||||
.append(env.getType().name().toLowerCase(Locale.ROOT))
|
||||
.append('>');
|
||||
}
|
||||
}
|
||||
|
||||
final Set<String> opts = env.getOptions();
|
||||
if (!opts.isEmpty()) {
|
||||
if (property != null) {
|
||||
help.append(';');
|
||||
}
|
||||
boolean first = true;
|
||||
for (String opt : opts) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
help.append(',');
|
||||
}
|
||||
help.append(opt);
|
||||
}
|
||||
if (env.getType() != OptionType.VOID) {
|
||||
help.append(" <")
|
||||
.append(env.getType().name().toLowerCase(Locale.ROOT))
|
||||
.append('>');
|
||||
}
|
||||
}
|
||||
help.append(' ');
|
||||
|
||||
spaces(help, indentPos - help.length());
|
||||
wrap(help, toPlainText(entry.getJavaDoc()), terminalWidth, lineEnd, indent);
|
||||
|
||||
if (env.isDocumentedAsDiscriminating()) {
|
||||
indentedLine(help, terminalWidth, "This is a discriminating start parameter.", indent);
|
||||
}
|
||||
if (env.getDefault() != null) {
|
||||
indentedLine(help, terminalWidth, "Default: " + env.getDefault(), indent);
|
||||
}
|
||||
if (env.getEnvironmentVariable() != null) {
|
||||
indentedLine(help, terminalWidth, "Env. variable: " + env.getEnvironmentVariable(), indent);
|
||||
}
|
||||
});
|
||||
|
||||
help.append(lineSeparator).append(lineSeparator).append("mvnd value types:");
|
||||
|
||||
OptionType.documentedEntries().forEach(entry -> {
|
||||
final OptionType type = entry.getEntry();
|
||||
help.append(lineSeparator);
|
||||
int indentPos = help.length() + indent.length();
|
||||
int lineEnd = help.length() + terminalWidth;
|
||||
spaces(help, HelpFormatter.DEFAULT_LEFT_PAD);
|
||||
help.append(type.name().toLowerCase(Locale.ROOT));
|
||||
spaces(help, indentPos - help.length());
|
||||
wrap(help, toPlainText(entry.getJavaDoc()), terminalWidth, lineEnd, indent);
|
||||
});
|
||||
|
||||
return help.toString();
|
||||
}
|
||||
|
||||
private static int getTerminalWidth() {
|
||||
int terminalWidth;
|
||||
try {
|
||||
terminalWidth = Environment.MVND_TERMINAL_WIDTH.asInt();
|
||||
} catch (Exception e) {
|
||||
terminalWidth = 80;
|
||||
}
|
||||
return terminalWidth;
|
||||
}
|
||||
|
||||
private static void indentedLine(StringBuilder stringBuilder, int terminalWidth, String text, String indent) {
|
||||
final int lineEnd = stringBuilder.length() + terminalWidth;
|
||||
stringBuilder.append(System.lineSeparator()).append(indent);
|
||||
wrap(stringBuilder, text, terminalWidth, 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
|
||||
*/
|
||||
static void spaces(StringBuilder stringBuilder, int count) {
|
||||
stringBuilder.append(" ".repeat(Math.max(0, count)));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user