diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdeBinaryDependencyResolver.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdeBinaryDependencyResolver.kt index 71fe61663ec..c326532f0cb 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdeBinaryDependencyResolver.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdeBinaryDependencyResolver.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.gradle.plugin.ide.dependencyResolvers import org.gradle.api.artifacts.ArtifactView import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.DependencySubstitutions import org.gradle.api.artifacts.component.* import org.gradle.api.attributes.AttributeContainer import org.gradle.api.logging.Logger @@ -75,7 +76,8 @@ class IdeBinaryDependencyResolver( data class PlatformLikeSourceSet( internal val setupPlatformResolutionAttributes: AttributeContainer.(sourceSet: KotlinSourceSet) -> Unit, internal val setupArtifactViewAttributes: AttributeContainer.(sourceSet: KotlinSourceSet) -> Unit = {}, - internal val componentFilter: ((ComponentIdentifier) -> Boolean)? = null + internal val componentFilter: ((ComponentIdentifier) -> Boolean)? = null, + internal val dependencySubstitution: ((DependencySubstitutions) -> Unit)? = null, ) : ArtifactResolutionStrategy() } @@ -194,6 +196,9 @@ class IdeBinaryDependencyResolver( platformLikeCompileDependenciesConfiguration.attributes.setupPlatformResolutionAttributes(sourceSet) platformLikeCompileDependenciesConfiguration.dependencies.addAll(sourceSet.resolvableMetadataConfiguration.allDependencies) + if (dependencySubstitution != null) + platformLikeCompileDependenciesConfiguration.resolutionStrategy.dependencySubstitution(dependencySubstitution) + return platformLikeCompileDependenciesConfiguration.incoming.artifactView { view -> view.isLenient = true view.attributes.setupArtifactViewAttributes(sourceSet) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdeJvmAndAndroidPlatformDependencyResolver.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdeJvmAndAndroidPlatformDependencyResolver.kt index 9a2996a9177..2021fa97bf8 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdeJvmAndAndroidPlatformDependencyResolver.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdeJvmAndAndroidPlatformDependencyResolver.kt @@ -8,11 +8,16 @@ package org.jetbrains.kotlin.gradle.plugin.ide.dependencyResolvers import org.gradle.api.Project +import org.gradle.api.artifacts.DependencySubstitutions +import org.gradle.api.artifacts.component.ModuleComponentSelector import org.gradle.api.artifacts.component.ProjectComponentIdentifier import org.gradle.api.attributes.Category import org.gradle.api.attributes.Usage import org.gradle.api.attributes.java.TargetJvmEnvironment import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinBinaryDependency +import org.jetbrains.kotlin.gradle.internal.KOTLIN_ANDROID_JVM_STDLIB_MODULE_NAME +import org.jetbrains.kotlin.gradle.internal.KOTLIN_MODULE_GROUP +import org.jetbrains.kotlin.gradle.internal.KOTLIN_STDLIB_COMMON_MODULE_NAME import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType import org.jetbrains.kotlin.gradle.plugin.ide.IdeDependencyResolver import org.jetbrains.kotlin.gradle.plugin.usageByName @@ -39,6 +44,23 @@ internal fun IdeJvmAndAndroidPlatformBinaryDependencyResolver(project: Project): Otherwise we would match the -jvm.jar from the dependency project which will result in matching the jvmMain source set as well (which is undesired) */ - componentFilter = { identifier -> identifier !is ProjectComponentIdentifier } + componentFilter = { identifier -> identifier !is ProjectComponentIdentifier }, + dependencySubstitution = ::substituteStdlibCommonWithAndroidJvm, ) ) + +/** + * This is a replacement for propagation of stdlib-jvm in non-KGP-based IDE import for JVM+Android source sets. + * It's possible to resolve regular dependencies of the source set with platform attributes to get the correct JVM variants. + * But stdlib is a special case, kotlin-stdlib-common is not a common variant for the JVM stdlib w.r.t. publication. + * Substituting kotlin-stdlib-common with the Android-JVM stdlib in requests workarounds the issue. + */ +internal fun substituteStdlibCommonWithAndroidJvm(dependencySubstitutions: DependencySubstitutions) { + dependencySubstitutions.all { dependency -> + val requested = dependency.requested + if (requested is ModuleComponentSelector + && requested.group == KOTLIN_MODULE_GROUP + && requested.module == KOTLIN_STDLIB_COMMON_MODULE_NAME + ) dependency.useTarget("$KOTLIN_MODULE_GROUP:$KOTLIN_ANDROID_JVM_STDLIB_MODULE_NAME:${requested.version}") + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/dependencyResolutionTests/tcs/IdeJvmAndAndroidDependencyResolutionTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/dependencyResolutionTests/tcs/IdeJvmAndAndroidDependencyResolutionTest.kt index 1a2057b4ea7..3e73a8081c0 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/dependencyResolutionTests/tcs/IdeJvmAndAndroidDependencyResolutionTest.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/dependencyResolutionTests/tcs/IdeJvmAndAndroidDependencyResolutionTest.kt @@ -10,7 +10,9 @@ package org.jetbrains.kotlin.gradle.dependencyResolutionTests.tcs import org.gradle.api.Project import org.jetbrains.kotlin.gradle.dependencyResolutionTests.mavenCentralCacheRedirector import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension +import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinResolvedBinaryDependency import org.jetbrains.kotlin.gradle.idea.testFixtures.tcs.* +import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion import org.jetbrains.kotlin.gradle.plugin.ide.dependencyResolvers.IdeJvmAndAndroidPlatformBinaryDependencyResolver import org.jetbrains.kotlin.gradle.plugin.ide.kotlinIdeMultiplatformImport import org.jetbrains.kotlin.gradle.util.* @@ -25,13 +27,14 @@ class IdeJvmAndAndroidDependencyResolutionTest { assumeAndroidSdkAvailable() } - private fun Project.configureAndroidAndMultiplatform() { - enableDefaultStdlibDependency(false) + private fun Project.configureAndroidAndMultiplatform(enableDefaultStdlib: Boolean = false) { + enableDefaultStdlibDependency(enableDefaultStdlib) enableDependencyVerification(false) setMultiplatformAndroidSourceSetLayoutVersion(2) applyMultiplatformPlugin() plugins.apply("com.android.library") androidExtension.compileSdkVersion(33) + if (enableDefaultStdlib) repositories.mavenLocal() repositories.mavenCentralCacheRedirector() project.multiplatformExtension.targetHierarchy.custom { @@ -48,9 +51,14 @@ class IdeJvmAndAndroidDependencyResolutionTest { } + private fun Project.assertBinaryDependencies(sourceSetName: String, notation: Any) { + project.kotlinIdeMultiplatformImport.resolveDependencies(sourceSetName).filterIsInstance() + .assertMatches(notation) + } + @Test fun `test - MVIKotlin - on jvmAndAndroidMain`() { - val project = buildProject { configureAndroidAndMultiplatform() } + val project = buildProject { configureAndroidAndMultiplatform(enableDefaultStdlib = false) } val kotlin = project.multiplatformExtension kotlin.sourceSets.getByName("commonMain").dependencies { implementation("com.arkivanov.mvikotlin:mvikotlin:3.0.2") @@ -62,12 +70,10 @@ class IdeJvmAndAndroidDependencyResolutionTest { binaryCoordinates("com.arkivanov.mvikotlin:mvikotlin-jvm:3.0.2"), binaryCoordinates("com.arkivanov.essenty:lifecycle-jvm:0.4.2"), binaryCoordinates("com.arkivanov.essenty:instance-keeper-jvm:0.4.2"), - binaryCoordinates("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.10"), + binaryCoordinates("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.10"), // transitive binaryCoordinates("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.10"), binaryCoordinates("org.jetbrains.kotlin:kotlin-stdlib:1.7.10"), binaryCoordinates("org.jetbrains:annotations:13.0"), - /* This resolver cannot differentiate between non-hmpp metadata libraries and platform libraries. This is OK */ - binaryCoordinates("org.jetbrains.kotlin:kotlin-stdlib-common:1.7.10") ) IdeJvmAndAndroidPlatformBinaryDependencyResolver(project).resolve(kotlin.sourceSets.getByName("jvmAndAndroidMain")) @@ -105,4 +111,50 @@ class IdeJvmAndAndroidDependencyResolutionTest { regularSourceDependency(":producer/jvmAndAndroidMain"), ) } + + @Test + fun `test - default stdlib with no other dependencies`() { + val project = buildProject { configureAndroidAndMultiplatform(enableDefaultStdlib = true) } + project.evaluate() + + val stdlibVersion = project.getKotlinPluginVersion() + val stdlibDependencies = listOf( + binaryCoordinates("org.jetbrains.kotlin:kotlin-stdlib:${stdlibVersion}"), + binaryCoordinates("org.jetbrains.kotlin:kotlin-stdlib-jdk7:${stdlibVersion}"), + binaryCoordinates("org.jetbrains.kotlin:kotlin-stdlib-jdk8:${stdlibVersion}"), + binaryCoordinates("org.jetbrains:annotations:13.0"), + ) + + project.assertBinaryDependencies("commonMain", stdlibDependencies) + project.assertBinaryDependencies("jvmAndAndroidMain", stdlibDependencies) + project.assertBinaryDependencies("commonTest", stdlibDependencies) + project.assertBinaryDependencies("jvmAndAndroidTest", stdlibDependencies) + } + + @Test + fun `test - MVIKotlin - binary dependencies - with stdlib enabled by default`() { + val project = buildProject { configureAndroidAndMultiplatform(enableDefaultStdlib = true) } + val kotlin = project.multiplatformExtension + kotlin.sourceSets.getByName("commonMain").dependencies { + implementation("com.arkivanov.mvikotlin:mvikotlin:3.0.2") + } + + project.evaluate() + + val kgpVersion = project.getKotlinPluginVersion() + val jvmAndAndroidDependencies = listOf( + binaryCoordinates("com.arkivanov.mvikotlin:mvikotlin-jvm:3.0.2"), + binaryCoordinates("com.arkivanov.essenty:lifecycle-jvm:0.4.2"), + binaryCoordinates("com.arkivanov.essenty:instance-keeper-jvm:0.4.2"), + binaryCoordinates("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kgpVersion"), // uplifted to plugin version + binaryCoordinates("org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kgpVersion"), + binaryCoordinates("org.jetbrains.kotlin:kotlin-stdlib:$kgpVersion"), + binaryCoordinates("org.jetbrains:annotations:13.0"), + ) + + project.assertBinaryDependencies("commonMain", jvmAndAndroidDependencies) + project.assertBinaryDependencies("jvmAndAndroidMain", jvmAndAndroidDependencies) + project.assertBinaryDependencies("commonTest", jvmAndAndroidDependencies) + project.assertBinaryDependencies("jvmAndAndroidTest", jvmAndAndroidDependencies) + } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/dependencyResolutionTests/tcs/IdeTransformedMetadataDependencyResolverTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/dependencyResolutionTests/tcs/IdeTransformedMetadataDependencyResolverTest.kt index c317938d36b..21352ccf804 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/dependencyResolutionTests/tcs/IdeTransformedMetadataDependencyResolverTest.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/dependencyResolutionTests/tcs/IdeTransformedMetadataDependencyResolverTest.kt @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinBinaryDependency import org.jetbrains.kotlin.gradle.idea.testFixtures.tcs.assertMatches import org.jetbrains.kotlin.gradle.idea.testFixtures.tcs.binaryCoordinates import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet +import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion import org.jetbrains.kotlin.gradle.plugin.ide.dependencyResolvers.IdeTransformedMetadataDependencyResolver import org.jetbrains.kotlin.gradle.plugin.ide.kotlinIdeMultiplatformImport import org.jetbrains.kotlin.gradle.util.* @@ -98,19 +99,20 @@ class IdeTransformedMetadataDependencyResolverTest { fun KotlinSourceSet.binaryDependencies() = project.kotlinIdeMultiplatformImport.resolveDependencies(this).filterIsInstance() + val kgpVersion = project.getKotlinPluginVersion() commonMain.binaryDependencies().assertMatches( binaryCoordinates("com.squareup.okio:okio-jvm:3.2.0"), - binaryCoordinates("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.20"), - binaryCoordinates("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.20"), - binaryCoordinates("org.jetbrains.kotlin:kotlin-stdlib:1.6.20"), + binaryCoordinates("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kgpVersion"), + binaryCoordinates("org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kgpVersion"), + binaryCoordinates("org.jetbrains.kotlin:kotlin-stdlib:$kgpVersion"), binaryCoordinates("org.jetbrains:annotations:13.0"), ) commonTest.binaryDependencies().assertMatches( binaryCoordinates("com.squareup.okio:okio-jvm:3.2.0"), - binaryCoordinates("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.20"), - binaryCoordinates("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.20"), - binaryCoordinates("org.jetbrains.kotlin:kotlin-stdlib:1.6.20"), + binaryCoordinates("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kgpVersion"), + binaryCoordinates("org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kgpVersion"), + binaryCoordinates("org.jetbrains.kotlin:kotlin-stdlib:$kgpVersion"), binaryCoordinates("org.jetbrains:annotations:13.0"), ) }