From 4f2a9c6dd848969a63ba389298dba4c59fc41196 Mon Sep 17 00:00:00 2001 From: Vaclav Haisman Date: Fri, 24 Jul 2026 19:50:21 +0200 Subject: [PATCH 1/2] Add regression test for issue 351 --- .../src/main/it/issues-351/invoker.properties | 19 ++++ .../src/main/it/issues-351/pom.xml | 89 +++++++++++++++++++ .../issues-351/src/main/java/foo/Example.java | 25 ++++++ .../src/main/it/issues-351/verify.groovy | 23 +++++ 4 files changed, 156 insertions(+) create mode 100644 plexus-compiler-its/src/main/it/issues-351/invoker.properties create mode 100644 plexus-compiler-its/src/main/it/issues-351/pom.xml create mode 100644 plexus-compiler-its/src/main/it/issues-351/src/main/java/foo/Example.java create mode 100644 plexus-compiler-its/src/main/it/issues-351/verify.groovy diff --git a/plexus-compiler-its/src/main/it/issues-351/invoker.properties b/plexus-compiler-its/src/main/it/issues-351/invoker.properties new file mode 100644 index 000000000..b0d469e98 --- /dev/null +++ b/plexus-compiler-its/src/main/it/issues-351/invoker.properties @@ -0,0 +1,19 @@ +# 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. + +invoker.os.family = windows +invoker.goals = clean compile diff --git a/plexus-compiler-its/src/main/it/issues-351/pom.xml b/plexus-compiler-its/src/main/it/issues-351/pom.xml new file mode 100644 index 000000000..2f12e87f8 --- /dev/null +++ b/plexus-compiler-its/src/main/it/issues-351/pom.xml @@ -0,0 +1,89 @@ + + + + + 4.0.0 + + org.codehaus.plexus.compiler.it + issues-351 + 1.0-SNAPSHOT + + Issue 351: preserve multiline compiler arguments on Windows + + + UTF-8 + @project.version@ + + + + + + org.apache.maven.plugins + maven-compiler-plugin + @maven.compiler.version@ + + true + 11 + + -J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED + -J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED + -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED + -XDcompilePolicy=simple + -Xplugin:ErrorProne \ + -Xep:DeadException:WARN + + + + com.google.errorprone + error_prone_core + 2.24.1 + + + + + + org.codehaus.plexus + plexus-compiler-api + ${plexus.compiler.version} + + + org.codehaus.plexus + plexus-compiler-manager + ${plexus.compiler.version} + + + org.codehaus.plexus + plexus-compiler-javac + ${plexus.compiler.version} + + + + + + diff --git a/plexus-compiler-its/src/main/it/issues-351/src/main/java/foo/Example.java b/plexus-compiler-its/src/main/it/issues-351/src/main/java/foo/Example.java new file mode 100644 index 000000000..76cc30e42 --- /dev/null +++ b/plexus-compiler-its/src/main/it/issues-351/src/main/java/foo/Example.java @@ -0,0 +1,25 @@ +/* + * 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 foo; + +public class Example { + public void createException() { + new RuntimeException(); + } +} diff --git a/plexus-compiler-its/src/main/it/issues-351/verify.groovy b/plexus-compiler-its/src/main/it/issues-351/verify.groovy new file mode 100644 index 000000000..bbede9184 --- /dev/null +++ b/plexus-compiler-its/src/main/it/issues-351/verify.groovy @@ -0,0 +1,23 @@ +/* + * 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. + */ + +assert new File(basedir, 'target/classes/foo/Example.class').exists() + +def buildLog = new File(basedir, 'build.log').getText('UTF-8') +assert buildLog.contains('[DeadException] Exception created but not thrown') From c19bbec0a40f16383c626721f9d504d570bdeecc Mon Sep 17 00:00:00 2001 From: Vaclav Haisman Date: Fri, 24 Jul 2026 20:48:05 +0200 Subject: [PATCH 2/2] Fix #351. Quote javac argument files --- .../src/main/it/issues-351/pom.xml | 2 +- .../plexus/compiler/javac/JavacCompiler.java | 39 +++++++++++++++++-- .../compiler/javac/JavacCompilerTest.java | 25 ++++++++++++ 3 files changed, 62 insertions(+), 4 deletions(-) diff --git a/plexus-compiler-its/src/main/it/issues-351/pom.xml b/plexus-compiler-its/src/main/it/issues-351/pom.xml index 2f12e87f8..70827bbae 100644 --- a/plexus-compiler-its/src/main/it/issues-351/pom.xml +++ b/plexus-compiler-its/src/main/it/issues-351/pom.xml @@ -55,7 +55,7 @@ -J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED -XDcompilePolicy=simple - -Xplugin:ErrorProne \ + -Xplugin:ErrorProne -Xep:DeadException:WARN diff --git a/plexus-compilers/plexus-compiler-javac/src/main/java/org/codehaus/plexus/compiler/javac/JavacCompiler.java b/plexus-compilers/plexus-compiler-javac/src/main/java/org/codehaus/plexus/compiler/javac/JavacCompiler.java index 9bf8fabe8..8d93e788a 100644 --- a/plexus-compilers/plexus-compiler-javac/src/main/java/org/codehaus/plexus/compiler/javac/JavacCompiler.java +++ b/plexus-compilers/plexus-compiler-javac/src/main/java/org/codehaus/plexus/compiler/javac/JavacCompiler.java @@ -1068,9 +1068,7 @@ private File createFileWithArguments(String[] args, String outputDirectory) thro writer = new PrintWriter(new FileWriter(tempFile)); for (String arg : args) { - String argValue = arg.replace(File.separatorChar, '/'); - writer.write("\"" + argValue + "\""); - writer.println(); + writer.println(quoteArgument(arg)); } writer.flush(); @@ -1083,6 +1081,41 @@ private File createFileWithArguments(String[] args, String outputDirectory) thro } } + /** + * Quotes an argument according to javac command-line argument file syntax. + */ + static String quoteArgument(String argument) { + StringBuilder quoted = new StringBuilder(argument.length() + 2); + quoted.append('"'); + for (int i = 0; i < argument.length(); i++) { + char c = argument.charAt(i); + switch (c) { + case '\\': + quoted.append("\\\\"); + break; + case '"': + quoted.append("\\\""); + break; + case '\n': + quoted.append("\\n"); + break; + case '\r': + quoted.append("\\r"); + break; + case '\t': + quoted.append("\\t"); + break; + case '\f': + quoted.append("\\f"); + break; + default: + quoted.append(c); + break; + } + } + return quoted.append('"').toString(); + } + /** * Get the path of the javac tool executable to use. * Either given through explicit configuration or via {@link #getJavacExecutable()}. diff --git a/plexus-compilers/plexus-compiler-javac/src/test/java/org/codehaus/plexus/compiler/javac/JavacCompilerTest.java b/plexus-compilers/plexus-compiler-javac/src/test/java/org/codehaus/plexus/compiler/javac/JavacCompilerTest.java index 82a311cd9..fec1e4c42 100644 --- a/plexus-compilers/plexus-compiler-javac/src/test/java/org/codehaus/plexus/compiler/javac/JavacCompilerTest.java +++ b/plexus-compilers/plexus-compiler-javac/src/test/java/org/codehaus/plexus/compiler/javac/JavacCompilerTest.java @@ -119,4 +119,29 @@ void testExtractMajorAndMinorVersion() { assertEquals("21", JavacCompiler.extractMajorAndMinorVersion("javac 21")); assertEquals("1.8", JavacCompiler.extractMajorAndMinorVersion("javac 1.8.0_432")); } + + @ParameterizedTest(name = "{0}") + @MethodSource("quoteArgumentArguments") + void quoteArgument(String description, String argument, String expected) { + assertEquals(expected, JavacCompiler.quoteArgument(argument)); + } + + private static Stream quoteArgumentArguments() { + return Stream.of( + Arguments.of("empty", "", "\"\""), + Arguments.of("plain", "-Xlint", "\"-Xlint\""), + Arguments.of("spaces", "two words", "\"two words\""), + Arguments.of("double quote", "say \"hello\"", "\"say \\\"hello\\\"\""), + Arguments.of( + "Windows path", + "C:\\Program Files\\Java\\bin\\javac.exe", + "\"C:\\\\Program Files\\\\Java\\\\bin\\\\javac.exe\""), + Arguments.of("trailing backslash", "C:\\temp\\", "\"C:\\\\temp\\\\\""), + Arguments.of("literal backslash-n", "-Dvalue=\\n", "\"-Dvalue=\\\\n\""), + Arguments.of( + "control whitespace", + "line1\nline2\rline3\tline4\fline5", + "\"line1\\nline2\\rline3\\tline4\\fline5\""), + Arguments.of("backslash and newline", "line1\\\nline2", "\"line1" + "\\\\" + "\\n" + "line2\"")); + } }