AA: introduce ProjectStructureProvider builder

This commit is contained in:
Jinseong Jeon
2022-05-31 10:40:48 -07:00
committed by Ilya Kirillov
parent 8528f6244d
commit 4cfefbd921
6 changed files with 181 additions and 128 deletions
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.PackagePartP
import org.jetbrains.kotlin.analysis.project.structure.KtModuleScopeProvider
import org.jetbrains.kotlin.analysis.project.structure.KtModuleScopeProviderImpl
import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider
import org.jetbrains.kotlin.analysis.project.structure.impl.KtModuleProviderByCompilerConfiguration
import org.jetbrains.kotlin.analysis.project.structure.impl.buildKtModuleProviderByCompilerConfiguration
import org.jetbrains.kotlin.analysis.providers.*
import org.jetbrains.kotlin.analysis.providers.impl.*
import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport
@@ -171,7 +171,7 @@ internal fun configureProjectEnvironment(
project.picoContainer.registerComponentInstance(
ProjectStructureProvider::class.qualifiedName,
KtModuleProviderByCompilerConfiguration(
buildKtModuleProviderByCompilerConfiguration(
compilerConfig,
project,
ktFiles,
@@ -12,9 +12,21 @@ import org.jetbrains.kotlin.platform.TargetPlatform
@KtModuleBuilderDsl
public abstract class KtModuleBuilder {
public val directRegularDependencies: MutableList<KtModule> = mutableListOf()
public val directRefinementDependencies: MutableList<KtModule> = mutableListOf()
public val directFriendDependencies: MutableList<KtModule> = mutableListOf()
protected val directRegularDependencies: MutableList<KtModule> = mutableListOf()
protected val directRefinementDependencies: MutableList<KtModule> = mutableListOf()
protected val directFriendDependencies: MutableList<KtModule> = mutableListOf()
public fun addRegularDependency(module: KtModule) {
directRegularDependencies.add(module)
}
public fun addRefinementDependency(module: KtModule) {
directRefinementDependencies.add(module)
}
public fun addFriendDependency(module: KtModule) {
directFriendDependencies.add(module)
}
public lateinit var contentScope: GlobalSearchScope
public lateinit var platform: TargetPlatform
@@ -0,0 +1,36 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.project.structure.builder
import org.jetbrains.kotlin.analysis.project.structure.KtModule
import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider
import org.jetbrains.kotlin.analysis.project.structure.impl.KtModuleProviderImpl
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
@KtModuleBuilderDsl
public class KtModuleProviderBuilder {
private val mainModules: MutableList<KtModule> = mutableListOf()
public fun addModule(module: KtModule) {
mainModules.add(module)
}
public fun build(): ProjectStructureProvider {
return KtModuleProviderImpl(
mainModules,
)
}
}
@OptIn(ExperimentalContracts::class)
public inline fun buildProjectStructureProvider(init: KtModuleProviderBuilder.() -> Unit): ProjectStructureProvider {
contract {
callsInPlace(init, InvocationKind.EXACTLY_ONCE)
}
return KtModuleProviderBuilder().apply(init).build()
}
@@ -1,123 +0,0 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.project.structure.impl
import com.google.common.io.Files
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.psi.PsiElement
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.ProjectScope
import com.intellij.util.io.URLUtil
import org.jetbrains.kotlin.analysis.api.impl.base.util.LibraryUtils
import org.jetbrains.kotlin.analysis.project.structure.KtBinaryModule
import org.jetbrains.kotlin.analysis.project.structure.KtLibraryModule
import org.jetbrains.kotlin.analysis.project.structure.KtModule
import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider
import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtLibraryModule
import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtSdkModule
import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtSourceModule
import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
import org.jetbrains.kotlin.cli.jvm.config.jvmModularRoots
import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.jvm.JdkPlatform
import org.jetbrains.kotlin.psi.KtFile
import java.nio.file.Paths
internal class KtModuleProviderByCompilerConfiguration(
compilerConfig: CompilerConfiguration,
project: Project,
ktFiles: List<KtFile>,
) : ProjectStructureProvider() {
private val sourceModule = buildKtSourceModule {
val platform = TargetPlatform(setOf(JdkPlatform(JvmTarget.DEFAULT)))
val moduleName = compilerConfig.get(CommonConfigurationKeys.MODULE_NAME) ?: "<no module name provided>"
val libraryRoots = compilerConfig.jvmModularRoots + compilerConfig.jvmClasspathRoots
val libraryRootsByType = libraryRoots.groupBy { it.isDirectory }
libraryRootsByType[true]?.let { directories ->
directories.forEach {
// E.g., project/app/build/intermediates/javac/debug/classes
val root = it.toPath()
directRegularDependencies.add(
buildKtLibraryModule {
contentScope = ProjectScope.getLibrariesScope(project)
this.platform = platform
this.project = project
binaryRoots = listOf(root)
libraryName = "$moduleName-${root.toString().replace("/", "-")}"
}
)
}
}
libraryRootsByType[false]?.let { jars ->
jars.forEach {
// E.g., project/libs/libA/a.jar
val root = it.toPath()
directRegularDependencies.add(
buildKtLibraryModule {
contentScope = ProjectScope.getLibrariesScope(project)
this.platform = platform
this.project = project
binaryRoots = listOf(root)
libraryName = Files.getNameWithoutExtension(root.toString())
isBuiltinsContainingStdlib =
libraryName.startsWith("kotlin-stdlib") &&
!libraryName.contains("common") && !libraryName.contains("jdk")
}
)
}
}
compilerConfig.get(JVMConfigurationKeys.JDK_HOME)?.let { jdkHome ->
val vfm = VirtualFileManager.getInstance()
val jdkHomePath = jdkHome.toPath()
val jdkHomeVirtualFile = vfm.findFileByNioPath(jdkHomePath)
val binaryRoots = LibraryUtils.findClassesFromJdkHome(jdkHomePath).map {
Paths.get(URLUtil.extractPath(it))
}
directRegularDependencies.add(
buildKtSdkModule {
contentScope = GlobalSearchScope.fileScope(project, jdkHomeVirtualFile)
this.platform = platform
this.project = project
this.binaryRoots = binaryRoots
sdkName = "JDK for $moduleName"
}
)
}
contentScope = TopDownAnalyzerFacadeForJVM.newModuleSearchScope(project, ktFiles)
this.platform = platform
this.project = project
this.moduleName = moduleName
}
private val binaryModules: Collection<KtBinaryModule> by lazy {
sourceModule.directRegularDependencies.filterIsInstance<KtBinaryModule>()
}
override fun getKtModuleForKtElement(element: PsiElement): KtModule {
val containingFile = element.containingFile.virtualFile
return if (containingFile in sourceModule.contentScope) {
sourceModule
} else {
binaryModules.find { libModule -> containingFile in libModule.contentScope }
?: error("Can't find module for ${containingFile.path}")
}
}
override fun getKtBinaryModules(): Collection<KtBinaryModule> {
return binaryModules
}
override fun getStdlibWithBuiltinsModule(module: KtModule): KtLibraryModule? {
return binaryModules
.filterIsInstance<KtLibraryModuleImpl>()
.firstOrNull { it.isBuiltinsContainingStdlib }
}
}
@@ -0,0 +1,36 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.project.structure.impl
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analysis.project.structure.*
internal class KtModuleProviderImpl(
private val mainModules: List<KtModule>,
) : ProjectStructureProvider() {
override fun getKtModuleForKtElement(element: PsiElement): KtModule {
val containingFile = element.containingFile.virtualFile
return mainModules.first { module ->
containingFile in module.contentScope
}
}
private val binaryModules: Collection<KtBinaryModule> by lazy {
mainModules
.flatMap { it.allDirectDependencies() }
.filterIsInstance<KtBinaryModule>()
}
override fun getKtBinaryModules(): Collection<KtBinaryModule> {
return binaryModules
}
override fun getStdlibWithBuiltinsModule(module: KtModule): KtLibraryModule? {
return binaryModules
.filterIsInstance<KtLibraryModuleImpl>()
.firstOrNull { it.isBuiltinsContainingStdlib }
}
}
@@ -5,16 +5,38 @@
package org.jetbrains.kotlin.analysis.project.structure.impl
import com.google.common.io.Files
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.ProjectScope
import com.intellij.util.io.URLUtil
import org.jetbrains.kotlin.analysis.api.impl.base.util.LibraryUtils
import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider
import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtLibraryModule
import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtSdkModule
import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtSourceModule
import org.jetbrains.kotlin.analysis.project.structure.builder.buildProjectStructureProvider
import org.jetbrains.kotlin.analyzer.common.CommonPlatformAnalyzerServices
import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
import org.jetbrains.kotlin.cli.jvm.config.jvmModularRoots
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.js.resolve.JsPlatformAnalyzerServices
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.isCommon
import org.jetbrains.kotlin.platform.js.isJs
import org.jetbrains.kotlin.platform.jvm.JdkPlatform
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.platform.konan.isNative
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices
import org.jetbrains.kotlin.resolve.konan.platform.NativePlatformAnalyzerServices
import java.nio.file.Paths
internal fun TargetPlatform.getAnalyzerServices(): PlatformDependentAnalyzerServices {
return when {
@@ -26,3 +48,73 @@ internal fun TargetPlatform.getAnalyzerServices(): PlatformDependentAnalyzerServ
}
}
internal fun buildKtModuleProviderByCompilerConfiguration(
compilerConfig: CompilerConfiguration,
project: Project,
ktFiles: List<KtFile>,
): ProjectStructureProvider = buildProjectStructureProvider {
addModule(
buildKtSourceModule {
val platform = TargetPlatform(setOf(JdkPlatform(JvmTarget.DEFAULT)))
val moduleName = compilerConfig.get(CommonConfigurationKeys.MODULE_NAME) ?: "<no module name provided>"
val libraryRoots = compilerConfig.jvmModularRoots + compilerConfig.jvmClasspathRoots
val libraryRootsByType = libraryRoots.groupBy { it.isDirectory }
libraryRootsByType[true]?.let { directories ->
directories.forEach {
// E.g., project/app/build/intermediates/javac/debug/classes
val root = it.toPath()
addRegularDependency(
buildKtLibraryModule {
contentScope = ProjectScope.getLibrariesScope(project)
this.platform = platform
this.project = project
binaryRoots = listOf(root)
libraryName = "$moduleName-${root.toString().replace("/", "-")}"
}
)
}
}
libraryRootsByType[false]?.let { jars ->
jars.forEach {
// E.g., project/libs/libA/a.jar
val root = it.toPath()
addRegularDependency(
buildKtLibraryModule {
contentScope = ProjectScope.getLibrariesScope(project)
this.platform = platform
this.project = project
binaryRoots = listOf(root)
libraryName = Files.getNameWithoutExtension(root.toString())
isBuiltinsContainingStdlib =
libraryName.startsWith("kotlin-stdlib") &&
!libraryName.contains("common") && !libraryName.contains("jdk")
}
)
}
}
compilerConfig.get(JVMConfigurationKeys.JDK_HOME)?.let { jdkHome ->
val vfm = VirtualFileManager.getInstance()
val jdkHomePath = jdkHome.toPath()
val jdkHomeVirtualFile = vfm.findFileByNioPath(jdkHomePath)
val binaryRoots = LibraryUtils.findClassesFromJdkHome(jdkHomePath).map {
Paths.get(URLUtil.extractPath(it))
}
addRegularDependency(
buildKtSdkModule {
contentScope = GlobalSearchScope.fileScope(project, jdkHomeVirtualFile)
this.platform = platform
this.project = project
this.binaryRoots = binaryRoots
sdkName = "JDK for $moduleName"
}
)
}
contentScope = TopDownAnalyzerFacadeForJVM.newModuleSearchScope(project, ktFiles)
this.platform = platform
this.project = project
this.moduleName = moduleName
}
)
}