diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/IdeMultiplatformImport.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/IdeMultiplatformImport.kt index 43d452d7bd7..4f2cf4c9610 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/IdeMultiplatformImport.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/IdeMultiplatformImport.kt @@ -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 diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/IdeMultiplatformImportFactory.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/IdeMultiplatformImportFactory.kt index d79fbd171c1..ed4456185ce 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/IdeMultiplatformImportFactory.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/IdeMultiplatformImportFactory.kt @@ -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 diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdePlatformDependencyResolver.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdePlatformDependencyResolver.kt index 2ec97dee99d..7bfbc42c847 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdePlatformDependencyResolver.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdePlatformDependencyResolver.kt @@ -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<*>? = { it.singleOrNull() }, + internal val compilationSelector: (Set>) -> KotlinCompilation<*>? = + { compilations -> compilations.singleOrNull { it.platformType != KotlinPlatformType.common } }, internal val setupArtifactViewAttributes: AttributeContainer.(sourceSet: KotlinSourceSet) -> Unit = {} ) : ArtifactResolutionStrategy() diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyTransformers/IdePlatformStdlibCommonDependencyFilter.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyTransformers/IdePlatformStdlibCommonDependencyFilter.kt new file mode 100644 index 00000000000..89db423b7b5 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyTransformers/IdePlatformStdlibCommonDependencyFilter.kt @@ -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): Set { + return dependencies.filterNotTo(mutableSetOf()) { dependency -> + dependency is IdeaKotlinResolvedBinaryDependency + && dependency.coordinates?.group == stdlibCoordinatesGroup + && dependency.coordinates?.module in stdlibCoordinatesModules + } + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt index cdcb27c5108..d054b89211c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt @@ -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 = sourceSet.internal.dependsOnClosure.toMutableSet().apply { addAll(getVisibleSourceSetsFromAssociateCompilations(sourceSet)) diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/ide/IdeResolveSourceDependenciesTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/ide/IdeResolveSourceDependenciesTest.kt index a17338aa296..eb77e4f37ed 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/ide/IdeResolveSourceDependenciesTest.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/ide/IdeResolveSourceDependenciesTest.kt @@ -188,6 +188,7 @@ class IdeResolveSourceDependenciesTest { enableDefaultStdlibDependency(false) applyMultiplatformPlugin() multiplatformExtension.jvm() + multiplatformExtension.linuxX64() multiplatformExtension.sourceSets.getByName("commonMain").dependencies { implementation(project(":producer")) } diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/ide/IdeStdlibImportTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/ide/IdeStdlibImportTest.kt new file mode 100644 index 00000000000..27ee3399877 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/ide/IdeStdlibImportTest.kt @@ -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()) + project.assertStdlibDependencies(kotlin.sourceSets.getByName("commonTest"), emptyList()) + } + + private fun Project.assertStdlibDependencies(sourceSet: KotlinSourceSet, dependencies: Any) { + project.kotlinIdeMultiplatformImport.resolveDependencies(sourceSet) + .filterIsInstance() + .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}") +}