Refactor JavaModule, support several roots in one module

In case of partial/incremental compilation, a module usually consists of
two roots, one of which is source and another is binary. Thus, it's
incorrect to divide modules into "binary" and "non-binary", and only
look for .class files in "binary" modules in
CliJavaModuleResolver.findJavaModule. The more correct way is to think
of a module as a collection of roots, and every root is either binary or
source
This commit is contained in:
Alexander Udalov
2017-09-20 18:57:14 +03:00
parent 03f97ff7f1
commit 8496944a36
4 changed files with 36 additions and 23 deletions
@@ -109,7 +109,10 @@ class ClasspathRootsResolver(
val psiFile = psiManager.findFile(moduleInfoFile) ?: return null val psiFile = psiManager.findFile(moduleInfoFile) ?: return null
val psiJavaModule = psiFile.children.singleOrNull { it is PsiJavaModule } as? PsiJavaModule ?: 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? { private fun modularBinaryRoot(root: VirtualFile, originalFile: File): JavaModule? {
@@ -124,14 +127,16 @@ class ClasspathRootsResolver(
if (moduleInfoFile != null) { if (moduleInfoFile != null) {
val moduleInfo = JavaModuleInfo.read(moduleInfoFile) ?: return 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 // Only .jar files can be automatic modules
if (isJar) { if (isJar) {
val moduleRoot = listOf(JavaModule.Root(root, isBinary = true))
val automaticModuleName = manifest?.getValue(AUTOMATIC_MODULE_NAME) val automaticModuleName = manifest?.getValue(AUTOMATIC_MODULE_NAME)
if (automaticModuleName != null) { if (automaticModuleName != null) {
return JavaModule.Automatic(automaticModuleName, root) return JavaModule.Automatic(automaticModuleName, moduleRoot)
} }
val moduleName = LightJavaModule.moduleName(originalFile.nameWithoutExtension) 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) report(ERROR, "Cannot infer automatic module name for the file", VfsUtilCore.getVirtualFileForJar(root) ?: root)
return null return null
} }
return JavaModule.Automatic(moduleName, root) return JavaModule.Automatic(moduleName, moduleRoot)
} }
return null return null
@@ -169,11 +174,15 @@ class ClasspathRootsResolver(
if (existing == null) { if (existing == null) {
javaModuleFinder.addUserModule(module) javaModuleFinder.addUserModule(module)
} }
else if (module.moduleRoot != existing.moduleRoot) { else if (module.moduleRoots != existing.moduleRoots) {
val jar = VfsUtilCore.getVirtualFileForJar(module.moduleRoot) ?: module.moduleRoot fun JavaModule.getRootFile() =
val existingPath = (VfsUtilCore.getVirtualFileForJar(existing.moduleRoot) ?: existing.moduleRoot).path 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}' " + 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") report(ERROR, "Module $moduleName cannot be found in the module graph")
} }
else { else {
result.add(JavaRoot( for ((root, isBinary) in module.moduleRoots) {
module.moduleRoot, result.add(JavaRoot(root, if (isBinary) JavaRoot.RootType.BINARY else JavaRoot.RootType.SOURCE))
if (module.isBinary) JavaRoot.RootType.BINARY else JavaRoot.RootType.SOURCE }
))
} }
} }
@@ -42,6 +42,6 @@ class CliJavaModuleFinder(jrtFileSystemRoot: VirtualFile?) : JavaModuleFinder {
private fun findSystemModule(moduleRoot: VirtualFile): JavaModule.Explicit? { private fun findSystemModule(moduleRoot: VirtualFile): JavaModule.Explicit? {
val file = moduleRoot.findChild(PsiJavaModule.MODULE_INFO_CLS_FILE) ?: return null val file = moduleRoot.findChild(PsiJavaModule.MODULE_INFO_CLS_FILE) ?: return null
val moduleInfo = JavaModuleInfo.read(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)
} }
} }
@@ -46,13 +46,13 @@ class CliJavaModuleResolver(
return when (file.fileType) { return when (file.fileType) {
KotlinFileType.INSTANCE, JavaFileType.INSTANCE -> sourceModule 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 else -> null
} }
} }
private operator fun JavaModule.contains(file: VirtualFile): Boolean = 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( override fun checkAccessibility(
fileFromOurModule: VirtualFile?, referencedFile: VirtualFile, referencedPackage: FqName? fileFromOurModule: VirtualFile?, referencedFile: VirtualFile, referencedPackage: FqName?
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.resolve.jvm.modules package org.jetbrains.kotlin.resolve.jvm.modules
import com.intellij.ide.highlighter.JavaClassFileType
import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
@@ -28,11 +29,11 @@ interface JavaModule {
val name: String 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 * 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
* was passed as an explicit argument to the compiler. * 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. * 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<Root>
/** /**
* A module-info.class or module-info.java file where this module was loaded from. * 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 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<Root>) : JavaModule {
override val moduleInfoFile: VirtualFile? get() = null override val moduleInfoFile: VirtualFile? get() = null
override val isBinary: Boolean get() = true override val isBinary: Boolean get() = true
@@ -78,13 +81,15 @@ interface JavaModule {
class Explicit( class Explicit(
val moduleInfo: JavaModuleInfo, val moduleInfo: JavaModuleInfo,
override val moduleRoot: VirtualFile, override val moduleRoots: List<Root>,
override val moduleInfoFile: VirtualFile, override val moduleInfoFile: VirtualFile
override val isBinary: Boolean
) : JavaModule { ) : JavaModule {
override val name: String override val name: String
get() = moduleInfo.moduleName get() = moduleInfo.moduleName
override val isBinary: Boolean
get() = moduleInfoFile.fileType == JavaClassFileType.INSTANCE
override fun exports(packageFqName: FqName): Boolean { override fun exports(packageFqName: FqName): Boolean {
return moduleInfo.exports.any { (fqName, toModules) -> return moduleInfo.exports.any { (fqName, toModules) ->
fqName == packageFqName && toModules.isEmpty() fqName == packageFqName && toModules.isEmpty()