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
This commit is contained in:
Alexander Udalov
2021-12-15 23:19:40 +01:00
parent f38e1b218d
commit 6acb0c3d06
2 changed files with 43 additions and 24 deletions
@@ -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<Attributes?>): 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"
}
@@ -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<String>(
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() {