[MPP] Add stdlib-common filter, extend platform dependency resolution

KT-54975
This commit is contained in:
Pavel Kirpichenkov
2022-11-29 20:31:33 +02:00
parent 13d1f0eab0
commit cba90c73e6
7 changed files with 280 additions and 3 deletions
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.gradle.plugin.ide.IdeMultiplatformImport.SourceSetCo
import org.jetbrains.kotlin.gradle.plugin.sources.internal
import org.jetbrains.kotlin.gradle.plugin.sources.project
import org.jetbrains.kotlin.gradle.targets.metadata.isNativeSourceSet
import org.jetbrains.kotlin.gradle.targets.metadata.isSinglePlatformTypeSourceSet
import org.jetbrains.kotlin.gradle.utils.getOrPut
import org.jetbrains.kotlin.tooling.core.Extras
@@ -143,7 +144,9 @@ interface IdeMultiplatformImport {
companion object {
val unconstrained = SourceSetConstraint { true }
val isNative = SourceSetConstraint { sourceSet -> isNativeSourceSet(sourceSet) }
val isNative = SourceSetConstraint { isNativeSourceSet(it) }
val isSinglePlatformType = SourceSetConstraint { isSinglePlatformTypeSourceSet(it) }
val isLeaf = SourceSetConstraint { sourceSet ->
(sourceSet.project.multiplatformExtensionOrNull ?: return@SourceSetConstraint true).sourceSets
@@ -13,8 +13,10 @@ import org.jetbrains.kotlin.gradle.idea.tcs.extras.isIdeaProjectLevelKey
import org.jetbrains.kotlin.gradle.idea.tcs.extras.isNativeDistributionKey
import org.jetbrains.kotlin.gradle.idea.tcs.extras.isNativeStdlibKey
import org.jetbrains.kotlin.gradle.kpm.idea.kotlinDebugKey
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.ide.IdeMultiplatformImport.SourceSetConstraint
import org.jetbrains.kotlin.gradle.plugin.ide.dependencyResolvers.*
import org.jetbrains.kotlin.gradle.plugin.ide.dependencyTransformers.IdePlatformStdlibCommonDependencyFilter
internal fun IdeMultiplatformImport(extension: KotlinProjectExtension): IdeMultiplatformImport {
return IdeMultiplatformImportImpl(extension).apply {
@@ -77,7 +79,7 @@ internal fun IdeMultiplatformImport(extension: KotlinProjectExtension): IdeMulti
registerDependencyResolver(
resolver = IdePlatformDependencyResolver(),
constraint = SourceSetConstraint.isLeaf,
constraint = SourceSetConstraint.isSinglePlatformType,
phase = IdeMultiplatformImport.DependencyResolutionPhase.BinaryDependencyResolution,
level = IdeMultiplatformImport.DependencyResolutionLevel.Default
)
@@ -101,6 +103,12 @@ internal fun IdeMultiplatformImport(extension: KotlinProjectExtension): IdeMulti
level = IdeMultiplatformImport.DependencyResolutionLevel.Overwrite
)
registerDependencyTransformer(
transformer = IdePlatformStdlibCommonDependencyFilter,
constraint = SourceSetConstraint.isSinglePlatformType,
phase = IdeMultiplatformImport.DependencyTransformationPhase.DependencyFilteringPhase,
)
registerDependencyEffect(
effect = IdeDependencyLogger,
constraint = SourceSetConstraint.unconstrained
@@ -17,6 +17,7 @@ import org.gradle.internal.component.local.model.OpaqueComponentArtifactIdentifi
import org.gradle.internal.resolve.ModuleVersionResolveException
import org.jetbrains.kotlin.gradle.idea.tcs.*
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.ide.IdeDependencyResolver
import org.jetbrains.kotlin.gradle.plugin.ide.IdeDependencyResolver.Companion.gradleArtifact
@@ -42,7 +43,8 @@ internal class IdePlatformDependencyResolver(
* @param setupArtifactViewAttributes: Additional attributes that will be used to create an [ArtifactView] for resolving the dependencies.
*/
data class Compilation(
internal val compilationSelector: (Set<KotlinCompilation<*>>) -> KotlinCompilation<*>? = { it.singleOrNull() },
internal val compilationSelector: (Set<KotlinCompilation<*>>) -> KotlinCompilation<*>? =
{ compilations -> compilations.singleOrNull { it.platformType != KotlinPlatformType.common } },
internal val setupArtifactViewAttributes: AttributeContainer.(sourceSet: KotlinSourceSet) -> Unit = {}
) : ArtifactResolutionStrategy()
@@ -0,0 +1,24 @@
/*
* Copyright 2010-2022 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.plugin.ide.dependencyTransformers
import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinDependency
import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinResolvedBinaryDependency
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.ide.IdeDependencyTransformer
object IdePlatformStdlibCommonDependencyFilter : IdeDependencyTransformer {
private const val stdlibCoordinatesGroup = "org.jetbrains.kotlin"
private val stdlibCoordinatesModules = setOf("kotlin-stdlib-common", "kotlin-test-common", "kotlin-test-annotations-common")
override fun transform(sourceSet: KotlinSourceSet, dependencies: Set<IdeaKotlinDependency>): Set<IdeaKotlinDependency> {
return dependencies.filterNotTo(mutableSetOf()) { dependency ->
dependency is IdeaKotlinResolvedBinaryDependency
&& dependency.coordinates?.group == stdlibCoordinatesGroup
&& dependency.coordinates?.module in stdlibCoordinatesModules
}
}
}
@@ -570,6 +570,11 @@ internal fun isNativeSourceSet(sourceSet: KotlinSourceSet): Boolean {
return compilations.isNotEmpty() && compilations.all { it.platformType == KotlinPlatformType.native }
}
internal fun isSinglePlatformTypeSourceSet(sourceSet: KotlinSourceSet): Boolean {
val platformCompilations = sourceSet.internal.compilations.filterNot { it.platformType == KotlinPlatformType.common }
return platformCompilations.map { it.platformType }.toSet().size == 1
}
internal fun dependsOnClosureWithInterCompilationDependencies(sourceSet: KotlinSourceSet): Set<KotlinSourceSet> =
sourceSet.internal.dependsOnClosure.toMutableSet().apply {
addAll(getVisibleSourceSetsFromAssociateCompilations(sourceSet))
@@ -188,6 +188,7 @@ class IdeResolveSourceDependenciesTest {
enableDefaultStdlibDependency(false)
applyMultiplatformPlugin()
multiplatformExtension.jvm()
multiplatformExtension.linuxX64()
multiplatformExtension.sourceSets.getByName("commonMain").dependencies {
implementation(project(":producer"))
}
@@ -0,0 +1,234 @@
/*
* Copyright 2010-2022 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.
*/
@file:Suppress("FunctionName", "DuplicatedCode")
package org.jetbrains.kotlin.gradle.ide
import org.gradle.api.Project
import org.jetbrains.kotlin.compilerRunner.konanVersion
import org.jetbrains.kotlin.gradle.*
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinResolvedBinaryDependency
import org.jetbrains.kotlin.gradle.idea.testFixtures.tcs.assertMatches
import org.jetbrains.kotlin.gradle.idea.testFixtures.tcs.binaryCoordinates
import org.jetbrains.kotlin.gradle.kpm.idea.mavenCentralCacheRedirector
import org.jetbrains.kotlin.gradle.plugin.KotlinJsCompilerType
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.ide.kotlinIdeMultiplatformImport
import org.jetbrains.kotlin.gradle.utils.androidExtension
import org.junit.Test
class IdeStdlibImportTest {
@Test
fun `test single jvm target`() {
val project = createProjectWithDefaultStdlibEnabled()
val kotlin = project.multiplatformExtension
kotlin.jvm()
project.evaluate()
project.assertStdlibDependencies(kotlin.sourceSets.getByName("commonMain"), jvmStdlibDependencies(kotlin))
project.assertStdlibDependencies(kotlin.sourceSets.getByName("commonTest"), jvmStdlibDependencies(kotlin))
project.assertStdlibDependencies(kotlin.sourceSets.getByName("jvmMain"), jvmStdlibDependencies(kotlin))
project.assertStdlibDependencies(kotlin.sourceSets.getByName("jvmTest"), jvmStdlibDependencies(kotlin))
}
@Test
fun `test single native target`() {
val project = createProjectWithDefaultStdlibEnabled()
val kotlin = project.multiplatformExtension
kotlin.linuxX64("linux")
project.evaluate()
project.assertStdlibDependencies(kotlin.sourceSets.getByName("commonMain"), nativeStdlibDependency(kotlin))
project.assertStdlibDependencies(kotlin.sourceSets.getByName("commonTest"), nativeStdlibDependency(kotlin))
project.assertStdlibDependencies(kotlin.sourceSets.getByName("linuxMain"), nativeStdlibDependency(kotlin))
project.assertStdlibDependencies(kotlin.sourceSets.getByName("linuxTest"), nativeStdlibDependency(kotlin))
}
@Test
fun `test single js target`() {
val project = createProjectWithDefaultStdlibEnabled()
val kotlin = project.multiplatformExtension
kotlin.js(KotlinJsCompilerType.IR)
project.evaluate()
project.assertStdlibDependencies(kotlin.sourceSets.getByName("commonMain"), jsStdlibDependency(kotlin))
project.assertStdlibDependencies(kotlin.sourceSets.getByName("commonTest"), jsStdlibDependency(kotlin))
project.assertStdlibDependencies(kotlin.sourceSets.getByName("jsMain"), jsStdlibDependency(kotlin))
project.assertStdlibDependencies(kotlin.sourceSets.getByName("jsTest"), jsStdlibDependency(kotlin))
}
@Test
fun `test jvm+native shared simple project`() {
val project = createProjectWithDefaultStdlibEnabled()
val kotlin = project.multiplatformExtension
kotlin.jvm()
kotlin.linuxX64("linux")
project.evaluate()
project.assertStdlibDependencies(kotlin.sourceSets.getByName("commonMain"), commonStdlibDependency(kotlin))
project.assertStdlibDependencies(kotlin.sourceSets.getByName("commonTest"), commonStdlibDependency(kotlin))
project.assertStdlibDependencies(kotlin.sourceSets.getByName("jvmMain"), jvmStdlibDependencies(kotlin))
project.assertStdlibDependencies(kotlin.sourceSets.getByName("jvmTest"), jvmStdlibDependencies(kotlin))
project.assertStdlibDependencies(kotlin.sourceSets.getByName("linuxMain"), nativeStdlibDependency(kotlin))
project.assertStdlibDependencies(kotlin.sourceSets.getByName("linuxTest"), nativeStdlibDependency(kotlin))
}
@Test
fun `test bamboo jvm`() {
val project = createProjectWithDefaultStdlibEnabled()
val kotlin = project.multiplatformExtension
kotlin.jvm()
kotlin.linuxX64("linux")
val commonMain = kotlin.sourceSets.getByName("commonMain")
val commonTest = kotlin.sourceSets.getByName("commonTest")
val jvmMain = kotlin.sourceSets.getByName("jvmMain")
val jvmTest = kotlin.sourceSets.getByName("jvmTest")
val jvmIntermediateMain = kotlin.sourceSets.create("jvmIntermediateMain") {
it.dependsOn(commonMain)
jvmMain.dependsOn(it)
}
val jvmIntermediateTest = kotlin.sourceSets.create("jvmIntermediateTest") {
it.dependsOn(commonTest)
jvmTest.dependsOn(it)
}
project.evaluate()
project.assertStdlibDependencies(commonMain, commonStdlibDependency(kotlin))
project.assertStdlibDependencies(commonTest, commonStdlibDependency(kotlin))
project.assertStdlibDependencies(jvmIntermediateMain, jvmStdlibDependencies(kotlin))
project.assertStdlibDependencies(jvmIntermediateTest, jvmStdlibDependencies(kotlin))
}
@Test
fun `test bamboo linux`() {
val project = createProjectWithDefaultStdlibEnabled()
val kotlin = project.multiplatformExtension
kotlin.jvm()
kotlin.linuxX64("linux")
val commonMain = kotlin.sourceSets.getByName("commonMain")
val commonTest = kotlin.sourceSets.getByName("commonTest")
val linuxMain = kotlin.sourceSets.getByName("linuxMain")
val linuxTest = kotlin.sourceSets.getByName("linuxTest")
val linuxIntermediateMain = kotlin.sourceSets.create("linuxIntermediateMain") {
it.dependsOn(commonMain)
linuxMain.dependsOn(it)
}
val linuxIntermediateTest = kotlin.sourceSets.create("linuxIntermediateTest") {
it.dependsOn(commonTest)
linuxTest.dependsOn(it)
}
project.evaluate()
project.assertStdlibDependencies(linuxIntermediateMain, nativeStdlibDependency(kotlin))
project.assertStdlibDependencies(linuxIntermediateTest, nativeStdlibDependency(kotlin))
}
@Test
fun `test nativeShared`() {
val project = createProjectWithDefaultStdlibEnabled()
val kotlin = project.multiplatformExtension
kotlin.jvm()
kotlin.linuxX64("x64")
kotlin.linuxArm64("arm64")
val commonMain = kotlin.sourceSets.getByName("commonMain")
val commonTest = kotlin.sourceSets.getByName("commonTest")
val x64Main = kotlin.sourceSets.getByName("x64Main")
val x64Test = kotlin.sourceSets.getByName("x64Test")
val arm64Main = kotlin.sourceSets.getByName("arm64Main")
val arm64Test = kotlin.sourceSets.getByName("arm64Test")
val linuxSharedMain = kotlin.sourceSets.create("linuxSharedMain") {
it.dependsOn(commonMain)
x64Main.dependsOn(it)
arm64Main.dependsOn(it)
}
val linuxSharedTest = kotlin.sourceSets.create("linuxSharedTest") {
it.dependsOn(commonTest)
x64Test.dependsOn(it)
arm64Test.dependsOn(it)
}
project.evaluate()
project.assertStdlibDependencies(linuxSharedMain, nativeStdlibDependency(kotlin))
project.assertStdlibDependencies(linuxSharedTest, nativeStdlibDependency(kotlin))
}
@Test
fun `test jvm + android`() {
val project = createProjectWithAndroidAndDefaultStdlibEnabled()
val kotlin = project.multiplatformExtension
kotlin.android()
kotlin.jvm()
project.evaluate()
// TODO think about jvm + android stdlib
project.assertStdlibDependencies(kotlin.sourceSets.getByName("commonMain"), emptyList<Any>())
project.assertStdlibDependencies(kotlin.sourceSets.getByName("commonTest"), emptyList<Any>())
}
private fun Project.assertStdlibDependencies(sourceSet: KotlinSourceSet, dependencies: Any) {
project.kotlinIdeMultiplatformImport.resolveDependencies(sourceSet)
.filterIsInstance<IdeaKotlinResolvedBinaryDependency>()
.filter { binaryDependency -> "stdlib" in binaryDependency.coordinates?.module.orEmpty() }
.assertMatches(dependencies)
}
private fun createProjectWithDefaultStdlibEnabled() = buildProject {
enableDependencyVerification(false)
enableDefaultStdlibDependency(true)
applyMultiplatformPlugin()
repositories.mavenLocal()
repositories.mavenCentralCacheRedirector()
}
private fun createProjectWithAndroidAndDefaultStdlibEnabled() = buildProject {
enableDefaultStdlibDependency(false)
enableDependencyVerification(false)
applyMultiplatformPlugin()
plugins.apply("com.android.library")
androidExtension.compileSdkVersion(33)
repositories.mavenLocal()
repositories.mavenCentralCacheRedirector()
repositories.google()
}
private fun commonStdlibDependency(kotlin: KotlinMultiplatformExtension) =
binaryCoordinates("org.jetbrains.kotlin:kotlin-stdlib-common:${kotlin.coreLibrariesVersion}")
private fun jvmStdlibDependencies(kotlin: KotlinMultiplatformExtension) = listOf(
binaryCoordinates("org.jetbrains.kotlin:kotlin-stdlib-jdk8:${kotlin.coreLibrariesVersion}"),
binaryCoordinates("org.jetbrains.kotlin:kotlin-stdlib-jdk7:${kotlin.coreLibrariesVersion}"),
binaryCoordinates("org.jetbrains.kotlin:kotlin-stdlib:${kotlin.coreLibrariesVersion}"),
)
private fun jsStdlibDependency(kotlin: KotlinMultiplatformExtension) =
binaryCoordinates("org.jetbrains.kotlin:kotlin-stdlib-js:${kotlin.coreLibrariesVersion}")
private fun nativeStdlibDependency(kotlin: KotlinMultiplatformExtension) =
binaryCoordinates("org.jetbrains.kotlin.native:stdlib:${kotlin.project.konanVersion}")
}