diff --git a/src/it/MCOMPILER-542/invoker.properties b/src/it/MCOMPILER-542/invoker.properties
index 95117185..1c24cc31 100644
--- a/src/it/MCOMPILER-542/invoker.properties
+++ b/src/it/MCOMPILER-542/invoker.properties
@@ -15,4 +15,4 @@
# specific language governing permissions and limitations
# under the License.
-invoker.java.version = 9+
+invoker.java.version = 11+
diff --git a/src/it/MCOMPILER-542/pom.xml b/src/it/MCOMPILER-542/pom.xml
index d8cbe3ef..b294fed3 100644
--- a/src/it/MCOMPILER-542/pom.xml
+++ b/src/it/MCOMPILER-542/pom.xml
@@ -1,50 +1,58 @@
-
-
-
-
- 4.0.0
-
- org.apache.maven.plugins.compiler.it
- MCOMPILER-542
- 1.0-SNAPSHOT
- ${java.specification.version}
-
-
- UTF-8
- 2023-08-14T15:12:12Z
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
- @project.version@
-
- ${java.specification.version}
-
-
-
-
-
-
+
+
+
+
+ 4.0.0
+
+ org.apache.maven.plugins.compiler.it
+ MCOMPILER-542
+ 1.0-SNAPSHOT
+ ${java.specification.version}
+
+
+ UTF-8
+ 2023-08-14T15:12:12Z
+
+
+
+
+ org.slf4j
+ slf4j-jdk-platform-logging
+ 2.0.9
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ @project.version@
+
+ ${java.specification.version}
+
+
+
+
+
+
diff --git a/src/it/MCOMPILER-542/src/main/java/module-info.java b/src/it/MCOMPILER-542/src/main/java/module-info.java
index cfa968a8..ad85f6fe 100644
--- a/src/it/MCOMPILER-542/src/main/java/module-info.java
+++ b/src/it/MCOMPILER-542/src/main/java/module-info.java
@@ -16,4 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
-module app {}
+module app {
+ requires java.logging;
+ requires jdk.zipfs;
+ requires org.slf4j.jdk.platform.logging;
+}
diff --git a/src/it/MCOMPILER-542/src/main/java/org/maven/test/Main.java b/src/it/MCOMPILER-542/src/main/java/org/maven/test/Main.java
index 8d175576..11bc3de4 100644
--- a/src/it/MCOMPILER-542/src/main/java/org/maven/test/Main.java
+++ b/src/it/MCOMPILER-542/src/main/java/org/maven/test/Main.java
@@ -20,9 +20,6 @@
public class Main {
- /**
- * @param args
- */
public static void main(String[] args) {
System.out.println("Hello World!");
}
diff --git a/src/it/MCOMPILER-542/verify.groovy b/src/it/MCOMPILER-542/verify.groovy
index 0ac0a7d2..30217ce6 100644
--- a/src/it/MCOMPILER-542/verify.groovy
+++ b/src/it/MCOMPILER-542/verify.groovy
@@ -1,30 +1,61 @@
-/*
- * 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.
- */
-
-def proc = 'javap -v target/classes/module-info.class'.execute(null,basedir)
-def sout = new StringBuilder(), serr = new StringBuilder()
-proc.consumeProcessOutput(sout, serr)
-proc.waitForOrKill(1000)
-def out = sout.toString()
-println "javap -v target/classes/module-info.class>\n$out\nerr> $serr"
-
-def module = out.substring(out.indexOf('Module:'))
-def javaVersion = System.getProperty('java.version')
-assert module.contains('// "java.base" ACC_MANDATED')
-assert !module.contains(javaVersion)
+/*
+ * 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.
+ */
+
+// Check if the javap tool is available
+def javapTool = java.util.spi.ToolProvider.findFirst("javap")
+assert javapTool.isPresent() : "javap tool not found. Make sure you have the JDK installed."
+
+def moduleDescriptor = new File(basedir, "target/classes/module-info.class")
+// Create a list of arguments to pass to the javap tool
+String[] args = ["-v", moduleDescriptor]
+
+def swout = new StringWriter(), swerr = new StringWriter()
+// Execute the javap tool with args
+def result = javapTool.get().run(new PrintWriter(swout), new PrintWriter(swerr), args)
+println swerr.toString().isEmpty() ? "javap output:\n$swout" : "javap error:\n$swerr"
+assert (result == 0) : "javap run failed"
+
+// Assertions of module content
+def out = swout.toString()
+assert out.contains('// "java.base" ACC_MANDATED') : "module not found in module-info.class"
+assert out.contains('// "java.logging"') : "module not found in module-info.class"
+assert out.contains('// "jdk.zipfs"') : "module not found in module-info.class"
+assert out.contains('// "org.slf4j.jdk.platform.logging"') : "module not found in module-info.class"
+assert out.contains('// 2.0.9') : "version of org.slf4j.jdk.platform.logging module not found"
+
+// Validation that the module-info should not contain the full java version but the spec version.
+def javaVersion = System.getProperty('java.version')
+def javaSpecVersion = System.getProperty('java.specification.version')
+if (javaVersion != javaSpecVersion) { // handle the case when is the first release
+ assert !out.contains('// ' + javaVersion) : "full java version found in module descriptor"
+}
+assert out.contains('// ' + javaSpecVersion) : "java specification version not found in module descriptor"
+
+// Additional validation that the checksum is always the same
+def checksumMap = [
+ '21': 'SHA-256 checksum ccc6515c8fc1bf4e675e205b2a5200d02545b06014b304c292eeddc68cffee8d',
+ '17': 'SHA-256 checksum 102f24c71aff97210d66ef791b7d56f8a25ff8692d2c97b21682bc7170aaca9c',
+ '11': 'MD5 checksum 5779cc6044dcba6ae4060e5a2f8a32c8'
+]
+
+def expectedChecksum = checksumMap[javaSpecVersion]
+if (expectedChecksum) {
+ println "Java version: $javaVersion"
+ assert out.contains(expectedChecksum) : "checksum doesn't match expected output"
+}
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 89754ee5..0af21429 100644
--- a/src/main/java/org/apache/maven/plugin/compiler/AbstractCompilerMojo.java
+++ b/src/main/java/org/apache/maven/plugin/compiler/AbstractCompilerMojo.java
@@ -644,6 +644,15 @@ protected final MavenProject getProject() {
return project;
}
+ protected final Optional getModuleDeclaration(final Set sourceFiles) {
+ for (File sourceFile : sourceFiles) {
+ if ("module-info.java".equals(sourceFile.getName())) {
+ return Optional.of(sourceFile.toPath());
+ }
+ }
+ return Optional.empty();
+ }
+
private boolean targetOrReleaseSet;
@Override
@@ -1177,6 +1186,8 @@ public void execute() throws MojoExecutionException, CompilationFailureException
}
}
+ patchJdkModuleVersion(compilerResult, sources);
+
if (useIncrementalCompilation) {
if (incrementalBuildHelperRequest.getOutputDirectory().exists()) {
getLog().debug("incrementalBuildHelper#afterRebuildExecution");
@@ -1798,4 +1809,29 @@ public void setRelease(String release) {
final String getImplicit() {
return implicit;
}
+
+ /**
+ * Patch module-info.class to set the java release version for java/jdk modules.
+ *
+ * @param compilerResult should succeed.
+ * @param sources the list of the source files to check for the "module-info.java"
+ *
+ * @see MCOMPILER-542
+ * @see JDK-8318913
+ */
+ private void patchJdkModuleVersion(CompilerResult compilerResult, Set sources) throws MojoExecutionException {
+ if (compilerResult.isSuccess() && getModuleDeclaration(sources).isPresent()) {
+ Path moduleDescriptor = getOutputDirectory().toPath().resolve("module-info.class");
+ if (Files.isRegularFile(moduleDescriptor)) {
+ try {
+ final byte[] descriptorOriginal = Files.readAllBytes(moduleDescriptor);
+ final byte[] descriptorMod =
+ ModuleInfoTransformer.transform(descriptorOriginal, getRelease(), getLog());
+ Files.write(moduleDescriptor, descriptorMod);
+ } catch (IOException ex) {
+ throw new MojoExecutionException("Error reading or writing module-info.class", ex);
+ }
+ }
+ }
+ }
}
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 a364e911..9ba8a753 100644
--- a/src/main/java/org/apache/maven/plugin/compiler/CompilerMojo.java
+++ b/src/main/java/org/apache/maven/plugin/compiler/CompilerMojo.java
@@ -20,6 +20,7 @@
import java.io.File;
import java.io.IOException;
+import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -29,6 +30,7 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
import org.apache.maven.artifact.Artifact;
@@ -228,18 +230,9 @@ protected Set getExcludes() {
protected void preparePaths(Set sourceFiles) {
// assert compilePath != null;
- File moduleDescriptorPath = null;
+ Optional moduleDeclaration = getModuleDeclaration(sourceFiles);
- boolean hasModuleDescriptor = false;
- for (File sourceFile : sourceFiles) {
- if ("module-info.java".equals(sourceFile.getName())) {
- moduleDescriptorPath = sourceFile;
- hasModuleDescriptor = true;
- break;
- }
- }
-
- if (hasModuleDescriptor) {
+ if (moduleDeclaration.isPresent()) {
// For now only allow named modules. Once we can create a graph with ASM we can specify exactly the modules
// and we can detect if auto modules are used. In that case, MavenProject.setFile() should not be used, so
// you cannot depend on this project and so it won't be distributed.
@@ -254,7 +247,7 @@ protected void preparePaths(Set sourceFiles) {
ResolvePathsRequest request = ResolvePathsRequest.ofFiles(dependencyArtifacts)
.setIncludeStatic(true)
- .setMainModuleDescriptor(moduleDescriptorPath);
+ .setMainModuleDescriptor(moduleDeclaration.get().toFile());
Toolchain toolchain = getToolchain();
if (toolchain instanceof DefaultJavaToolChain) {
diff --git a/src/main/java/org/apache/maven/plugin/compiler/ModuleInfoTransformer.java b/src/main/java/org/apache/maven/plugin/compiler/ModuleInfoTransformer.java
new file mode 100644
index 00000000..2b40481d
--- /dev/null
+++ b/src/main/java/org/apache/maven/plugin/compiler/ModuleInfoTransformer.java
@@ -0,0 +1,67 @@
+/*
+ * 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 org.apache.maven.plugin.compiler;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.maven.plugin.logging.Log;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.ModuleVisitor;
+import org.objectweb.asm.Opcodes;
+
+final class ModuleInfoTransformer {
+
+ private ModuleInfoTransformer() {}
+
+ static byte[] transform(byte[] originalBytecode, String javaVersion, Log log) {
+ List modulesModified = new ArrayList<>();
+ ClassReader reader = new ClassReader(originalBytecode);
+ ClassWriter writer = new ClassWriter(0);
+
+ ClassVisitor classVisitor = new ClassVisitor(Opcodes.ASM9, writer) {
+ @Override
+ public ModuleVisitor visitModule(String name, int access, String version) {
+ ModuleVisitor originalModuleVisitor = super.visitModule(name, access, version);
+ return new ModuleVisitor(Opcodes.ASM9, originalModuleVisitor) {
+ @Override
+ public void visitRequire(String module, int access, String version) {
+ // Check if the module name matches the java/jdk modules
+ if (module.startsWith("java.") || module.startsWith("jdk.")) {
+ // Patch the version from the java.* and jdk.* modules
+ // with the --release N version.
+ super.visitRequire(module, access, javaVersion);
+ modulesModified.add(module);
+ } else {
+ // Keep the original require statement
+ super.visitRequire(module, access, version);
+ }
+ }
+ };
+ }
+ };
+
+ reader.accept(classVisitor, 0);
+
+ log.info(String.format("Patch module-info.class %s with version %s", modulesModified, javaVersion));
+ return writer.toByteArray();
+ }
+}