All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.maven.cli.MavenCli Maven / Gradle / Ivy

There is a newer version: 4.0.0-beta-5
Show newest version
/*
 * 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.BufferedInputStream;
import java.io.Console;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
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.commons.lang3.math.NumberUtils;
import org.apache.maven.BuildAbort;
import org.apache.maven.InternalErrorException;
import org.apache.maven.Maven;
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.DefaultEventSpyContext;
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.internal.extension.model.io.xpp3.CoreExtensionsXpp3Reader;
import org.apache.maven.cli.logging.Slf4jConfiguration;
import org.apache.maven.cli.logging.Slf4jConfigurationFactory;
import org.apache.maven.cli.logging.Slf4jLoggerManager;
import org.apache.maven.cli.logging.Slf4jStdoutLogger;
import org.apache.maven.cli.transfer.ConsoleMavenTransferListener;
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.DefaultMavenExecutionRequest;
import org.apache.maven.execution.ExecutionListener;
import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.execution.MavenExecutionRequestPopulationException;
import org.apache.maven.execution.MavenExecutionRequestPopulator;
import org.apache.maven.execution.MavenExecutionResult;
import org.apache.maven.execution.scope.internal.MojoExecutionScopeModule;
import org.apache.maven.extension.internal.CoreExports;
import org.apache.maven.extension.internal.CoreExtensionEntry;
import org.apache.maven.lifecycle.LifecycleExecutionException;
import org.apache.maven.model.building.ModelProcessor;
import org.apache.maven.project.MavenProject;
import org.apache.maven.properties.internal.EnvironmentUtils;
import org.apache.maven.properties.internal.SystemProperties;
import org.apache.maven.session.scope.internal.SessionScopeModule;
import org.apache.maven.shared.utils.logging.MessageBuilder;
import org.apache.maven.shared.utils.logging.MessageUtils;
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.classworlds.realm.NoSuchRealmException;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.interpolation.AbstractValueSource;
import org.codehaus.plexus.interpolation.InterpolationException;
import org.codehaus.plexus.interpolation.StringSearchInterpolator;
import org.codehaus.plexus.logging.LoggerManager;
import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
import org.eclipse.aether.transfer.TransferListener;
import org.slf4j.ILoggerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonatype.plexus.components.cipher.DefaultPlexusCipher;
import org.sonatype.plexus.components.sec.dispatcher.DefaultSecDispatcher;
import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher;
import org.sonatype.plexus.components.sec.dispatcher.SecUtil;
import org.sonatype.plexus.components.sec.dispatcher.model.SettingsSecurity;

import static org.apache.maven.cli.CLIManager.COLOR;
import static org.apache.maven.cli.ResolveFile.resolveFile;
import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;

// TODO push all common bits back to plexus cli and prepare for transition to Guice. We don't need 50 ways to make CLIs

/**
 * @author Jason van Zyl
 */
public class MavenCli {
    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 DOT_MVN = ".mvn";

    private static final String UNABLE_TO_FIND_ROOT_PROJECT_MESSAGE = "Unable to find the root directory. Create a "
            + DOT_MVN + " directory in the project root directory to identify it.";

    private static final String EXTENSIONS_FILENAME = DOT_MVN + "/extensions.xml";

    private static final String MVN_MAVEN_CONFIG = DOT_MVN + "/maven.config";

    public static final String STYLE_COLOR_PROPERTY = "style.color";

    private ClassWorld classWorld;

    private LoggerManager plexusLoggerManager;

    private ILoggerFactory slf4jLoggerFactory;

    private Logger slf4jLogger;

    private EventSpyDispatcher eventSpyDispatcher;

    private ModelProcessor modelProcessor;

    private Maven maven;

    private MavenExecutionRequestPopulator executionRequestPopulator;

    private ToolchainsBuilder toolchainsBuilder;

    private DefaultSecDispatcher dispatcher;

    private Map configurationProcessors;

    private CLIManager cliManager;

    private static final Pattern NEXT_LINE = Pattern.compile("\r?\n");

    public MavenCli() {
        this(null);
    }

    // This supports painless invocation by the Verifier during embedded execution of the core ITs
    public MavenCli(ClassWorld classWorld) {
        this.classWorld = classWorld;
    }

    public static void main(String[] args) {
        int result = main(args, null);

        System.exit(result);
    }

    public static int main(String[] args, ClassWorld classWorld) {
        MavenCli cli = new MavenCli();

        MessageUtils.systemInstall();
        MessageUtils.registerShutdownHook();
        int result = cli.doMain(new CliRequest(args, classWorld));
        MessageUtils.systemUninstall();

        return result;
    }

    // TODO need to externalize CliRequest
    public static int doMain(String[] args, ClassWorld classWorld) {
        MavenCli cli = new MavenCli();
        return cli.doMain(new CliRequest(args, classWorld));
    }

    /**
     * This supports painless invocation by the Verifier during embedded execution of the core ITs.
     * See 
     * Embedded3xLauncher in maven-verifier
     */
    public int doMain(String[] args, String workingDirectory, PrintStream stdout, PrintStream stderr) {
        PrintStream oldout = System.out;
        PrintStream olderr = System.err;

        final Set realms;
        if (classWorld != null) {
            realms = new HashSet<>();
            for (ClassRealm realm : classWorld.getRealms()) {
                realms.add(realm.getId());
            }
        } else {
            realms = Collections.emptySet();
        }

        try {
            if (stdout != null) {
                System.setOut(stdout);
            }
            if (stderr != null) {
                System.setErr(stderr);
            }

            CliRequest cliRequest = new CliRequest(args, classWorld);
            cliRequest.workingDirectory = workingDirectory;

            return doMain(cliRequest);
        } finally {
            if (classWorld != null) {
                for (ClassRealm realm : new ArrayList<>(classWorld.getRealms())) {
                    String realmId = realm.getId();
                    if (!realms.contains(realmId)) {
                        try {
                            classWorld.disposeRealm(realmId);
                        } catch (NoSuchRealmException ignored) {
                            // can't happen
                        }
                    }
                }
            }
            System.setOut(oldout);
            System.setErr(olderr);
        }
    }

    // TODO need to externalize CliRequest
    public int doMain(CliRequest cliRequest) {
        PlexusContainer localContainer = null;
        try {
            initialize(cliRequest);
            cli(cliRequest);
            properties(cliRequest);
            logging(cliRequest);
            informativeCommands(cliRequest);
            version(cliRequest);
            localContainer = container(cliRequest);
            commands(cliRequest);
            configure(cliRequest);
            toolchains(cliRequest);
            populateRequest(cliRequest);
            encryption(cliRequest);
            return execute(cliRequest);
        } catch (ExitException e) {
            return e.exitCode;
        } catch (UnrecognizedOptionException e) {
            // pure user error, suppress stack trace
            return 1;
        } catch (BuildAbort e) {
            CLIReportingUtils.showError(slf4jLogger, "ABORTED", e, cliRequest.showErrors);

            return 2;
        } catch (Exception e) {
            CLIReportingUtils.showError(slf4jLogger, "Error executing Maven.", e, cliRequest.showErrors);

            return 1;
        } finally {
            if (localContainer != null) {
                localContainer.dispose();
            }
        }
    }

    void initialize(CliRequest cliRequest) throws ExitException {
        if (cliRequest.workingDirectory == null) {
            cliRequest.workingDirectory = System.getProperty("user.dir");
        }

        if (cliRequest.multiModuleProjectDirectory == null) {
            String basedirProperty = System.getProperty(MULTIMODULE_PROJECT_DIRECTORY);
            if (basedirProperty == null) {
                System.err.format("-D%s system property is not set.", MULTIMODULE_PROJECT_DIRECTORY);
                throw new ExitException(1);
            }
            File basedir = basedirProperty != null ? new File(basedirProperty) : new File("");
            try {
                cliRequest.multiModuleProjectDirectory = basedir.getCanonicalFile();
            } catch (IOException e) {
                cliRequest.multiModuleProjectDirectory = basedir.getAbsoluteFile();
            }
        }

        // 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 on searchAcceptableRootDirectory method to find us acceptable directory.
        // The method may return null if nothing acceptable found.
        cliRequest.rootDirectory = searchAcceptableRootDirectory(topDirectory);

        //
        // Make sure the Maven home directory is an absolute path to save us from confusion with say drive-relative
        // Windows paths.
        //
        String mavenHome = System.getProperty("maven.home");

        if (mavenHome != null) {
            System.setProperty("maven.home", new File(mavenHome).getAbsolutePath());
        }
    }

    void cli(CliRequest cliRequest) throws Exception {
        //
        // Parsing errors can happen during the processing of the arguments and we prefer not having to check if
        // the logger is null and construct this so we can use an SLF4J logger everywhere.
        //
        slf4jLogger = new Slf4jStdoutLogger();

        cliManager = new CLIManager();

        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) {
            System.err.println("Unable to parse maven.config file options: " + e.getMessage());
            cliManager.displayHelp(System.out);
            throw e;
        }

        try {
            CommandLine mavenCli = cliManager.parse(cliRequest.args);
            if (mavenConfig == null) {
                cliRequest.commandLine = mavenCli;
            } else {
                cliRequest.commandLine = cliMerge(mavenConfig, mavenCli);
            }
        } catch (ParseException e) {
            System.err.println("Unable to parse command line options: " + e.getMessage());
            cliManager.displayHelp(System.out);
            throw e;
        }

        // 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 ExitException {
        if (cliRequest.commandLine.hasOption(CLIManager.HELP)) {
            cliManager.displayHelp(System.out);
            throw new ExitException(0);
        }

        if (cliRequest.commandLine.hasOption(CLIManager.VERSION)) {
            if (cliRequest.commandLine.hasOption(CLIManager.QUIET)) {
                System.out.println(CLIReportingUtils.showVersionMinimal());
            } else {
                System.out.println(CLIReportingUtils.showVersion());
            }
            throw new ExitException(0);
        }
    }

    private CommandLine cliMerge(CommandLine mavenConfig, CommandLine mavenCli) {
        CommandLine.Builder commandLineBuilder = new CommandLine.Builder();

        // the args are easy, CLI only since maven.config file can only contain options
        for (String arg : mavenCli.getArgs()) {
            commandLineBuilder.addArg(arg);
        }

        /* Although this looks wrong in terms of order Commons CLI stores the value of options in
         * an array and when a value is potentionally overriden it is added to the array. The single
         * arg option value is retrieved and instead of returning values[values.length-1] it returns
         * values[0] which means that the original value instead of the overridden one is returned
         * (first wins). With properties values are truely overriden since at the end a map is used
         * to merge which means last wins.
         *
         * TODO Report this behavioral bug with Commons CLI
         */
        // now add all options, except for user properties with CLI first then maven.config file
        List

Then the returned value is {@code groupId:artifactId} when there is a name clash and * {@code :artifactId} if there is no conflict. * * @param mavenProjects Maven projects which are part of build execution. * @param failedProject Project which has failed. * @return Value for -rf flag to resume build exactly from place where it failed ({@code :artifactId} in * general and {@code groupId:artifactId} when there is a name clash). */ private String getResumeFrom(List mavenProjects, MavenProject failedProject) { for (MavenProject buildProject : mavenProjects) { if (failedProject.getArtifactId().equals(buildProject.getArtifactId()) && !failedProject.equals(buildProject)) { return failedProject.getGroupId() + ":" + failedProject.getArtifactId(); } } return ":" + failedProject.getArtifactId(); } private void logSummary( ExceptionSummary summary, Map references, String indent, boolean showErrors) { String referenceKey = ""; if (StringUtils.isNotEmpty(summary.getReference())) { referenceKey = references.get(summary.getReference()); if (referenceKey == null) { referenceKey = "[Help " + (references.size() + 1) + "]"; references.put(summary.getReference(), referenceKey); } } String msg = summary.getMessage(); if (StringUtils.isNotEmpty(referenceKey)) { if (msg.indexOf('\n') < 0) { msg += " -> " + buffer().strong(referenceKey); } else { msg += "\n-> " + buffer().strong(referenceKey); } } String[] lines = NEXT_LINE.split(msg); String currentColor = ""; for (int i = 0; i < lines.length; i++) { // add eventual current color inherited from previous line String line = currentColor + lines[i]; // look for last ANSI escape sequence to check if nextColor Matcher matcher = LAST_ANSI_SEQUENCE.matcher(line); String nextColor = ""; if (matcher.find()) { nextColor = matcher.group(1); if (ANSI_RESET.equals(nextColor)) { // last ANSI escape code is reset: no next color nextColor = ""; } } // effective line, with indent and reset if end is colored line = indent + line + ("".equals(nextColor) ? "" : ANSI_RESET); if ((i == lines.length - 1) && (showErrors || (summary.getException() instanceof InternalErrorException))) { slf4jLogger.error(line, summary.getException()); } else { slf4jLogger.error(line); } currentColor = nextColor; } indent += " "; for (ExceptionSummary child : summary.getChildren()) { logSummary(child, references, indent, showErrors); } } private static final Pattern LAST_ANSI_SEQUENCE = Pattern.compile("(\u001B\\[[;\\d]*[ -/]*[@-~])[^\u001B]*$"); private static final String ANSI_RESET = "\u001B\u005Bm"; private void configure(CliRequest cliRequest) throws Exception { // // This is not ideal but there are events specifically for configuration from the CLI which I don't // believe are really valid but there are ITs which assert the right events are published so this // needs to be supported so the EventSpyDispatcher needs to be put in the CliRequest so that // it can be accessed by configuration processors. // cliRequest.request.setEventSpyDispatcher(eventSpyDispatcher); // // We expect at most 2 implementations to be available. The SettingsXmlConfigurationProcessor implementation // is always available in the core and likely always will be, but we may have another ConfigurationProcessor // present supplied by the user. The rule is that we only allow the execution of one ConfigurationProcessor. // If there is more than one then we execute the one supplied by the user, otherwise we execute the // the default SettingsXmlConfigurationProcessor. // int userSuppliedConfigurationProcessorCount = configurationProcessors.size() - 1; if (userSuppliedConfigurationProcessorCount == 0) { // // Our settings.xml source is historically how we have configured Maven from the CLI so we are going to // have to honour its existence forever. So let's run it. // configurationProcessors.get(SettingsXmlConfigurationProcessor.HINT).process(cliRequest); } else if (userSuppliedConfigurationProcessorCount == 1) { // // Run the user supplied ConfigurationProcessor // for (Entry entry : configurationProcessors.entrySet()) { String hint = entry.getKey(); if (!hint.equals(SettingsXmlConfigurationProcessor.HINT)) { ConfigurationProcessor configurationProcessor = entry.getValue(); configurationProcessor.process(cliRequest); } } } else if (userSuppliedConfigurationProcessorCount > 1) { // // There are too many ConfigurationProcessors so we don't know which one to run so report the error. // StringBuilder sb = new StringBuilder(String.format( "\nThere can only be one user supplied ConfigurationProcessor, there are %s:\n\n", userSuppliedConfigurationProcessorCount)); for (Entry entry : configurationProcessors.entrySet()) { String hint = entry.getKey(); if (!hint.equals(SettingsXmlConfigurationProcessor.HINT)) { ConfigurationProcessor configurationProcessor = entry.getValue(); sb.append(String.format( "%s\n", configurationProcessor.getClass().getName())); } } sb.append("\n"); throw new Exception(sb.toString()); } } void toolchains(CliRequest cliRequest) throws Exception { File userToolchainsFile; if (cliRequest.commandLine.hasOption(CLIManager.ALTERNATE_USER_TOOLCHAINS)) { userToolchainsFile = new File(cliRequest.commandLine.getOptionValue(CLIManager.ALTERNATE_USER_TOOLCHAINS)); userToolchainsFile = resolveFile(userToolchainsFile, cliRequest.workingDirectory); if (!userToolchainsFile.isFile()) { throw new FileNotFoundException( "The specified user toolchains file does not exist: " + userToolchainsFile); } } else { userToolchainsFile = DEFAULT_USER_TOOLCHAINS_FILE; } File globalToolchainsFile; if (cliRequest.commandLine.hasOption(CLIManager.ALTERNATE_GLOBAL_TOOLCHAINS)) { globalToolchainsFile = new File(cliRequest.commandLine.getOptionValue(CLIManager.ALTERNATE_GLOBAL_TOOLCHAINS)); globalToolchainsFile = resolveFile(globalToolchainsFile, cliRequest.workingDirectory); if (!globalToolchainsFile.isFile()) { throw new FileNotFoundException( "The specified global toolchains file does not exist: " + globalToolchainsFile); } } else { globalToolchainsFile = DEFAULT_GLOBAL_TOOLCHAINS_FILE; } cliRequest.request.setGlobalToolchainsFile(globalToolchainsFile); cliRequest.request.setUserToolchainsFile(userToolchainsFile); DefaultToolchainsBuildingRequest toolchainsRequest = new DefaultToolchainsBuildingRequest(); if (globalToolchainsFile.isFile()) { toolchainsRequest.setGlobalToolchainsSource(new FileSource(globalToolchainsFile)); } if (userToolchainsFile.isFile()) { toolchainsRequest.setUserToolchainsSource(new FileSource(userToolchainsFile)); } eventSpyDispatcher.onEvent(toolchainsRequest); slf4jLogger.debug( "Reading global toolchains from {}", getLocation(toolchainsRequest.getGlobalToolchainsSource(), globalToolchainsFile)); slf4jLogger.debug( "Reading user toolchains from {}", getLocation(toolchainsRequest.getUserToolchainsSource(), userToolchainsFile)); ToolchainsBuildingResult toolchainsResult = toolchainsBuilder.build(toolchainsRequest); eventSpyDispatcher.onEvent(toolchainsResult); executionRequestPopulator.populateFromToolchains(cliRequest.request, toolchainsResult.getEffectiveToolchains()); if (!toolchainsResult.getProblems().isEmpty() && slf4jLogger.isWarnEnabled()) { slf4jLogger.warn(""); slf4jLogger.warn("Some problems were encountered while building the effective toolchains"); for (Problem problem : toolchainsResult.getProblems()) { slf4jLogger.warn("{} @ {}", problem.getMessage(), problem.getLocation()); } slf4jLogger.warn(""); } } private Object getLocation(Source source, File defaultLocation) { if (source != null) { return source.getLocation(); } return defaultLocation; } private MavenExecutionRequest populateRequest(CliRequest cliRequest) { return populateRequest(cliRequest, cliRequest.request); } @SuppressWarnings("checkstyle:methodlength") private MavenExecutionRequest populateRequest(CliRequest cliRequest, MavenExecutionRequest request) { CommandLine commandLine = cliRequest.commandLine; String workingDirectory = cliRequest.workingDirectory; boolean quiet = cliRequest.quiet; boolean showErrors = cliRequest.showErrors; String[] deprecatedOptions = {"up", "npu", "cpu", "npr"}; for (String deprecatedOption : deprecatedOptions) { if (commandLine.hasOption(deprecatedOption)) { slf4jLogger.warn( "Command line option -{} is deprecated and will be removed in future Maven versions.", deprecatedOption); } } // ---------------------------------------------------------------------- // Now that we have everything that we need we will fire up plexus and // bring the maven component to life for use. // ---------------------------------------------------------------------- if (commandLine.hasOption(CLIManager.BATCH_MODE)) { request.setInteractiveMode(false); } boolean noSnapshotUpdates = false; if (commandLine.hasOption(CLIManager.SUPRESS_SNAPSHOT_UPDATES)) { noSnapshotUpdates = true; } // ---------------------------------------------------------------------- // // ---------------------------------------------------------------------- List goals = commandLine.getArgList(); boolean recursive = true; // this is the default behavior. String reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_FAST; if (commandLine.hasOption(CLIManager.NON_RECURSIVE)) { recursive = false; } if (commandLine.hasOption(CLIManager.FAIL_FAST)) { reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_FAST; } else if (commandLine.hasOption(CLIManager.FAIL_AT_END)) { reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_AT_END; } else if (commandLine.hasOption(CLIManager.FAIL_NEVER)) { reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_NEVER; } if (commandLine.hasOption(CLIManager.OFFLINE)) { request.setOffline(true); } boolean updateSnapshots = false; if (commandLine.hasOption(CLIManager.UPDATE_SNAPSHOTS)) { updateSnapshots = true; } String globalChecksumPolicy = null; if (commandLine.hasOption(CLIManager.CHECKSUM_FAILURE_POLICY)) { globalChecksumPolicy = MavenExecutionRequest.CHECKSUM_POLICY_FAIL; } else if (commandLine.hasOption(CLIManager.CHECKSUM_WARNING_POLICY)) { globalChecksumPolicy = MavenExecutionRequest.CHECKSUM_POLICY_WARN; } File baseDirectory = new File(workingDirectory, "").getAbsoluteFile(); // ---------------------------------------------------------------------- // Profile Activation // ---------------------------------------------------------------------- List activeProfiles = new ArrayList<>(); List inactiveProfiles = new ArrayList<>(); if (commandLine.hasOption(CLIManager.ACTIVATE_PROFILES)) { String[] profileOptionValues = commandLine.getOptionValues(CLIManager.ACTIVATE_PROFILES); if (profileOptionValues != null) { for (String profileOptionValue : profileOptionValues) { StringTokenizer profileTokens = new StringTokenizer(profileOptionValue, ","); while (profileTokens.hasMoreTokens()) { String profileAction = profileTokens.nextToken().trim(); if (profileAction.startsWith("-") || profileAction.startsWith("!")) { inactiveProfiles.add(profileAction.substring(1)); } else if (profileAction.startsWith("+")) { activeProfiles.add(profileAction.substring(1)); } else { activeProfiles.add(profileAction); } } } } } TransferListener transferListener; if (quiet || cliRequest.commandLine.hasOption(CLIManager.NO_TRANSFER_PROGRESS)) { transferListener = new QuietMavenTransferListener(); } else if (request.isInteractiveMode() && !cliRequest.commandLine.hasOption(CLIManager.LOG_FILE)) { // // If we're logging to a file then we don't want the console transfer listener as it will spew // download progress all over the place // transferListener = getConsoleTransferListener(cliRequest.commandLine.hasOption(CLIManager.DEBUG)); } else { transferListener = getBatchTransferListener(); } ExecutionListener executionListener = new ExecutionEventLogger(); if (eventSpyDispatcher != null) { executionListener = eventSpyDispatcher.chainListener(executionListener); } String alternatePomFile = null; if (commandLine.hasOption(CLIManager.ALTERNATE_POM_FILE)) { alternatePomFile = commandLine.getOptionValue(CLIManager.ALTERNATE_POM_FILE); } request.setBaseDirectory(baseDirectory) .setGoals(goals) .setSystemProperties(cliRequest.systemProperties) .setUserProperties(cliRequest.userProperties) .setReactorFailureBehavior(reactorFailureBehaviour) // default: fail fast .setRecursive(recursive) // default: true .setShowErrors(showErrors) // default: false .addActiveProfiles(activeProfiles) // optional .addInactiveProfiles(inactiveProfiles) // optional .setExecutionListener(executionListener) .setTransferListener(transferListener) // default: batch mode which goes along with interactive .setUpdateSnapshots(updateSnapshots) // default: false .setNoSnapshotUpdates(noSnapshotUpdates) // default: false .setGlobalChecksumPolicy(globalChecksumPolicy) // default: warn .setMultiModuleProjectDirectory(cliRequest.multiModuleProjectDirectory); if (alternatePomFile != null) { File pom = resolveFile(new File(alternatePomFile), workingDirectory); if (pom.isDirectory()) { pom = new File(pom, "pom.xml"); } request.setPom(pom); } else if (modelProcessor != null) { File pom = modelProcessor.locatePom(baseDirectory); if (pom.isFile()) { request.setPom(pom); } } if ((request.getPom() != null) && (request.getPom().getParentFile() != null)) { request.setBaseDirectory(request.getPom().getParentFile()); } if (commandLine.hasOption(CLIManager.RESUME_FROM)) { request.setResumeFrom(commandLine.getOptionValue(CLIManager.RESUME_FROM)); } if (commandLine.hasOption(CLIManager.PROJECT_LIST)) { String[] projectOptionValues = commandLine.getOptionValues(CLIManager.PROJECT_LIST); List inclProjects = new ArrayList<>(); List exclProjects = new ArrayList<>(); if (projectOptionValues != null) { for (String projectOptionValue : projectOptionValues) { StringTokenizer projectTokens = new StringTokenizer(projectOptionValue, ","); while (projectTokens.hasMoreTokens()) { String projectAction = projectTokens.nextToken().trim(); if (projectAction.startsWith("-") || projectAction.startsWith("!")) { exclProjects.add(projectAction.substring(1)); } else if (projectAction.startsWith("+")) { inclProjects.add(projectAction.substring(1)); } else { inclProjects.add(projectAction); } } } } request.setSelectedProjects(inclProjects); request.setExcludedProjects(exclProjects); } if (commandLine.hasOption(CLIManager.ALSO_MAKE) && !commandLine.hasOption(CLIManager.ALSO_MAKE_DEPENDENTS)) { request.setMakeBehavior(MavenExecutionRequest.REACTOR_MAKE_UPSTREAM); } else if (!commandLine.hasOption(CLIManager.ALSO_MAKE) && commandLine.hasOption(CLIManager.ALSO_MAKE_DEPENDENTS)) { request.setMakeBehavior(MavenExecutionRequest.REACTOR_MAKE_DOWNSTREAM); } else if (commandLine.hasOption(CLIManager.ALSO_MAKE) && commandLine.hasOption(CLIManager.ALSO_MAKE_DEPENDENTS)) { request.setMakeBehavior(MavenExecutionRequest.REACTOR_MAKE_BOTH); } String localRepoProperty = request.getUserProperties().getProperty(MavenCli.LOCAL_REPO_PROPERTY); // TODO Investigate why this can also be a Java system property and not just a Maven user property like // other properties if (localRepoProperty == null) { localRepoProperty = request.getSystemProperties().getProperty(MavenCli.LOCAL_REPO_PROPERTY); } if (localRepoProperty != null) { request.setLocalRepositoryPath(localRepoProperty); } request.setCacheNotFound(true); request.setCacheTransferError(false); // // Builder, concurrency and parallelism // // We preserve the existing methods for builder selection which is to look for various inputs in the threading // configuration. We don't have an easy way to allow a pluggable builder to provide its own configuration // parameters but this is sufficient for now. Ultimately we want components like Builders to provide a way to // extend the command line to accept its own configuration parameters. // final String threadConfiguration = commandLine.hasOption(CLIManager.THREADS) ? commandLine.getOptionValue(CLIManager.THREADS) : null; if (threadConfiguration != null) { int degreeOfConcurrency = calculateDegreeOfConcurrency(threadConfiguration); if (degreeOfConcurrency > 1) { request.setBuilderId("multithreaded"); request.setDegreeOfConcurrency(degreeOfConcurrency); } } // // Allow the builder to be overridden by the user if requested. The builders are now pluggable. // if (commandLine.hasOption(CLIManager.BUILDER)) { request.setBuilderId(commandLine.getOptionValue(CLIManager.BUILDER)); } return request; } int calculateDegreeOfConcurrency(String threadConfiguration) { if (threadConfiguration.endsWith("C")) { threadConfiguration = threadConfiguration.substring(0, threadConfiguration.length() - 1); if (!NumberUtils.isParsable(threadConfiguration)) { throw new IllegalArgumentException("Invalid threads core multiplier value: '" + threadConfiguration + "C'. Supported are int and float values ending with C."); } float coreMultiplier = Float.parseFloat(threadConfiguration); if (coreMultiplier <= 0.0f) { throw new IllegalArgumentException("Invalid threads core multiplier value: '" + threadConfiguration + "C'. Value must be positive."); } int procs = Runtime.getRuntime().availableProcessors(); int threads = (int) (coreMultiplier * procs); return threads == 0 ? 1 : threads; } else { if (!NumberUtils.isParsable(threadConfiguration)) { throw new IllegalArgumentException( "Invalid threads value: '" + threadConfiguration + "'. Supported are int values."); } try { int threads = Integer.parseInt(threadConfiguration); if (threads <= 0) { throw new IllegalArgumentException( "Invalid threads value: '" + threadConfiguration + "'. Value must be positive."); } return threads; } catch (NumberFormatException e) { throw new IllegalArgumentException( "Invalid threads value: '" + threadConfiguration + "'. Supported are integer values."); } } } // ---------------------------------------------------------------------- // Properties handling // ---------------------------------------------------------------------- static void populateProperties(CliRequest cliRequest, Properties systemProperties, Properties userProperties) throws InterpolationException { // ---------------------------------------------------------------------- // Options that are set on the command line become system properties // and therefore are set in the session properties. System properties // are most dominant. // ---------------------------------------------------------------------- Properties cliProperties = new Properties(); if (cliRequest.commandLine.hasOption(CLIManager.SET_USER_PROPERTY)) { String[] defStrs = cliRequest.commandLine.getOptionValues(CLIManager.SET_USER_PROPERTY); if (defStrs != null) { String name; String value; for (String property : defStrs) { int i = property.indexOf('='); if (i <= 0) { name = property.trim(); value = "true"; } else { name = property.substring(0, i).trim(); value = property.substring(i + 1); } cliProperties.setProperty(name, value); } } } EnvironmentUtils.addEnvVars(systemProperties); SystemProperties.addSystemProperties(systemProperties); StringSearchInterpolator interpolator = createInterpolator(cliRequest, cliProperties, systemProperties); for (Map.Entry e : cliProperties.entrySet()) { String name = (String) e.getKey(); String value = interpolator.interpolate((String) e.getValue()); userProperties.setProperty(name, value); } systemProperties.putAll(userProperties); // ---------------------------------------------------------------------- // I'm leaving the setting of system properties here as not to break // the SystemPropertyProfileActivator. This won't harm embedding. jvz. // ---------------------------------------------------------------------- userProperties.forEach((k, v) -> System.setProperty((String) k, (String) v)); // ---------------------------------------------------------------------- // Properties containing info about the currently running version of Maven // These override any corresponding properties set on the command line // ---------------------------------------------------------------------- Properties buildProperties = CLIReportingUtils.getBuildProperties(); String mavenVersion = buildProperties.getProperty(CLIReportingUtils.BUILD_VERSION_PROPERTY); systemProperties.setProperty("maven.version", mavenVersion); String mavenBuildVersion = CLIReportingUtils.createMavenVersionString(buildProperties); systemProperties.setProperty("maven.build.version", mavenBuildVersion); } protected boolean isAcceptableRootDirectory(Path path) { return path != null && Files.isDirectory(path.resolve(DOT_MVN)); } protected Path searchAcceptableRootDirectory(Path path) { if (path == null) { return null; } if (isAcceptableRootDirectory(path)) { return path; } return searchAcceptableRootDirectory(path.getParent()); } protected static StringSearchInterpolator createInterpolator(CliRequest cliRequest, Properties... properties) { StringSearchInterpolator interpolator = new StringSearchInterpolator(); interpolator.addValueSource(new AbstractValueSource(false) { @Override public Object getValue(String expression) { if ("session.topDirectory".equals(expression)) { Path topDirectory = cliRequest.topDirectory; if (topDirectory != null) { return topDirectory.toString(); } else { throw new IllegalUseOfUndefinedProperty(expression); } } else if ("session.rootDirectory".equals(expression)) { Path rootDirectory = cliRequest.rootDirectory; if (rootDirectory != null) { return rootDirectory.toString(); } else { throw new IllegalUseOfUndefinedProperty(expression); } } return null; } }); interpolator.addValueSource(new AbstractValueSource(false) { @Override public Object getValue(String expression) { for (Properties props : properties) { Object val = props.getProperty(expression); if (val != null) { return val; } } return null; } }); return interpolator; } private static Path getCanonicalPath(Path path) { try { return path.toRealPath(); } catch (IOException e) { return getCanonicalPath(path.getParent()).resolve(path.getFileName()); } } static class ExitException extends Exception { int exitCode; ExitException(int exitCode) { this.exitCode = exitCode; } } static class IllegalUseOfUndefinedProperty extends IllegalArgumentException { final String property; IllegalUseOfUndefinedProperty(String property) { this.property = property; } } // // Customizations available via the CLI // protected TransferListener getConsoleTransferListener(boolean printResourceNames) { return new ConsoleMavenTransferListener(System.out, printResourceNames); } protected TransferListener getBatchTransferListener() { return new Slf4jMavenTransferListener(); } protected void customizeContainer(PlexusContainer container) {} protected ModelProcessor createModelProcessor(PlexusContainer container) throws ComponentLookupException { return container.lookup(ModelProcessor.class); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy