Simplify matching of Kotlin variant name and Gradle configuration name

Earlier, the matching was non-trivial and required the consumer to look
through all of the producer's targets to find the Kotlin variant given
the name of the configuration that Gradle chose during variant-aware
dependency resolution. This is not feasible in composite builds as we
can't look into a project model of an included build.

To fix this, simplify the matching of the Gradle configuration name and
the Kotlin variant name by just using a suffix for the published
configurations.

Namely, instead of names like `jvm-api`, use the original configuration
name `jvmApiElements` for Kotlin variant names. To avoid ambiguity with
Gradle configurations (which we might want to customize for publishing,
like by altering the attributes or the published artifacts compared to
the configuration exposed for inter-project dependencies), create
publishing configurations
This commit is contained in:
Sergey Igushkin
2020-09-28 16:53:15 +03:00
parent 16d493558e
commit a0fbf54d11
8 changed files with 72 additions and 62 deletions
@@ -452,11 +452,11 @@ class HierarchicalMppIT : BaseGradleIT() {
val jsSourceSets = setOf("commonMain", "jvmAndJsMain", "linuxAndJsMain") val jsSourceSets = setOf("commonMain", "jvmAndJsMain", "linuxAndJsMain")
return KotlinProjectStructureMetadata( return KotlinProjectStructureMetadata(
sourceSetNamesByVariantName = mapOf( sourceSetNamesByVariantName = mapOf(
"js-api" to jsSourceSets, "jsApiElements" to jsSourceSets,
"js-runtime" to jsSourceSets, "jsRuntimeElements" to jsSourceSets,
"jvm-api" to jvmSourceSets, "jvmApiElements" to jvmSourceSets,
"jvm-runtime" to jvmSourceSets, "jvmRuntimeElements" to jvmSourceSets,
"linuxX64-api" to setOf("commonMain", "linuxAndJsMain") "linuxX64ApiElements" to setOf("commonMain", "linuxAndJsMain")
), ),
sourceSetsDependsOnRelation = mapOf( sourceSetsDependsOnRelation = mapOf(
"jvmAndJsMain" to setOf("commonMain"), "jvmAndJsMain" to setOf("commonMain"),
@@ -978,9 +978,9 @@ class NewMultiplatformIT : BaseGradleIT() {
// but check that the module metadata contains all variants: // but check that the module metadata contains all variants:
val gradleModuleMetadata = projectDir.resolve("repo/com/example/sample-lib/1.0/sample-lib-1.0.module").readText() val gradleModuleMetadata = projectDir.resolve("repo/com/example/sample-lib/1.0/sample-lib-1.0.module").readText()
assertTrue(""""name": "linux64-api"""" in gradleModuleMetadata) assertTrue(""""name": "linux64ApiElements-published"""" in gradleModuleMetadata)
assertTrue(""""name": "mingw64-api"""" in gradleModuleMetadata) assertTrue(""""name": "mingw64ApiElements-published"""" in gradleModuleMetadata)
assertTrue(""""name": "macos64-api"""" in gradleModuleMetadata) assertTrue(""""name": "macos64ApiElements-published"""" in gradleModuleMetadata)
} }
} }
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.gradle.plugin
import org.gradle.api.DefaultTask import org.gradle.api.DefaultTask
import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.Dependency import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.PublishArtifact import org.gradle.api.artifacts.PublishArtifact
@@ -34,7 +33,6 @@ import org.jetbrains.kotlin.gradle.targets.js.KotlinJsCompilerAttribute
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget
import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask
import org.jetbrains.kotlin.gradle.tasks.locateTask
import org.jetbrains.kotlin.gradle.tasks.registerTask import org.jetbrains.kotlin.gradle.tasks.registerTask
import org.jetbrains.kotlin.gradle.tasks.withType import org.jetbrains.kotlin.gradle.tasks.withType
import org.jetbrains.kotlin.gradle.utils.COMPILE import org.jetbrains.kotlin.gradle.utils.COMPILE
@@ -406,25 +404,27 @@ abstract class KotlinOnlyTargetConfigurator<KotlinCompilationType : KotlinCompil
jarArtifact.type = archiveType jarArtifact.type = archiveType
val apiElementsConfiguration = project.configurations.getByName(target.apiElementsConfigurationName) val apiElementsConfiguration = project.configurations.getByName(target.apiElementsConfigurationName)
addJar(apiElementsConfiguration, jarArtifact) // If the target adds its own artifact to this configuration until this happens, don't add another one:
addJarIfNoArtifactsPresent(apiElementsConfiguration, jarArtifact)
if (mainCompilation is KotlinCompilationToRunnableFiles<*>) { if (mainCompilation is KotlinCompilationToRunnableFiles<*>) {
val runtimeConfiguration = project.configurations.getByName(mainCompilation.deprecatedRuntimeConfigurationName) val runtimeConfiguration = project.configurations.getByName(mainCompilation.deprecatedRuntimeConfigurationName)
val runtimeElementsConfiguration = project.configurations.getByName(target.runtimeElementsConfigurationName) val runtimeElementsConfiguration = project.configurations.getByName(target.runtimeElementsConfigurationName)
addJar(runtimeConfiguration, jarArtifact) addJarIfNoArtifactsPresent(runtimeConfiguration, jarArtifact)
addJar(runtimeElementsConfiguration, jarArtifact) addJarIfNoArtifactsPresent(runtimeElementsConfiguration, jarArtifact)
// TODO Check Gradle's special split into variants for classes & resources -- do we need that too?
} }
} }
} }
} }
private fun addJar(configuration: Configuration, jarArtifact: PublishArtifact) { private fun addJarIfNoArtifactsPresent(configuration: Configuration, jarArtifact: PublishArtifact) {
val publications = configuration.outgoing if (configuration.artifacts.isEmpty()) {
val publications = configuration.outgoing
// Configure an implicit variant // Configure an implicit variant
publications.artifacts.add(jarArtifact) publications.artifacts.add(jarArtifact)
publications.attributes.attribute(ArtifactAttributes.ARTIFACT_FORMAT, archiveType) publications.attributes.attribute(ArtifactAttributes.ARTIFACT_FORMAT, archiveType)
}
} }
} }
@@ -20,6 +20,14 @@ internal const val COMMON_MAIN_ELEMENTS_CONFIGURATION_NAME = "commonMainMetadata
open class KotlinMetadataTarget @Inject constructor(project: Project) : open class KotlinMetadataTarget @Inject constructor(project: Project) :
KotlinOnlyTarget<AbstractKotlinCompilation<*>>(project, KotlinPlatformType.common) { KotlinOnlyTarget<AbstractKotlinCompilation<*>>(project, KotlinPlatformType.common) {
override val artifactsTaskName: String
// The IDE import looks at this task name to determine the artifact and register the path to the artifact;
// in HMPP, since the project resolves to the all-metadata JAR, the IDE import needs to work with that JAR, too
get() = if (project.isKotlinGranularMetadataEnabled) KotlinMetadataTargetConfigurator.ALL_METADATA_JAR_NAME else super.artifactsTaskName
internal val legacyArtifactsTaskName: String
get() = super.artifactsTaskName
override val kotlinComponents: Set<KotlinTargetComponent> by lazy { override val kotlinComponents: Set<KotlinTargetComponent> by lazy {
if (!project.isKotlinGranularMetadataEnabled) if (!project.isKotlinGranularMetadataEnabled)
super.kotlinComponents super.kotlinComponents
@@ -72,11 +72,7 @@ class DefaultKotlinUsageContext(
override fun getUsage(): Usage = usage override fun getUsage(): Usage = usage
override fun getName(): String = kotlinTarget.targetName + when (dependencyConfigurationName) { override fun getName(): String = dependencyConfigurationName
kotlinTarget.apiElementsConfigurationName -> "-api"
kotlinTarget.runtimeElementsConfigurationName -> "-runtime"
else -> "-$dependencyConfigurationName"
}
private val configuration: Configuration private val configuration: Configuration
get() = project.configurations.getByName(dependencyConfigurationName) get() = project.configurations.getByName(dependencyConfigurationName)
@@ -13,7 +13,6 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinCompilationToRunnableFiles
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
import org.jetbrains.kotlin.gradle.targets.metadata.getPublishedPlatformCompilations
import java.io.File import java.io.File
internal data class SourceSetVisibilityResult( internal data class SourceSetVisibilityResult(
@@ -59,16 +58,6 @@ internal class SourceSetVisibilityProvider(
val mppModuleIdentifier = ModuleIds.fromComponent(project, resolvedRootMppDependency ?: resolvedMetadataDependency) val mppModuleIdentifier = ModuleIds.fromComponent(project, resolvedRootMppDependency ?: resolvedMetadataDependency)
/**
* When Gradle resolves a project dependency, as opposed to an external dependency, the variant name it returns in the resolution
* results is the name of the configuration that is chosen during variant-aware resolution, not the actual name of the variant that
* is written to the Gradle module metadata and the Kotlin project structure metadata. To fix this, get the published Kotlin
* variants of the dependency project and find among them the one that owns the configuration by the given name.
*/
val projectPublishedCompilations = resolvedToOtherProject?.let(::getPublishedPlatformCompilations)?.keys
fun platformVariantName(resolvedToVariantName: String): String =
projectPublishedCompilations?.first { it.dependencyConfigurationName == resolvedToVariantName }?.name ?: resolvedToVariantName
val firstConfigurationByVariant = mutableMapOf<String, Configuration>() val firstConfigurationByVariant = mutableMapOf<String, Configuration>()
val visiblePlatformVariantNames: Set<String?> = val visiblePlatformVariantNames: Set<String?> =
@@ -81,7 +70,7 @@ internal class SourceSetVisibilityProvider(
} }
.mapTo(mutableSetOf()) { configuration -> .mapTo(mutableSetOf()) { configuration ->
val resolvedVariant = resolvedVariantsProvider.getResolvedVariantName(mppModuleIdentifier, configuration) val resolvedVariant = resolvedVariantsProvider.getResolvedVariantName(mppModuleIdentifier, configuration)
?.let { platformVariantName(it) } ?.let { kotlinVariantNameFromPublishedVariantName(it) }
?: return@mapTo null ?: return@mapTo null
firstConfigurationByVariant.putIfAbsent(resolvedVariant, configuration) firstConfigurationByVariant.putIfAbsent(resolvedVariant, configuration)
@@ -135,6 +124,9 @@ internal class SourceSetVisibilityProvider(
} }
} }
private fun kotlinVariantNameFromPublishedVariantName(resolvedToVariantName: String): String =
originalVariantNameFromPublished(resolvedToVariantName) ?: resolvedToVariantName
private fun Project.resolvableConfigurationFromCompilationByScope( private fun Project.resolvableConfigurationFromCompilationByScope(
compilation: KotlinCompilation<*>, compilation: KotlinCompilation<*>,
scope: KotlinDependencyScope scope: KotlinDependencyScope
@@ -21,7 +21,6 @@ import org.gradle.api.component.SoftwareComponentFactory
import org.gradle.api.internal.component.SoftwareComponentInternal import org.gradle.api.internal.component.SoftwareComponentInternal
import org.gradle.api.internal.component.UsageContext import org.gradle.api.internal.component.UsageContext
import org.gradle.api.internal.project.ProjectInternal import org.gradle.api.internal.project.ProjectInternal
import org.gradle.api.provider.Provider
import org.gradle.api.publish.maven.MavenPublication import org.gradle.api.publish.maven.MavenPublication
import org.gradle.util.ConfigureUtil import org.gradle.util.ConfigureUtil
import org.gradle.util.WrapUtil import org.gradle.util.WrapUtil
@@ -95,8 +94,9 @@ abstract class AbstractKotlinTarget(
project.whenEvaluated { project.whenEvaluated {
(kotlinVariant as SoftwareComponentInternal).usages.filterIsInstance<KotlinUsageContext>().forEach { kotlinUsageContext -> (kotlinVariant as SoftwareComponentInternal).usages.filterIsInstance<KotlinUsageContext>().forEach { kotlinUsageContext ->
val configuration = project.configurations.findByName(kotlinUsageContext.name) val publishedConfigurationName = publishedConfigurationName(kotlinUsageContext.name)
?: project.configurations.create(kotlinUsageContext.name).also { configuration -> val configuration = project.configurations.findByName(publishedConfigurationName)
?: project.configurations.create(publishedConfigurationName).also { configuration ->
configuration.isCanBeConsumed = false configuration.isCanBeConsumed = false
configuration.isCanBeResolved = false configuration.isCanBeResolved = false
configuration.extendsFrom(project.configurations.getByName(kotlinUsageContext.dependencyConfigurationName)) configuration.extendsFrom(project.configurations.getByName(kotlinUsageContext.dependencyConfigurationName))
@@ -219,6 +219,12 @@ abstract class AbstractKotlinTarget(
internal set internal set
} }
private val publishedConfigurationNameSuffix = "-published"
internal fun publishedConfigurationName(originalVariantName: String) = originalVariantName + publishedConfigurationNameSuffix
internal fun originalVariantNameFromPublished(publishedConfigurationName: String): String? =
publishedConfigurationName.takeIf { it.endsWith(publishedConfigurationNameSuffix) }?.removeSuffix(publishedConfigurationNameSuffix)
internal fun KotlinTarget.disambiguateName(simpleName: String) = internal fun KotlinTarget.disambiguateName(simpleName: String) =
lowerCamelCaseName(targetName, simpleName) lowerCamelCaseName(targetName, simpleName)
@@ -83,8 +83,13 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
setupDependencyTransformationForCommonSourceSets(target) setupDependencyTransformationForCommonSourceSets(target)
target.project.configurations.getByName(target.apiElementsConfigurationName).attributes target.project.configurations.getByName(target.apiElementsConfigurationName).run {
.attribute(USAGE_ATTRIBUTE, target.project.usageByName(KotlinUsages.KOTLIN_METADATA)) attributes.attribute(USAGE_ATTRIBUTE, target.project.usageByName(KotlinUsages.KOTLIN_METADATA))
/** Note: to add this artifact here is enough to avoid duplicate artifacts in this configuration: the default artifact
* won't be added (later) if there's already an artifact in the configuration, see
* [KotlinOnlyTargetConfigurator.configureArchivesAndComponent] */
target.project.artifacts.add(target.apiElementsConfigurationName, allMetadataJar)
}
if (target.project.isCompatibilityMetadataVariantEnabled) { if (target.project.isCompatibilityMetadataVariantEnabled) {
createCommonMainElementsConfiguration(target) createCommonMainElementsConfiguration(target)
@@ -126,30 +131,33 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
} }
override fun createArchiveTasks(target: KotlinMetadataTarget): TaskProvider<out Zip> { override fun createArchiveTasks(target: KotlinMetadataTarget): TaskProvider<out Zip> {
val result = super.createArchiveTasks(target) if (!target.project.isKotlinGranularMetadataEnabled)
return super.createArchiveTasks(target)
if (target.project.isKotlinGranularMetadataEnabled) { val result = target.project.registerTask<Jar>(target.artifactsTaskName) {
target.project.locateTask<Jar>(target.artifactsTaskName)!!.configure { it.group = BasePlugin.BUILD_GROUP
if (!target.project.isCompatibilityMetadataVariantEnabled) { /** The content is added to this JAR in [KotlinMetadataTargetConfigurator.configureTarget]. */
it.archiveClassifier.set("commonMain") }
}
it.onlyIf { target.project.isCompatibilityMetadataVariantEnabled } result.configure { allMetadataJar ->
allMetadataJar.description = "Assembles a jar archive containing the metadata for all Kotlin source sets."
allMetadataJar.group = BasePlugin.BUILD_GROUP
allMetadataJar.archiveAppendix.set(target.name.toLowerCase())
if (target.project.isCompatibilityMetadataVariantEnabled) {
allMetadataJar.archiveClassifier.set("all")
} }
}
/** This JAR is created in addition to the main one, published with a classifier, but is by default used val legacyJar = target.project.registerTask<Jar>(target.legacyArtifactsTaskName)
* for project dependencies (as the Kotlin Granular metadata is enabled across all projects in a build, this is OK). legacyJar.configure {
* See also [KotlinMetadataTarget.kotlinComponents] it.description = "Assembles an archive containing the Kotin metadata of the commonMain source set."
*/ if (!target.project.isCompatibilityMetadataVariantEnabled) {
target.project.registerTask<Jar>(ALL_METADATA_JAR_NAME) { allMetadataJar -> it.archiveClassifier.set("commonMain")
allMetadataJar.description = "Assembles a jar archive containing the metadata for all Kotlin source sets."
allMetadataJar.group = BasePlugin.BUILD_GROUP
allMetadataJar.archiveAppendix.set(target.name.toLowerCase())
if (target.project.isCompatibilityMetadataVariantEnabled) {
allMetadataJar.archiveClassifier.set("all")
}
} }
it.onlyIf { target.project.isCompatibilityMetadataVariantEnabled }
it.from(target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME).output.allOutputs)
} }
return result return result
@@ -499,7 +507,7 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
) )
extendsFrom(commonMainApiConfiguration) extendsFrom(commonMainApiConfiguration)
project.artifacts.add(name, project.tasks.getByName(target.artifactsTaskName)) project.artifacts.add(name, project.tasks.getByName(target.legacyArtifactsTaskName))
} }
} }