From 6d1c7ac456d69474651f670449a07fe549a62b14 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Fri, 4 Oct 2024 12:52:36 +0200 Subject: [PATCH 01/24] Maven Daemon CLIng Dropped the MavenCLI mutilated copy, using CLIng. WIP --- client/pom.xml | 4 - .../mvnd/client/DaemonParameters.java | 2 +- daemon/pom.xml | 2 +- .../java/org/apache/maven/cli/DaemonCli.java | 2 +- .../org/apache/maven/cli/DaemonMavenCli.java | 1549 ----------------- .../apache/maven/cli/DaemonMavenCling.java | 82 + .../apache/maven/cli/DaemonMavenInvoker.java | 58 + .../org/mvndaemon/mvnd/daemon/Server.java | 8 +- pom.xml | 7 +- 9 files changed, 155 insertions(+), 1559 deletions(-) delete mode 100644 daemon/src/main/java/org/apache/maven/cli/DaemonMavenCli.java create mode 100644 daemon/src/main/java/org/apache/maven/cli/DaemonMavenCling.java create mode 100644 daemon/src/main/java/org/apache/maven/cli/DaemonMavenInvoker.java diff --git a/client/pom.xml b/client/pom.xml index 92e8fb69f..6293a80c4 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -45,10 +45,6 @@ org.apache.maven.daemon mvnd-logging - - org.apache.maven - maven-embedder - org.slf4j jul-to-slf4j diff --git a/client/src/main/java/org/mvndaemon/mvnd/client/DaemonParameters.java b/client/src/main/java/org/mvndaemon/mvnd/client/DaemonParameters.java index ccf7d4371..23aaa5d9d 100644 --- a/client/src/main/java/org/mvndaemon/mvnd/client/DaemonParameters.java +++ b/client/src/main/java/org/mvndaemon/mvnd/client/DaemonParameters.java @@ -45,8 +45,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import org.apache.maven.api.cli.extensions.CoreExtension; import org.apache.maven.cli.internal.extension.io.CoreExtensionsStaxReader; -import org.apache.maven.cli.internal.extension.model.CoreExtension; import org.mvndaemon.mvnd.common.Environment; import org.mvndaemon.mvnd.common.InterpolationHelper; import org.mvndaemon.mvnd.common.Os; diff --git a/daemon/pom.xml b/daemon/pom.xml index 3d575b09e..dbd4dea1d 100644 --- a/daemon/pom.xml +++ b/daemon/pom.xml @@ -60,7 +60,7 @@ org.apache.maven - maven-embedder + maven-cli org.codehaus.plexus diff --git a/daemon/src/main/java/org/apache/maven/cli/DaemonCli.java b/daemon/src/main/java/org/apache/maven/cli/DaemonCli.java index e08151675..b474e1dab 100644 --- a/daemon/src/main/java/org/apache/maven/cli/DaemonCli.java +++ b/daemon/src/main/java/org/apache/maven/cli/DaemonCli.java @@ -26,7 +26,7 @@ /** * Simple interface to bridge maven 3.9.x and 4.0.x CLI */ -public interface DaemonCli { +public interface DaemonCli extends AutoCloseable { int main( List args, String workingDir, diff --git a/daemon/src/main/java/org/apache/maven/cli/DaemonMavenCli.java b/daemon/src/main/java/org/apache/maven/cli/DaemonMavenCli.java deleted file mode 100644 index 6129c961d..000000000 --- a/daemon/src/main/java/org/apache/maven/cli/DaemonMavenCli.java +++ /dev/null @@ -1,1549 +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.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintStream; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.*; -import java.util.Map.Entry; -import java.util.function.Consumer; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import com.google.inject.AbstractModule; -import org.apache.commons.cli.CommandLine; -import org.apache.commons.cli.Option; -import org.apache.commons.cli.ParseException; -import org.apache.commons.cli.UnrecognizedOptionException; -import org.apache.maven.InternalErrorException; -import org.apache.maven.Maven; -import org.apache.maven.api.services.MessageBuilder; -import org.apache.maven.api.services.MessageBuilderFactory; -import org.apache.maven.building.FileSource; -import org.apache.maven.building.Problem; -import org.apache.maven.building.Source; -import org.apache.maven.cli.configuration.ConfigurationProcessor; -import org.apache.maven.cli.configuration.SettingsXmlConfigurationProcessor; -import org.apache.maven.cli.event.ExecutionEventLogger; -import org.apache.maven.cli.internal.BootstrapCoreExtensionManager; -import org.apache.maven.cli.internal.extension.model.CoreExtension; -import org.apache.maven.cli.logging.Slf4jConfiguration; -import org.apache.maven.cli.logging.Slf4jConfigurationFactory; -import org.apache.maven.cli.transfer.QuietMavenTransferListener; -import org.apache.maven.cli.transfer.Slf4jMavenTransferListener; -import org.apache.maven.eventspy.internal.EventSpyDispatcher; -import org.apache.maven.exception.DefaultExceptionHandler; -import org.apache.maven.exception.ExceptionHandler; -import org.apache.maven.exception.ExceptionSummary; -import org.apache.maven.execution.*; -import org.apache.maven.execution.scope.internal.MojoExecutionScope; -import org.apache.maven.execution.scope.internal.MojoExecutionScopeModule; -import org.apache.maven.extension.internal.CoreExports; -import org.apache.maven.extension.internal.CoreExportsProvider; -import org.apache.maven.extension.internal.CoreExtensionEntry; -import org.apache.maven.jline.MessageUtils; -import org.apache.maven.lifecycle.LifecycleExecutionException; -import org.apache.maven.model.building.ModelProcessor; -import org.apache.maven.model.root.RootLocator; -import org.apache.maven.plugin.ExtensionRealmCache; -import org.apache.maven.plugin.PluginArtifactsCache; -import org.apache.maven.project.MavenProject; -import org.apache.maven.project.artifact.ProjectArtifactsCache; -import org.apache.maven.properties.internal.SystemProperties; -import org.apache.maven.session.scope.internal.SessionScope; -import org.apache.maven.session.scope.internal.SessionScopeModule; -import org.apache.maven.toolchain.building.DefaultToolchainsBuildingRequest; -import org.apache.maven.toolchain.building.ToolchainsBuilder; -import org.apache.maven.toolchain.building.ToolchainsBuildingResult; -import org.codehaus.plexus.ContainerConfiguration; -import org.codehaus.plexus.DefaultContainerConfiguration; -import org.codehaus.plexus.DefaultPlexusContainer; -import org.codehaus.plexus.PlexusConstants; -import org.codehaus.plexus.PlexusContainer; -import org.codehaus.plexus.classworlds.ClassWorld; -import org.codehaus.plexus.classworlds.realm.ClassRealm; -import org.codehaus.plexus.component.repository.exception.ComponentLookupException; -import org.codehaus.plexus.interpolation.AbstractValueSource; -import org.codehaus.plexus.interpolation.BasicInterpolator; -import org.codehaus.plexus.interpolation.StringSearchInterpolator; -import org.eclipse.aether.transfer.TransferListener; -import org.jline.terminal.Terminal; -import org.jline.terminal.impl.ExternalTerminal; -import org.mvndaemon.mvnd.cache.invalidating.InvalidatingExtensionRealmCache; -import org.mvndaemon.mvnd.cache.invalidating.InvalidatingPluginArtifactsCache; -import org.mvndaemon.mvnd.cache.invalidating.InvalidatingProjectArtifactsCache; -import org.mvndaemon.mvnd.cli.EnvHelper; -import org.mvndaemon.mvnd.common.Environment; -import org.mvndaemon.mvnd.common.Os; -import org.mvndaemon.mvnd.logging.internal.Slf4jLoggerManager; -import org.mvndaemon.mvnd.logging.slf4j.MvndSimpleLogger; -import org.mvndaemon.mvnd.logging.smart.BuildEventListener; -import org.mvndaemon.mvnd.logging.smart.LoggingExecutionListener; -import org.mvndaemon.mvnd.logging.smart.LoggingOutputStream; -import org.mvndaemon.mvnd.transfer.DaemonMavenTransferListener; -import org.slf4j.ILoggerFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.slf4j.spi.LocationAwareLogger; -import org.sonatype.plexus.components.sec.dispatcher.DefaultSecDispatcher; -import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher; - -import static java.util.Comparator.comparing; -import static org.apache.maven.cli.CLIManager.BATCH_MODE; -import static org.apache.maven.cli.CLIManager.FORCE_INTERACTIVE; -import static org.apache.maven.cli.CLIManager.NON_INTERACTIVE; - -/** - * File origin: - * https://github.com/apache/maven/blob/maven-3.6.2/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java - * - * @author Jason van Zyl - */ -public class DaemonMavenCli implements DaemonCli { - public static final String LOCAL_REPO_PROPERTY = "maven.repo.local"; - - public static final String MULTIMODULE_PROJECT_DIRECTORY = "maven.multiModuleProjectDirectory"; - - public static final String USER_HOME = System.getProperty("user.home"); - - public static final File USER_MAVEN_CONFIGURATION_HOME = new File(USER_HOME, ".m2"); - - public static final File DEFAULT_USER_TOOLCHAINS_FILE = new File(USER_MAVEN_CONFIGURATION_HOME, "toolchains.xml"); - - public static final File DEFAULT_GLOBAL_TOOLCHAINS_FILE = - new File(System.getProperty("maven.conf"), "toolchains.xml"); - - private static final String EXT_CLASS_PATH = "maven.ext.class.path"; - - private static final String EXTENSIONS_FILENAME = ".mvn/extensions.xml"; - - private static final String MVN_MAVEN_CONFIG = ".mvn/maven.config"; - - public static final String STYLE_COLOR_PROPERTY = "style.color"; - - public static final String RAW_STREAMS = "raw-streams"; - - private final Slf4jLoggerManager plexusLoggerManager; - - private final ILoggerFactory slf4jLoggerFactory; - - private final Logger slf4jLogger; - - private final ClassWorld classWorld; - - private final DefaultPlexusContainer container; - - private final EventSpyDispatcher eventSpyDispatcher; - - private final ModelProcessor modelProcessor; - - private final Maven maven; - - private final MavenExecutionRequestPopulator executionRequestPopulator; - - private final ToolchainsBuilder toolchainsBuilder; - - private final DefaultSecDispatcher dispatcher; - - private final Map configurationProcessors; - - private final LoggingExecutionListener executionListener; - - /** - * Non-volatile, assuming that it is accessed only from the main thread - */ - private BuildEventListener buildEventListener = BuildEventListener.dummy(); - - private MessageBuilderFactory messageBuilderFactory; - - public DaemonMavenCli() throws Exception { - slf4jLoggerFactory = LoggerFactory.getILoggerFactory(); - slf4jLogger = slf4jLoggerFactory.getLogger(this.getClass().getName()); - plexusLoggerManager = new Slf4jLoggerManager(); - - this.classWorld = ((ClassRealm) Thread.currentThread().getContextClassLoader()).getWorld(); - this.messageBuilderFactory = new DaemonMessageBuilderFactory(); - - container = container(); - - eventSpyDispatcher = container.lookup(EventSpyDispatcher.class); - maven = container.lookup(Maven.class); - executionRequestPopulator = container.lookup(MavenExecutionRequestPopulator.class); - modelProcessor = createModelProcessor(container); - configurationProcessors = container.lookupMap(ConfigurationProcessor.class); - toolchainsBuilder = container.lookup(ToolchainsBuilder.class); - dispatcher = (DefaultSecDispatcher) container.lookup(SecDispatcher.class, "maven"); - executionListener = container.lookup(LoggingExecutionListener.class); - } - - public int main( - List arguments, - String workingDirectory, - String projectDirectory, - Map clientEnv, - BuildEventListener buildEventListener) - throws Exception { - Terminal terminal = new ExternalTerminal( - "Maven", - "ansi", - new ByteArrayInputStream(new byte[0]), - new ByteArrayOutputStream(), - StandardCharsets.UTF_8); - MessageUtils.systemInstall(terminal); - this.buildEventListener = buildEventListener; - try { - CliRequest req = new CliRequest(null, null); - req.args = arguments.toArray(new String[0]); - req.workingDirectory = new File(workingDirectory).getCanonicalPath(); - req.multiModuleProjectDirectory = new File(projectDirectory); - return doMain(req, clientEnv); - } finally { - this.buildEventListener = BuildEventListener.dummy(); - } - } - - public int doMain(CliRequest cliRequest, Map clientEnv) throws Exception { - Properties props = (Properties) System.getProperties().clone(); - ClassLoader tccl = Thread.currentThread().getContextClassLoader(); - try { - Thread.currentThread().setContextClassLoader(container.getContainerRealm()); - initialize(cliRequest); - environment(cliRequest.workingDirectory, clientEnv); - cli(cliRequest); - properties(cliRequest); - logging(cliRequest); - informativeCommands(cliRequest); - version(cliRequest); - container(cliRequest); - configure(cliRequest, eventSpyDispatcher, configurationProcessors); - toolchains(cliRequest); - populateRequest(cliRequest); - encryption(cliRequest); - return execute(cliRequest); - } catch (ExitException e) { - return e.exitCode; - } finally { - eventSpyDispatcher.close(); - System.setProperties(props); - Thread.currentThread().setContextClassLoader(tccl); - } - } - - void initialize(CliRequest cliRequest) throws ExitException { - cliRequest.classWorld = classWorld; - - if (cliRequest.workingDirectory == null) { - cliRequest.workingDirectory = System.getProperty("user.dir"); - } - - if (cliRequest.multiModuleProjectDirectory == null) { - buildEventListener.log(String.format("-D%s system property is not set.", MULTIMODULE_PROJECT_DIRECTORY)); - throw new ExitException(1); - } - System.setProperty("maven.multiModuleProjectDirectory", cliRequest.multiModuleProjectDirectory.toString()); - - // We need to locate the top level project which may be pointed at using - // the -f/--file option. However, the command line isn't parsed yet, so - // we need to iterate through the args to find it and act upon it. - Path topDirectory = Paths.get(cliRequest.workingDirectory); - boolean isAltFile = false; - for (String arg : cliRequest.args) { - if (isAltFile) { - // this is the argument following -f/--file - Path path = topDirectory.resolve(arg); - if (Files.isDirectory(path)) { - topDirectory = path; - } else if (Files.isRegularFile(path)) { - topDirectory = path.getParent(); - if (!Files.isDirectory(topDirectory)) { - System.err.println("Directory " + topDirectory - + " extracted from the -f/--file command-line argument " + arg + " does not exist"); - throw new ExitException(1); - } - } else { - System.err.println( - "POM file " + arg + " specified with the -f/--file command line argument does not exist"); - throw new ExitException(1); - } - break; - } else { - // Check if this is the -f/--file option - isAltFile = arg.equals(String.valueOf(CLIManager.ALTERNATE_POM_FILE)) || arg.equals("file"); - } - } - topDirectory = getCanonicalPath(topDirectory); - cliRequest.topDirectory = topDirectory; - // We're very early in the process, and we don't have the container set up yet, - // so we rely on the JDK services to eventually look up a custom RootLocator. - // This is used to compute {@code session.rootDirectory} but all {@code project.rootDirectory} - // properties will be computed through the RootLocator found in the container. - RootLocator rootLocator = - ServiceLoader.load(RootLocator.class).iterator().next(); - Path rootDirectory = rootLocator.findRoot(topDirectory); - if (rootDirectory == null) { - System.err.println(RootLocator.UNABLE_TO_FIND_ROOT_PROJECT_MESSAGE); - } - cliRequest.rootDirectory = rootDirectory; - } - - void cli(CliRequest cliRequest) throws Exception { - CLIManager cliManager = newCLIManager(); - - CommandLine mavenConfig = null; - try { - File configFile = new File(cliRequest.multiModuleProjectDirectory, MVN_MAVEN_CONFIG); - - if (configFile.isFile()) { - try (Stream lines = Files.lines(configFile.toPath(), Charset.defaultCharset())) { - String[] args = lines.filter(arg -> !arg.isEmpty() && !arg.startsWith("#")) - .toArray(String[]::new); - mavenConfig = cliManager.parse(args); - List unrecognized = mavenConfig.getArgList(); - if (!unrecognized.isEmpty()) { - // This file can only contain options, not args (goals or phases) - throw new ParseException("Unrecognized maven.config file entries: " + unrecognized); - } - } - } - } catch (ParseException e) { - buildEventListener.log("Unable to parse maven.config: " + e.getMessage()); - buildEventListener.log("Run 'mvnd --help' for available options."); - throw new ExitException(1); - } - - try { - if (mavenConfig == null) { - cliRequest.commandLine = cliManager.parse(cliRequest.args); - } else { - cliRequest.commandLine = cliMerge(cliManager.parse(cliRequest.args), mavenConfig); - } - } catch (ParseException e) { - buildEventListener.log("Unable to parse command line options: " + e.getMessage()); - buildEventListener.log("Run 'mvnd --help' for available options."); - throw new ExitException(1); - } - - // check for presence of unsupported command line options - try { - if (cliRequest.commandLine.hasOption("llr")) { - throw new UnrecognizedOptionException("Option '-llr' is not supported starting with Maven 3.9.1"); - } - } catch (ParseException e) { - System.err.println("Unsupported options: " + e.getMessage()); - cliManager.displayHelp(System.out); - throw e; - } - } - - private void informativeCommands(CliRequest cliRequest) throws Exception { - if (cliRequest.commandLine.hasOption(CLIManager.HELP)) { - buildEventListener.log(MvndHelpFormatter.displayHelp(newCLIManager())); - throw new ExitException(0); - } - - if (cliRequest.commandLine.hasOption(CLIManager.VERSION)) { - if (cliRequest.commandLine.hasOption(CLIManager.QUIET)) { - buildEventListener.log(CLIReportingUtils.showVersionMinimal()); - } else { - buildEventListener.log(CLIReportingUtils.showVersion()); - } - throw new ExitException(0); - } - } - - private CLIManager newCLIManager() { - CLIManager cliManager = new CLIManager(); - cliManager.options.addOption(Option.builder() - .longOpt(RAW_STREAMS) - .desc("Do not decorate output and error streams") - .build()); - return cliManager; - } - - private CommandLine cliMerge(CommandLine mavenArgs, CommandLine mavenConfig) { - CommandLine.Builder commandLineBuilder = new CommandLine.Builder(); - - // the args are easy, cli first then config file - for (String arg : mavenArgs.getArgs()) { - commandLineBuilder.addArg(arg); - } - for (String arg : mavenConfig.getArgs()) { - commandLineBuilder.addArg(arg); - } - - // now add all options, except for -D with cli first then config file - List