diff --git a/src/it/MCOMPILER-374_mrjar/invoker.properties b/src/it/MCOMPILER-374_mrjar/invoker.properties new file mode 100644 index 000000000..4b2641284 --- /dev/null +++ b/src/it/MCOMPILER-374_mrjar/invoker.properties @@ -0,0 +1,18 @@ +# 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.java.version = 11+ diff --git a/src/it/MCOMPILER-374_mrjar/pom.xml b/src/it/MCOMPILER-374_mrjar/pom.xml new file mode 100644 index 000000000..3cb9cfd8f --- /dev/null +++ b/src/it/MCOMPILER-374_mrjar/pom.xml @@ -0,0 +1,72 @@ + + + + 4.0.0 + + org.maven.bug + mcompiler374 + 1.0-SNAPSHOT + + + + + org.apache.maven.plugins + maven-compiler-plugin + @project.version@ + + 8 + 11 + + + + java9 + compile + + compile + + + 9 + + ${project.basedir}/src/main/java9 + + true + + + + java11 + compile + + compile + + + 11 + + ${project.basedir}/src/main/java11 + + true + + + + + + + diff --git a/src/it/MCOMPILER-374_mrjar/src/main/java/org/maven/bug/A.java b/src/it/MCOMPILER-374_mrjar/src/main/java/org/maven/bug/A.java new file mode 100644 index 000000000..7360b4a51 --- /dev/null +++ b/src/it/MCOMPILER-374_mrjar/src/main/java/org/maven/bug/A.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 org.maven.bug; + +public class A { + static int getCount() { + return 1; + } +} diff --git a/src/it/MCOMPILER-374_mrjar/src/main/java/org/maven/bug/B.java b/src/it/MCOMPILER-374_mrjar/src/main/java/org/maven/bug/B.java new file mode 100644 index 000000000..aec29939d --- /dev/null +++ b/src/it/MCOMPILER-374_mrjar/src/main/java/org/maven/bug/B.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 org.maven.bug; + +public class B { + static int getCount() { + return A.getCount() + 1; + } +} diff --git a/src/it/MCOMPILER-374_mrjar/src/main/java11/module-info.java b/src/it/MCOMPILER-374_mrjar/src/main/java11/module-info.java new file mode 100644 index 000000000..b8dbb6c29 --- /dev/null +++ b/src/it/MCOMPILER-374_mrjar/src/main/java11/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 mrjar { + exports org.maven.bug; +} diff --git a/src/it/MCOMPILER-374_mrjar/src/main/java11/org/maven/bug/B.java b/src/it/MCOMPILER-374_mrjar/src/main/java11/org/maven/bug/B.java new file mode 100644 index 000000000..94356bc9b --- /dev/null +++ b/src/it/MCOMPILER-374_mrjar/src/main/java11/org/maven/bug/B.java @@ -0,0 +1,29 @@ +/* + * 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.maven.bug; + +public class B { + static int getCount() { + return A9.getCount() + 2; + } + + static int getVersion() { + return 11; + } +} diff --git a/src/it/MCOMPILER-374_mrjar/src/main/java9/org/maven/bug/A9.java b/src/it/MCOMPILER-374_mrjar/src/main/java9/org/maven/bug/A9.java new file mode 100644 index 000000000..4b56281d3 --- /dev/null +++ b/src/it/MCOMPILER-374_mrjar/src/main/java9/org/maven/bug/A9.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 org.maven.bug; + +public class A9 { + static int getCount() { + return A.getCount() + 1; + } +} diff --git a/src/it/MCOMPILER-374_mrjar/src/main/java9/org/maven/bug/B.java b/src/it/MCOMPILER-374_mrjar/src/main/java9/org/maven/bug/B.java new file mode 100644 index 000000000..1e54a47e0 --- /dev/null +++ b/src/it/MCOMPILER-374_mrjar/src/main/java9/org/maven/bug/B.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 org.maven.bug; + +public class B { + static int getCount() { + return A9.getCount() + 1; + } +} diff --git a/src/it/MCOMPILER-374_mrjar/src/test/java/org/maven/bug/MrJarTest.java b/src/it/MCOMPILER-374_mrjar/src/test/java/org/maven/bug/MrJarTest.java new file mode 100644 index 000000000..7e7bceb3e --- /dev/null +++ b/src/it/MCOMPILER-374_mrjar/src/test/java/org/maven/bug/MrJarTest.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 org.maven.bug; + +class MrJarTest { + static int getCount() { + return A.getCount() + A9.getCount() + B.getVersion(); + } +} diff --git a/src/it/MCOMPILER-374_mrjar/verify.groovy b/src/it/MCOMPILER-374_mrjar/verify.groovy new file mode 100644 index 000000000..1f5c7beb6 --- /dev/null +++ b/src/it/MCOMPILER-374_mrjar/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/org/maven/bug/A.class').exists() +assert new File(basedir, 'target/classes/org/maven/bug/B.class').exists() +assert new File(basedir, 'target/classes/META-INF/versions/9/org/maven/bug/A9.class').exists() +assert new File(basedir, 'target/classes/META-INF/versions/9/org/maven/bug/B.class').exists() +assert new File(basedir, 'target/classes/META-INF/versions/11/module-info.class').exists() +assert new File(basedir, 'target/classes/META-INF/versions/11/org/maven/bug/B.class').exists() +assert new File(basedir, 'target/test-classes/org/maven/bug/MrJarTest.class').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 edb9ad7ed..3fd460892 100644 --- a/src/main/java/org/apache/maven/plugin/compiler/AbstractCompilerMojo.java +++ b/src/main/java/org/apache/maven/plugin/compiler/AbstractCompilerMojo.java @@ -701,6 +701,32 @@ protected final Optional getModuleDeclaration(final Set sourceFiles) return Optional.empty(); } + /** + * Returns existing multirelease JAR output directories through the given release in descending order. + * The base output directory is returned last. + */ + protected static List getProjectOutputDirectories(File outputDirectory, int release) { + List directories = new ArrayList<>(); + File versionsFolder = new File(outputDirectory, "META-INF/versions"); + + for (int version = release; version >= 9; version--) { + File versionSubFolder = new File(versionsFolder, String.valueOf(version)); + if (versionSubFolder.isDirectory()) { + directories.add(versionSubFolder); + } + } + + directories.add(outputDirectory); + return directories; + } + + /** + * Returns the major Java version from its legacy or current string representation. + */ + protected static int getJavaMajorVersion(String version) { + return Integer.parseInt(version.startsWith("1.") ? version.substring(2) : version); + } + private boolean targetOrReleaseSet; @Override 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 0638d2991..101762020 100644 --- a/src/main/java/org/apache/maven/plugin/compiler/CompilerMojo.java +++ b/src/main/java/org/apache/maven/plugin/compiler/CompilerMojo.java @@ -32,6 +32,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.StringJoiner; import org.apache.maven.artifact.Artifact; import org.apache.maven.plugin.MojoExecutionException; @@ -257,6 +258,7 @@ protected void preparePaths(Set sourceFiles) { // assert compilePath != null; Optional moduleDeclaration = getModuleDeclaration(sourceFiles); + List projectOutputDirectories = getProjectOutputDirectories(getProject()); if (moduleDeclaration.isPresent()) { // For now only allow named modules. Once we can create a graph with ASM we can specify exactly the modules @@ -269,7 +271,8 @@ protected void preparePaths(Set sourceFiles) { ResolvePathsResult resolvePathsResult; try { - Collection dependencyArtifacts = getCompileClasspathElements(getProject()); + Collection dependencyArtifacts = + getCompileClasspathElements(getProject(), projectOutputDirectories); ResolvePathsRequest request = ResolvePathsRequest.ofFiles(dependencyArtifacts) .setIncludeStatic(true) @@ -307,13 +310,15 @@ protected void preparePaths(Set sourceFiles) { for (File file : resolvePathsResult.getClasspathElements()) { classpathElements.add(file.getPath()); + } - if (multiReleaseOutput) { - if (getOutputDirectory().toPath().startsWith(file.getPath())) { - compilerArgs.add("--patch-module"); - compilerArgs.add(String.format("%s=%s", moduleDescriptor.name(), file.getPath())); - } + if (multiReleaseOutput) { + StringJoiner patchPath = new StringJoiner(PS); + for (File directory : projectOutputDirectories) { + patchPath.add(directory.getPath()); } + compilerArgs.add("--patch-module"); + compilerArgs.add(moduleDescriptor.name() + '=' + patchPath); } for (File file : resolvePathsResult.getModulepathElements().keySet()) { @@ -329,7 +334,7 @@ protected void preparePaths(Set sourceFiles) { } } else { classpathElements = new ArrayList<>(); - for (File element : getCompileClasspathElements(getProject())) { + for (File element : getCompileClasspathElements(getProject(), projectOutputDirectories)) { classpathElements.add(element.getPath()); } modulepathElements = Collections.emptyList(); @@ -362,23 +367,23 @@ private void detectFilenameBasedAutomodules( } } - private List getCompileClasspathElements(MavenProject project) { - // 3 is outputFolder + 2 preserved for multirelease - List list = new ArrayList<>(project.getArtifacts().size() + 3); + /** + * Returns the project output directories visible to the current compilation. For a multi-release output, existing + * earlier release directories are returned in descending order before the base output directory. + */ + private List getProjectOutputDirectories(MavenProject project) { + File outputDirectory = new File(project.getBuild().getOutputDirectory()); if (multiReleaseOutput) { - File versionsFolder = new File(project.getBuild().getOutputDirectory(), "META-INF/versions"); - - // in reverse order - for (int version = Integer.parseInt(getRelease()) - 1; version >= 9; version--) { - File versionSubFolder = new File(versionsFolder, String.valueOf(version)); - if (versionSubFolder.exists()) { - list.add(versionSubFolder); - } - } + return getProjectOutputDirectories(outputDirectory, getJavaMajorVersion(getRelease()) - 1); } - list.add(new File(project.getBuild().getOutputDirectory())); + return Collections.singletonList(outputDirectory); + } + + private List getCompileClasspathElements(MavenProject project, List projectOutputDirectories) { + List list = new ArrayList<>(project.getArtifacts().size() + projectOutputDirectories.size()); + list.addAll(projectOutputDirectories); for (Artifact a : project.getArtifacts()) { if (a.getArtifactHandler().isAddedToClasspath()) { 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 1e0b76ea8..6983e5a92 100644 --- a/src/main/java/org/apache/maven/plugin/compiler/TestCompilerMojo.java +++ b/src/main/java/org/apache/maven/plugin/compiler/TestCompilerMojo.java @@ -21,6 +21,7 @@ import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collection; @@ -31,6 +32,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.StringJoiner; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.LifecyclePhase; @@ -235,11 +237,14 @@ protected File getOutputDirectory() { protected void preparePaths(Set sourceFiles) { File mainOutputDirectory = new File(getProject().getBuild().getOutputDirectory()); - File mainModuleDescriptorClassFile = new File(mainOutputDirectory, "module-info.class"); + List mainOutputDirectories = + getProjectOutputDirectories(mainOutputDirectory, getEffectiveTestJavaVersion()); + File mainModuleDescriptorClassFile = getModuleDescriptor(mainOutputDirectories); JavaModuleDescriptor mainModuleDescriptor = null; File testModuleDescriptorJavaFile = new File("module-info.java"); JavaModuleDescriptor testModuleDescriptor = null; + List effectiveTestPath = testPath; // Go through the source files to respect includes/excludes for (File sourceFile : sourceFiles) { @@ -251,11 +256,14 @@ protected void preparePaths(Set sourceFiles) { } // Get additional information from the main module descriptor, if available - if (mainModuleDescriptorClassFile.exists()) { + if (mainModuleDescriptorClassFile != null) { ResolvePathsResult result; try { - ResolvePathsRequest request = ResolvePathsRequest.ofStrings(testPath) + // LocationManager needs the directory containing the selected descriptor to recognize the main module. + effectiveTestPath = replacePathElement( + testPath, mainOutputDirectory, mainModuleDescriptorClassFile.getParentFile()); + ResolvePathsRequest request = ResolvePathsRequest.ofStrings(effectiveTestPath) .setIncludeStatic(true) .setMainModuleDescriptor(mainModuleDescriptorClassFile.getAbsolutePath()); @@ -294,7 +302,7 @@ protected void preparePaths(Set sourceFiles) { ResolvePathsResult result; try { - ResolvePathsRequest request = ResolvePathsRequest.ofStrings(testPath) + ResolvePathsRequest request = ResolvePathsRequest.ofStrings(effectiveTestPath) .setMainModuleDescriptor(testModuleDescriptorJavaFile.getAbsolutePath()); Toolchain toolchain = getToolchain(); @@ -331,7 +339,7 @@ protected void preparePaths(Set sourceFiles) { } if (testModuleDescriptor != null) { - modulepathElements = testPath; + modulepathElements = effectiveTestPath; classpathElements = Collections.emptyList(); if (mainModuleDescriptor != null) { @@ -342,23 +350,22 @@ protected void preparePaths(Set sourceFiles) { } if (testModuleDescriptor.name().equals(mainModuleDescriptor.name())) { - if (compilerArgs == null) { - compilerArgs = new ArrayList<>(); - } - compilerArgs.add("--patch-module"); - - StringBuilder patchModuleValue = new StringBuilder(); - patchModuleValue.append(testModuleDescriptor.name()); - patchModuleValue.append('='); - + List mainSourceRoots = new ArrayList<>(); for (String root : getProject().getCompileSourceRoots()) { if (Files.exists(Paths.get(root))) { - patchModuleValue.append(root).append(PS); + mainSourceRoots.add(root); } } - compilerArgs.add(patchModuleValue.toString()); + // Tests in the main module need the layered MR-JAR output and the main sources as one patch. + List outputPatches = + mainOutputDirectories.size() > 1 ? mainOutputDirectories : Collections.emptyList(); + addPatchModule(testModuleDescriptor.name(), outputPatches, mainSourceRoots); } else { + // Patch all output layers in multirelease lookup order so newer classes shadow older ones. + if (mainOutputDirectories.size() > 1) { + addPatchModule(mainModuleDescriptor.name(), mainOutputDirectories, Collections.emptyList()); + } getLog().debug("Black-box testing - all is ready to compile"); } } else { @@ -375,20 +382,8 @@ protected void preparePaths(Set sourceFiles) { } } else { if (mainModuleDescriptor != null) { - if (compilerArgs == null) { - compilerArgs = new ArrayList<>(); - } - compilerArgs.add("--patch-module"); - - StringBuilder patchModuleValue = new StringBuilder(mainModuleDescriptor.name()) - .append('=') - .append(mainOutputDirectory) - .append(PS); - for (String root : compileSourceRoots) { - patchModuleValue.append(root).append(PS); - } - - compilerArgs.add(patchModuleValue.toString()); + // Unnamed tests are compiled as a patch of the layered main module. + addPatchModule(mainModuleDescriptor.name(), mainOutputDirectories, compileSourceRoots); compilerArgs.add("--add-reads"); compilerArgs.add(mainModuleDescriptor.name() + "=ALL-UNNAMED"); @@ -399,6 +394,57 @@ protected void preparePaths(Set sourceFiles) { } } + private int getEffectiveTestJavaVersion() { + String version = StringUtils.isNotEmpty(getRelease()) ? getRelease() : getTarget(); + return getJavaMajorVersion(version); + } + + static File getModuleDescriptor(List outputDirectories) { + for (File outputDirectory : outputDirectories) { + File descriptor = new File(outputDirectory, "module-info.class"); + if (descriptor.isFile()) { + return descriptor; + } + } + return null; + } + + private static List replacePathElement(List path, File element, File replacement) { + List result = new ArrayList<>(path.size()); + Path elementPath = element.toPath().toAbsolutePath().normalize(); + boolean replaced = false; + + for (String value : path) { + if (Paths.get(value).toAbsolutePath().normalize().equals(elementPath)) { + result.add(replacement.getAbsolutePath()); + replaced = true; + } else { + result.add(value); + } + } + if (!replaced) { + result.add(0, replacement.getAbsolutePath()); + } + return result; + } + + private void addPatchModule(String moduleName, List outputDirectories, Collection sourceRoots) { + if (compilerArgs == null) { + compilerArgs = new ArrayList<>(); + } + + StringJoiner patchPath = new StringJoiner(PS); + for (File dir : outputDirectories) { + patchPath.add(dir.getPath()); + } + for (String sourceRoot : sourceRoots) { + patchPath.add(sourceRoot); + } + + compilerArgs.add("--patch-module"); + compilerArgs.add(moduleName + '=' + patchPath); + } + protected SourceInclusionScanner getSourceInclusionScanner(int staleMillis) { SourceInclusionScanner scanner; @@ -438,11 +484,7 @@ protected SourceInclusionScanner getSourceInclusionScanner(String inputFileEndin } static boolean isOlderThanJDK9(String version) { - if (version.startsWith("1.")) { - return Integer.parseInt(version.substring(2)) < 9; - } - - return Integer.parseInt(version) < 9; + return getJavaMajorVersion(version) < 9; } protected String getSource() { diff --git a/src/test/java/org/apache/maven/plugin/compiler/TestCompilerMojoTest.java b/src/test/java/org/apache/maven/plugin/compiler/TestCompilerMojoTest.java index 20bc7eafc..42f537d42 100644 --- a/src/test/java/org/apache/maven/plugin/compiler/TestCompilerMojoTest.java +++ b/src/test/java/org/apache/maven/plugin/compiler/TestCompilerMojoTest.java @@ -19,10 +19,12 @@ package org.apache.maven.plugin.compiler; import java.io.File; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import java.util.HashSet; +import java.util.List; import java.util.Set; import java.util.stream.Stream; @@ -30,6 +32,7 @@ import org.apache.maven.api.plugin.testing.MojoTest; import org.apache.maven.plugin.compiler.stubs.CompilerManagerStub; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -204,4 +207,26 @@ static Stream olderThanJDK9() { void olderThanJDK9(String version, boolean expected) { assertEquals(expected, TestCompilerMojo.isOlderThanJDK9(version)); } + + @Test + void shouldSelectApplicableModuleDescriptor(@TempDir Path temporaryDirectory) throws Exception { + Path outputDirectory = temporaryDirectory.resolve("classes"); + Path version9 = Files.createDirectories(outputDirectory.resolve("META-INF/versions/9")); + Path version11 = Files.createDirectories(outputDirectory.resolve("META-INF/versions/11")); + Files.createDirectories(outputDirectory.resolve("META-INF/versions/17")); + + Path version9Descriptor = Files.createFile(version9.resolve("module-info.class")); + Path version11Descriptor = Files.createFile(version11.resolve("module-info.class")); + Path rootDescriptor = Files.createFile(outputDirectory.resolve("module-info.class")); + + List outputDirectories = AbstractCompilerMojo.getProjectOutputDirectories(outputDirectory.toFile(), 11); + assertEquals(Arrays.asList(version11.toFile(), version9.toFile(), outputDirectory.toFile()), outputDirectories); + assertEquals(version11Descriptor.toFile(), TestCompilerMojo.getModuleDescriptor(outputDirectories)); + + Files.delete(version11Descriptor); + assertEquals(version9Descriptor.toFile(), TestCompilerMojo.getModuleDescriptor(outputDirectories)); + + Files.delete(version9Descriptor); + assertEquals(rootDescriptor.toFile(), TestCompilerMojo.getModuleDescriptor(outputDirectories)); + } }