diff --git a/impl/maven-cli/pom.xml b/impl/maven-cli/pom.xml
index ac80ed94b77d..15e4ce5837f7 100644
--- a/impl/maven-cli/pom.xml
+++ b/impl/maven-cli/pom.xml
@@ -310,6 +310,8 @@ under the License.
false
${basedir}/src/test/resources/mavenHome
+ ${basedir}/src/test/resources/userHome
+ ${basedir}/src/test/resources/userDir
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseParser.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseParser.java
index d4b99f2ec17a..4e9724b59a46 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseParser.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseParser.java
@@ -56,6 +56,7 @@
import org.apache.maven.properties.internal.SystemProperties;
import static java.util.Objects.requireNonNull;
+import static org.apache.maven.cling.invoker.CliUtils.createInterpolator;
import static org.apache.maven.cling.invoker.CliUtils.getCanonicalPath;
import static org.apache.maven.cling.invoker.CliUtils.or;
import static org.apache.maven.cling.invoker.CliUtils.prefix;
@@ -399,6 +400,7 @@ protected Map populateSystemProperties(LocalContext context) {
protected Map populateUserProperties(LocalContext context) {
Properties userProperties = new Properties();
+ Map paths = context.extraInterpolationSource();
// ----------------------------------------------------------------------
// Options that are set on the command line become system properties
@@ -407,12 +409,12 @@ protected Map populateUserProperties(LocalContext context) {
// ----------------------------------------------------------------------
Map userSpecifiedProperties =
- context.options.userProperties().orElse(new HashMap<>());
+ new HashMap<>(context.options.userProperties().orElse(new HashMap<>()));
+ createInterpolator().interpolate(userSpecifiedProperties, paths::get);
// ----------------------------------------------------------------------
// Load config files
// ----------------------------------------------------------------------
- Map paths = context.extraInterpolationSource();
UnaryOperator callback =
or(paths::get, prefix("cli.", userSpecifiedProperties::get), context.systemProperties::get);
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/CommonsCliOptions.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/CommonsCliOptions.java
index 028e3b54cf4d..f78bcb0f5216 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/CommonsCliOptions.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/CommonsCliOptions.java
@@ -46,6 +46,10 @@
import static org.apache.maven.cling.invoker.CliUtils.toMap;
public class CommonsCliOptions implements Options {
+ public static CommonsCliOptions parse(String source, String[] args) throws ParseException {
+ CLIManager cliManager = new CLIManager();
+ return new CommonsCliOptions(source, cliManager, cliManager.parse(args));
+ }
protected final String source;
protected final CLIManager cliManager;
diff --git a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/BaseParserTest.java b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/BaseParserTest.java
new file mode 100644
index 000000000000..08546b194a9a
--- /dev/null
+++ b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/BaseParserTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.cling.invoker;
+
+import java.nio.file.Path;
+import java.util.Arrays;
+
+import org.apache.commons.cli.ParseException;
+import org.apache.maven.api.cli.InvokerRequest;
+import org.apache.maven.api.cli.Options;
+import org.apache.maven.api.cli.ParserRequest;
+import org.apache.maven.api.services.MessageBuilderFactory;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import static org.mockito.Mockito.mock;
+
+public class BaseParserTest {
+ private final BaseParser subject = new BaseParser() {
+ @Override
+ protected Options parseCliOptions(LocalContext context) {
+ try {
+ return CommonsCliOptions.parse(
+ "test", context.parserRequest.args().toArray(new String[0]));
+ } catch (ParseException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+ };
+
+ @Test
+ void happy() {
+ InvokerRequest invokerRequest =
+ subject.parseInvocation(ParserRequest.mvn(Arrays.asList("-e", "-X"), mock(MessageBuilderFactory.class))
+ .cwd(Path.of(System.getProperty("userDir")))
+ .userHome(Path.of(System.getProperty("userHome")))
+ .build());
+
+ Assertions.assertTrue(invokerRequest.options().isPresent());
+ Options options = invokerRequest.options().orElseThrow();
+ Assertions.assertFalse(options.showVersion().orElse(false));
+ Assertions.assertFalse(options.showVersionAndExit().orElse(false));
+ Assertions.assertTrue(options.showErrors().orElse(false));
+ Assertions.assertTrue(options.verbose().orElse(false));
+
+ // user home
+ Assertions.assertTrue(invokerRequest.userProperties().containsKey("user.property"));
+ Assertions.assertEquals("yes it is", invokerRequest.userProperties().get("user.property"));
+
+ // maven installation
+ Assertions.assertTrue(invokerRequest.userProperties().containsKey("maven.property"));
+ Assertions.assertEquals("yes it is", invokerRequest.userProperties().get("maven.property"));
+ }
+
+ @Test
+ void notHappy() {
+ InvokerRequest invokerRequest = subject.parseInvocation(
+ ParserRequest.mvn(Arrays.asList("--what-is-this-option", "-X"), mock(MessageBuilderFactory.class))
+ .cwd(Path.of(System.getProperty("userDir")))
+ .userHome(Path.of(System.getProperty("userHome")))
+ .build());
+
+ Assertions.assertFalse(invokerRequest.options().isPresent());
+ Assertions.assertTrue(invokerRequest.parsingFailed());
+ }
+
+ @Test
+ void specials() {
+ InvokerRequest invokerRequest = subject.parseInvocation(ParserRequest.mvn(
+ Arrays.asList("-Dfoo=${session.rootDirectory}", "-X"), mock(MessageBuilderFactory.class))
+ .cwd(Path.of(System.getProperty("userDir")))
+ .userHome(Path.of(System.getProperty("userHome")))
+ .build());
+
+ Assertions.assertTrue(invokerRequest.options().isPresent());
+ Assertions.assertTrue(invokerRequest.userProperties().containsKey("foo"));
+ Assertions.assertNotEquals(
+ "${session.rootDirectory}", invokerRequest.userProperties().get("foo"));
+ Assertions.assertFalse(invokerRequest.userProperties().get("foo").trim().isEmpty());
+ }
+}
diff --git a/impl/maven-cli/src/test/resources/mavenHome/conf/maven.properties b/impl/maven-cli/src/test/resources/mavenHome/conf/maven.properties
index 1e53fa5df399..7660a67d3102 100644
--- a/impl/maven-cli/src/test/resources/mavenHome/conf/maven.properties
+++ b/impl/maven-cli/src/test/resources/mavenHome/conf/maven.properties
@@ -23,6 +23,7 @@
# The properties defined in this file will be made available through
# user properties at the very beginning of Maven's boot process.
#
+maven.property = yes it is
maven.installation.conf = ${maven.home}/conf
maven.user.conf = ${user.home}/.m2
diff --git a/impl/maven-cli/src/test/resources/userHome/.m2/maven.properties b/impl/maven-cli/src/test/resources/userHome/.m2/maven.properties
new file mode 100644
index 000000000000..388eea918068
--- /dev/null
+++ b/impl/maven-cli/src/test/resources/userHome/.m2/maven.properties
@@ -0,0 +1 @@
+user.property=yes it is
\ No newline at end of file
diff --git a/impl/maven-cli/src/test/resources/userHome/.m2/settings.xml b/impl/maven-cli/src/test/resources/userHome/.m2/settings.xml
new file mode 100644
index 000000000000..0ead13db8d6f
--- /dev/null
+++ b/impl/maven-cli/src/test/resources/userHome/.m2/settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file