From 6acb0c3d06a00353d2d261cea02b885192151325 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Wed, 15 Dec 2021 23:19:40 +0100 Subject: [PATCH] CLI: load module-info from META-INF/versions/... for any version Not just 9, since at least some libraries are putting module-info to META-INF/versions/11 nowadays. #KT-48027 Fixed --- .../jvm/compiler/ClasspathRootsResolver.kt | 26 +++++++++--- .../compiler/Java11ModulesIntegrationTest.kt | 41 +++++++++++-------- 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/ClasspathRootsResolver.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/ClasspathRootsResolver.kt index ce762a037c2..4b3fc881556 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/ClasspathRootsResolver.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/ClasspathRootsResolver.kt @@ -156,13 +156,11 @@ class ClasspathRootsResolver( private fun modularBinaryRoot(root: VirtualFile): JavaModule? { val isJar = root.fileSystem.protocol == StandardFileSystems.JAR_PROTOCOL - val manifest: Attributes? by lazy(NONE) { readManifestAttributes(root) } + val manifest = lazy(NONE) { readManifestAttributes(root) } val moduleInfoFile = root.findChild(PsiJavaModule.MODULE_INFO_CLS_FILE) - ?: root.takeIf { isJar }?.findFileByRelativePath(MULTI_RELEASE_MODULE_INFO_CLS_FILE)?.takeIf { - manifest?.getValue(IS_MULTI_RELEASE)?.equals("true", ignoreCase = true) == true - } + ?: if (isJar) tryLoadVersionSpecificModuleInfo(root, manifest) else null if (moduleInfoFile != null) { val moduleInfo = JavaModuleInfo.read(moduleInfoFile, javaFileManager, searchScope) ?: return null @@ -173,7 +171,7 @@ class ClasspathRootsResolver( if (isJar) { val moduleRoot = listOf(JavaModule.Root(root, isBinary = true)) - val automaticModuleName = manifest?.getValue(AUTOMATIC_MODULE_NAME) + val automaticModuleName = manifest.value?.getValue(AUTOMATIC_MODULE_NAME) if (automaticModuleName != null) { return JavaModule.Automatic(automaticModuleName, moduleRoot) } @@ -190,6 +188,23 @@ class ClasspathRootsResolver( return null } + private fun tryLoadVersionSpecificModuleInfo(root: VirtualFile, manifest: Lazy): VirtualFile? { + val versionsDir = root.findChild("META-INF")?.findChild("versions") ?: return null + + val isMultiReleaseJar = manifest.value?.getValue(IS_MULTI_RELEASE)?.equals("true", ignoreCase = true) + if (isMultiReleaseJar != true) return null + + val versions = versionsDir.children.filter { + val version = it.name.toIntOrNull() + version != null && version >= 9 + }.sortedBy { it.name.toInt() } + for (version in versions) { + val file = version.findChild(PsiJavaModule.MODULE_INFO_CLS_FILE) + if (file != null) return file + } + return null + } + private fun readManifestAttributes(jarRoot: VirtualFile): Attributes? { val manifestFile = jarRoot.findChild("META-INF")?.findChild("MANIFEST.MF") return try { @@ -323,7 +338,6 @@ class ClasspathRootsResolver( } private companion object { - const val MULTI_RELEASE_MODULE_INFO_CLS_FILE = "META-INF/versions/9/${PsiJavaModule.MODULE_INFO_CLS_FILE}" const val AUTOMATIC_MODULE_NAME = "Automatic-Module-Name" const val IS_MULTI_RELEASE = "Multi-Release" } diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/Java11ModulesIntegrationTest.kt b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/Java11ModulesIntegrationTest.kt index 546117f1313..bab357f8138 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/Java11ModulesIntegrationTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/Java11ModulesIntegrationTest.kt @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.test.util.KtTestUtil import java.io.File import java.util.concurrent.TimeUnit import java.util.jar.Manifest +import kotlin.test.fail class Java11ModulesIntegrationTest : AbstractKotlinCompilerIntegrationTest() { override val testDataPath: String @@ -78,13 +79,13 @@ class Java11ModulesIntegrationTest : AbstractKotlinCompilerIntegrationTest() { ) } - private fun createMultiReleaseJar(jdk9Home: File, destination: File, mainRoot: File, java9Root: File): File { + private fun createMultiReleaseJar(jdkHome: File, destination: File, mainRoot: File, version: Int, versionSpecificRoot: File): File { val command = listOf( - File(jdk9Home, "bin/jar").path, - "--create", "--file=$destination", - "-C", mainRoot.path, ".", - "--release", "9", - "-C", java9Root.path, "." + File(jdkHome, "bin/jar").path, + "--create", "--file=$destination", + "-C", mainRoot.path, ".", + "--release", version.toString(), + "-C", versionSpecificRoot.path, "." ) val process = ProcessBuilder().command(command).inheritIO().start() @@ -124,8 +125,7 @@ class Java11ModulesIntegrationTest : AbstractKotlinCompilerIntegrationTest() { fun testAllModulePathAndNamedModule() { try { module("main", addModules = listOf("ALL-MODULE-PATH")) - } - catch (e: JavaCompilationError) { + } catch (e: JavaCompilationError) { // Java compilation should fail, it's expected } } @@ -176,10 +176,10 @@ class Java11ModulesIntegrationTest : AbstractKotlinCompilerIntegrationTest() { "-Xmodule-path=${a.path}" ) compileLibrary( - "moduleB", - additionalOptions = kotlinOptions, - compileJava = { _, _, _ -> error("No .java files in moduleB in this test") }, - checkKotlinOutput = checkKotlinOutput("moduleB") + "moduleB", + additionalOptions = kotlinOptions, + compileJava = { _, _, _ -> error("No .java files in moduleB in this test") }, + checkKotlinOutput = checkKotlinOutput("moduleB") ) } @@ -192,12 +192,17 @@ class Java11ModulesIntegrationTest : AbstractKotlinCompilerIntegrationTest() { libraryOut11.mkdirs() File(libraryOut, "module-info.class").renameTo(File(libraryOut11, "module-info.class")) - // Use the name other from 'library' to prevent it from being loaded as an automatic module if module-info.class is not found - val libraryJar = createMultiReleaseJar( - KtTestUtil.getJdk11Home(), File(tmpdir, "multi-release-library.jar"), libraryOut, libraryOut11 - ) - - module("main", listOf(libraryJar)) + for (version in listOf(9, 10, 11)) { + try { + // Use the name other from 'library' to prevent it from being loaded as an automatic module if module-info.class is not found. + val libraryJar = createMultiReleaseJar( + KtTestUtil.getJdk11Home(), File(tmpdir, "multi-release-library-jdk$version.jar"), libraryOut, version, libraryOut11 + ) + module("main", listOf(libraryJar)) + } catch (e: Throwable) { + fail("Fail on testing that module-info.class is loaded from META-INF/versions/$version", e) + } + } } fun testAutomaticModuleNames() {