Cache module dependencies on project import

#KT-27832 Fixed
This commit is contained in:
Andrey Uskov
2018-11-13 17:06:58 +03:00
parent daa54978d1
commit 53414aca87
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.configuration
import com.intellij.openapi.externalSystem.model.DataNode import com.intellij.openapi.externalSystem.model.DataNode
import com.intellij.openapi.externalSystem.model.ProjectKeys import com.intellij.openapi.externalSystem.model.ProjectKeys
import com.intellij.openapi.externalSystem.model.project.ExternalSystemSourceType
import com.intellij.openapi.externalSystem.model.project.ModuleData import com.intellij.openapi.externalSystem.model.project.ModuleData
import com.intellij.openapi.externalSystem.model.project.ModuleDependencyData import com.intellij.openapi.externalSystem.model.project.ModuleDependencyData
import com.intellij.openapi.externalSystem.model.project.ProjectData import com.intellij.openapi.externalSystem.model.project.ProjectData
@@ -35,12 +36,14 @@ import org.jetbrains.kotlin.idea.inspections.gradle.getDependencyModules
import org.jetbrains.kotlin.idea.util.CopyableDataNodeUserDataProperty import org.jetbrains.kotlin.idea.util.CopyableDataNodeUserDataProperty
import org.jetbrains.kotlin.idea.util.NotNullableCopyableDataNodeUserDataProperty import org.jetbrains.kotlin.idea.util.NotNullableCopyableDataNodeUserDataProperty
import org.jetbrains.plugins.gradle.model.ExternalProjectDependency import org.jetbrains.plugins.gradle.model.ExternalProjectDependency
import org.jetbrains.plugins.gradle.model.ExternalSourceSet
import org.jetbrains.plugins.gradle.model.FileCollectionDependency import org.jetbrains.plugins.gradle.model.FileCollectionDependency
import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData
import org.jetbrains.plugins.gradle.service.project.AbstractProjectResolverExtension import org.jetbrains.plugins.gradle.service.project.AbstractProjectResolverExtension
import org.jetbrains.plugins.gradle.service.project.GradleProjectResolver import org.jetbrains.plugins.gradle.service.project.GradleProjectResolver
import java.io.File import java.io.File
import java.util.* import java.util.*
import kotlin.collections.HashMap
var DataNode<ModuleData>.isResolved var DataNode<ModuleData>.isResolved
by NotNullableCopyableDataNodeUserDataProperty(Key.create<Boolean>("IS_RESOLVED"), false) by NotNullableCopyableDataNodeUserDataProperty(Key.create<Boolean>("IS_RESOLVED"), false)
@@ -54,6 +57,13 @@ var DataNode<ModuleData>.platformPluginId
by CopyableDataNodeUserDataProperty(Key.create<String>("PLATFORM_PLUGIN_ID")) by CopyableDataNodeUserDataProperty(Key.create<String>("PLATFORM_PLUGIN_ID"))
var DataNode<out ModuleData>.implementedModuleNames var DataNode<out ModuleData>.implementedModuleNames
by NotNullableCopyableDataNodeUserDataProperty(Key.create<List<String>>("IMPLEMENTED_MODULE_NAME"), emptyList()) by NotNullableCopyableDataNodeUserDataProperty(Key.create<List<String>>("IMPLEMENTED_MODULE_NAME"), emptyList())
// Project is usually the same during all import, thus keeping Map Project->Dependencies makes model a bit more complicated but allows to avoid future problems
var DataNode<out ModuleData>.dependenciesCache
by NotNullableCopyableDataNodeUserDataProperty(
Key.create<MutableMap<DataNode<ProjectData>, Collection<DataNode<out ModuleData>>>>("MODULE_DEPENDENCIES_CACHE"),
HashMap()
)
class KotlinGradleProjectResolverExtension : AbstractProjectResolverExtension() { class KotlinGradleProjectResolverExtension : AbstractProjectResolverExtension() {
val isAndroidProjectKey = Key.findKeyByName("IS_ANDROID_PROJECT_KEY") val isAndroidProjectKey = Key.findKeyByName("IS_ANDROID_PROJECT_KEY")
@@ -74,24 +84,11 @@ class KotlinGradleProjectResolverExtension : AbstractProjectResolverExtension()
return resolverCtx.isResolveModulePerSourceSet return resolverCtx.isResolveModulePerSourceSet
} }
override fun populateModuleDependencies( private fun getDependencyByFiles(
gradleModule: IdeaModule, files: Collection<File>,
ideModule: DataNode<ModuleData>, outputToSourceSet: Map<String, com.intellij.openapi.util.Pair<String, ExternalSystemSourceType>>?,
ideProject: DataNode<ProjectData> sourceSetByName: Map<String, com.intellij.openapi.util.Pair<DataNode<GradleSourceSetData>, ExternalSourceSet>>?
) { ) = files
if (resolverCtx.getExtraProject(gradleModule, KotlinMPPGradleModel::class.java) != null) {
return super.populateModuleDependencies(gradleModule, ideModule, ideProject)
}
val outputToSourceSet = ideProject.getUserData(GradleProjectResolver.MODULES_OUTPUTS)
val sourceSetByName = ideProject.getUserData(GradleProjectResolver.RESOLVED_SOURCE_SETS)
val gradleModel = resolverCtx.getExtraProject(gradleModule, KotlinGradleModel::class.java)
?: return super.populateModuleDependencies(gradleModule, ideModule, ideProject)
val gradleIdeaProject = gradleModule.project
fun getDependencyByFiles(files: Collection<File>) = files
.mapTo(HashSet()) { .mapTo(HashSet()) {
val path = FileUtil.toSystemIndependentName(it.path) val path = FileUtil.toSystemIndependentName(it.path)
val targetSourceSetId = outputToSourceSet?.get(path)?.first ?: return@mapTo null val targetSourceSetId = outputToSourceSet?.get(path)?.first ?: return@mapTo null
@@ -99,11 +96,15 @@ class KotlinGradleProjectResolverExtension : AbstractProjectResolverExtension()
} }
.singleOrNull() .singleOrNull()
fun DataNode<out ModuleData>.getDependencies(): Collection<DataNode<out ModuleData>> { private fun DataNode<out ModuleData>.getDependencies(ideProject: DataNode<ProjectData>): Collection<DataNode<out ModuleData>> {
if (useModulePerSourceSet()) { if (dependenciesCache.containsKey(ideProject)) {
if (sourceSetByName == null) return emptySet() return dependenciesCache[ideProject]!!
}
val outputToSourceSet = ideProject.getUserData(GradleProjectResolver.MODULES_OUTPUTS)
val sourceSetByName = ideProject.getUserData(GradleProjectResolver.RESOLVED_SOURCE_SETS) ?: return emptySet()
val externalSourceSet = sourceSetByName[data.id]?.second ?: return emptySet() val externalSourceSet = sourceSetByName[data.id]?.second ?: return emptySet()
return externalSourceSet.dependencies.mapNotNullTo(LinkedHashSet()) { dependency -> val result = externalSourceSet.dependencies.mapNotNullTo(LinkedHashSet()) { dependency ->
when (dependency) { when (dependency) {
is ExternalProjectDependency -> { is ExternalProjectDependency -> {
if (dependency.configurationName == Dependency.DEFAULT_CONFIGURATION) { if (dependency.configurationName == Dependency.DEFAULT_CONFIGURATION) {
@@ -113,21 +114,24 @@ class KotlinGradleProjectResolverExtension : AbstractProjectResolverExtension()
ExternalSystemApiUtil.findAll(targetModuleNode, GradleSourceSetData.KEY) ExternalSystemApiUtil.findAll(targetModuleNode, GradleSourceSetData.KEY)
.firstOrNull { it.sourceSetName == "main" } .firstOrNull { it.sourceSetName == "main" }
} else { } else {
getDependencyByFiles(dependency.projectDependencyArtifacts) getDependencyByFiles(dependency.projectDependencyArtifacts, outputToSourceSet, sourceSetByName)
} }
} }
is FileCollectionDependency -> { is FileCollectionDependency -> {
getDependencyByFiles(dependency.files) getDependencyByFiles(dependency.files, outputToSourceSet, sourceSetByName)
} }
else -> null else -> null
} }
} }
dependenciesCache[ideProject] = result
return result
} }
return getDependencyModules(ideModule, gradleIdeaProject) private fun addTransitiveDependenciesOnImplementedModules(
} gradleModule: IdeaModule,
ideModule: DataNode<ModuleData>,
fun addTransitiveDependenciesOnImplementedModules() { ideProject: DataNode<ProjectData>
) {
val moduleNodesToProcess = if (useModulePerSourceSet()) { val moduleNodesToProcess = if (useModulePerSourceSet()) {
ExternalSystemApiUtil.findAll(ideModule, GradleSourceSetData.KEY) ExternalSystemApiUtil.findAll(ideModule, GradleSourceSetData.KEY)
} else listOf(ideModule) } else listOf(ideModule)
@@ -138,7 +142,7 @@ class KotlinGradleProjectResolverExtension : AbstractProjectResolverExtension()
toProcess.add(currentModuleNode) toProcess.add(currentModuleNode)
while (toProcess.isNotEmpty()) { while (toProcess.isNotEmpty()) {
val moduleNode = toProcess.pollFirst() val moduleNode = toProcess.pollLast()
if (processed.contains(moduleNode)) continue if (processed.contains(moduleNode)) continue
processed.add(moduleNode) processed.add(moduleNode)
@@ -146,9 +150,11 @@ class KotlinGradleProjectResolverExtension : AbstractProjectResolverExtension()
val moduleNodeForGradleModel = if (useModulePerSourceSet()) { val moduleNodeForGradleModel = if (useModulePerSourceSet()) {
ExternalSystemApiUtil.findParent(moduleNode, ProjectKeys.MODULE) ExternalSystemApiUtil.findParent(moduleNode, ProjectKeys.MODULE)
} else moduleNode } else moduleNode
val ideaModule = if (moduleNodeForGradleModel != ideModule) { val ideaModule = if (moduleNodeForGradleModel != ideModule) {
gradleIdeaProject.modules.firstOrNull { it.gradleProject.path == moduleNodeForGradleModel?.data?.id } gradleModule.project.modules.firstOrNull { it.gradleProject.path == moduleNodeForGradleModel?.data?.id }
} else gradleModule } else gradleModule
val implementsModuleIds = resolverCtx.getExtraProject(ideaModule, KotlinGradleModel::class.java)?.implements val implementsModuleIds = resolverCtx.getExtraProject(ideaModule, KotlinGradleModel::class.java)?.implements
?: emptyList() ?: emptyList()
@@ -172,16 +178,30 @@ class KotlinGradleProjectResolverExtension : AbstractProjectResolverExtension()
} }
} }
moduleNode.getDependencies().filterTo(toProcess) { it !in processed } val dependencies = if (useModulePerSourceSet()) moduleNode.getDependencies(ideProject) else getDependencyModules(ideModule, gradleModule.project)
dependencies.filterTo(toProcess) { it !in processed }
} }
} }
} }
override fun populateModuleDependencies(
gradleModule: IdeaModule,
ideModule: DataNode<ModuleData>,
ideProject: DataNode<ProjectData>
) {
if (resolverCtx.getExtraProject(gradleModule, KotlinMPPGradleModel::class.java) != null) {
return super.populateModuleDependencies(gradleModule, ideModule, ideProject)
}
val gradleModel = resolverCtx.getExtraProject(gradleModule, KotlinGradleModel::class.java)
?: return super.populateModuleDependencies(gradleModule, ideModule, ideProject)
if (!useModulePerSourceSet()) { if (!useModulePerSourceSet()) {
super.populateModuleDependencies(gradleModule, ideModule, ideProject) super.populateModuleDependencies(gradleModule, ideModule, ideProject)
} }
addTransitiveDependenciesOnImplementedModules() addTransitiveDependenciesOnImplementedModules(gradleModule, ideModule, ideProject)
ideModule.isResolved = true ideModule.isResolved = true
ideModule.hasKotlinPlugin = gradleModel.hasKotlinPlugin ideModule.hasKotlinPlugin = gradleModel.hasKotlinPlugin