You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by gn...@apache.org on 2026/03/23 12:07:01 UTC
(maven) 01/01: [MNG-8571] Fix MAVEN_ARGS parsing to handle quoted strings with spaces
This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a commit to branch fix/maven-args-parsing
in repository https://gitbox.apache.org/repos/asf/maven.git
commit f3a7fb92113beed72312e6c5f15655ef778fddd6
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Mon Mar 23 13:06:47 2026 +0100
[MNG-8571] Fix MAVEN_ARGS parsing to handle quoted strings with spaces
Replace naive String.split(" ") with a proper argument parser that
handles single and double quoted strings, preserving backslashes
for Windows paths. This fixes MAVEN_ARGS values like:
-f "C:\Program Files\project\pom.xml"
Applied to both EmbeddedMavenExecutor and ForkedMavenExecutor.
Co-Authored-By: Claude Opus 4.6 <no...@anthropic.com>
---
.../executor/embedded/EmbeddedMavenExecutor.java | 50 ++++++++++++++++++++--
.../cling/executor/forked/ForkedMavenExecutor.java | 48 +++++++++++++++++++--
.../embedded/EmbeddedMavenExecutorTest.java | 35 +++++++++++++++
.../executor/forked/ForkedMavenExecutorTest.java | 34 +++++++++++++++
4 files changed, 159 insertions(+), 8 deletions(-)
diff --git a/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/embedded/EmbeddedMavenExecutor.java b/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/embedded/EmbeddedMavenExecutor.java
index e5eda275d5..8986bb371b 100644
--- a/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/embedded/EmbeddedMavenExecutor.java
+++ b/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/embedded/EmbeddedMavenExecutor.java
@@ -31,7 +31,6 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -225,9 +224,9 @@ protected Context doCreate(Path mavenHome, ExecutorRequest executorRequest) {
ArrayList<String> mavenArgs = new ArrayList<>();
String mavenArgsEnv = System.getenv("MAVEN_ARGS");
if (useMavenArgsEnv && mavenArgsEnv != null && !mavenArgsEnv.isEmpty()) {
- Arrays.stream(mavenArgsEnv.split(" "))
- .filter(s -> !s.trim().isEmpty())
- .forEach(s -> mavenArgs.add(0, s));
+ List<String> parsed = parseArguments(mavenArgsEnv);
+ Collections.reverse(parsed);
+ mavenArgs.addAll(parsed);
}
Properties properties = prepareProperties(executorRequest);
@@ -441,4 +440,47 @@ protected String getMavenVersion(Class<?> clazz) throws IOException {
return UNKNOWN_VERSION;
}
}
+
+ /**
+ * Parses a string of arguments respecting quoted strings.
+ * Handles both single and double quotes, and preserves backslashes
+ * (important for Windows paths).
+ */
+ static List<String> parseArguments(String args) {
+ List<String> result = new ArrayList<>();
+ StringBuilder current = new StringBuilder();
+ boolean inDoubleQuotes = false;
+ boolean inSingleQuotes = false;
+ for (int i = 0; i < args.length(); i++) {
+ char c = args.charAt(i);
+ if (inDoubleQuotes) {
+ if (c == '"') {
+ inDoubleQuotes = false;
+ } else {
+ current.append(c);
+ }
+ } else if (inSingleQuotes) {
+ if (c == '\'') {
+ inSingleQuotes = false;
+ } else {
+ current.append(c);
+ }
+ } else if (c == '"') {
+ inDoubleQuotes = true;
+ } else if (c == '\'') {
+ inSingleQuotes = true;
+ } else if (Character.isWhitespace(c)) {
+ if (!current.isEmpty()) {
+ result.add(current.toString());
+ current.setLength(0);
+ }
+ } else {
+ current.append(c);
+ }
+ }
+ if (!current.isEmpty()) {
+ result.add(current.toString());
+ }
+ return result;
+ }
}
diff --git a/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/forked/ForkedMavenExecutor.java b/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/forked/ForkedMavenExecutor.java
index a559a24baf..4bea89c720 100644
--- a/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/forked/ForkedMavenExecutor.java
+++ b/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/forked/ForkedMavenExecutor.java
@@ -27,7 +27,6 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.CountDownLatch;
@@ -116,9 +115,7 @@ protected int doExecute(ExecutorRequest executorRequest) throws ExecutorExceptio
String mavenArgsEnv = System.getenv("MAVEN_ARGS");
if (useMavenArgsEnv && mavenArgsEnv != null && !mavenArgsEnv.isEmpty()) {
- Arrays.stream(mavenArgsEnv.split(" "))
- .filter(s -> !s.trim().isEmpty())
- .forEach(cmdAndArguments::add);
+ cmdAndArguments.addAll(parseArguments(mavenArgsEnv));
}
cmdAndArguments.addAll(executorRequest.arguments());
@@ -223,4 +220,47 @@ public void close() throws ExecutorException {
// nothing yet
}
}
+
+ /**
+ * Parses a string of arguments respecting quoted strings.
+ * Handles both single and double quotes, and preserves backslashes
+ * (important for Windows paths).
+ */
+ static List<String> parseArguments(String args) {
+ List<String> result = new ArrayList<>();
+ StringBuilder current = new StringBuilder();
+ boolean inDoubleQuotes = false;
+ boolean inSingleQuotes = false;
+ for (int i = 0; i < args.length(); i++) {
+ char c = args.charAt(i);
+ if (inDoubleQuotes) {
+ if (c == '"') {
+ inDoubleQuotes = false;
+ } else {
+ current.append(c);
+ }
+ } else if (inSingleQuotes) {
+ if (c == '\'') {
+ inSingleQuotes = false;
+ } else {
+ current.append(c);
+ }
+ } else if (c == '"') {
+ inDoubleQuotes = true;
+ } else if (c == '\'') {
+ inSingleQuotes = true;
+ } else if (Character.isWhitespace(c)) {
+ if (!current.isEmpty()) {
+ result.add(current.toString());
+ current.setLength(0);
+ }
+ } else {
+ current.append(c);
+ }
+ }
+ if (!current.isEmpty()) {
+ result.add(current.toString());
+ }
+ return result;
+ }
}
diff --git a/impl/maven-executor/src/test/java/org/apache/maven/cling/executor/embedded/EmbeddedMavenExecutorTest.java b/impl/maven-executor/src/test/java/org/apache/maven/cling/executor/embedded/EmbeddedMavenExecutorTest.java
index c214fc6ffe..45beda77a9 100644
--- a/impl/maven-executor/src/test/java/org/apache/maven/cling/executor/embedded/EmbeddedMavenExecutorTest.java
+++ b/impl/maven-executor/src/test/java/org/apache/maven/cling/executor/embedded/EmbeddedMavenExecutorTest.java
@@ -18,8 +18,13 @@
*/
package org.apache.maven.cling.executor.embedded;
+import java.util.List;
+
import org.apache.maven.api.cli.Executor;
import org.apache.maven.cling.executor.MavenExecutorTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* Embedded executor UT
@@ -30,4 +35,34 @@ public class EmbeddedMavenExecutorTest extends MavenExecutorTestSupport {
protected Executor doSelectExecutor() {
return new EmbeddedMavenExecutor();
}
+
+ @Test
+ void testParseArgumentsSimple() {
+ assertEquals(
+ List.of("-T", "4", "clean", "install"), EmbeddedMavenExecutor.parseArguments("-T 4 clean install"));
+ }
+
+ @Test
+ void testParseArgumentsDoubleQuoted() {
+ assertEquals(
+ List.of("-f", "C:\\Program Files\\project\\pom.xml"),
+ EmbeddedMavenExecutor.parseArguments("-f \"C:\\Program Files\\project\\pom.xml\""));
+ }
+
+ @Test
+ void testParseArgumentsSingleQuoted() {
+ assertEquals(
+ List.of("-f", "/path with spaces/pom.xml"),
+ EmbeddedMavenExecutor.parseArguments("-f '/path with spaces/pom.xml'"));
+ }
+
+ @Test
+ void testParseArgumentsEmpty() {
+ assertEquals(List.of(), EmbeddedMavenExecutor.parseArguments(""));
+ }
+
+ @Test
+ void testParseArgumentsExtraWhitespace() {
+ assertEquals(List.of("clean", "install"), EmbeddedMavenExecutor.parseArguments(" clean install "));
+ }
}
diff --git a/impl/maven-executor/src/test/java/org/apache/maven/cling/executor/forked/ForkedMavenExecutorTest.java b/impl/maven-executor/src/test/java/org/apache/maven/cling/executor/forked/ForkedMavenExecutorTest.java
index 5555e0ba34..8e6ee7cad8 100644
--- a/impl/maven-executor/src/test/java/org/apache/maven/cling/executor/forked/ForkedMavenExecutorTest.java
+++ b/impl/maven-executor/src/test/java/org/apache/maven/cling/executor/forked/ForkedMavenExecutorTest.java
@@ -18,8 +18,13 @@
*/
package org.apache.maven.cling.executor.forked;
+import java.util.List;
+
import org.apache.maven.api.cli.Executor;
import org.apache.maven.cling.executor.MavenExecutorTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* Forked executor UT
@@ -30,4 +35,33 @@ public class ForkedMavenExecutorTest extends MavenExecutorTestSupport {
protected Executor doSelectExecutor() {
return new ForkedMavenExecutor();
}
+
+ @Test
+ void testParseArgumentsSimple() {
+ assertEquals(List.of("-T", "4", "clean", "install"), ForkedMavenExecutor.parseArguments("-T 4 clean install"));
+ }
+
+ @Test
+ void testParseArgumentsDoubleQuoted() {
+ assertEquals(
+ List.of("-f", "C:\\Program Files\\project\\pom.xml"),
+ ForkedMavenExecutor.parseArguments("-f \"C:\\Program Files\\project\\pom.xml\""));
+ }
+
+ @Test
+ void testParseArgumentsSingleQuoted() {
+ assertEquals(
+ List.of("-f", "/path with spaces/pom.xml"),
+ ForkedMavenExecutor.parseArguments("-f '/path with spaces/pom.xml'"));
+ }
+
+ @Test
+ void testParseArgumentsEmpty() {
+ assertEquals(List.of(), ForkedMavenExecutor.parseArguments(""));
+ }
+
+ @Test
+ void testParseArgumentsExtraWhitespace() {
+ assertEquals(List.of("clean", "install"), ForkedMavenExecutor.parseArguments(" clean install "));
+ }
}