mirror of
https://github.com/apache/maven-mvnd.git
synced 2025-09-26 15:38:34 +00:00
Let -h/--help display also mvnd specific options #243
This commit is contained in:
103
build-plugin/pom.xml
Normal file
103
build-plugin/pom.xml
Normal file
@@ -0,0 +1,103 @@
|
||||
<!--
|
||||
|
||||
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.
|
||||
|
||||
-->
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.mvndaemon.mvnd</groupId>
|
||||
<artifactId>mvnd</artifactId>
|
||||
<version>0.1.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>mvnd-build-maven-plugin</artifactId>
|
||||
|
||||
<packaging>maven-plugin</packaging>
|
||||
<name>Maven Daemon - Documentation Maven Plugin</name>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
|
||||
<roaster.version>2.22.2.Final</roaster.version>
|
||||
<maven.plugin-tools.version>3.6.0</maven.plugin-tools.version>
|
||||
<maven-plugin-plugin.version>${maven.plugin-tools.version}</maven-plugin-plugin.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-plugin-api</artifactId>
|
||||
<version>${maven.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.plugin-tools</groupId>
|
||||
<artifactId>maven-plugin-annotations</artifactId>
|
||||
<version>${maven.plugin-tools.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-plugin-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.plugin-tools</groupId>
|
||||
<artifactId>maven-plugin-annotations</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jboss.forge.roaster</groupId>
|
||||
<artifactId>roaster-jdt</artifactId>
|
||||
<version>${roaster.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-plugin-plugin</artifactId>
|
||||
<version>${maven-plugin-plugin.version}</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-plugin-plugin</artifactId>
|
||||
<configuration>
|
||||
<goalPrefix>mvnd-build</goalPrefix>
|
||||
<mojoDependencies>
|
||||
<dep>org.apache.maven:maven-plugin-api</dep>
|
||||
</mojoDependencies>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* 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.mvndaemon.mvnd.plugin.doc;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Comparator;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import javax.security.sasl.SaslClientFactory;
|
||||
import org.apache.maven.plugin.AbstractMojo;
|
||||
import org.apache.maven.plugin.MojoExecutionException;
|
||||
import org.apache.maven.plugin.MojoFailureException;
|
||||
import org.apache.maven.plugin.logging.Log;
|
||||
import org.apache.maven.plugins.annotations.LifecyclePhase;
|
||||
import org.apache.maven.plugins.annotations.Mojo;
|
||||
import org.apache.maven.plugins.annotations.Parameter;
|
||||
import org.apache.maven.plugins.annotations.ResolutionScope;
|
||||
import org.jboss.forge.roaster.Roaster;
|
||||
import org.jboss.forge.roaster.model.source.EnumConstantSource;
|
||||
import org.jboss.forge.roaster.model.source.JavaDocSource;
|
||||
import org.jboss.forge.roaster.model.source.JavaEnumSource;
|
||||
|
||||
/**
|
||||
* Extracts JavaDoc blocks from enum entries and stores them into a properties file.
|
||||
*/
|
||||
@Mojo(name = "doc", defaultPhase = LifecyclePhase.NONE, threadSafe = true, requiresProject = true, requiresDependencyResolution = ResolutionScope.NONE)
|
||||
public class DocMojo extends AbstractMojo {
|
||||
|
||||
/**
|
||||
* The current project's <code>${basedir}</code>
|
||||
*/
|
||||
@Parameter(readonly = true, defaultValue = "${project.basedir}")
|
||||
File baseDir;
|
||||
|
||||
/** A list of fully qualified enum names to process */
|
||||
@Parameter(defaultValue = "org.mvndaemon.mvnd.common.Environment,org.mvndaemon.mvnd.common.OptionType")
|
||||
String[] enums;
|
||||
|
||||
/** If {@code true} the execution of this mojo will be skipped altogether; otherwise this mojo will be executed. */
|
||||
@Parameter(defaultValue = "false", property = "mvnd.build.doc.skip")
|
||||
boolean skip;
|
||||
|
||||
@Override
|
||||
public void execute() throws MojoExecutionException, MojoFailureException {
|
||||
final Log log = getLog();
|
||||
if (skip) {
|
||||
log.info(getClass().getSimpleName() + " skipped per skip parameter");
|
||||
return;
|
||||
}
|
||||
|
||||
final Path basePath = baseDir.toPath();
|
||||
|
||||
for (String enumClassName : enums) {
|
||||
extractEnumJavaDoc(basePath, enumClassName);
|
||||
}
|
||||
}
|
||||
|
||||
static void extractEnumJavaDoc(Path basePath, String enumClassName) throws MojoFailureException {
|
||||
final String classRelPath = enumClassName.replace('.', '/');
|
||||
final Path enumClassLocation = basePath.resolve("src/main/java")
|
||||
.resolve(classRelPath + ".java");
|
||||
final Path propsPath = basePath.resolve("target/classes/" + classRelPath + ".javadoc.properties");
|
||||
try {
|
||||
Files.createDirectories(propsPath.getParent());
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Could not create " + propsPath.getParent(), e);
|
||||
}
|
||||
|
||||
if (!Files.isRegularFile(enumClassLocation)) {
|
||||
throw new IllegalStateException(enumClassLocation + " does not exist: ");
|
||||
}
|
||||
|
||||
try {
|
||||
final JavaEnumSource source = Roaster.parse(JavaEnumSource.class, enumClassLocation.toFile());
|
||||
|
||||
final Properties optionsProperties = new SortedProperties();
|
||||
for (EnumConstantSource enumConst : source.getEnumConstants()) {
|
||||
final JavaDocSource<EnumConstantSource> javaDoc = enumConst.getJavaDoc();
|
||||
final String javadocText = javaDoc.getText();
|
||||
optionsProperties.setProperty(enumConst.getName(), javadocText);
|
||||
}
|
||||
optionsProperties.store(Files.newOutputStream(propsPath), null);
|
||||
} catch (IOException e) {
|
||||
throw new MojoFailureException("Could not parse " + enumClassLocation, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link Properties} with a binarily reproducible {@code store()} operation.
|
||||
*/
|
||||
static class SortedProperties extends Properties {
|
||||
private static final long serialVersionUID = 5983297690254771479L;
|
||||
|
||||
@Override
|
||||
public synchronized Enumeration<Object> keys() {
|
||||
final Iterator<Object> it = new TreeSet<>(keySet()).iterator();
|
||||
return new Enumeration<Object>() {
|
||||
public boolean hasMoreElements() {
|
||||
return it.hasNext();
|
||||
}
|
||||
|
||||
public Object nextElement() {
|
||||
return (SaslClientFactory) it.next();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public Set<Map.Entry<Object, Object>> entrySet() {
|
||||
Comparator<Map.Entry<Object, Object>> comparator = Comparator.comparing(e -> (Comparable) e.getKey());
|
||||
final Set<Map.Entry<Object, Object>> result = new TreeSet<>(comparator);
|
||||
result.addAll(super.entrySet());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(Writer writer, String comments)
|
||||
throws IOException {
|
||||
super.store(new SkipFirstLineBufferedWriter(writer), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(OutputStream out, String comments)
|
||||
throws IOException {
|
||||
this.store(new OutputStreamWriter(out, "8859_1"), comments);
|
||||
}
|
||||
|
||||
static class SkipFirstLineBufferedWriter extends BufferedWriter {
|
||||
private boolean firstLine = true;
|
||||
|
||||
public SkipFirstLineBufferedWriter(Writer out) {
|
||||
super(out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void newLine() throws IOException {
|
||||
if (firstLine) {
|
||||
firstLine = false;
|
||||
} else {
|
||||
write('\n');
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(String s, int off, int len) throws IOException {
|
||||
if (!firstLine) {
|
||||
super.write(s, off, len);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(char cbuf[], int off, int len) throws IOException {
|
||||
if (!firstLine) {
|
||||
super.write(cbuf, off, len);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -327,13 +327,13 @@ public class DaemonConnector {
|
||||
args.add("-Xmx" + maxHeapSize);
|
||||
}
|
||||
|
||||
args.add(Environment.MVND_HOME.asCommandLineProperty(mvndHome.toString()));
|
||||
args.add(Environment.LOGBACK_CONFIGURATION_FILE
|
||||
.asCommandLineProperty(parameters.logbackConfigurationPath().toString()));
|
||||
args.add(Environment.MVND_UID.asCommandLineProperty(uid));
|
||||
args.add(Environment.MVND_DAEMON_STORAGE.asCommandLineProperty(parameters.daemonStorage().toString()));
|
||||
args.add(Environment.MVND_REGISTRY.asCommandLineProperty(parameters.registry().toString()));
|
||||
args.addAll(parameters.getDaemonCommandLineProperties());
|
||||
Environment.MVND_HOME.appendAsCommandLineOption(args::add, mvndHome.toString());
|
||||
Environment.LOGBACK_CONFIGURATION_FILE
|
||||
.appendAsCommandLineOption(args::add, parameters.logbackConfigurationPath().toString());
|
||||
Environment.MVND_UID.appendAsCommandLineOption(args::add, uid);
|
||||
Environment.MVND_DAEMON_STORAGE.appendAsCommandLineOption(args::add, parameters.daemonStorage().toString());
|
||||
Environment.MVND_REGISTRY.appendAsCommandLineOption(args::add, parameters.registry().toString());
|
||||
parameters.discriminatingCommandLineOptions(args::add);
|
||||
args.add(MavenDaemon.class.getName());
|
||||
command = String.join(" ", args);
|
||||
|
||||
|
@@ -31,10 +31,12 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.IntUnaryOperator;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.apache.maven.cli.internal.extension.model.CoreExtension;
|
||||
import org.apache.maven.cli.internal.extension.model.io.xpp3.CoreExtensionsXpp3Reader;
|
||||
import org.codehaus.plexus.util.StringUtils;
|
||||
@@ -71,24 +73,28 @@ public class DaemonParameters {
|
||||
}
|
||||
|
||||
public List<String> getDaemonOpts() {
|
||||
return Arrays.stream(Environment.values())
|
||||
.filter(Environment::isDiscriminating)
|
||||
.map(v -> v.asDaemonOpt(property(v).orFail().asString()))
|
||||
return discriminatingValues()
|
||||
.map(envValue -> envValue.envKey.asDaemonOpt(envValue.asString()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public Map<String, String> getDaemonOptsMap() {
|
||||
return Arrays.stream(Environment.values())
|
||||
.filter(Environment::isDiscriminating)
|
||||
.collect(Collectors.toMap(Environment::getProperty,
|
||||
v -> property(v).orFail().asString()));
|
||||
return discriminatingValues()
|
||||
.collect(Collectors.toMap(
|
||||
envValue -> envValue.envKey.getProperty(),
|
||||
EnvValue::asString));
|
||||
}
|
||||
|
||||
public List<String> getDaemonCommandLineProperties() {
|
||||
Stream<EnvValue> discriminatingValues() {
|
||||
return Arrays.stream(Environment.values())
|
||||
.filter(Environment::isDiscriminating)
|
||||
.map(v -> v.asCommandLineProperty(property(v).orFail().asString()))
|
||||
.collect(Collectors.toList());
|
||||
.map(env -> property(env))
|
||||
.filter(EnvValue::isSet);
|
||||
}
|
||||
|
||||
public void discriminatingCommandLineOptions(Consumer<String> args) {
|
||||
discriminatingValues()
|
||||
.forEach(envValue -> envValue.envKey.appendAsCommandLineOption(args, envValue.asString()));
|
||||
}
|
||||
|
||||
public Path mvndHome() {
|
||||
@@ -350,7 +356,7 @@ public class DaemonParameters {
|
||||
throw new RuntimeException("Unable to parse core extensions", e);
|
||||
}
|
||||
} else {
|
||||
return env.getDef();
|
||||
return env.getDefault();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -506,7 +512,7 @@ public class DaemonParameters {
|
||||
}
|
||||
|
||||
public EnvValue orDefault() {
|
||||
return orDefault(envKey::getDef);
|
||||
return orDefault(envKey::getDefault);
|
||||
}
|
||||
|
||||
public EnvValue orDefault(Supplier<String> defaultSupplier) {
|
||||
@@ -516,28 +522,32 @@ public class DaemonParameters {
|
||||
|
||||
public EnvValue orFail() {
|
||||
return new EnvValue(this, envKey, new ValueSource(sb -> sb, () -> {
|
||||
final StringBuilder sb = new StringBuilder("Could not get value for ")
|
||||
.append(Environment.class.getSimpleName())
|
||||
.append(".").append(envKey.name()).append(" from any of the following sources: ");
|
||||
|
||||
/*
|
||||
* Compose the description functions to invert the order thus getting the resolution order in the
|
||||
* message
|
||||
*/
|
||||
Function<StringBuilder, StringBuilder> description = (s -> s);
|
||||
EnvValue val = this;
|
||||
while (val != null) {
|
||||
description = description.compose(val.valueSource.descriptionFunction);
|
||||
val = val.previous;
|
||||
if (val != null) {
|
||||
description = description.compose(s -> s.append(", "));
|
||||
}
|
||||
}
|
||||
description.apply(sb);
|
||||
throw new IllegalStateException(sb.toString());
|
||||
throw couldNotgetValue();
|
||||
}));
|
||||
}
|
||||
|
||||
IllegalStateException couldNotgetValue() {
|
||||
EnvValue val = this;
|
||||
final StringBuilder sb = new StringBuilder("Could not get value for ")
|
||||
.append(Environment.class.getSimpleName())
|
||||
.append(".").append(envKey.name()).append(" from any of the following sources: ");
|
||||
|
||||
/*
|
||||
* Compose the description functions to invert the order thus getting the resolution order in the
|
||||
* message
|
||||
*/
|
||||
Function<StringBuilder, StringBuilder> description = (s -> s);
|
||||
while (val != null) {
|
||||
description = description.compose(val.valueSource.descriptionFunction);
|
||||
val = val.previous;
|
||||
if (val != null) {
|
||||
description = description.compose(s -> s.append(", "));
|
||||
}
|
||||
}
|
||||
description.apply(sb);
|
||||
return new IllegalStateException(sb.toString());
|
||||
}
|
||||
|
||||
String get() {
|
||||
if (previous != null) {
|
||||
final String result = previous.get();
|
||||
@@ -591,5 +601,15 @@ public class DaemonParameters {
|
||||
return TimeUtils.toDuration(get());
|
||||
}
|
||||
|
||||
public boolean isSet() {
|
||||
if (get() != null) {
|
||||
return true;
|
||||
} else if (envKey.isOptional()) {
|
||||
return false;
|
||||
} else {
|
||||
throw couldNotgetValue();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -82,6 +82,19 @@
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.mvndaemon.mvnd</groupId>
|
||||
<artifactId>mvnd-build-maven-plugin</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>doc</goal>
|
||||
</goals>
|
||||
<phase>generate-resources</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>io.takari.maven.plugins</groupId>
|
||||
<artifactId>takari-lifecycle-plugin</artifactId>
|
||||
|
@@ -15,13 +15,21 @@
|
||||
*/
|
||||
package org.mvndaemon.mvnd.common;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
@@ -29,7 +37,7 @@ import java.util.stream.Stream;
|
||||
*
|
||||
* Duration properties such as {@link #MVND_IDLE_TIMEOUT}, {@link #MVND_KEEP_ALIVE},
|
||||
* {@link #MVND_EXPIRATION_CHECK_DELAY} or {@link #MVND_LOG_PURGE_PERIOD} are expressed
|
||||
* in a human readable format such as {@code 2h30m}, {@code 600ms} or {@code 10 seconds}.
|
||||
* in a human readable format such as <code>2h30m</code>, <code>600ms</code> or <code>10 seconds</code>.
|
||||
* The available units are <i>d/day/days</i>, <i>h/hour/hours</i>, <i>m/min/minute/minutes</i>,
|
||||
* <i>s/sec/second/seconds</i> and <i>ms/millis/msec/milliseconds</i>.
|
||||
*/
|
||||
@@ -40,139 +48,150 @@ public enum Environment {
|
||||
//
|
||||
|
||||
/**
|
||||
* The location of the logback configuration file
|
||||
* The location of the Logback configuration file the daemon should use to configure its logging.
|
||||
*/
|
||||
LOGBACK_CONFIGURATION_FILE("logback.configurationFile", null, null, false),
|
||||
LOGBACK_CONFIGURATION_FILE("logback.configurationFile", null, null, OptionType.PATH, Flags.NONE),
|
||||
|
||||
//
|
||||
// System properties
|
||||
//
|
||||
/** java home directory */
|
||||
JAVA_HOME("java.home", "JAVA_HOME", null, false),
|
||||
/** mvnd home directory */
|
||||
MVND_HOME("mvnd.home", "MVND_HOME", null, false),
|
||||
/** user home directory */
|
||||
USER_HOME("user.home", null, null, false),
|
||||
/** user current dir */
|
||||
USER_DIR("user.dir", null, null, false),
|
||||
/** Java home for starting the daemon */
|
||||
JAVA_HOME("java.home", "JAVA_HOME", null, OptionType.PATH, Flags.NONE),
|
||||
/**
|
||||
* The daemon installation directory. The client normally sets this according to where its <code>mvnd</code>
|
||||
* executable is located
|
||||
*/
|
||||
MVND_HOME("mvnd.home", "MVND_HOME", null, OptionType.PATH, Flags.NONE),
|
||||
/** The user home directory */
|
||||
USER_HOME("user.home", null, null, OptionType.PATH, Flags.NONE),
|
||||
/** The current working directory */
|
||||
USER_DIR("user.dir", null, null, OptionType.PATH, Flags.NONE),
|
||||
|
||||
//
|
||||
// Maven properties
|
||||
//
|
||||
/** path to the maven local repository */
|
||||
MAVEN_REPO_LOCAL("maven.repo.local", null, null, false),
|
||||
/** location of the maven settings file */
|
||||
MAVEN_SETTINGS("maven.settings", null, null, false, new String[] { "--settings", "-s" }),
|
||||
/** root directory of a multi module project */
|
||||
MAVEN_MULTIMODULE_PROJECT_DIRECTORY("maven.multiModuleProjectDirectory", null, null, false),
|
||||
/** The path to the Maven local repository */
|
||||
MAVEN_REPO_LOCAL("maven.repo.local", null, null, OptionType.PATH, Flags.NONE),
|
||||
/** The location of the maven settings file */
|
||||
MAVEN_SETTINGS("maven.settings", null, null, OptionType.PATH, Flags.NONE, "-s", "--settings"),
|
||||
/** The root directory of the current multi module Maven project */
|
||||
MAVEN_MULTIMODULE_PROJECT_DIRECTORY("maven.multiModuleProjectDirectory", null, null, OptionType.PATH, Flags.NONE),
|
||||
|
||||
//
|
||||
// mvnd properties
|
||||
//
|
||||
|
||||
/**
|
||||
* Location of the user supplied mvnd properties
|
||||
* The location of the user supplied <code>mvnd.properties</code> file.
|
||||
*/
|
||||
MVND_PROPERTIES_PATH("mvnd.propertiesPath", "MVND_PROPERTIES_PATH", null, false),
|
||||
MVND_PROPERTIES_PATH("mvnd.propertiesPath", "MVND_PROPERTIES_PATH", null, OptionType.PATH, Flags.NONE),
|
||||
/**
|
||||
* Directory where mvnd stores its files (the registry and the daemon logs).
|
||||
* The directory under which the daemon stores its registry, log files, etc.
|
||||
* Default: <code>${user.home}/.m2/mvnd</code>
|
||||
*/
|
||||
MVND_DAEMON_STORAGE("mvnd.daemonStorage", null, null, false),
|
||||
MVND_DAEMON_STORAGE("mvnd.daemonStorage", null, null, OptionType.PATH, Flags.NONE),
|
||||
/**
|
||||
* The path to the daemon registry, defaults to <code>${mvnd.daemonStorage}/registry.bin</code>
|
||||
* The path to the daemon registry.
|
||||
* Default: <code>${mvnd.daemonStorage}/registry.bin</code>
|
||||
*/
|
||||
MVND_REGISTRY("mvnd.registry", null, null, false),
|
||||
MVND_REGISTRY("mvnd.registry", null, null, OptionType.PATH, Flags.NONE),
|
||||
/**
|
||||
* Property that can be set to avoid buffering the output and display events continuously, closer to the usual maven
|
||||
* display. Passing {@code -B} or {@code --batch-mode} on the command line enables this too for the given build.
|
||||
* If <code>true</code> the log messages are displayed continuously like with stock Maven; otherwise buffer the
|
||||
* messages and output at the end of the build, grouped by module. Passing <code>-B</code> or
|
||||
* <code>--batch-mode</code> on the command line enables this too for the given build.
|
||||
*/
|
||||
MVND_NO_BUFERING("mvnd.noBuffering", null, "false", false),
|
||||
MVND_NO_BUFERING("mvnd.noBuffering", null, Boolean.FALSE, OptionType.BOOLEAN, Flags.NONE),
|
||||
/**
|
||||
* The number of log lines to display for each Maven module that is built in parallel.
|
||||
* The number of log lines to display for each Maven module that is built in parallel. The value can be increased
|
||||
* or decreased by pressing + or - key during the build respectively. This option has no effect with
|
||||
* <code>-Dmvnd.noBuffering=true</code>, <code>-B</code> or <code>--batch-mode</code>.
|
||||
*/
|
||||
MVND_ROLLING_WINDOW_SIZE("mvnd.rollingWindowSize", null, "0", false),
|
||||
MVND_ROLLING_WINDOW_SIZE("mvnd.rollingWindowSize", null, "0", OptionType.INTEGER, Flags.NONE),
|
||||
/**
|
||||
* The automatic log purge period.
|
||||
* Daemon log files older than this value will be removed automatically.
|
||||
*/
|
||||
MVND_LOG_PURGE_PERIOD("mvnd.logPurgePeriod", null, "7d", false, true),
|
||||
MVND_LOG_PURGE_PERIOD("mvnd.logPurgePeriod", null, "7 days", OptionType.DURATION, Flags.NONE),
|
||||
/**
|
||||
* Property to disable using a daemon (usefull for debugging, and only available in non native mode).
|
||||
* If <code>true</code>, the client and daemon will run in the same JVM that exits when the build is finished;
|
||||
* otherwise the client starts/connects to a long living daemon process. This option is only available with
|
||||
* non-native clients and is useful mostly for debugging.
|
||||
*/
|
||||
MVND_NO_DAEMON("mvnd.noDaemon", "MVND_NO_DAEMON", "false", true),
|
||||
MVND_NO_DAEMON("mvnd.noDaemon", "MVND_NO_DAEMON", Boolean.FALSE, OptionType.BOOLEAN, Flags.DISCRIMINATING),
|
||||
/**
|
||||
* Property to launch the daemon in debug mode with the following JVM argument
|
||||
* <code>-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=8000</code>
|
||||
* If <code>true</code>, the daemon will be launched in debug mode with the following JVM argument:
|
||||
* <code>-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=8000</code>; otherwise the debug argument is
|
||||
* not passed to the daemon.
|
||||
*/
|
||||
MVND_DEBUG("mvnd.debug", null, false, true),
|
||||
MVND_DEBUG("mvnd.debug", null, Boolean.FALSE, OptionType.BOOLEAN, Flags.DISCRIMINATING),
|
||||
/**
|
||||
* Duration after which an usused daemon will shut down.
|
||||
* A time period after which an unused daemon will terminate by itself.
|
||||
*/
|
||||
MVND_IDLE_TIMEOUT("mvnd.idleTimeout", null, "3 hours", true, true),
|
||||
MVND_IDLE_TIMEOUT("mvnd.idleTimeout", null, "3 hours", OptionType.DURATION, Flags.DISCRIMINATING),
|
||||
/**
|
||||
* Time after which a daemon will send a keep-alive message to the client if the current build
|
||||
* has produced no output.
|
||||
* If the daemon does not send any message to the client in this period of time, send a keep-alive message so that
|
||||
* the client knows that the daemon is still alive.
|
||||
*/
|
||||
MVND_KEEP_ALIVE("mvnd.keepAlive", null, "100 ms", true, true),
|
||||
MVND_KEEP_ALIVE("mvnd.keepAlive", null, "100 ms", OptionType.DURATION, Flags.DISCRIMINATING),
|
||||
/**
|
||||
* The maximum number of keep alive message that can be lost before the client considers the daemon
|
||||
* as having had a failure.
|
||||
* The maximum number of keep alive messages that can be missed by the client before the client considers the daemon
|
||||
* to be dead.
|
||||
*/
|
||||
MVND_MAX_LOST_KEEP_ALIVE("mvnd.maxLostKeepAlive", null, 30, false),
|
||||
MVND_MAX_LOST_KEEP_ALIVE("mvnd.maxLostKeepAlive", null, 30, OptionType.INTEGER, Flags.NONE),
|
||||
/**
|
||||
* The minimum number of threads to use when constructing the default {@code -T} parameter for the daemon.
|
||||
* This value is ignored if the user passes @{@code -T}, @{@code --threads} or {@code -Dmvnd.threads} on the command
|
||||
* line or if he sets {@code mvnd.threads} in {@code ~/.m2/mvnd.properties}.
|
||||
* The minimum number of threads to use when constructing the default <code>-T</code> parameter for the daemon.
|
||||
* This value is ignored if the user passes <code>-T</code>, <code>--threads</code> or <code>-Dmvnd.threads</code>
|
||||
* on the command line or if he sets <code>mvnd.threads</code> in <code>~/.m2/mvnd.properties</code>.
|
||||
*/
|
||||
MVND_MIN_THREADS("mvnd.minThreads", null, 1, false),
|
||||
MVND_MIN_THREADS("mvnd.minThreads", null, 1, OptionType.INTEGER, Flags.NONE),
|
||||
/**
|
||||
* The number of threads to pass to the daemon; same syntax as Maven's {@code -T}/{@code --threads} option. Ignored
|
||||
* if the user passes @{@code -T}, @{@code --threads} or {@code -Dmvnd.threads} on the command
|
||||
* line.
|
||||
* The number of threads to pass to the daemon; same syntax as Maven's <code>-T</code>/<code>--threads</code>
|
||||
* option.
|
||||
*/
|
||||
MVND_THREADS("mvnd.threads", null, null, false, new String[] { "--threads", "-T" }),
|
||||
MVND_THREADS("mvnd.threads", null, null, OptionType.STRING, Flags.NONE, "-T", "--threads"),
|
||||
/**
|
||||
* The maven builder name to use. Ignored if the user passes
|
||||
*
|
||||
* {@code -b} or {@code --builder} on the command line
|
||||
* The builder implementation the daemon should use
|
||||
*/
|
||||
MVND_BUILDER("mvnd.builder", null, "smart", false, new String[] { "--builder", "-b" }),
|
||||
MVND_BUILDER("mvnd.builder", null, "smart", OptionType.STRING, Flags.NONE, "-b", "--builder"),
|
||||
/**
|
||||
* Internal system property set by the client when starting the daemon to identify its id
|
||||
* An ID for a newly started daemon
|
||||
*/
|
||||
MVND_UID("mvnd.uid", null, null, false),
|
||||
MVND_UID("mvnd.uid", null, null, OptionType.STRING, Flags.INTERNAL),
|
||||
/**
|
||||
* Internal option to specify the maven extension classpath
|
||||
*/
|
||||
MVND_EXT_CLASSPATH("mvnd.extClasspath", null, null, true),
|
||||
MVND_EXT_CLASSPATH("mvnd.extClasspath", null, null, OptionType.STRING, Flags.DISCRIMINATING | Flags.INTERNAL),
|
||||
/**
|
||||
* Internal option to specify the list of maven extension to register
|
||||
*/
|
||||
MVND_CORE_EXTENSIONS("mvnd.coreExtensions", null, null, true),
|
||||
MVND_CORE_EXTENSIONS("mvnd.coreExtensions", null, null, OptionType.STRING, Flags.DISCRIMINATING | Flags.INTERNAL),
|
||||
/**
|
||||
* JVM options for the daemon
|
||||
* The <code>-Xms</code> value to pass to the daemon
|
||||
*/
|
||||
MVND_MIN_HEAP_SIZE("mvnd.minHeapSize", null, "128M", true),
|
||||
MVND_MIN_HEAP_SIZE("mvnd.minHeapSize", null, "128M", OptionType.MEMORY_SIZE, Flags.DISCRIMINATING),
|
||||
/**
|
||||
* JVM options for the daemon
|
||||
* The <code>-Xmx</code> value to pass to the daemon
|
||||
*/
|
||||
MVND_MAX_HEAP_SIZE("mvnd.maxHeapSize", null, "2G", true),
|
||||
MVND_MAX_HEAP_SIZE("mvnd.maxHeapSize", null, "2G", OptionType.MEMORY_SIZE, Flags.DISCRIMINATING),
|
||||
/**
|
||||
* Additional JVM args for the daemon
|
||||
* Additional JVM args to pass to the daemon
|
||||
*/
|
||||
MVND_JVM_ARGS("mvnd.jvmArgs", null, "", true),
|
||||
MVND_JVM_ARGS("mvnd.jvmArgs", null, null, OptionType.STRING, Flags.DISCRIMINATING | Flags.OPTIONAL),
|
||||
/**
|
||||
* JVM options for the daemon
|
||||
* If <code>true</code>, the <code>-ea</code> option will be passed to the daemon; otherwise the <code>-ea</code>
|
||||
* option is not passed to the daemon.
|
||||
*/
|
||||
MVND_ENABLE_ASSERTIONS("mvnd.enableAssertions", null, false, true),
|
||||
MVND_ENABLE_ASSERTIONS("mvnd.enableAssertions", null, Boolean.FALSE, OptionType.BOOLEAN, Flags.DISCRIMINATING),
|
||||
/**
|
||||
* Interval to check if the daemon should expire
|
||||
* The daemon will check this often whether it should exit.
|
||||
*/
|
||||
MVND_EXPIRATION_CHECK_DELAY("mvnd.expirationCheckDelay", null, "10 seconds", true, true),
|
||||
MVND_EXPIRATION_CHECK_DELAY("mvnd.expirationCheckDelay", null, "10 seconds", OptionType.DURATION, Flags.DISCRIMINATING),
|
||||
/**
|
||||
* Period after which idle daemons will shut down
|
||||
* Period after which idle duplicate daemons will be shut down. Duplicate daemons are daemons with the same set of
|
||||
* discriminating start parameters.
|
||||
*/
|
||||
MVND_DUPLICATE_DAEMON_GRACE_PERIOD("mvnd.duplicateDaemonGracePeriod", null, "10 seconds", true, true),
|
||||
;
|
||||
MVND_DUPLICATE_DAEMON_GRACE_PERIOD("mvnd.duplicateDaemonGracePeriod", null, "10 seconds", OptionType.DURATION,
|
||||
Flags.DISCRIMINATING),
|
||||
;
|
||||
|
||||
static Properties properties;
|
||||
|
||||
@@ -190,31 +209,19 @@ public enum Environment {
|
||||
|
||||
private final String property;
|
||||
private final String environmentVariable;
|
||||
private final String def;
|
||||
private final boolean discriminating;
|
||||
private final boolean duration;
|
||||
private final String[] options;
|
||||
private final String default_;
|
||||
private final int flags;
|
||||
private final OptionType type;
|
||||
private final List<String> options;
|
||||
|
||||
Environment(String property, String environmentVariable, Object def, boolean discriminating) {
|
||||
this(property, environmentVariable, def, discriminating, false, null);
|
||||
}
|
||||
|
||||
Environment(String property, String environmentVariable, Object def, boolean discriminating, boolean duration) {
|
||||
this(property, environmentVariable, def, discriminating, duration, null);
|
||||
}
|
||||
|
||||
Environment(String property, String environmentVariable, Object def, boolean discriminating, String[] options) {
|
||||
this(property, environmentVariable, def, discriminating, false, options);
|
||||
}
|
||||
|
||||
Environment(String property, String environmentVariable, Object def, boolean discriminating, boolean duration,
|
||||
String[] options) {
|
||||
Environment(String property, String environmentVariable, Object default_, OptionType type, int flags,
|
||||
String... options) {
|
||||
this.property = Objects.requireNonNull(property);
|
||||
this.environmentVariable = environmentVariable;
|
||||
this.def = def != null ? def.toString() : null;
|
||||
this.discriminating = discriminating;
|
||||
this.duration = duration;
|
||||
this.options = options;
|
||||
this.default_ = default_ != null ? default_.toString() : null;
|
||||
this.flags = flags;
|
||||
this.type = type;
|
||||
this.options = options.length == 0 ? Collections.emptyList() : Collections.unmodifiableList(Arrays.asList(options));
|
||||
}
|
||||
|
||||
public String getProperty() {
|
||||
@@ -225,12 +232,28 @@ public enum Environment {
|
||||
return environmentVariable;
|
||||
}
|
||||
|
||||
public String getDef() {
|
||||
return def;
|
||||
public String getDefault() {
|
||||
return default_;
|
||||
}
|
||||
|
||||
public List<String> getOptions() {
|
||||
return options;
|
||||
}
|
||||
|
||||
public OptionType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public boolean isDiscriminating() {
|
||||
return discriminating;
|
||||
return (flags & Flags.DISCRIMINATING) != 0;
|
||||
}
|
||||
|
||||
public boolean isInternal() {
|
||||
return (flags & Flags.INTERNAL) != 0;
|
||||
}
|
||||
|
||||
public boolean isOptional() {
|
||||
return (flags & Flags.OPTIONAL) != 0;
|
||||
}
|
||||
|
||||
public String asString() {
|
||||
@@ -241,6 +264,17 @@ public enum Environment {
|
||||
return val;
|
||||
}
|
||||
|
||||
public Optional<String> asOptional() {
|
||||
String val = getProperty(property);
|
||||
if (val != null) {
|
||||
return Optional.of(val);
|
||||
} else if (isOptional()) {
|
||||
return Optional.empty();
|
||||
} else {
|
||||
throw new IllegalStateException("The system property " + property + " is missing");
|
||||
}
|
||||
}
|
||||
|
||||
public int asInt() {
|
||||
return Integer.parseInt(asString());
|
||||
}
|
||||
@@ -261,21 +295,30 @@ public enum Environment {
|
||||
return TimeUtils.toDuration(asString());
|
||||
}
|
||||
|
||||
protected String prepareValue(String value) {
|
||||
// For durations, we need to make sure spaces are removed, so reformat the value
|
||||
return duration ? TimeUtils.printDuration(TimeUtils.toMilliSeconds(value)) : value;
|
||||
}
|
||||
|
||||
public String asDaemonOpt(String value) {
|
||||
return property + "=" + prepareValue(value);
|
||||
return property + "=" + type.normalize(value);
|
||||
}
|
||||
|
||||
public String asCommandLineProperty(String value) {
|
||||
return (options != null ? options[0] : "-D" + property) + "=" + prepareValue(value);
|
||||
public void appendAsCommandLineOption(Consumer<String> args, String value) {
|
||||
if (!options.isEmpty()) {
|
||||
args.accept(options.get(0));
|
||||
args.accept(type.normalize(value));
|
||||
} else {
|
||||
args.accept("-D" + property + "=" + type.normalize(value));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasCommandLineProperty(Collection<String> args) {
|
||||
final String[] prefixes = options != null ? options : new String[] { "-D" + property + "=" };
|
||||
public boolean hasCommandOption(Collection<String> args) {
|
||||
final String[] prefixes;
|
||||
if (options.isEmpty()) {
|
||||
prefixes = new String[] { "-D" + property + "=" };
|
||||
} else if (property != null) {
|
||||
prefixes = new String[options.size() + 1];
|
||||
options.toArray(prefixes);
|
||||
prefixes[options.size()] = "-D" + property + "=";
|
||||
} else {
|
||||
prefixes = options.toArray(new String[options.size()]);
|
||||
}
|
||||
return args.stream().anyMatch(arg -> Stream.of(prefixes).anyMatch(arg::startsWith));
|
||||
}
|
||||
|
||||
@@ -292,4 +335,45 @@ public enum Environment {
|
||||
return "executable".equals(System.getProperty("org.graalvm.nativeimage.kind"));
|
||||
}
|
||||
|
||||
public static Stream<DocumentedEnumEntry<Environment>> documentedEntries() {
|
||||
Properties props = new Properties();
|
||||
Environment[] values = values();
|
||||
final String cliOptionsPath = values[0].getClass().getSimpleName() + ".javadoc.properties";
|
||||
try (InputStream in = Environment.class.getResourceAsStream(cliOptionsPath)) {
|
||||
props.load(in);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Could not read " + cliOptionsPath, e);
|
||||
}
|
||||
return Stream.of(values)
|
||||
.filter(env -> !env.isInternal())
|
||||
.sorted(Comparator.comparing(Environment::getProperty))
|
||||
.map(env -> new DocumentedEnumEntry<>(env, props.getProperty(env.name())));
|
||||
}
|
||||
|
||||
public static class DocumentedEnumEntry<E> {
|
||||
|
||||
private final E entry;
|
||||
private final String javaDoc;
|
||||
|
||||
public DocumentedEnumEntry(E entry, String javaDoc) {
|
||||
this.entry = entry;
|
||||
this.javaDoc = javaDoc;
|
||||
}
|
||||
|
||||
public E getEntry() {
|
||||
return entry;
|
||||
}
|
||||
|
||||
public String getJavaDoc() {
|
||||
return javaDoc;
|
||||
}
|
||||
}
|
||||
|
||||
static class Flags {
|
||||
private static final int NONE = 0b0;
|
||||
private static final int DISCRIMINATING = 0b1;
|
||||
private static final int INTERNAL = 0b10;
|
||||
public static final int OPTIONAL = 0b100;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.mvndaemon.mvnd.common;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Comparator;
|
||||
import java.util.Properties;
|
||||
import java.util.stream.Stream;
|
||||
import org.mvndaemon.mvnd.common.Environment.DocumentedEnumEntry;
|
||||
|
||||
public enum OptionType {
|
||||
/** <code>true</code> or <code>false</code> */
|
||||
BOOLEAN,
|
||||
/**
|
||||
* An unlabeled whole number of milliseconds or a whole number followed by an optional space and a unit
|
||||
* which may be one of the following (in EBNF notation): <code>d[ay[s]]</code>, <code>h[our[s]]</code>,
|
||||
* <code>m[in[ute[s]]]</code>, <code>s[ec[ond[s]]]</code> or <code>m[illi]s[ec[ond[s]]]</code>.
|
||||
* <p>
|
||||
* Examples: <code>7 days</code>, <code>7days</code>, <code>7d</code>, <code>100 milliseconds</code>,
|
||||
* <code>100 millis</code>, <code>100ms</code>, <code>100</code>
|
||||
*/
|
||||
DURATION {
|
||||
@Override
|
||||
public String normalize(String value) {
|
||||
return TimeUtils.printDuration(TimeUtils.toMilliSeconds(value));
|
||||
}
|
||||
},
|
||||
/** A whole number */
|
||||
INTEGER,
|
||||
/**
|
||||
* An amount of memory as accepted by the <code>-Xm*</code> family of HotSpot JVM options. Examples:
|
||||
* <code>1024m</code>, <code>2g</code>, <code>5G</code>
|
||||
*/
|
||||
MEMORY_SIZE,
|
||||
/** A local file system path */
|
||||
PATH,
|
||||
/** A string */
|
||||
STRING;
|
||||
|
||||
public String normalize(String value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static Stream<DocumentedEnumEntry<OptionType>> documentedEntries() {
|
||||
Properties props = new Properties();
|
||||
OptionType[] values = values();
|
||||
final String cliOptionsPath = values[0].getClass().getSimpleName() + ".javadoc.properties";
|
||||
try (InputStream in = Environment.class.getResourceAsStream(cliOptionsPath)) {
|
||||
props.load(in);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Could not read " + cliOptionsPath, e);
|
||||
}
|
||||
return Stream.of(values)
|
||||
.sorted(Comparator.comparing(OptionType::name))
|
||||
.map(env -> new DocumentedEnumEntry<>(env, props.getProperty(env.name())));
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
|
183
daemon/src/main/java/org/apache/maven/cli/MvndHelpFormatter.java
Normal file
183
daemon/src/main/java/org/apache/maven/cli/MvndHelpFormatter.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
@@ -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);
|
||||
|
@@ -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());
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -205,9 +205,9 @@ public class MvndTestExtension implements BeforeAllCallback, BeforeEachCallback,
|
||||
Paths.get(System.getProperty("java.home")).toAbsolutePath().normalize(),
|
||||
localMavenRepository, settingsPath,
|
||||
logback,
|
||||
TimeUtils.toDuration(Environment.MVND_IDLE_TIMEOUT.getDef()),
|
||||
TimeUtils.toDuration(Environment.MVND_KEEP_ALIVE.getDef()),
|
||||
Integer.parseInt(Environment.MVND_MAX_LOST_KEEP_ALIVE.getDef()));
|
||||
TimeUtils.toDuration(Environment.MVND_IDLE_TIMEOUT.getDefault()),
|
||||
TimeUtils.toDuration(Environment.MVND_KEEP_ALIVE.getDefault()),
|
||||
Integer.parseInt(Environment.MVND_MAX_LOST_KEEP_ALIVE.getDefault()));
|
||||
final TestRegistry registry = new TestRegistry(parameters.registry());
|
||||
|
||||
return new MvndResource(parameters, registry, isNative, timeoutMs);
|
||||
|
@@ -52,23 +52,23 @@ public class NativeTestClient implements Client {
|
||||
final List<String> cmd = new ArrayList<String>(args.size() + 1);
|
||||
cmd.add(mvndNativeExecutablePath.toString());
|
||||
cmd.addAll(args);
|
||||
if (!Environment.MVND_DAEMON_STORAGE.hasCommandLineProperty(args)) {
|
||||
if (!Environment.MVND_DAEMON_STORAGE.hasCommandOption(args)) {
|
||||
Path daemonStorage = parameters.daemonStorage();
|
||||
cmd.add(Environment.MVND_DAEMON_STORAGE.asCommandLineProperty(daemonStorage.toString()));
|
||||
Environment.MVND_DAEMON_STORAGE.appendAsCommandLineOption(cmd::add, daemonStorage.toString());
|
||||
}
|
||||
if (!Environment.MAVEN_REPO_LOCAL.hasCommandLineProperty(args)) {
|
||||
if (!Environment.MAVEN_REPO_LOCAL.hasCommandOption(args)) {
|
||||
Path mavenRepoLocal = parameters.mavenRepoLocal();
|
||||
cmd.add(Environment.MAVEN_REPO_LOCAL.asCommandLineProperty(mavenRepoLocal.toString()));
|
||||
Environment.MAVEN_REPO_LOCAL.appendAsCommandLineOption(cmd::add, mavenRepoLocal.toString());
|
||||
}
|
||||
if (!Environment.MAVEN_SETTINGS.hasCommandLineProperty(args)) {
|
||||
if (!Environment.MAVEN_SETTINGS.hasCommandOption(args)) {
|
||||
final Path settings = parameters.settings();
|
||||
if (settings != null) {
|
||||
cmd.add(Environment.MAVEN_SETTINGS.asCommandLineProperty(settings.toString()));
|
||||
Environment.MAVEN_SETTINGS.appendAsCommandLineOption(cmd::add, settings.toString());
|
||||
}
|
||||
}
|
||||
if (!Environment.MVND_THREADS.hasCommandLineProperty(args)) {
|
||||
if (!Environment.MVND_THREADS.hasCommandOption(args)) {
|
||||
final String threads = parameters.threads();
|
||||
cmd.add(Environment.MVND_THREADS.asCommandLineProperty(threads));
|
||||
Environment.MVND_THREADS.appendAsCommandLineOption(cmd::add, threads);
|
||||
}
|
||||
|
||||
final ProcessBuilder builder = new ProcessBuilder(cmd.toArray(new String[0]))
|
||||
@@ -76,10 +76,10 @@ public class NativeTestClient implements Client {
|
||||
.redirectErrorStream(true);
|
||||
|
||||
final Map<String, String> env = builder.environment();
|
||||
if (!Environment.MVND_HOME.hasCommandLineProperty(args)) {
|
||||
if (!Environment.MVND_HOME.hasCommandOption(args)) {
|
||||
env.put("MVND_HOME", System.getProperty("mvnd.home"));
|
||||
}
|
||||
if (!Environment.JAVA_HOME.hasCommandLineProperty(args)) {
|
||||
if (!Environment.JAVA_HOME.hasCommandOption(args)) {
|
||||
env.put("JAVA_HOME", System.getProperty("java.home"));
|
||||
}
|
||||
final String cmdString = String.join(" ", cmd);
|
||||
|
Reference in New Issue
Block a user