KT-48709: Fix CInterop configurations ambiguity with user variants

* Introduce a new org.gradle.usage value: kotlin-cinterop

* Add compatibility+disambiguation rules to ensure that kotlin-cinterop
  consumers can also fall back to ordinary (kotlin-api) published
  variants, but not vice versa

  * This ensures that ordinary kotlin-api consumers don't encounter
    equally-compatible candidates where one is the normal API elements
    and the other is C interop API elements

* Set org.gradle.usage = kotlin-cinterop in the C interop API elements
  configurations and dependency-consuming configurations

Issue #KT-48709
This commit is contained in:
Sergey Igushkin
2021-10-20 23:32:52 +04:00
committed by Space
parent a474e8a00b
commit 8868738ac8
3 changed files with 80 additions and 4 deletions
@@ -19,6 +19,7 @@ object KotlinUsages {
const val KOTLIN_API = "kotlin-api"
const val KOTLIN_RUNTIME = "kotlin-runtime"
const val KOTLIN_METADATA = "kotlin-metadata"
const val KOTLIN_CINTEROP = "kotlin-cinterop"
const val KOTLIN_SOURCES = "kotlin-sources"
val values = setOf(KOTLIN_API, KOTLIN_RUNTIME)
@@ -90,6 +91,29 @@ object KotlinUsages {
}
}
private class KotlinCinteropCompatibility : AttributeCompatibilityRule<Usage> {
private val compatibleProducerValues = setOf(KOTLIN_API, JAVA_API, JAVA_RUNTIME)
override fun execute(details: CompatibilityCheckDetails<Usage>) = with(details) {
if (consumerValue?.name == KOTLIN_CINTEROP && producerValue?.name in compatibleProducerValues) {
compatible()
}
}
}
private class KotlinCinteropDisambiguation : AttributeDisambiguationRule<Usage> {
override fun execute(details: MultipleCandidatesDetails<Usage?>) = details.run {
if (consumerValue?.name == KOTLIN_CINTEROP) {
val candidateNames = candidateValues.map { it?.name }
when {
KOTLIN_CINTEROP in candidateNames -> chooseCandidateByName(KOTLIN_CINTEROP)
KOTLIN_API in candidateNames -> chooseCandidateByName(KOTLIN_API)
JAVA_API in candidateNames -> chooseCandidateByName(JAVA_API)
else -> Unit
}
}
}
}
private class KotlinMetadataDisambiguation : AttributeDisambiguationRule<Usage> {
override fun execute(details: MultipleCandidatesDetails<Usage>) = details.run {
if (consumerValue?.name == KOTLIN_METADATA) {
@@ -106,8 +130,6 @@ object KotlinUsages {
override fun execute(details: MultipleCandidatesDetails<Usage?>) = 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)) {
@@ -135,11 +157,18 @@ object KotlinUsages {
}
}
private fun MultipleCandidatesDetails<Usage?>.chooseCandidateByName(name: String?): Unit {
closestMatch(candidateValues.single { it?.name == name }!!)
}
internal fun setupAttributesMatchingStrategy(project: Project, attributesSchema: AttributesSchema) {
attributesSchema.attribute(USAGE_ATTRIBUTE) { strategy ->
strategy.compatibilityRules.add(KotlinJavaRuntimeJarsCompatibility::class.java)
strategy.disambiguationRules.add(KotlinUsagesDisambiguation::class.java)
strategy.compatibilityRules.add(KotlinCinteropCompatibility::class.java)
strategy.disambiguationRules.add(KotlinCinteropDisambiguation::class.java)
if (project.isKotlinGranularMetadataEnabled) {
strategy.compatibilityRules.add(KotlinMetadataCompatibility::class.java)
strategy.disambiguationRules.add(KotlinMetadataDisambiguation::class.java)
@@ -49,7 +49,7 @@ internal fun Project.locateOrCreateCInteropDependencyConfiguration(
usesPlatformOf(target)
attributes.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, cinteropKlibLibraryElements())
attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage::class.java, KotlinUsages.KOTLIN_API))
attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage::class.java, KotlinUsages.KOTLIN_CINTEROP))
attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.categoryByName(Category.LIBRARY))
description = "Dependencies for cinterop '${cinterop.name}' (compilation '${compilation.name}')."
}
@@ -65,7 +65,7 @@ internal fun Project.locateOrCreateCInteropApiElementsConfiguration(target: Kotl
usesPlatformOf(target)
attributes.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, cinteropKlibLibraryElements())
attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage::class.java, KotlinUsages.KOTLIN_API))
attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage::class.java, KotlinUsages.KOTLIN_CINTEROP))
attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.categoryByName(Category.LIBRARY))
attributes.attribute(ArtifactAttributes.ARTIFACT_FORMAT, NativeArtifactFormat.KLIB)
}