diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/InvokerRequest.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/InvokerRequest.java index 34418a5fcc..d38d0cc633 100644 --- a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/InvokerRequest.java +++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/InvokerRequest.java @@ -125,13 +125,14 @@ public interface InvokerRequest { Path topDirectory(); /** - * Returns the root directory of the Maven invocation. - * This is determined by the presence of a .mvn directory or a POM with the root="true" property. + * Returns the root directory of the Maven invocation, if found. This is determined by the presence of a + * {@code .mvn} directory or a POM with the root="true" property but is not always applicable (ie invocation + * from outside a checkout). * - * @return the root directory path + * @return the root directory path, if present */ @Nonnull - Path rootDirectory(); + Optional rootDirectory(); /** * Returns the input stream for the Maven execution, if running in embedded mode. diff --git a/maven-api-impl/src/main/java/org/apache/maven/api/services/model/RootLocator.java b/maven-api-impl/src/main/java/org/apache/maven/api/services/model/RootLocator.java index 5b41bec688..5cb8bd841f 100644 --- a/maven-api-impl/src/main/java/org/apache/maven/api/services/model/RootLocator.java +++ b/maven-api-impl/src/main/java/org/apache/maven/api/services/model/RootLocator.java @@ -22,6 +22,7 @@ import java.nio.file.Path; import org.apache.maven.api.Service; import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; /** * Interface used to locate the root directory for a given project. @@ -40,6 +41,9 @@ public interface RootLocator extends Service { + "Create a .mvn directory in the root directory or add the root=\"true\"" + " attribute on the root project's model to identify it."; + @Nullable + Path findRoot(@Nonnull Path basedir); + @Nonnull Path findMandatoryRoot(@Nonnull Path basedir); diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/rootlocator/DefaultRootLocator.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/rootlocator/DefaultRootLocator.java index 19d514b84d..83850082dd 100644 --- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/rootlocator/DefaultRootLocator.java +++ b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/rootlocator/DefaultRootLocator.java @@ -46,13 +46,19 @@ public class DefaultRootLocator implements RootLocator { .toList(); } - @Nonnull - public Path findMandatoryRoot(@Nonnull Path basedir) { + @Override + public Path findRoot(Path basedir) { requireNonNull(basedir, "basedir is null"); Path rootDirectory = basedir; while (rootDirectory != null && !isRootDirectory(rootDirectory)) { rootDirectory = rootDirectory.getParent(); } + return rootDirectory; + } + + @Nonnull + public Path findMandatoryRoot(@Nonnull Path basedir) { + Path rootDirectory = findRoot(basedir); Optional rdf = getRootDirectoryFallback(); if (rootDirectory == null) { rootDirectory = rdf.orElseThrow(() -> new IllegalStateException(getNoRootMessage())); diff --git a/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseInvokerRequest.java b/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseInvokerRequest.java index 1aa26ca7be..0b1bd847d7 100644 --- a/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseInvokerRequest.java +++ b/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseInvokerRequest.java @@ -114,8 +114,8 @@ public abstract class BaseInvokerRequest implements InvokerRe } @Override - public Path rootDirectory() { - return rootDirectory; + public Optional rootDirectory() { + return Optional.ofNullable(rootDirectory); } @Override diff --git a/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseParser.java b/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseParser.java index 2ae2bd94d6..144ed3d583 100644 --- a/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseParser.java +++ b/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseParser.java @@ -32,17 +32,16 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; -import java.util.ServiceLoader; import java.util.function.Function; import org.apache.maven.api.Constants; +import org.apache.maven.api.annotations.Nullable; import org.apache.maven.api.cli.InvokerRequest; import org.apache.maven.api.cli.Options; import org.apache.maven.api.cli.Parser; import org.apache.maven.api.cli.ParserException; import org.apache.maven.api.cli.ParserRequest; import org.apache.maven.api.cli.extensions.CoreExtension; -import org.apache.maven.api.services.model.RootLocator; import org.apache.maven.cli.CLIReportingUtils; import org.apache.maven.cli.internal.extension.io.CoreExtensionsStaxReader; import org.apache.maven.cli.props.MavenPropertiesLoader; @@ -76,14 +75,19 @@ public abstract class BaseParser> public Map systemProperties; public Map userProperties; public Path topDirectory; + + @Nullable public Path rootDirectory; + public List extensions; public Options options; public Map extraInterpolationSource() { Map extra = new HashMap<>(); extra.put("session.topDirectory", topDirectory.toString()); - extra.put("session.rootDirectory", rootDirectory.toString()); + if (rootDirectory != null) { + extra.put("session.rootDirectory", rootDirectory.toString()); + } return extra; } } @@ -101,7 +105,7 @@ public abstract class BaseParser> // top/root context.topDirectory = requireNonNull(getTopDirectory(context)); - context.rootDirectory = requireNonNull(getRootDirectory(context)); + context.rootDirectory = getRootDirectory(context); // options List parsedOptions = parseCliOptions(context); @@ -194,11 +198,9 @@ public abstract class BaseParser> return getCanonicalPath(topDirectory); } + @Nullable protected Path getRootDirectory(LocalContext context) throws ParserException { - return getCanonicalPath(ServiceLoader.load(RootLocator.class) - .iterator() - .next() - .findMandatoryRoot(requireNonNull(context.topDirectory))); + return Utils.findRoot(context.topDirectory); } protected Map populateSystemProperties(LocalContext context) throws ParserException { diff --git a/maven-cli/src/main/java/org/apache/maven/cling/invoker/LookupInvoker.java b/maven-cli/src/main/java/org/apache/maven/cling/invoker/LookupInvoker.java index 2055c54b60..095a2c0226 100644 --- a/maven-cli/src/main/java/org/apache/maven/cling/invoker/LookupInvoker.java +++ b/maven-cli/src/main/java/org/apache/maven/cling/invoker/LookupInvoker.java @@ -643,11 +643,13 @@ public abstract class LookupInvoker< request.setBaseDirectory(context.invokerRequest.topDirectory().toFile()); request.setSystemProperties(toProperties(context.invokerRequest.systemProperties())); request.setUserProperties(toProperties(context.invokerRequest.userProperties())); - request.setMultiModuleProjectDirectory( - context.invokerRequest.rootDirectory().toFile()); - request.setRootDirectory(context.invokerRequest.rootDirectory()); request.setTopDirectory(context.invokerRequest.topDirectory()); + if (context.invokerRequest.rootDirectory().isPresent()) { + request.setMultiModuleProjectDirectory( + context.invokerRequest.rootDirectory().get().toFile()); + request.setRootDirectory(context.invokerRequest.rootDirectory().get()); + } request.addPluginGroup("org.apache.maven.plugins"); request.addPluginGroup("org.codehaus.mojo"); diff --git a/maven-cli/src/main/java/org/apache/maven/cling/invoker/Utils.java b/maven-cli/src/main/java/org/apache/maven/cling/invoker/Utils.java index 8d358bd22d..2cb01e2c19 100644 --- a/maven-cli/src/main/java/org/apache/maven/cling/invoker/Utils.java +++ b/maven-cli/src/main/java/org/apache/maven/cling/invoker/Utils.java @@ -24,9 +24,14 @@ import java.nio.file.Path; import java.util.Collection; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import java.util.Properties; +import java.util.ServiceLoader; import java.util.function.Function; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; +import org.apache.maven.api.services.model.RootLocator; import org.apache.maven.cli.logging.Slf4jConfiguration; import org.apache.maven.execution.MavenExecutionRequest; import org.codehaus.plexus.interpolation.AbstractValueSource; @@ -42,6 +47,7 @@ import static java.util.Objects.requireNonNull; public final class Utils { private Utils() {} + @Nullable public static File toFile(Path path) { if (path != null) { return path.toFile(); @@ -49,6 +55,7 @@ public final class Utils { return null; } + @Nonnull public static String stripLeadingAndTrailingQuotes(String str) { requireNonNull(str, "str"); final int length = str.length(); @@ -61,6 +68,7 @@ public final class Utils { return str; } + @Nonnull public static Path getCanonicalPath(Path path) { requireNonNull(path, "path"); try { @@ -70,6 +78,7 @@ public final class Utils { } } + @Nonnull public static Map toMap(Properties properties) { requireNonNull(properties, "properties"); HashMap map = new HashMap<>(); @@ -79,6 +88,7 @@ public final class Utils { return map; } + @Nonnull public static Properties toProperties(Map properties) { requireNonNull(properties, "properties"); Properties map = new Properties(); @@ -88,6 +98,7 @@ public final class Utils { return map; } + @Nonnull public static BasicInterpolator createInterpolator(Collection> properties) { StringSearchInterpolator interpolator = new StringSearchInterpolator(); interpolator.addValueSource(new AbstractValueSource(false) { @@ -105,6 +116,7 @@ public final class Utils { return interpolator; } + @Nonnull public static Function prefix(String prefix, Function cb) { return s -> { String v = null; @@ -115,6 +127,8 @@ public final class Utils { }; } + @SafeVarargs + @Nonnull public static Function or(Function... callbacks) { return s -> { for (Function cb : callbacks) { @@ -144,4 +158,23 @@ public final class Utils { case ERROR -> Logger.LEVEL_ERROR; }; } + + @Nullable + public static Path findRoot(Path topDirectory) { + requireNonNull(topDirectory, "topDirectory"); + Path rootDirectory = + ServiceLoader.load(RootLocator.class).iterator().next().findRoot(topDirectory); + if (rootDirectory != null) { + return getCanonicalPath(rootDirectory); + } + return null; + } + + @Nonnull + public static Path findMandatoryRoot(Path topDirectory) { + requireNonNull(topDirectory, "topDirectory"); + return getCanonicalPath(Optional.ofNullable( + ServiceLoader.load(RootLocator.class).iterator().next().findMandatoryRoot(topDirectory)) + .orElseThrow()); + } } diff --git a/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/BaseMavenParser.java b/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/BaseMavenParser.java index 76dbc5de51..d2b5752f0c 100644 --- a/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/BaseMavenParser.java +++ b/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/BaseMavenParser.java @@ -42,8 +42,8 @@ public abstract class BaseMavenParser