[Gradle] Merge Android Source Sets into one K2 Fragment

Android projects has a number of build type/flavor specific source set.
These source sets are included into compilation of multiple variants.
In Android compilation model they should be compiled together and they
should see each-others declarations. With Kotlin MPP plugin applied
these android source sets gets associated with Kotlin Source Sets
counter parts. However in Kotlin Multiplatform Source sets have
a semantics about "expect/actual" refinement and with K2 it also got
stricter and visibility is checked. This is not compatible with Android
compilation model thus the solution is to merge Android Source Sets
into one K2 Fragment.

^KT-62508 Verification Pending
This commit is contained in:
Anton Lakotka
2024-02-13 10:24:25 +01:00
committed by Space Team
parent 0a86f03457
commit b5ed2326e7
@@ -6,7 +6,9 @@
package org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl
import org.jetbrains.kotlin.gradle.dsl.usesK2
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.factory.KotlinCompilationImplFactory
import org.jetbrains.kotlin.gradle.plugin.sources.android.androidSourceSetInfoOrNull
import org.jetbrains.kotlin.gradle.tasks.K2MultiplatformCompilationTask
import org.jetbrains.kotlin.gradle.tasks.K2MultiplatformStructure
@@ -16,20 +18,38 @@ internal object KotlinCompilationK2MultiplatformConfigurator : KotlinCompilation
if (compileTask.name != compilation.compileKotlinTaskName) return@configureEach
if (compileTask !is K2MultiplatformCompilationTask) return@configureEach
/**
* Returns fragment name of [this]
* by default it is name of [KotlinSourceSet] but for android it should name of compilation's default source set.
* i.e. all android-specific source sets (fragments) should be combined into one.
* See KT-62508 for detailed explanation
*/
fun KotlinSourceSet.fragmentName(): String =
if (androidSourceSetInfoOrNull != null) {
compilation.defaultSourceSet.name
} else {
name
}
compileTask.multiplatformStructure.refinesEdges.set(compilation.project.provider {
if (!compileTask.compilerOptions.usesK2.get()) return@provider emptyList()
compilation.allKotlinSourceSets.flatMap { sourceSet ->
sourceSet.dependsOn.map { dependsOn ->
K2MultiplatformStructure.RefinesEdge(sourceSet.name, dependsOn.name)
sourceSet.dependsOn.mapNotNull { dependsOn ->
val from = sourceSet.fragmentName()
val to = dependsOn.fragmentName()
if (from == to) return@mapNotNull null
K2MultiplatformStructure.RefinesEdge(from, to)
}
}
})
compileTask.multiplatformStructure.fragments.set(compilation.project.provider {
if (!compileTask.compilerOptions.usesK2.get()) return@provider emptyList()
compilation.allKotlinSourceSets.map { sourceSet ->
K2MultiplatformStructure.Fragment(sourceSet.name, sourceSet.kotlin.asFileTree)
}
compilation.allKotlinSourceSets
.groupBy(keySelector = { it.fragmentName() }) { sourceSet -> sourceSet.kotlin.asFileTree }
.map { (fragmentName, sourceFiles) ->
K2MultiplatformStructure.Fragment(fragmentName, sourceFiles.reduce { acc, fileTree -> acc + fileTree })
}
})
}
}