Skip to content

Commit 335ba39

Browse files
authored
[MNG-4559] Fix .mvn/jvm.config parsing with spaces, quotes and comments (#2213)
The .mvn/jvm.config file parsing in mvn.cmd needed improvements to properly handle multiple JVM arguments, especially when dealing with comments, quotes and spaces in values. This PR: - Ensures proper parsing of multiple JVM arguments from .mvn/jvm.config - Maintains proper spacing between arguments - Handles both inline and full-line comments correctly - Fixes a bug in the space trimming logic (%%x -> %%i) - Preserves spaces within quoted values The changes are validated by MavenITmng4559MultipleJvmArgsTest which verifies: - Multiple JVM arguments are properly handled - Comments are correctly processed - Arguments with spaces in quotes are preserved - Arguments from multiple lines are properly combined Fixes: https://issues.apache.org/jira/browse/MNG-4559
1 parent 4f33ed4 commit 335ba39

File tree

9 files changed

+309
-24
lines changed

9 files changed

+309
-24
lines changed

apache-maven/src/assembly/maven/bin/mvn

+37-14
Original file line numberDiff line numberDiff line change
@@ -168,11 +168,25 @@ find_file_argument_basedir() {
168168
# concatenates all lines of a file and replaces variables
169169
concat_lines() {
170170
if [ -f "$1" ]; then
171-
# First transform line endings to spaces
172-
content=$(tr -s '\r\n' ' ' < "$1")
173-
# Handle both ${var} and $var formats, only substitute MAVEN_PROJECTBASEDIR
174-
echo "$content" | sed -e "s|\${MAVEN_PROJECTBASEDIR}|$MAVEN_PROJECTBASEDIR|g" \
175-
-e "s|\$MAVEN_PROJECTBASEDIR|$MAVEN_PROJECTBASEDIR|g"
171+
# First convert all CR to LF using tr
172+
tr '\r' '\n' < "$1" | \
173+
sed -e '/^$/d' -e 's/#.*$//' | \
174+
# Split into words and process each argument
175+
xargs -n 1 | \
176+
while read -r arg; do
177+
# Replace variables first
178+
arg=$(echo "$arg" | sed \
179+
-e "s@\${MAVEN_PROJECTBASEDIR}@$MAVEN_PROJECTBASEDIR@g" \
180+
-e "s@\$MAVEN_PROJECTBASEDIR@$MAVEN_PROJECTBASEDIR@g")
181+
182+
# Add quotes only if argument contains spaces and isn't already quoted
183+
if echo "$arg" | grep -q " " && ! echo "$arg" | grep -q "^\".*\"$"; then
184+
echo "\"$arg\""
185+
else
186+
echo "$arg"
187+
fi
188+
done | \
189+
tr '\n' ' '
176190
fi
177191
}
178192

@@ -224,16 +238,25 @@ handle_args() {
224238
handle_args "$@"
225239
MAVEN_MAIN_CLASS=${MAVEN_MAIN_CLASS:=org.apache.maven.cling.MavenCling}
226240

227-
exec "$JAVACMD" \
241+
cmd="\"$JAVACMD\" \
228242
$MAVEN_OPTS \
229243
$MAVEN_DEBUG_OPTS \
230244
--enable-native-access=ALL-UNNAMED \
231-
-classpath "$LAUNCHER_JAR" \
232-
"-Dclassworlds.conf=$CLASSWORLDS_CONF" \
233-
"-Dmaven.home=$MAVEN_HOME" \
234-
"-Dmaven.mainClass=$MAVEN_MAIN_CLASS" \
235-
"-Dlibrary.jline.path=${MAVEN_HOME}/lib/jline-native" \
236-
"-Dmaven.multiModuleProjectDirectory=$MAVEN_PROJECTBASEDIR" \
245+
-classpath \"$LAUNCHER_JAR\" \
246+
\"-Dclassworlds.conf=$CLASSWORLDS_CONF\" \
247+
\"-Dmaven.home=$MAVEN_HOME\" \
248+
\"-Dmaven.mainClass=$MAVEN_MAIN_CLASS\" \
249+
\"-Dlibrary.jline.path=${MAVEN_HOME}/lib/jline-native\" \
250+
\"-Dmaven.multiModuleProjectDirectory=$MAVEN_PROJECTBASEDIR\" \
237251
$LAUNCHER_CLASS \
238-
$MAVEN_ARGS \
239-
"$@"
252+
$MAVEN_ARGS"
253+
# Add remaining arguments with proper quoting
254+
for arg in "$@"; do
255+
cmd="$cmd \"$arg\""
256+
done
257+
258+
# Debug: print the command that will be executed
259+
#echo "About to execute:"
260+
#echo "$cmd"
261+
262+
eval exec "$cmd"

apache-maven/src/assembly/maven/bin/mvn.cmd

+26-5
Original file line numberDiff line numberDiff line change
@@ -177,13 +177,34 @@ if not exist "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadJvmConfig
177177

178178
@setlocal EnableExtensions EnableDelayedExpansion
179179
set JVM_CONFIG_MAVEN_OPTS=
180-
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do (
180+
for /F "usebackq tokens=* delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do (
181181
set "line=%%a"
182-
set "line=!line:$MAVEN_PROJECTBASEDIR=%MAVEN_PROJECTBASEDIR%!"
183-
set "line=!line:${MAVEN_PROJECTBASEDIR}=%MAVEN_PROJECTBASEDIR%!"
184-
set JVM_CONFIG_MAVEN_OPTS=!JVM_CONFIG_MAVEN_OPTS! !line!
182+
183+
rem Skip empty lines and full-line comments
184+
echo !line! | findstr /b /r /c:"[ ]*#" >nul
185+
if errorlevel 1 (
186+
rem Handle end-of-line comments by taking everything before #
187+
for /f "tokens=1* delims=#" %%i in ("!line!") do set "line=%%i"
188+
189+
rem Trim leading/trailing spaces while preserving spaces in quotes
190+
set "trimmed=!line!"
191+
for /f "tokens=* delims= " %%i in ("!trimmed!") do set "trimmed=%%i"
192+
for /l %%i in (1,1,100) do if "!trimmed:~-1!"==" " set "trimmed=!trimmed:~0,-1!"
193+
194+
rem Replace MAVEN_PROJECTBASEDIR placeholders
195+
set "trimmed=!trimmed:${MAVEN_PROJECTBASEDIR}=%MAVEN_PROJECTBASEDIR%!"
196+
set "trimmed=!trimmed:$MAVEN_PROJECTBASEDIR=%MAVEN_PROJECTBASEDIR%!"
197+
198+
if not "!trimmed!"=="" (
199+
if "!JVM_CONFIG_MAVEN_OPTS!"=="" (
200+
set "JVM_CONFIG_MAVEN_OPTS=!trimmed!"
201+
) else (
202+
set "JVM_CONFIG_MAVEN_OPTS=!JVM_CONFIG_MAVEN_OPTS! !trimmed!"
203+
)
204+
)
205+
)
185206
)
186-
@endlocal & set MAVEN_OPTS=%MAVEN_OPTS% %JVM_CONFIG_MAVEN_OPTS%
207+
@endlocal & set "MAVEN_OPTS=%MAVEN_OPTS% %JVM_CONFIG_MAVEN_OPTS%"
187208

188209
:endReadJvmConfig
189210

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.maven.it;
20+
21+
import java.io.File;
22+
import java.nio.file.Files;
23+
import java.util.Properties;
24+
25+
import org.junit.jupiter.api.Test;
26+
27+
import static org.junit.jupiter.api.Assertions.assertEquals;
28+
29+
/**
30+
* This is a test set for MNG-4559:
31+
* - Verifies that multiple JVM arguments in .mvn/jvm.config are properly handled
32+
* - Ensures arguments are correctly split and passed to the JVM
33+
*/
34+
public class MavenITmng4559MultipleJvmArgsTest extends AbstractMavenIntegrationTestCase {
35+
public MavenITmng4559MultipleJvmArgsTest() {
36+
super("[4.0.0-rc-4,)");
37+
}
38+
39+
@Test
40+
void testMultipleJvmArgs() throws Exception {
41+
File testDir = extractResources("/mng-4559-multiple-jvm-args");
42+
File mvnDir = new File(testDir, ".mvn");
43+
File jvmConfig = new File(mvnDir, "jvm.config");
44+
45+
mvnDir.mkdirs();
46+
Files.writeString(
47+
jvmConfig.toPath(),
48+
"# This is a comment\n" + "-Xmx2048m -Xms1024m -Dtest.prop1=value1 # end of line comment\n"
49+
+ "# Another comment\n"
50+
+ "-Dtest.prop2=\"value 2\"");
51+
52+
Verifier verifier = newVerifier(testDir.getAbsolutePath());
53+
verifier.setForkJvm(true);
54+
verifier.addCliArgument("validate");
55+
verifier.execute();
56+
verifier.verifyErrorFreeLog();
57+
58+
Properties props = verifier.loadProperties("target/jvm.properties");
59+
assertEquals("value1", props.getProperty("project.properties.pom.test.prop1"));
60+
assertEquals("value 2", props.getProperty("project.properties.pom.test.prop2"));
61+
}
62+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.maven.it;
20+
21+
import java.nio.file.Path;
22+
import java.util.Properties;
23+
24+
import org.junit.jupiter.api.Test;
25+
26+
import static org.junit.jupiter.api.Assertions.assertEquals;
27+
28+
/**
29+
* This is a test set for <a href="https://issues.apache.org/jira/browse/MNG-4559">MNG-4559</a>.
30+
*/
31+
class MavenITmng4559SpacesInJvmOptsTest extends AbstractMavenIntegrationTestCase {
32+
33+
MavenITmng4559SpacesInJvmOptsTest() {
34+
super("[4.0.0-rc-4,)");
35+
}
36+
37+
/**
38+
* Verify the dependency management of the consumer POM is computed correctly
39+
*/
40+
@Test
41+
void testIt() throws Exception {
42+
Path basedir =
43+
extractResources("/mng-4559-spaces-jvm-opts").getAbsoluteFile().toPath();
44+
45+
Verifier verifier = newVerifier(basedir.toString());
46+
verifier.setEnvironmentVariable("MAVEN_OPTS", "-Dprop.maven-opts=\"foo bar\"");
47+
verifier.addCliArguments("validate");
48+
verifier.execute();
49+
verifier.verifyErrorFreeLog();
50+
51+
Properties props = verifier.loadProperties("target/pom.properties");
52+
assertEquals("foo bar", props.getProperty("project.properties.pom.prop.jvm-opts"));
53+
assertEquals("foo bar", props.getProperty("project.properties.pom.prop.maven-opts"));
54+
}
55+
}

its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng6255FixConcatLines.java

+8-5
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.nio.file.Files;
2323
import java.util.Properties;
2424

25+
import org.junit.jupiter.api.Disabled;
2526
import org.junit.jupiter.api.Test;
2627

2728
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -31,8 +32,8 @@
3132
* Check that the <code>.mvn/jvm.config</code> file contents are concatenated properly, no matter
3233
* what line endings are used.
3334
*/
34-
public class MavenITmng6255FixConcatLines extends AbstractMavenIntegrationTestCase {
35-
public MavenITmng6255FixConcatLines() {
35+
class MavenITmng6255FixConcatLines extends AbstractMavenIntegrationTestCase {
36+
MavenITmng6255FixConcatLines() {
3637
super("[3.5.3,)");
3738
}
3839

@@ -43,7 +44,9 @@ public MavenITmng6255FixConcatLines() {
4344
*
4445
* @throws Exception in case of failure
4546
*/
46-
public void disabledJvmConfigFileCR() throws Exception {
47+
@Test
48+
@Disabled
49+
void testJvmConfigFileCR() throws Exception {
4750
runWithLineEndings("\r");
4851
}
4952

@@ -53,7 +56,7 @@ public void disabledJvmConfigFileCR() throws Exception {
5356
* @throws Exception in case of failure
5457
*/
5558
@Test
56-
public void testJvmConfigFileLF() throws Exception {
59+
void testJvmConfigFileLF() throws Exception {
5760
runWithLineEndings("\n");
5861
}
5962

@@ -63,7 +66,7 @@ public void testJvmConfigFileLF() throws Exception {
6366
* @throws Exception in case of failure
6467
*/
6568
@Test
66-
public void testJvmConfigFileCRLF() throws Exception {
69+
void testJvmConfigFileCRLF() throws Exception {
6770
runWithLineEndings("\r\n");
6871
}
6972

its/core-it-suite/src/test/java/org/apache/maven/it/TestSuiteOrdering.java

+2
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ public TestSuiteOrdering() {
101101
* the tests are to finishing. Newer tests are also more likely to fail, so this is
102102
* a fail fast technique as well.
103103
*/
104+
suite.addTestSuite(MavenITmng4559MultipleJvmArgsTest.class);
105+
suite.addTestSuite(MavenITmng4559SpacesInJvmOptsTest.class);
104106
suite.addTestSuite(MavenITmng8598JvmConfigSubstitutionTest.class);
105107
suite.addTestSuite(MavenITmng8653AfterAndEachPhasesWithConcurrentBuilderTest.class);
106108
suite.addTestSuite(MavenITmng5668AfterPhaseExecutionTest.class);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
Licensed to the Apache Software Foundation (ASF) under one
4+
or more contributor license agreements. See the NOTICE file
5+
distributed with this work for additional information
6+
regarding copyright ownership. The ASF licenses this file
7+
to you under the Apache License, Version 2.0 (the
8+
"License"); you may not use this file except in compliance
9+
with the License. You may obtain a copy of the License at
10+
11+
http://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing,
14+
software distributed under the License is distributed on an
15+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
KIND, either express or implied. See the License for the
17+
specific language governing permissions and limitations
18+
under the License.
19+
-->
20+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
21+
<modelVersion>4.0.0</modelVersion>
22+
23+
<groupId>org.apache.maven.its.mng4559</groupId>
24+
<artifactId>multiple-jvm-args</artifactId>
25+
<version>1.0-SNAPSHOT</version>
26+
27+
<description>Test that multiple JVM arguments in .mvn/jvm.config are properly handled</description>
28+
29+
<properties>
30+
<pom.test.prop1>${test.prop1}</pom.test.prop1>
31+
<pom.test.prop2>${test.prop2}</pom.test.prop2>
32+
</properties>
33+
34+
<build>
35+
<plugins>
36+
<plugin>
37+
<groupId>org.apache.maven.its.plugins</groupId>
38+
<artifactId>maven-it-plugin-expression</artifactId>
39+
<version>2.1-SNAPSHOT</version>
40+
<executions>
41+
<execution>
42+
<id>test</id>
43+
<goals>
44+
<goal>eval</goal>
45+
</goals>
46+
<phase>validate</phase>
47+
<configuration>
48+
<outputFile>target/jvm.properties</outputFile>
49+
<expressions>
50+
<expression>project/properties</expression>
51+
</expressions>
52+
</configuration>
53+
</execution>
54+
</executions>
55+
</plugin>
56+
</plugins>
57+
</build>
58+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# One comment
2+
-Dprop.jvm-opts="foo bar"

0 commit comments

Comments
 (0)