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 46cd58d5360..b154fc080f6 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 @@ -109,7 +109,10 @@ class ClasspathRootsResolver( val psiFile = psiManager.findFile(moduleInfoFile) ?: return null val psiJavaModule = psiFile.children.singleOrNull { it is PsiJavaModule } as? PsiJavaModule ?: return null - return JavaModule.Explicit(JavaModuleInfo.create(psiJavaModule), root, moduleInfoFile, isBinary = false) + + val roots = listOf(JavaModule.Root(root, isBinary = false)) + + return JavaModule.Explicit(JavaModuleInfo.create(psiJavaModule), roots, moduleInfoFile) } private fun modularBinaryRoot(root: VirtualFile, originalFile: File): JavaModule? { @@ -124,14 +127,16 @@ class ClasspathRootsResolver( if (moduleInfoFile != null) { val moduleInfo = JavaModuleInfo.read(moduleInfoFile) ?: return null - return JavaModule.Explicit(moduleInfo, root, moduleInfoFile, isBinary = true) + return JavaModule.Explicit(moduleInfo, listOf(JavaModule.Root(root, isBinary = true)), moduleInfoFile) } // Only .jar files can be automatic modules if (isJar) { + val moduleRoot = listOf(JavaModule.Root(root, isBinary = true)) + val automaticModuleName = manifest?.getValue(AUTOMATIC_MODULE_NAME) if (automaticModuleName != null) { - return JavaModule.Automatic(automaticModuleName, root) + return JavaModule.Automatic(automaticModuleName, moduleRoot) } val moduleName = LightJavaModule.moduleName(originalFile.nameWithoutExtension) @@ -139,7 +144,7 @@ class ClasspathRootsResolver( report(ERROR, "Cannot infer automatic module name for the file", VfsUtilCore.getVirtualFileForJar(root) ?: root) return null } - return JavaModule.Automatic(moduleName, root) + return JavaModule.Automatic(moduleName, moduleRoot) } return null @@ -169,11 +174,15 @@ class ClasspathRootsResolver( if (existing == null) { javaModuleFinder.addUserModule(module) } - else if (module.moduleRoot != existing.moduleRoot) { - val jar = VfsUtilCore.getVirtualFileForJar(module.moduleRoot) ?: module.moduleRoot - val existingPath = (VfsUtilCore.getVirtualFileForJar(existing.moduleRoot) ?: existing.moduleRoot).path + else if (module.moduleRoots != existing.moduleRoots) { + fun JavaModule.getRootFile() = + moduleRoots.firstOrNull()?.file?.let { VfsUtilCore.getVirtualFileForJar(it) ?: it } + + val thisFile = module.getRootFile() + val existingFile = existing.getRootFile() + val atExistingPath = if (existingFile == null) "" else " at: ${existingFile.path}" report(STRONG_WARNING, "The root is ignored because a module with the same name '${module.name}' " + - "has been found earlier on the module path at: $existingPath", jar) + "has been found earlier on the module path$atExistingPath", thisFile) } } @@ -212,10 +221,9 @@ class ClasspathRootsResolver( report(ERROR, "Module $moduleName cannot be found in the module graph") } else { - result.add(JavaRoot( - module.moduleRoot, - if (module.isBinary) JavaRoot.RootType.BINARY else JavaRoot.RootType.SOURCE - )) + for ((root, isBinary) in module.moduleRoots) { + result.add(JavaRoot(root, if (isBinary) JavaRoot.RootType.BINARY else JavaRoot.RootType.SOURCE)) + } } } diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/modules/CliJavaModuleFinder.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/modules/CliJavaModuleFinder.kt index 87e9eda494e..5abe6b073c1 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/modules/CliJavaModuleFinder.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/modules/CliJavaModuleFinder.kt @@ -42,6 +42,6 @@ class CliJavaModuleFinder(jrtFileSystemRoot: VirtualFile?) : JavaModuleFinder { private fun findSystemModule(moduleRoot: VirtualFile): JavaModule.Explicit? { val file = moduleRoot.findChild(PsiJavaModule.MODULE_INFO_CLS_FILE) ?: return null val moduleInfo = JavaModuleInfo.read(file) ?: return null - return JavaModule.Explicit(moduleInfo, moduleRoot, file, isBinary = true) + return JavaModule.Explicit(moduleInfo, listOf(JavaModule.Root(moduleRoot, isBinary = true)), file) } } diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/modules/CliJavaModuleResolver.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/modules/CliJavaModuleResolver.kt index a7e15343179..d3550ba7109 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/modules/CliJavaModuleResolver.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/modules/CliJavaModuleResolver.kt @@ -46,13 +46,13 @@ class CliJavaModuleResolver( return when (file.fileType) { KotlinFileType.INSTANCE, JavaFileType.INSTANCE -> sourceModule - JavaClassFileType.INSTANCE -> userModules.firstOrNull { module -> module.isBinary && file in module } + JavaClassFileType.INSTANCE -> userModules.firstOrNull { module -> file in module } else -> null } } private operator fun JavaModule.contains(file: VirtualFile): Boolean = - VfsUtilCore.isAncestor(moduleRoot, file, false) + moduleRoots.any { (root, isBinary) -> isBinary && VfsUtilCore.isAncestor(root, file, false) } override fun checkAccessibility( fileFromOurModule: VirtualFile?, referencedFile: VirtualFile, referencedPackage: FqName? diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/modules/JavaModule.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/modules/JavaModule.kt index 3d183abb2aa..da76e58a924 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/modules/JavaModule.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/modules/JavaModule.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.resolve.jvm.modules +import com.intellij.ide.highlighter.JavaClassFileType import com.intellij.openapi.vfs.VirtualFile import org.jetbrains.kotlin.name.FqName @@ -28,11 +29,11 @@ interface JavaModule { val name: String /** - * A directory or the root of a .jar file on the module path. Can also be the path to the `module-info.java` file if that path - * was passed as an explicit argument to the compiler. - * In case of an explicit module, module-info.class file can be found in its children. + * A non-empty list of directories or roots of .jar files on the module path. Can also contain the path to the `module-info.java` file + * if that path was passed as an explicit argument to the compiler. + * In case of an explicit module, module-info.class or module-info.java file must exist in at least one of these roots (the first wins). */ - val moduleRoot: VirtualFile + val moduleRoots: List /** * A module-info.class or module-info.java file where this module was loaded from. @@ -64,7 +65,9 @@ interface JavaModule { */ fun exportsTo(packageFqName: FqName, moduleName: String): Boolean - class Automatic(override val name: String, override val moduleRoot: VirtualFile) : JavaModule { + data class Root(val file: VirtualFile, val isBinary: Boolean) + + class Automatic(override val name: String, override val moduleRoots: List) : JavaModule { override val moduleInfoFile: VirtualFile? get() = null override val isBinary: Boolean get() = true @@ -78,13 +81,15 @@ interface JavaModule { class Explicit( val moduleInfo: JavaModuleInfo, - override val moduleRoot: VirtualFile, - override val moduleInfoFile: VirtualFile, - override val isBinary: Boolean + override val moduleRoots: List, + override val moduleInfoFile: VirtualFile ) : JavaModule { override val name: String get() = moduleInfo.moduleName + override val isBinary: Boolean + get() = moduleInfoFile.fileType == JavaClassFileType.INSTANCE + override fun exports(packageFqName: FqName): Boolean { return moduleInfo.exports.any { (fqName, toModules) -> fqName == packageFqName && toModules.isEmpty()