Fix import of MPP projects in IDEA 2020.1

This commit is contained in:
Andrey Uskov
2020-02-27 06:53:46 +03:00
parent 22a6cdcc16
commit e8a54d6c75
5 changed files with 141 additions and 55 deletions
@@ -142,59 +142,15 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
}
}
private fun ExternalDependency.getDependencyArtifacts(): Collection<File> =
when (this) {
is ExternalProjectDependency -> this.projectDependencyArtifacts
is FileCollectionDependency -> this.files
else -> emptyList()
}
private fun ExternalDependency.addDependencyArtifactInternal(file: File) {
when (this) {
is ExternalProjectDependency -> this.projectDependencyArtifacts.add(file)
is FileCollectionDependency -> this.files.add(file)
}
}
override fun populateModuleDependencies(gradleModule: IdeaModule, ideModule: DataNode<ModuleData>, ideProject: DataNode<ProjectData>) {
if (resolverCtx.getExtraProject(gradleModule, KotlinMPPGradleModel::class.java) == null) {
// Add mpp-artifacts into map used for dependency substitution
val mppArtifacts = ideProject.getUserData(MPP_CONFIGURATION_ARTIFACTS)
val configArtifacts = ideProject.getUserData(CONFIGURATION_ARTIFACTS)
if (mppArtifacts != null && configArtifacts != null) {
// processing case when one artifact could be produced by several (actualized!)source sets
if (mppArtifacts.isNotEmpty() && resolverCtx.isResolveModulePerSourceSet) {
val externalProject = resolverCtx.getExtraProject(gradleModule, ExternalProject::class.java)
//Note! Should not use MultiValuesMap as it contains Set of values, but we need comparision === instead of ==
val artifactToDependency = HashMap<String, MutableCollection<ExternalDependency>>()
externalProject?.sourceSets?.values?.forEach { sourceSet ->
sourceSet.dependencies.forEach { dependency ->
dependency.getDependencyArtifacts().map { toCanonicalPath(it.absolutePath) }
.filter { mppArtifacts.keys.contains(it) }.forEach { filePath ->
(artifactToDependency[filePath] ?: ArrayList<ExternalDependency>().also { newCollection ->
artifactToDependency[filePath] = newCollection
}).add(dependency)
}
}
}
// create 'fake' dependency artifact files and put them into dependency substitution map
mppArtifacts.forEach { (filePath, moduleIds) ->
moduleIds.firstOrNull()?.also { configArtifacts[filePath] = it }
artifactToDependency[filePath]?.forEach { externalDependency ->
for ((index, module) in moduleIds.withIndex()) {
if (index != 0) {
val fakeArtifact = "$filePath-MPP-$index"
configArtifacts[fakeArtifact] = module
externalDependency.addDependencyArtifactInternal(File(fakeArtifact))
}
}
}
}
}
val mppModel = resolverCtx.getExtraProject(gradleModule, KotlinMPPGradleModel::class.java)
if (mppModel == null) {
resolverCtx.getExtraProject(gradleModule, ExternalProject::class.java)?.sourceSets?.values?.forEach { sourceSet ->
sourceSet.dependencies.modifyDependenciesOnMppModules(ideProject, resolverCtx)
}
super.populateModuleDependencies(gradleModule, ideModule, ideProject)//TODO add dependencies on mpp module
} else {
mppModel.dependencyMap.values.modifyDependenciesOnMppModules(ideProject, resolverCtx)
}
populateModuleDependencies(gradleModule, ideProject, ideModule, resolverCtx)
}
@@ -236,6 +192,72 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
private var nativeDebugAdvertised = false
private val androidPluginPresent = PluginManager.getPlugin(PluginId.findId("org.jetbrains.android"))?.isEnabled ?: false
private fun ExternalDependency.getDependencyArtifacts(): Collection<File> =
when (this) {
is ExternalProjectDependency -> this.projectDependencyArtifacts
is FileCollectionDependency -> this.files
else -> emptyList()
}
private fun Collection<ExternalDependency>.modifyDependenciesOnMppModules(
ideProject: DataNode<ProjectData>,
resolverCtx: ProjectResolverContext
) {
// Add mpp-artifacts into map used for dependency substitution
val mppArtifacts = ideProject.getUserData(MPP_CONFIGURATION_ARTIFACTS)
val configArtifacts = ideProject.getUserData(CONFIGURATION_ARTIFACTS)
if (mppArtifacts != null && configArtifacts != null) {
val reverseConfigArtifacts = HashMap(configArtifacts.map { it.value to it.key }.toMap())
// processing case when one artifact could be produced by several (actualized!)source sets
if (mppArtifacts.isNotEmpty() && resolverCtx.isResolveModulePerSourceSet) {
//Note! Should not use MultiValuesMap as it contains Set of values, but we need comparision === instead of ==
val artifactToDependency = HashMap<String, MutableCollection<ExternalDependency>>()
this.forEach { dependency ->
dependency.getDependencyArtifacts().map { toCanonicalPath(it.absolutePath) }
.filter { mppArtifacts.keys.contains(it) }.forEach { filePath ->
(artifactToDependency[filePath] ?: ArrayList<ExternalDependency>().also { newCollection ->
artifactToDependency[filePath] = newCollection
}).add(dependency)
}
}
// create 'fake' dependency artifact files and put them into dependency substitution map
mppArtifacts.forEach { (filePath, moduleIds) ->
moduleIds.firstOrNull()?.also { configArtifacts[filePath] = it }
artifactToDependency[filePath]?.forEach { externalDependency ->
for ((index, module) in moduleIds.withIndex()) {
if (index != 0) {
val fakeArtifact = if (reverseConfigArtifacts.containsKey(module)) {
reverseConfigArtifacts[module]
} else {
val result = "$filePath-MPP-$index"
configArtifacts[result] = module
reverseConfigArtifacts[module] = result
result
}
externalDependency.addDependencyArtifactInternal(File(fakeArtifact))
}
}
}
}
}
}
}
private fun ExternalDependency.addDependencyArtifactInternal(file: File) {
when (this) {
is DefaultExternalProjectDependency -> this.projectDependencyArtifacts =
ArrayList<File>(this.projectDependencyArtifacts).also {
it.add(file)
}
is ExternalProjectDependency -> try {
this.projectDependencyArtifacts.add(file)
} catch (_: Exception) {
// ignore
}
is FileCollectionDependency -> this.files.add(file)
}
}
fun initializeModuleData(
gradleModule: IdeaModule,
mainModuleNode: DataNode<ModuleData>,
@@ -358,6 +380,10 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
targetData.moduleIds = compilationIds
}
sourceSetMap.values.forEach {
it.getSecond().dependencies?.modifyDependenciesOnMppModules(projectDataNode, resolverCtx)
}
val ignoreCommonSourceSets by lazy { externalProject.notImportedCommonSourceSets() }
for (sourceSet in mppModel.sourceSets.values) {
if (delegateToAndroidPlugin(sourceSet)) continue
@@ -532,7 +558,8 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
val processedModuleIds = HashSet<String>()
processCompilations(gradleModule, mppModel, ideModule, resolverCtx) { dataNode, compilation ->
if (processedModuleIds.add(getKotlinModuleId(gradleModule, compilation, resolverCtx))) {
val substitutedDependencies = substitutor.substituteDependencies(compilation)
val substitutedDependencies =
substitutor.substituteDependencies(compilation.dependencies.mapNotNull { mppModel.dependencyMap[it] })
buildDependencies(
resolverCtx,
sourceSetMap,
@@ -722,8 +749,17 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
}
private fun addDependency(fromModule: DataNode<*>, toModule: DataNode<*>, dependOnTestModule: Boolean) {
if (fromModule.data == toModule.data) return
val fromData = fromModule.data as? ModuleData ?: return
val toData = toModule.data as? ModuleData ?: return
val existing = fromModule.children.mapNotNull { it.data as? ModuleDependencyData }.filter { it.target.id == (toModule.data as? ModuleData)?.id }
val nodeToModify =
existing.singleOrNull() ?: existing.firstOrNull { it.scope == DependencyScope.COMPILE } ?: existing.firstOrNull()
if (nodeToModify != null) {
nodeToModify.scope = DependencyScope.COMPILE
nodeToModify.isProductionOnTestDependency = nodeToModify.isProductionOnTestDependency || dependOnTestModule
return
}
val moduleDependencyData = ModuleDependencyData(fromData, toData).also {
it.scope = DependencyScope.COMPILE
it.isExported = false
@@ -201,7 +201,15 @@ class ModuleInfo(
}
fun moduleDependency(moduleName: String, scope: DependencyScope, productionOnTest: Boolean? = null) {
val moduleEntry = rootModel.orderEntries.filterIsInstance<ModuleOrderEntry>().singleOrNull { it.moduleName == moduleName }
val moduleEntries = rootModel.orderEntries.filterIsInstance<ModuleOrderEntry>().filter { it.moduleName == moduleName }
if (moduleEntries.size > 1) {
projectInfo.messageCollector.report(
"Found multiple order entries for module $moduleName: ${rootModel.orderEntries.filterIsInstance<ModuleOrderEntry>()}"
)
return
}
val moduleEntry = moduleEntries.singleOrNull()
if (moduleEntry == null) {
val allModules = rootModel.orderEntries.filterIsInstance<ModuleOrderEntry>().map { it.moduleName }.joinToString(", ")
projectInfo.messageCollector.report("Module '${module.name}': No module dependency found: '$moduleName'. All module names: $allModules")
@@ -565,6 +565,20 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
)
}
/**
* Returns only those dependencies with RUNTIME scope which are not present with compile scope
*/
private fun Collection<KotlinDependency>.onlyNewDependencies(compileDependencies: Collection<KotlinDependency>): List<KotlinDependency> {
val compileDependencyArtefacts = compileDependencies.flatMap { (it as? ExternalProjectDependency)?.projectDependencyArtifacts ?: emptyList() }
return this.filter {
if (it is ExternalProjectDependency)
!(compileDependencyArtefacts.containsAll(it.projectDependencyArtifacts))
else
true
}
}
private fun buildCompilationDependencies(
gradleCompilation: Named,
classifier: String?,
@@ -580,11 +594,14 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
)
this += buildDependencies(
gradleCompilation, dependencyResolver, "getRuntimeDependencyConfigurationName", "RUNTIME", project, transformationBuilder
)
).onlyNewDependencies(this)
this += sourceSetMap[compilationFullName(
gradleCompilation.name,
classifier
)]?.dependencies?.map { dependencyMapper.getDependency(it) }?.filterNotNull() ?: emptySet()
makeConfigurationNamesDefault(dependencyMapper)
}
}
@@ -658,7 +675,8 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
)
this += buildDependencies(
gradleSourceSet, dependencyResolver, "getRuntimeOnlyMetadataConfigurationName", "RUNTIME", project, transformationBuilder
)
).onlyNewDependencies(this)
this += buildAndroidSourceSetDependencies(androidDeps, gradleSourceSet)
}
}
@@ -793,7 +811,6 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
private val projectDependencyTransformation =
transformations.filter { it.projectPath != null }.associateBy { it.projectPath }
//TODO
val dependenciesByProjectPath by lazy {
configuration
@@ -0,0 +1,10 @@
/*
* Copyright 2010-2020 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.gradle
//BUNCH: 193
fun makeConfigurationNamesDefault(dependencyMapper: KotlinDependencyMapper) {
}
@@ -0,0 +1,15 @@
/*
* Copyright 2010-2020 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.gradle
import org.jetbrains.plugins.gradle.model.DefaultExternalProjectDependency
//BUNCH: 193
fun makeConfigurationNamesDefault(dependencyMapper: KotlinDependencyMapper) {
dependencyMapper.toDependencyMap().values.forEach {
(it as? DefaultExternalProjectDependency)?.configurationName = "default"
}
}