Implement precise platforms importing in MPP

- skip metadata target from importing. That lead to metadata
compilations being imported is well -> some source-sets were
participating in metadata compilations as well -> logic for determining
platforms used to consider metadata compilations as well, adding COMMON
platform to set of platforms

Seems like metadata was never needed in import/IDE, and got there purely
by accident

- Use only targets, actually present in the project, as the default
platform. This is needed mostly for corner-cases/miconfigurations, like
orphan source-sets (source-sets which are created but not included into
any configuraion). Still, for those source-sets the tooling is required
to behave properly; presence of non-existing target can lead to various
issues like showing gutters for test runs, which would fail on launch
(because tests for that target actually do not exist)

^KT-37127 Fixed
This commit is contained in:
Dmitry Savvinov
2021-01-15 11:21:10 +03:00
committed by Yaroslav Chernyshev
parent 7a5b4ccb9a
commit c3f5d57d3b
5 changed files with 39 additions and 11 deletions
@@ -360,7 +360,7 @@ class HierarchicalMultiplatformProjectImportingTest : MultiplePluginVersionGradl
targetPlatform(jvm)
}
module("my-app.commonMain") { targetPlatform(jvm, anyNative, js) }
module("my-app.commonMain") { targetPlatform(jvm, anyNative) }
module("my-app.commonTest") { targetPlatform(jvm, anyNative) }
module("my-app.jvmAndLinuxMain") { targetPlatform(jvm, anyNative) }
@@ -395,7 +395,7 @@ class HierarchicalMultiplatformProjectImportingTest : MultiplePluginVersionGradl
targetPlatform(jvm)
}
module("my-app.commonMain") { targetPlatform(jvm, anyNative, js) } // :( should be (jvm, anyNative)
module("my-app.commonMain") { targetPlatform(jvm, anyNative) }
module("my-app.commonTest") { targetPlatform(jvm, anyNative) }
module("my-app.jvmAndLinuxMain") { targetPlatform(jvm, anyNative) }
@@ -414,7 +414,7 @@ class HierarchicalMultiplatformProjectImportingTest : MultiplePluginVersionGradl
// submodule
module("my-app.submodule") { targetPlatform(jvm) }
module("my-app.submodule.commonMain") { targetPlatform(js, jvm, anyNative) } // :( should be (js, jvm)
module("my-app.submodule.commonMain") { targetPlatform(js, jvm) }
module("my-app.submodule.commonTest") { targetPlatform(js, jvm) }
module("my-app.submodule.jvmMain") { targetPlatform(jvm) }
@@ -438,11 +438,11 @@ class HierarchicalMultiplatformProjectImportingTest : MultiplePluginVersionGradl
checkProjectStructure(exhaustiveModuleList = false, exhaustiveSourceSourceRootList = false, exhaustiveDependencyList = false) {
module("my-app.commonMain") {
// must not be (jvm, js, native)
targetPlatform(jvm, js, native)
targetPlatform(jvm, js)
}
module("my-app.orphan") {
targetPlatform(jvm, js, native)
targetPlatform(jvm, js)
}
module("my-app") {
assertDiagnosticsCount<OrphanSourceSetsImportingDiagnostic>(1)
@@ -462,7 +462,7 @@ class HierarchicalMultiplatformProjectImportingTest : MultiplePluginVersionGradl
checkProjectStructure(exhaustiveModuleList = false, exhaustiveSourceSourceRootList = false, exhaustiveDependencyList = false) {
module("my-app.commonMain") {
targetPlatform(jvm, js, native) // must not be (jvm, js, native)
targetPlatform(jvm, js) // must not be (jvm, js, native)
}
module("my-app.includedIntoJvm") {
@@ -487,7 +487,7 @@ class HierarchicalMultiplatformProjectImportingTest : MultiplePluginVersionGradl
checkProjectStructure(exhaustiveModuleList = false, exhaustiveSourceSourceRootList = false, exhaustiveDependencyList = false) {
module("my-app.commonMain") {
targetPlatform(jvm, js, native) // must not be (jvm, js, native)
targetPlatform(jvm, js) // must not be (jvm, js, native)
}
module("my-app.intermediateBetweenJsAndCommon") {
@@ -542,19 +542,19 @@ class HierarchicalMultiplatformProjectImportingTest : MultiplePluginVersionGradl
// (jvm, js, native) is highly undesirable
module("my-app.danglingOnJvm") {
targetPlatform(jvm, js, native)
targetPlatform(jvm, js)
}
module("my-app.commonMain") {
targetPlatform(jvm, js, native)
targetPlatform(jvm, js)
}
module("my-app.danglingOnCommon") {
targetPlatform(jvm, js, native)
targetPlatform(jvm, js)
}
module("my-app.danglingOnJvmAndJs") {
targetPlatform(jvm, js, native)
targetPlatform(jvm, js)
}
}
}
@@ -142,6 +142,7 @@ enum class KotlinPlatform(val id: String) {
interface KotlinPlatformContainer : Serializable {
val platforms: Collection<KotlinPlatform>
val arePlatformsInitialized: Boolean
fun supports(simplePlatform: KotlinPlatform): Boolean
@@ -305,6 +305,10 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
dependencyMapper: KotlinDependencyMapper
): KotlinTarget? {
val targetClass = gradleTarget.javaClass
val metadataTargetClass = targetClass.classLoader.loadClass("org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMetadataTarget")
if (metadataTargetClass.isInstance(gradleTarget)) return null
val getPlatformType = targetClass.getMethodOrNull("getPlatformType") ?: return null
val getDisambiguationClassifier = targetClass.getMethodOrNull("getDisambiguationClassifier") ?: return null
val platformId = (getPlatformType.invoke(gradleTarget) as? Named)?.name ?: return null
@@ -740,6 +744,18 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
}
private fun MultiplatformModelImportingContext.computeSourceSetPlatforms(sourceSet: KotlinSourceSetImpl) {
require(!sourceSet.actualPlatforms.arePlatformsInitialized) {
"Attempt to re-initialize platforms for source set ${sourceSet}. Already present platforms: ${sourceSet.actualPlatforms}"
}
if (isOrphanSourceSet(sourceSet)) {
// Explicitly set platform of orphan source-sets to only used platforms, not all supported platforms
// Otherwise, the tooling might be upset after trying to provide some support for a target which actually
// doesn't exist in this project (e.g. after trying to draw gutters, while test tasks do not exist)
sourceSet.actualPlatforms.addSimplePlatforms(projectPlatforms)
return
}
if (shouldCoerceToCommon(sourceSet)) {
sourceSet.actualPlatforms.addSimplePlatforms(listOf(KotlinPlatform.COMMON))
return
@@ -285,6 +285,8 @@ class KotlinPlatformContainerImpl() : KotlinPlatformContainer {
private val defaultCommonPlatform = setOf(KotlinPlatform.COMMON)
private var myPlatforms: MutableSet<KotlinPlatform>? = null
override val arePlatformsInitialized: Boolean
get() = myPlatforms != null
constructor(platform: KotlinPlatformContainer) : this() {
myPlatforms = HashSet<KotlinPlatform>(platform.platforms)
@@ -23,6 +23,11 @@ internal interface MultiplatformModelImportingContext {
val sourceSets: Collection<KotlinSourceSetImpl>
val sourceSetsByNames: Map<String, KotlinSourceSetImpl>
/**
* Platforms, which are actually used in this project (i.e. platforms, for which targets has been created)
*/
val projectPlatforms: Collection<KotlinPlatform>
fun sourceSetByName(name: String): KotlinSourceSet?
fun compilationsBySourceSet(sourceSet: KotlinSourceSet): Collection<KotlinCompilation>?
@@ -86,6 +91,9 @@ internal class MultiplatformModelImportingContextImpl(override val project: Proj
override lateinit var targets: Collection<KotlinTarget>
private set
override lateinit var projectPlatforms: Collection<KotlinPlatform>
private set
internal fun initializeSourceSets(sourceSetsByNames: Map<String, KotlinSourceSetImpl>) {
require(!this::sourceSetsByNames.isInitialized) {
"Attempt to re-initialize source sets for $this. Previous value: ${this.sourceSetsByNames}"
@@ -118,6 +126,7 @@ internal class MultiplatformModelImportingContextImpl(override val project: Proj
internal fun initializeTargets(targets: Collection<KotlinTarget>) {
require(!this::targets.isInitialized) { "Attempt to re-initialize targets for $this. Previous value: ${this.targets}" }
this.targets = targets
this.projectPlatforms = targets.map { it.platform }
}
// overload for small optimization