From f995afd50d9768c67d9c8d7de0723d3a6913790a Mon Sep 17 00:00:00 2001 From: Sergey Igushkin Date: Mon, 29 Oct 2018 16:28:00 +0300 Subject: [PATCH] Fix runtime elements configuration resolved for compile classpath The existing disambiguation rule for the Usage attribute lead to runtime variants being resolved even for compile-scoped input configurations, because Gradle runs disambiguation rules even if the consumer value is present in the candidates list. For example, an `app` project's `jvmCompileClasspath` configuration would get its `project('lib')` dependency resolved to the `jvmLibRuntimeElements` instead of `jvmLibApiElements`. Fix this by: 1) running the part of the disambiguation rule only with Gradle 4.1+, so as to use the consumer value for proper disambiguation; 2) choosing the JAVA_API usage when the consumer is JAVA_API or KOTLIN_API, and choosing one of the JAVA_RUNTIME usages if the consumer is either KOTLIN_RUNTIME, one of the JAVA_RUNTIME usages, or does not specify its usage. Issue #KT-27849 Fixed --- .../gradle/VariantAwareDependenciesIT.kt | 24 ++++++++++++++++ .../kotlin/gradle/plugin/mpp/KotlinUsages.kt | 28 ++++++++++++++----- 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/VariantAwareDependenciesIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/VariantAwareDependenciesIT.kt index 8e35b424570..6c693436f54 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/VariantAwareDependenciesIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/VariantAwareDependenciesIT.kt @@ -241,6 +241,30 @@ class VariantAwareDependenciesIT : BaseGradleIT() { } } + @Test + fun testCompileAndRuntimeResolutionOfElementsConfigurations() = + with(Project("sample-app", gradleVersion, "new-mpp-lib-and-app")) { + val libProject = Project("sample-lib", gradleVersion, "new-mpp-lib-and-app") + embedProject(libProject) + gradleBuildScript().modify { + it.replace("'com.example:sample-lib:1.0'", "project('${libProject.projectName}')") + } + + listOf("jvm6" to "Classpath", "nodeJs" to "Classpath", "wasm32" to "Klibraries").forEach { (target, suffix) -> + build("dependencyInsight", "--configuration", "${target}Compile$suffix", "--dependency", "sample-lib") { + assertSuccessful() + assertContains("variant \"${target}ApiElements\" [") + } + + if (suffix == "Classpath") { + build("dependencyInsight", "--configuration", "${target}Runtime$suffix", "--dependency", "sample-lib") { + assertSuccessful() + assertContains("variant \"${target}RuntimeElements\" [") + } + } + } + } + private fun Project.embedProject(other: Project) { setupWorkingDir() other.setupWorkingDir() diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinUsages.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinUsages.kt index bd64c2ce9d2..f807c969edf 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinUsages.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinUsages.kt @@ -65,19 +65,33 @@ object KotlinUsages { override fun execute(details: MultipleCandidatesDetails) = with(details) { val candidateNames = candidateValues.map { it?.name }.toSet() + fun chooseCandidateByName(name: String?): Unit = closestMatch(candidateValues.single { it?.name == name }!!) + // if both API and runtime artifacts are chosen according to the compatibility rules, then // the consumer requested nothing specific, so provide them with the runtime variant, which is more complete: if (candidateNames.filterNotNull().toSet() == setOf(KOTLIN_RUNTIME, KOTLIN_API)) { - details.closestMatch(candidateValues.single { it?.name == KOTLIN_RUNTIME }!!) + chooseCandidateByName(KOTLIN_RUNTIME) } - if (JAVA_API in candidateNames && JAVA_RUNTIME_JARS in candidateNames && values.none { it in candidateNames }) { - details.closestMatch(candidateValues.single { it?.name == JAVA_RUNTIME_JARS }!!) - } - if (JAVA_API in candidateNames && JAVA_RUNTIME in candidateNames && values.none { it in candidateNames }) { - details.closestMatch(candidateValues.single { it?.name == JAVA_RUNTIME }!!) + + // The consumer value is available and can be used for disambiguation in Gradle 4.1+ + if (isGradleVersionAtLeast(4, 1)) { + val javaRuntimeUsages = setOf(JAVA_RUNTIME_JARS, JAVA_RUNTIME) + + if (JAVA_API in candidateNames && + javaRuntimeUsages.any { it in candidateNames } && + values.none { it in candidateNames } + ) { + when (consumerValue?.name) { + KOTLIN_API, JAVA_API -> + chooseCandidateByName(JAVA_API) + null, KOTLIN_RUNTIME, in javaRuntimeUsages -> + chooseCandidateByName(javaRuntimeUsages.first { it in candidateNames }) + } + } } + if (JAVA_RUNTIME_CLASSES in candidateNames && JAVA_RUNTIME_RESOURCES in candidateNames && KOTLIN_RUNTIME in candidateNames) { - closestMatch(candidateValues.single { it?.name == KOTLIN_RUNTIME }!!) + chooseCandidateByName(KOTLIN_RUNTIME) } } }