[MPP] KGP dependency resolution: JVM stdlib for JVM+Android source sets

KTIJ-24701
This commit is contained in:
Pavel Kirpichenkov
2023-03-07 15:45:59 +02:00
committed by Space Team
parent 0c33e89c02
commit 7dd438cb9e
4 changed files with 95 additions and 14 deletions
@@ -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)
@@ -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}")
}
}
@@ -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<IdeaKotlinResolvedBinaryDependency>()
.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)
}
}
@@ -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<IdeaKotlinBinaryDependency>()
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"),
)
}