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(MapBy 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 OptionalBy 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