Simplify building of source-sets during MPP Import

Build dependsOn closure straight away by using KotlinSourceSetProto
instead of building first KotlinSourceSetImpls with incorrect
dependsOn-names, and then discarding it

^KT-37127
This commit is contained in:
Dmitry Savvinov
2020-12-16 13:20:02 +03:00
parent 9e58e3c3fd
commit 1d0a696a62
2 changed files with 29 additions and 45 deletions
@@ -108,25 +108,6 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
return getCoroutines(experimentalExt) as? String
}
private fun calculateDependsOnClosure(
sourceSet: KotlinSourceSetImpl?,
sourceSetsMap: Map<String, KotlinSourceSetImpl>,
cache: MutableMap<String, Set<String>>
): Set<String> {
return if (sourceSet == null) {
emptySet()
} else {
cache[sourceSet.name] ?: sourceSet.dependsOnSourceSets.flatMap { name ->
calculateDependsOnClosure(
sourceSetsMap[name],
sourceSetsMap,
cache
).union(setOf(name))
}.toSet().also { cache[sourceSet.name] = it }
}
}
private fun buildSourceSets(
importingContext: MultiplatformModelImportingContext,
dependencyResolver: DependencyResolver,
@@ -140,35 +121,22 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
(getSourceSets(kotlinExt) as? NamedDomainObjectContainer<Named>)?.asMap?.values ?: emptyList<Named>()
val androidDeps = buildAndroidDeps(importingContext, kotlinExt.javaClass.classLoader)
val allSourceSetsProtos = sourceSets.mapNotNull {
val allSourceSetsProtosByNames = sourceSets.mapNotNull {
buildSourceSet(it, dependencyResolver, importingContext.project, dependencyMapper, androidDeps)
}
}.associateBy { it.name }
val dependsOnCache = HashMap<String, Set<String>>()
// Some performance optimisation: do not build metadata dependencies if source set is not common
val allSourceSets = if (importingContext.getProperty(BUILD_METADATA_DEPENDENCIES)) {
allSourceSetsProtos.map { proto -> proto.buildKotlinSourceSetImpl(true) }
return if (importingContext.getProperty(BUILD_METADATA_DEPENDENCIES)) {
allSourceSetsProtosByNames.mapValues { (_, proto) ->
proto.buildKotlinSourceSetImpl(true, allSourceSetsProtosByNames, dependsOnCache)
}
} else {
val unactualizedSourceSets = allSourceSetsProtos.flatMap { it.dependsOnSourceSets }.distinct()
allSourceSetsProtos.map { proto -> proto.buildKotlinSourceSetImpl(unactualizedSourceSets.contains(proto.name)) }
val unactualizedSourceSets = allSourceSetsProtosByNames.values.flatMap { it.dependsOnSourceSets }.distinct()
allSourceSetsProtosByNames.mapValues { (name, proto) ->
proto.buildKotlinSourceSetImpl(unactualizedSourceSets.contains(name), allSourceSetsProtosByNames, dependsOnCache)
}
}
// TODO: Weird quirk, we actually *recreate* all source-sets, because those contain
// wrong (non-closured) dependsOnNames, so we can not save this map in the importingContext straight away
// This will be fixed in later commit
val map = allSourceSets.map { it.name to it }.toMap()
val dependsOnCache = HashMap<String, Set<String>>()
return allSourceSets.map { sourceSet ->
sourceSet.name to KotlinSourceSetImpl(
sourceSet.name,
sourceSet.languageSettings,
sourceSet.sourceDirs,
sourceSet.resourceDirs,
sourceSet.dependencies,
calculateDependsOnClosure(sourceSet, map, dependsOnCache),
sourceSet.actualPlatforms as KotlinPlatformContainerImpl,
sourceSet.isTestModule
)
}.toMap()
}
private fun buildAndroidDeps(importingContext: MultiplatformModelImportingContext, classLoader: ClassLoader): Map<String, List<Any>>? {
@@ -17,15 +17,31 @@ class KotlinSourceSetProto(
val dependsOnSourceSets: Set<String>
) {
fun buildKotlinSourceSetImpl(doBuildDependencies: Boolean) = KotlinSourceSetImpl(
fun buildKotlinSourceSetImpl(
doBuildDependencies: Boolean,
allSourceSetsProtosByNames: Map<String, KotlinSourceSetProto>,
dependsOnCache: HashMap<String, Set<String>>
) = KotlinSourceSetImpl(
name,
languageSettings,
sourceDirs,
resourceDirs,
if (doBuildDependencies) dependencies.invoke() else emptyArray(),
dependsOnSourceSets
calculateDependsOnClosure(this, allSourceSetsProtosByNames, dependsOnCache)
)
private fun calculateDependsOnClosure(
currentSourceSetProto: KotlinSourceSetProto,
sourceSetsMap: Map<String, KotlinSourceSetProto>,
cache: MutableMap<String, Set<String>>
): Set<String> {
return cache.computeIfAbsent(currentSourceSetProto.name) {
currentSourceSetProto.dependsOnSourceSets.flatMap { name ->
val nextSourceSet = sourceSetsMap[name] ?: error("Source set $name is not found in map $sourceSetsMap")
calculateDependsOnClosure(nextSourceSet, sourceSetsMap, cache).union(setOf(name))
}.toSet()
}
}
}
class KotlinSourceSetImpl(