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/10/15 07:17:13 UTC

(maven) branch maven-4.0.x updated: [Backport maven-4.0.x] Fix #13405: Do not capture JVM stderr notices into jvm.config options (#13530)

This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch maven-4.0.x
in repository https://gitbox.apache.org/repos/asf/maven.git


The following commit(s) were added to refs/heads/maven-4.0.x by this push:
     new 2dacffff2b [Backport maven-4.0.x] Fix #13405: Do not capture JVM stderr notices into jvm.config options (#13530)
2dacffff2b is described below

commit 2dacffff2b2640179305bb0da7ad180fbcbd430c
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Thu Oct 15 09:17:07 2026 +0200

    [Backport maven-4.0.x] Fix #13405: Do not capture JVM stderr notices into jvm.config options (#13530)
    
    - Separate stdout from stderr in bin/mvn when executing JvmConfigParser.java
    - Redirect stderr to a temporary file; display it only on non-zero exit to avoid polluting MAVEN_OPTS with JVM tool notices (JAVA_TOOL_OPTIONS, JDK_JAVA_OPTIONS, _JAVA_OPTIONS)
    - Add test-mvn-jvm-config.sh to verify JVM notices do not pollute JVM arguments and parser errors are still reported
    - Register the test script in the shell-script-tests profile in apache-maven/pom.xml
    
    Backport of #13447
    
    Co-authored-by: Yashi Singhal <ya...@gmail.com>
---
 apache-maven/pom.xml                               |  14 ++
 apache-maven/src/assembly/maven/bin/mvn            |  36 +++-
 .../src/test/scripts/test-mvn-jvm-config.sh        | 190 +++++++++++++++++++++
 3 files changed, 233 insertions(+), 7 deletions(-)

diff --git a/apache-maven/pom.xml b/apache-maven/pom.xml
index 21ec647e2c..8d811a6899 100644
--- a/apache-maven/pom.xml
+++ b/apache-maven/pom.xml
@@ -492,6 +492,20 @@ under the License.
                   </arguments>
                 </configuration>
               </execution>
+              <execution>
+                <id>test-mvn-jvm-config</id>
+                <goals>
+                  <goal>exec</goal>
+                </goals>
+                <phase>test</phase>
+                <configuration>
+                  <skip>${skipTests}</skip>
+                  <executable>sh</executable>
+                  <arguments>
+                    <argument>${project.basedir}/src/test/scripts/test-mvn-jvm-config.sh</argument>
+                  </arguments>
+                </configuration>
+              </execution>
             </executions>
           </plugin>
         </plugins>
diff --git a/apache-maven/src/assembly/maven/bin/mvn b/apache-maven/src/assembly/maven/bin/mvn
index 15e899f678..4727dbf63e 100755
--- a/apache-maven/src/assembly/maven/bin/mvn
+++ b/apache-maven/src/assembly/maven/bin/mvn
@@ -221,26 +221,48 @@ concat_lines() {
     }
 
     # Run the parser
-    # Capture both stdout and stderr for comprehensive error reporting
-    parser_output=$("$JAVACMD" -cp "$MAVEN_HOME/bin" JvmConfigParser "$1" "$_basedir_native" 2>&1)
+    # Capture stdout for JVM options; capture stderr separately so JVM notices
+    # (e.g. JAVA_TOOL_OPTIONS) do not pollute MAVEN_OPTS.
+    parser_stderr_file=$(mktemp "${TMPDIR:-/tmp}/mvn-jvm-config-stderr.XXXXXX" 2>/dev/null || echo "${TMPDIR:-/tmp}/mvn-jvm-config-stderr.$$")
+    trap 'rm -f "$parser_stderr_file"' EXIT INT TERM
+    parser_output=$("$JAVACMD" -cp "$MAVEN_HOME/bin" JvmConfigParser "$1" "$_basedir_native" 2>"$parser_stderr_file")
     parser_exit=$?
 
     if [ -n "$MAVEN_DEBUG_SCRIPT" ]; then
       echo "[DEBUG] JvmConfigParser exit code: $parser_exit" >&2
       printf '[DEBUG] JvmConfigParser output: %s\n' "$parser_output" >&2
+      if [ -s "$parser_stderr_file" ]; then
+        while IFS= read -r parser_err_line || [ -n "$parser_err_line" ]; do
+          printf '[DEBUG] JvmConfigParser stderr: %s\n' "$parser_err_line" >&2
+        done < "$parser_stderr_file"
+      fi
     fi
 
     if [ $parser_exit -ne 0 ]; then
       # Parser failed - print comprehensive error information
       echo "ERROR: JvmConfigParser failed with exit code $parser_exit" >&2
-      echo "  jvm.config path: $1" >&2
-      echo "  Maven basedir: $MAVEN_PROJECTBASEDIR" >&2
-      echo "  Java command: $JAVACMD" >&2
-      printf '  Parser output:\n' >&2
-      printf '%s\n' "$parser_output" | sed 's/^/    /' >&2
+      printf '  jvm.config path: %s\n' "$1" >&2
+      printf '  Maven basedir: %s\n' "$MAVEN_PROJECTBASEDIR" >&2
+      printf '  Java command: %s\n' "$JAVACMD" >&2
+      echo "  Parser output:" >&2
+      # Print captured stderr (parser exceptions/diagnostics) and stdout so no diagnostic info is dropped
+      if [ -s "$parser_stderr_file" ]; then
+        while IFS= read -r parser_err_line || [ -n "$parser_err_line" ]; do
+          printf '    %s\n' "$parser_err_line" >&2
+        done < "$parser_stderr_file"
+      fi
+      if [ -n "$parser_output" ]; then
+        printf '%s\n' "$parser_output" | while IFS= read -r parser_out_line || [ -n "$parser_out_line" ]; do
+          printf '    %s\n' "$parser_out_line" >&2
+        done
+      fi
+      rm -f "$parser_stderr_file" 2>/dev/null || :
+      trap - EXIT INT TERM
       exit 1
     fi
 
+    rm -f "$parser_stderr_file" 2>/dev/null || :
+    trap - EXIT INT TERM
     printf '%s\n' "$parser_output"
   fi
 }
diff --git a/apache-maven/src/test/scripts/test-mvn-jvm-config.sh b/apache-maven/src/test/scripts/test-mvn-jvm-config.sh
new file mode 100644
index 0000000000..f4a95f2657
--- /dev/null
+++ b/apache-maven/src/test/scripts/test-mvn-jvm-config.sh
@@ -0,0 +1,190 @@
+#!/bin/sh
+
+# 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.
+
+# -----------------------------------------------------------------------------
+# Tests that JVM options from .mvn/jvm.config are correctly parsed and that
+# JVM notices printed to stderr (e.g. JAVA_TOOL_OPTIONS, JDK_JAVA_OPTIONS,
+# _JAVA_OPTIONS) do not pollute MAVEN_OPTS.
+#
+# Usage: sh apache-maven/src/test/scripts/test-mvn-jvm-config.sh
+# -----------------------------------------------------------------------------
+
+set -e
+
+script_dir=`cd "\`dirname "$0"\`" && pwd`
+mvn_script="$script_dir/../../assembly/maven/bin/mvn"
+
+if [ ! -f "$mvn_script" ]; then
+  echo "Cannot locate the mvn script at $mvn_script" >&2
+  exit 1
+fi
+
+sh_bin=`unset -f command; command -v sh`
+
+work_dir=`mktemp -d "${TMPDIR:-/tmp}/mvn-jvm-config.XXXXXX"`
+work_dir=`cd "$work_dir" && pwd`
+trap 'rm -rf "$work_dir"' EXIT INT TERM
+
+stub_dir="$work_dir/stubs"
+maven_home="$work_dir/maven-home"
+project_dir="$work_dir/project"
+
+mkdir -p "$stub_dir" "$maven_home/bin" "$maven_home/boot" "$project_dir/.mvn"
+
+cp "$mvn_script" "$maven_home/bin/mvn"
+touch "$maven_home/bin/m2.conf" "$maven_home/boot/plexus-classworlds-9.9.9.jar"
+
+# uname stub
+cat > "$stub_dir/uname" <<'STUB'
+#!/bin/sh
+echo "Linux"
+STUB
+
+# dirname stub
+cat > "$stub_dir/dirname" <<'STUB'
+#!/bin/sh
+path="$1"
+case "$path" in
+  */*)
+    path="${path%/*}"
+    [ -n "$path" ] || path="/"
+    ;;
+  *) path="." ;;
+esac
+printf '%s\n' "$path"
+STUB
+
+# java stub: simulates Java runtime notices on stderr when tool options env vars are set
+cat > "$stub_dir/java" <<'STUB'
+#!/bin/sh
+
+# If tool options are set, print JVM notices on stderr just like real JDK
+if [ -n "$JAVA_TOOL_OPTIONS" ]; then
+  echo "Picked up JAVA_TOOL_OPTIONS: $JAVA_TOOL_OPTIONS" >&2
+fi
+if [ -n "$JDK_JAVA_OPTIONS" ]; then
+  echo "NOTE: Picked up JDK_JAVA_OPTIONS: $JDK_JAVA_OPTIONS" >&2
+fi
+if [ -n "$_JAVA_OPTIONS" ]; then
+  echo "Picked up _JAVA_OPTIONS: $_JAVA_OPTIONS" >&2
+fi
+
+case " $* " in
+  *" -version "*) exit 0 ;;
+esac
+
+case "$3" in
+  JvmConfigParser)
+    if [ "$FAIL_PARSER" = "1" ]; then
+      echo "Error: simulated parser failure" >&2
+      exit 1
+    fi
+    # Emulate JvmConfigParser output; args are: -cp <bin-dir> JvmConfigParser <config> <basedir>
+    while IFS= read -r line || [ -n "$line" ]; do
+      line="${line%%#*}"
+      for token in $line; do
+        printf '"%s" ' "$token"
+      done
+    done < "$4"
+    printf '\n'
+    exit 0 ;;
+esac
+
+# For the main launch, echo all arguments bracketed
+for arg in "$@"; do printf '[%s]' "$arg"; done
+printf '\n'
+STUB
+
+chmod +x "$stub_dir/uname" "$stub_dir/dirname" "$stub_dir/java" "$maven_home/bin/mvn"
+
+echo '-Xmx512m -Dcustom.prop=hello' > "$project_dir/.mvn/jvm.config"
+
+failures=0
+
+assert_contains() {
+  desc="$1"; haystack="$2"; needle="$3"
+  case "$haystack" in
+    *"$needle"*) printf 'ok - %s\n' "$desc" ;;
+    *)
+      printf 'FAILED - %s\n' "$desc"
+      printf '  expected to contain: %s\n' "$needle"
+      printf '  actual: %s\n' "$haystack"
+      failures=`expr "$failures" + 1`
+      ;;
+  esac
+}
+
+assert_not_contains() {
+  desc="$1"; haystack="$2"; needle="$3"
+  case "$haystack" in
+    *"$needle"*)
+      printf 'FAILED - %s\n' "$desc"
+      printf '  expected NOT to contain: %s\n' "$needle"
+      printf '  actual: %s\n' "$haystack"
+      failures=`expr "$failures" + 1`
+      ;;
+    *) printf 'ok - %s\n' "$desc" ;;
+  esac
+}
+
+run_mvn() {
+  ( cd "$project_dir" &&
+    PATH="$stub_dir" JAVA_HOME= MAVEN_SKIP_RC=1 \
+      "$sh_bin" "$maven_home/bin/mvn" validate 2>/dev/null )
+}
+
+run_mvn_stderr() {
+  ( cd "$project_dir" &&
+    PATH="$stub_dir" JAVA_HOME= MAVEN_SKIP_RC=1 \
+      "$sh_bin" "$maven_home/bin/mvn" validate 2>&1 >/dev/null )
+}
+
+# Test 1: Clean environment - JVM options from jvm.config are present
+out=`run_mvn`
+assert_contains "clean env: jvm.config options present" "$out" "[-Xmx512m]"
+assert_contains "clean env: custom prop present" "$out" "[-Dcustom.prop=hello]"
+
+# Test 2: JAVA_TOOL_OPTIONS is set
+out=`JAVA_TOOL_OPTIONS="-Dfoo=bar" run_mvn`
+assert_contains "JAVA_TOOL_OPTIONS: jvm.config options present" "$out" "[-Xmx512m]"
+assert_not_contains "JAVA_TOOL_OPTIONS: 'Picked' not in args" "$out" "[Picked]"
+assert_not_contains "JAVA_TOOL_OPTIONS: 'up' not in args" "$out" "[up]"
+assert_not_contains "JAVA_TOOL_OPTIONS: 'JAVA_TOOL_OPTIONS:' not in args" "$out" "[JAVA_TOOL_OPTIONS:]"
+
+# Test 3: JDK_JAVA_OPTIONS is set
+out=`JDK_JAVA_OPTIONS="-Dfoo=bar" run_mvn`
+assert_contains "JDK_JAVA_OPTIONS: jvm.config options present" "$out" "[-Xmx512m]"
+assert_not_contains "JDK_JAVA_OPTIONS: 'NOTE:' not in args" "$out" "[NOTE:]"
+
+# Test 4: _JAVA_OPTIONS is set
+out=`_JAVA_OPTIONS="-Dfoo=bar" run_mvn`
+assert_contains "_JAVA_OPTIONS: jvm.config options present" "$out" "[-Xmx512m]"
+assert_not_contains "_JAVA_OPTIONS: 'Picked' not in args" "$out" "[Picked]"
+
+# Test 5: Parser failure displays stderr output
+err_out=`FAIL_PARSER=1 run_mvn_stderr || true`
+assert_contains "parser failure: error message displayed" "$err_out" "ERROR: JvmConfigParser failed with exit code 1"
+assert_contains "parser failure: parser stderr displayed" "$err_out" "simulated parser failure"
+
+if [ "$failures" -ne 0 ]; then
+  printf '%s assertion(s) failed\n' "$failures"
+  exit 1
+fi
+
+echo "All assertions passed"