diff --git a/src/it/default-fork_modular/pom.xml b/src/it/default-fork_modular/pom.xml new file mode 100644 index 000000000..6ec9e15bf --- /dev/null +++ b/src/it/default-fork_modular/pom.xml @@ -0,0 +1,46 @@ + + + + 4.0.0 + + org.apache.maven.plugins.compiler.it + default-fork-modular + 1.0-SNAPSHOT + + Test for default configuration in a modular project + + + UTF-8 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + @pom.version@ + + true + + + + + + diff --git a/src/it/default-fork_modular/src/main/java/foo/MyClass.java b/src/it/default-fork_modular/src/main/java/foo/MyClass.java new file mode 100644 index 000000000..d4132bb28 --- /dev/null +++ b/src/it/default-fork_modular/src/main/java/foo/MyClass.java @@ -0,0 +1,21 @@ +/* + * 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 MyClass {} diff --git a/src/it/default-fork_modular/src/main/java/module-info.java b/src/it/default-fork_modular/src/main/java/module-info.java new file mode 100644 index 000000000..980a29d59 --- /dev/null +++ b/src/it/default-fork_modular/src/main/java/module-info.java @@ -0,0 +1,21 @@ +/* + * 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. + */ +module com.foo { + exports foo; +} diff --git a/src/it/default-fork_modular/src/test/java/foo/MyTest.java b/src/it/default-fork_modular/src/test/java/foo/MyTest.java new file mode 100644 index 000000000..08064cd35 --- /dev/null +++ b/src/it/default-fork_modular/src/test/java/foo/MyTest.java @@ -0,0 +1,21 @@ +/* + * 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 MyTest {} diff --git a/src/it/default-fork_modular/verify.groovy b/src/it/default-fork_modular/verify.groovy new file mode 100644 index 000000000..016f4091d --- /dev/null +++ b/src/it/default-fork_modular/verify.groovy @@ -0,0 +1,26 @@ +/* + * 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/MyClass.class').exists() +assert new File( basedir, 'target/test-classes/foo/MyTest.class').exists() +assert new File( basedir, 'target/classes/module-info.class').exists() + +// For each line, exactly one of the two files should exist. +assert new File( basedir, 'target/javac.sh').exists() != new File( basedir, 'target/javac.bat').exists() +assert new File( basedir, 'target/javac-test.sh').exists() != new File( basedir, 'target/javac-test.bat').exists() diff --git a/src/main/java/org/apache/maven/plugin/compiler/AbstractCompilerMojo.java b/src/main/java/org/apache/maven/plugin/compiler/AbstractCompilerMojo.java index 66636598d..c65674c14 100644 --- a/src/main/java/org/apache/maven/plugin/compiler/AbstractCompilerMojo.java +++ b/src/main/java/org/apache/maven/plugin/compiler/AbstractCompilerMojo.java @@ -24,7 +24,6 @@ import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; import javax.tools.OptionChecker; -import javax.tools.Tool; import javax.tools.ToolProvider; import java.io.BufferedReader; @@ -213,6 +212,13 @@ final Charset charset() { */ private boolean targetOrReleaseSet; + /** + * The highest version supported by the compiler, or {@code null} if not yet determined. + * + * @see #isVersionEqualOrNewer(String) + */ + private SourceVersion supportedVersion; + /** * Whether to enable preview language features of the java compiler. * If {@code true}, then the {@code --enable-preview} option will be added to compiler arguments. @@ -290,12 +296,16 @@ final Charset charset() { * * * The default value depends on the JDK used for the build. - * Prior to Java 22, the default was {@code full}, so annotation processing and compilation were executed without explicit configuration. + * Prior to Java 23, the default was {@code full}, + * so annotation processing and compilation were executed without explicit configuration. * * For security reasons, starting with Java 23 no annotation processing is done if neither - * any {@code -processor}, {@code -processor path} or {@code -processor module} are set, or either {@code only} or {@code full} is set. + * any {@code -processor}, {@code -processor path} or {@code -processor module} are set, + * or either {@code only} or {@code full} is set. * So literally the default is {@code none}. - * It is recommended to always list the annotation processors you want to execute instead of using the {@code proc} configuration, to ensure that only desired processors are executed and not any "hidden" (and maybe malicious). + * It is recommended to always list the annotation processors you want to execute + * instead of using the {@code proc} configuration, + * to ensure that only desired processors are executed and not any "hidden" (and maybe malicious). * * @see #annotationProcessors * @see Inside Java 2024-06-18 Quality Heads up @@ -472,6 +482,7 @@ final Charset charset() { /** * Whether to show messages about what the compiler is doing. * If {@code true}, then the {@code -verbose} option will be added to compiler arguments. + * In addition, files such as {@code target/javac.args} will be generated even on successful compilation. * * @see javac -verbose */ @@ -661,7 +672,7 @@ final EnumSet incrementalCompilationConfiguration() { IncrementalBuild.Aspect.OPTIONS, IncrementalBuild.Aspect.DEPENDENCIES, IncrementalBuild.Aspect.SOURCES); - if (hasAnnotationProcessor()) { + if (hasAnnotationProcessor(false)) { aspects.add(IncrementalBuild.Aspect.REBUILD_ON_ADD); aspects.add(IncrementalBuild.Aspect.REBUILD_ON_CHANGE); } @@ -1096,12 +1107,16 @@ boolean hasModuleDeclaration(final List roots) throws IOExcepti * The debug file will contain the compiler options together with the list of source files to compile. * *

Note: debug logging should not be confused with the {@link #debug} flag.

+ * + * @see CompilerMojo#debugFileName + * @see TestCompilerMojo#debugFileName */ @Nullable protected abstract String getDebugFileName(); /** * {@return the debug file name with its path, or null if none}. + * This method does not check if the debug file will be written, as the compilation result is not yet known. */ final Path getDebugFilePath() { String filename = getDebugFileName(); @@ -1112,6 +1127,15 @@ final Path getDebugFilePath() { return Path.of(project.getBuild().getOutputDirectory()).resolveSibling(filename); } + /** + * Returns whether the debug file should be written after a successful build. + * By default, debug files are written only if the build failed. + * However, some options can change this behavior. + */ + final boolean shouldWriteDebugFile() { + return verbose || logger.isDebugEnabled(); + } + /** * Runs the Java compiler. This method performs the following steps: * @@ -1129,6 +1153,11 @@ final Path getDebugFilePath() { @Override public void execute() throws MojoException { JavaCompiler compiler = compiler(); + for (SourceVersion version : compiler.getSourceVersions()) { + if (supportedVersion == null || version.compareTo(supportedVersion) >= 0) { + supportedVersion = version; + } + } Options configuration = parseParameters(compiler); try { compile(compiler, configuration); @@ -1352,7 +1381,7 @@ private void compile(final JavaCompiler compiler, final Options configuration) t * In case of failure, or if debugging is enabled, dump the options to a file. * By default, the file will have the ".args" extension. */ - if (!success || verbose || logger.isDebugEnabled()) { + if (!success || shouldWriteDebugFile()) { IOException suppressed = null; try { writeDebugFile(executor, configuration, success); @@ -1390,7 +1419,7 @@ private void compile(final JavaCompiler compiler, final Options configuration) t * Note: a previous version used as an heuristic way to detect if Reproducible Build was enabled. This check * has been removed because Reproducible Build are enabled by default in Maven now. */ - if (!isVersionEqualOrNewer(compiler, "RELEASE_22")) { + if (!isVersionEqualOrNewer("RELEASE_22")) { Path moduleDescriptor = executor.outputDirectory.resolve(MODULE_INFO + CLASS_FILE_SUFFIX); if (Files.isRegularFile(moduleDescriptor)) { byte[] oridinal = Files.readAllBytes(moduleDescriptor); @@ -1403,12 +1432,12 @@ private void compile(final JavaCompiler compiler, final Options configuration) t } /** - * Returns whether the given tool (usually the compiler) supports the given source version or newer versions. + * Returns whether the compiler supports the given source version or newer versions. * The specified source version shall be the name of one of the {@link SourceVersion} enumeration values. - * Note that a return value of {@code true} does not mean that the tool support that version, - * as it may be too old. This method is rather for checking whether a tool need to be patched. + * Note that a return value of {@code true} does not mean that the compiler supports that exact version, + * as it may supports only newer versions. */ - private static boolean isVersionEqualOrNewer(Tool tool, String sourceVersion) { + private boolean isVersionEqualOrNewer(String sourceVersion) { final SourceVersion requested; try { requested = SourceVersion.valueOf(sourceVersion); @@ -1416,7 +1445,10 @@ private static boolean isVersionEqualOrNewer(Tool tool, String sourceVersion) { // The current tool is from a JDK older than the one for the requested source release. return false; } - return tool.getSourceVersions().stream().anyMatch((v) -> v.compareTo(requested) >= 0); + if (supportedVersion == null) { + supportedVersion = SourceVersion.latestSupported(); + } + return supportedVersion.compareTo(requested) >= 0; } /** @@ -1577,17 +1609,21 @@ final void resolveProcessorPathEntries(Map> addTo) throws M * {@return whether an annotation processor seems to be present}. * This method is invoked if the user did not specified explicit incremental compilation options. * + * @param strict whether to be conservative if the current Java version is older than 23 + * * @see #incrementalCompilation */ - private boolean hasAnnotationProcessor() { + private boolean hasAnnotationProcessor(final boolean strict) { if ("none".equalsIgnoreCase(proc)) { return false; } if (proc == null || proc.isBlank()) { + if (strict && !isVersionEqualOrNewer("RELEASE_23")) { + return true; // Before Java 23, default value of `-proc` was `full`. + } /* * If the `proc` parameter was not specified, its default value depends on the Java version. - * It was "full" prior Java 21 and become "none if no other processor option" since Java 21. - * Since even the full" case may do nothing, always check if a processor is declared. + * It was "full" prior Java 23 and become "none if no other processor option" since Java 23. */ if (annotationProcessors == null || annotationProcessors.length == 0) { if (annotationProcessorPaths == null || annotationProcessorPaths.isEmpty()) { @@ -1628,14 +1664,12 @@ final Set addGeneratedSourceDirectory() throws IOException { * Do not create an empty directory if this plugin is not going to generate new source files. * However, if a directory already exists, use it because maybe its content was generated by * another plugin executed before the compiler plugin. - * - * TODO: "none" become the default starting with Java 23. */ - if ("none".equalsIgnoreCase(proc) && Files.notExists(generatedSourcesDirectory)) { - return Set.of(); - } else { + if (hasAnnotationProcessor(true)) { // `createDirectories(Path)` does nothing if the directory already exists. generatedSourcesDirectory = Files.createDirectories(generatedSourcesDirectory); + } else if (Files.notExists(generatedSourcesDirectory)) { + return Set.of(); } ProjectScope scope = compileScope.projectScope(); projectManager.addSourceRoot(project, scope, Language.JAVA_FAMILY, generatedSourcesDirectory.toAbsolutePath()); @@ -1713,15 +1747,18 @@ private void writeDebugFile(final ToolExecutor executor, final Options configura return; } final var commandLine = new StringBuilder("For trying to compile from the command-line, use:"); - final var chdir = - Path.of(System.getProperty("user.dir")).relativize(basedir).toString(); - if (!chdir.isEmpty()) { - boolean isWindows = (File.separatorChar == '\\'); - commandLine - .append(System.lineSeparator()) - .append(" ") - .append(isWindows ? "chdir " : "cd ") - .append(chdir); + Path dir = basedir; + if (dir != null) { // Should never be null, but it has been observed with some Maven versions. + dir = Path.of(System.getProperty("user.dir")).relativize(dir); + String chdir = dir.toString(); + if (!chdir.isEmpty()) { + boolean isWindows = (File.separatorChar == '\\'); + commandLine + .append(System.lineSeparator()) + .append(" ") + .append(isWindows ? "chdir " : "cd ") + .append(chdir); + } } commandLine.append(System.lineSeparator()).append(" ").append(executable != null ? executable : compilerId); Path pathForRelease = debugFilePath; @@ -1806,12 +1843,15 @@ private void writeOption(BufferedWriter out, PathType type, Collection fil * @return the given path, potentially relative to the base directory */ private Path relativize(Path file) { - Path root = project.getRootDirectory(); - if (root != null && file.startsWith(root)) { - try { - file = basedir.relativize(file); - } catch (IllegalArgumentException e) { - // Ignore, keep the absolute path. + final Path dir = basedir; + if (dir != null) { // Should never be null, but it has been observed with some Maven versions. + Path root = project.getRootDirectory(); + if (root != null && file.startsWith(root)) { + try { + file = dir.relativize(file); + } catch (IllegalArgumentException e) { + // Ignore, keep the absolute path. + } } } return file; diff --git a/src/main/java/org/apache/maven/plugin/compiler/CompilerMojo.java b/src/main/java/org/apache/maven/plugin/compiler/CompilerMojo.java index 0127d8633..dcd7cb20a 100644 --- a/src/main/java/org/apache/maven/plugin/compiler/CompilerMojo.java +++ b/src/main/java/org/apache/maven/plugin/compiler/CompilerMojo.java @@ -137,6 +137,10 @@ public class CompilerMojo extends AbstractCompilerMojo { * command-line by typing {@code javac @target/javac.args}. * The debug file will contain the compiler options together with the list of source files to compile. * + *

By default, this debug file is written only if the compilation of main code failed. + * The writing of the debug files can be forced by setting the {@link #verbose} flag to {@code true} + * or by specifying the {@code --verbose} option to Maven on the command-line.

+ * * @since 3.10.0 */ @Parameter(defaultValue = "javac.args") @@ -270,6 +274,8 @@ protected Path getOutputDirectory() { /** * {@return the file where to dump the command-line when debug is activated or when the compilation failed}. + * + * @see #debugFileName */ @Nullable @Override diff --git a/src/main/java/org/apache/maven/plugin/compiler/ForkedTool.java b/src/main/java/org/apache/maven/plugin/compiler/ForkedTool.java index 1a774a590..778ac2106 100644 --- a/src/main/java/org/apache/maven/plugin/compiler/ForkedTool.java +++ b/src/main/java/org/apache/maven/plugin/compiler/ForkedTool.java @@ -62,6 +62,11 @@ class ForkedTool implements Tool, OptionChecker { */ private final Path debugFilePath; + /** + * Whether to write the {@code target/javac.sh} file even on successful compilation. + */ + private final boolean verbose; + /** * Creates a new forked compiler. * @@ -71,6 +76,7 @@ class ForkedTool implements Tool, OptionChecker { basedir = mojo.basedir; executable = Objects.requireNonNull(mojo.executable); debugFilePath = mojo.getDebugFilePath(); + verbose = mojo.shouldWriteDebugFile(); } /** @@ -191,14 +197,24 @@ public int run(InputStream in, OutputStream out, OutputStream err, String... arg } /** - * Starts the process and wait for its completion. - * If a debug file has been specified, writes in that file the command which is about to be executed. + * Starts the process and waits for its completion. + * If the process fails and a debug file has been specified, writes the command in that file. * * @param builder builder of the process to start * @param out where to send additional compiler output + * @return the exit value of the process */ private int start(ProcessBuilder builder, Appendable out) throws IOException { - if (debugFilePath != null) { + Process process = builder.start(); + int status; + try { + status = process.waitFor(); + } catch (InterruptedException e) { + out.append("Compilation has been interrupted by " + e).append(System.lineSeparator()); + process.destroy(); + status = 1; + } + if ((status != 0 || verbose) && debugFilePath != null) { // Use the path separator as a way to identify the operating system. final boolean windows = File.separatorChar == '\\'; String filename = debugFilePath.getFileName().toString(); @@ -221,13 +237,6 @@ private int start(ProcessBuilder builder, Appendable out) throws IOException { debugFile.newLine(); } } - Process process = builder.start(); - try { - return process.waitFor(); - } catch (InterruptedException e) { - out.append("Compilation has been interrupted by " + e).append(System.lineSeparator()); - process.destroy(); - return 1; - } + return status; } } diff --git a/src/main/java/org/apache/maven/plugin/compiler/ForkedToolSources.java b/src/main/java/org/apache/maven/plugin/compiler/ForkedToolSources.java index c34bd3587..fac6b5d3c 100644 --- a/src/main/java/org/apache/maven/plugin/compiler/ForkedToolSources.java +++ b/src/main/java/org/apache/maven/plugin/compiler/ForkedToolSources.java @@ -63,48 +63,45 @@ final class ForkedToolSources implements StandardJavaFileManager { * Option for source files. These options are not declared in * {@link JavaPathType} because they are not about dependencies. */ - private enum OtherPathType implements PathType { + private record OtherPathType(String name, String optionString, String moduleName) implements PathType { /** * The option for the directory of source files. */ - SOURCES("--source-path"), + static final OtherPathType SOURCES = new OtherPathType("SOURCES", "--source-path", null); /** * The option for the directory of generated sources. */ - GENERATED_SOURCES("-s"), + static final OtherPathType GENERATED_SOURCES = new OtherPathType("GENERATED_SOURCES", "-s", null); /** * The option for the directory of compiled class files. */ - OUTPUT("-d"); - - /** - * The Java option for this enumeration value. - */ - private final String option; - - OtherPathType(String option) { - this.option = option; - } + static final OtherPathType OUTPUT = new OtherPathType("OUTPUT", "-d", null); @Override public String id() { - return name(); + return name; } @Override public Optional option() { - return Optional.of(option); + return Optional.of(optionString); } + /** + * Formats the option with the paths, without quotes. + * The quotes are omitted because the paths will be given to + * {@link java.lang.ProcessBuilder}, not to a command-line. + */ @Override public String[] option(Iterable paths) { - var builder = new StringJoiner(File.pathSeparator); + String prefix = (moduleName == null) ? "" : (moduleName + '='); + var builder = new StringJoiner(File.pathSeparator, prefix, ""); paths.forEach((path) -> builder.add(path.toString())); - return new String[] {option, builder.toString()}; + return new String[] {optionString, builder.toString()}; } - }; + } /** * Search paths associated to locations. @@ -427,6 +424,28 @@ public void setLocationFromPaths(Location location, Collection p } } + /** + * Associates the given search paths for a module with the given location. + * Any previous value will be discarded. + */ + @Override + public void setLocationForModule(Location location, String moduleName, Collection paths) + throws IOException { + PathType type; + if (location == StandardLocation.PATCH_MODULE_PATH) { + type = JavaPathType.patchModule(moduleName); + } else if (location == StandardLocation.MODULE_SOURCE_PATH) { + type = new OtherPathType("MODULE_SOURCE_PATH", "--module-source-path", moduleName); + } else { + throw new IllegalArgumentException("Unsupported location: " + location); + } + if (paths == null || paths.isEmpty()) { + locations.remove(type); + } else { + locations.put(type, paths); + } + } + /** * Returns the search path associated with the given location, or {@code null} if none. */ @@ -451,9 +470,7 @@ public boolean hasLocation(Location location) { */ void addAllLocations(List command) { for (Map.Entry> entry : locations.entrySet()) { - for (String element : entry.getKey().option(entry.getValue())) { - command.add(element); - } + command.addAll(Arrays.asList(entry.getKey().option(entry.getValue()))); } } diff --git a/src/main/java/org/apache/maven/plugin/compiler/IncrementalBuild.java b/src/main/java/org/apache/maven/plugin/compiler/IncrementalBuild.java index e0a8ee574..4de248603 100644 --- a/src/main/java/org/apache/maven/plugin/compiler/IncrementalBuild.java +++ b/src/main/java/org/apache/maven/plugin/compiler/IncrementalBuild.java @@ -32,6 +32,7 @@ import java.nio.file.attribute.FileTime; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.List; @@ -338,8 +339,11 @@ static EnumSet parse(final String values) { throws IOException { this.sourceFiles = sourceFiles; this.saveSourceList = saveSourceList; - Path file = mojo.mojoStatusPath; - cacheFile = Files.createDirectories(file.getParent()).resolve(file.getFileName()); + cacheFile = mojo.mojoStatusPath; + if (cacheFile != null) { + // Should never be null, but it has been observed to happen with some Maven versions. + Files.createDirectories(cacheFile.getParent()); + } showCompilationChanges = mojo.showCompilationChanges; buildTime = System.currentTimeMillis(); previousBuildTime = buildTime; @@ -355,7 +359,10 @@ static EnumSet parse(final String values) { * @throws IOException if an error occurred while deleting the file */ public void deleteCache() throws IOException { - Files.deleteIfExists(cacheFile); + if (cacheFile != null) { + // Should never be null, but it has been observed to happen with some Maven versions. + Files.deleteIfExists(cacheFile); + } } /** @@ -386,6 +393,10 @@ public void deleteCache() throws IOException { */ @SuppressWarnings({"checkstyle:InnerAssignment", "checkstyle:NeedBraces"}) public void writeCache() throws IOException { + if (cacheFile == null) { + // Should never be null, but it has been observed to happen with some Maven versions. + return; + } try (DataOutputStream out = new DataOutputStream(new BufferedOutputStream(Files.newOutputStream( cacheFile, StandardOpenOption.WRITE, @@ -432,6 +443,10 @@ public void writeCache() throws IOException { */ @SuppressWarnings("checkstyle:NeedBraces") private Map loadCache() throws IOException { + if (cacheFile == null) { + // Should never be null, but it has been observed to happen with some Maven versions. + return Collections.emptyMap(); // Not `Map.of()` because we need to allow `Map.remove(…)`. + } final Map previousBuild; try (DataInputStream in = new DataInputStream( new BufferedInputStream(Files.newInputStream(cacheFile, StandardOpenOption.READ)))) { diff --git a/src/main/java/org/apache/maven/plugin/compiler/TestCompilerMojo.java b/src/main/java/org/apache/maven/plugin/compiler/TestCompilerMojo.java index 691b12faf..afcbc00b2 100644 --- a/src/main/java/org/apache/maven/plugin/compiler/TestCompilerMojo.java +++ b/src/main/java/org/apache/maven/plugin/compiler/TestCompilerMojo.java @@ -225,6 +225,10 @@ public class TestCompilerMojo extends AbstractCompilerMojo { * from the command-line by typing {@code javac @target/javac-test.args}. * The debug file will contain the compiler options together with the list of source files to compile. * + *

By default, this debug file is written only if the compilation of test code failed. + * The writing of the debug files can be forced by setting the {@link #verbose} flag to {@code true} + * or by specifying the {@code --verbose} option to Maven on the command-line.

+ * * @see CompilerMojo#debugFileName * @since 3.10.0 */ @@ -355,6 +359,8 @@ protected Path getOutputDirectory() { /** * {@return the file where to dump the command-line when debug is activated or when the compilation failed}. + * + * @see #debugFileName */ @Nullable @Override diff --git a/src/main/java/org/apache/maven/plugin/compiler/ToolExecutor.java b/src/main/java/org/apache/maven/plugin/compiler/ToolExecutor.java index 1b9a20c2f..ec3c5217a 100644 --- a/src/main/java/org/apache/maven/plugin/compiler/ToolExecutor.java +++ b/src/main/java/org/apache/maven/plugin/compiler/ToolExecutor.java @@ -521,7 +521,7 @@ private Collection groupByReleaseAndModule() { */ private StandardJavaFileManager createFileManager(JavaCompiler compiler, boolean workaround) { StandardJavaFileManager fileManager = compiler.getStandardFileManager(listener, LOCALE, encoding); - if (workaround && WorkaroundForPatchModule.ENABLED) { + if (WorkaroundForPatchModule.ENABLED && workaround && !(compiler instanceof ForkedTool)) { fileManager = new WorkaroundForPatchModule(fileManager); } return fileManager; diff --git a/src/main/java/org/apache/maven/plugin/compiler/WorkaroundForPatchModule.java b/src/main/java/org/apache/maven/plugin/compiler/WorkaroundForPatchModule.java index e659a84fd..a290991be 100644 --- a/src/main/java/org/apache/maven/plugin/compiler/WorkaroundForPatchModule.java +++ b/src/main/java/org/apache/maven/plugin/compiler/WorkaroundForPatchModule.java @@ -32,6 +32,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.Map; import java.util.Set; @@ -126,7 +127,7 @@ void copyTo(final StandardJavaFileManager target) throws IOException { * @param type the type of path together with the module name * @param paths the paths to set * @param cause the exception that occurred when invoking the standard API - * @throws CompilationFailureException if this workaround doesn't work neither + * @throws IllegalArgumentException if this workaround doesn't work neither */ private static void specifyAsOption( StandardJavaFileManager fileManager, @@ -135,10 +136,18 @@ private static void specifyAsOption( UnsupportedOperationException cause) throws IOException { - var it = Arrays.asList(type.option(paths)).iterator(); - if (!fileManager.handleOption(it.next(), it) || it.hasNext()) { - throw new CompilationFailureException("Cannot handle " + type, cause); + String message; + Iterator it = Arrays.asList(type.option(paths)).iterator(); + if (!fileManager.handleOption(it.next(), it)) { + message = "Failed to set the %s option for module %s"; + } else if (it.hasNext()) { + message = "Unexpected number of arguments after the %s option for module %s"; + } else { + return; } + JavaPathType rawType = type.rawType(); + throw new IllegalArgumentException( + String.format(message, rawType.option().orElse(rawType.name()), type.moduleName()), cause); } /**