Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions src/it/default-fork_modular/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>org.apache.maven.plugins.compiler.it</groupId>
<artifactId>default-fork-modular</artifactId>
<version>1.0-SNAPSHOT</version>

<name>Test for default configuration in a modular project</name>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>@pom.version@</version>
<configuration>
<fork>true</fork>
</configuration>
</plugin>
</plugins>
</build>

</project>
21 changes: 21 additions & 0 deletions src/it/default-fork_modular/src/main/java/foo/MyClass.java
Original file line number Diff line number Diff line change
@@ -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 {}
21 changes: 21 additions & 0 deletions src/it/default-fork_modular/src/main/java/module-info.java
Original file line number Diff line number Diff line change
@@ -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;
}
21 changes: 21 additions & 0 deletions src/it/default-fork_modular/src/test/java/foo/MyTest.java
Original file line number Diff line number Diff line change
@@ -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 {}
26 changes: 26 additions & 0 deletions src/it/default-fork_modular/verify.groovy
Original file line number Diff line number Diff line change
@@ -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()
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -290,12 +296,16 @@ final Charset charset() {
* </ul>
*
* 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 <a href="https://inside.java/2024/06/18/quality-heads-up/">Inside Java 2024-06-18 Quality Heads up</a>
Expand Down Expand 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 <a href="https://docs.oracle.com/en/java/javase/17/docs/specs/man/javac.html#option-verbose">javac -verbose</a>
*/
Expand Down Expand Up @@ -661,7 +672,7 @@ final EnumSet<IncrementalBuild.Aspect> 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);
}
Expand Down Expand Up @@ -1096,12 +1107,16 @@ boolean hasModuleDeclaration(final List<SourceDirectory> roots) throws IOExcepti
* The debug file will contain the compiler options together with the list of source files to compile.
*
* <p>Note: debug logging should not be confused with the {@link #debug} flag.</p>
*
* @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();
Expand All @@ -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:
*
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -1403,20 +1432,23 @@ 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);
} catch (IllegalArgumentException e) {
// 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;
}

/**
Expand Down Expand Up @@ -1577,17 +1609,21 @@ final void resolveProcessorPathEntries(Map<PathType, List<Path>> 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()) {
Expand Down Expand Up @@ -1628,14 +1664,12 @@ final Set<Path> 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());
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -1806,12 +1843,15 @@ private void writeOption(BufferedWriter out, PathType type, Collection<Path> 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;
Expand Down
Loading
Loading