KPM: reshape the API for setting up publications

Accept a more structured requests for setting up publication.

Allow passing lower-level overrides for publication
settings of a particular configuration,
which is needed to set up legacy targets'
publications in KPM.
This commit is contained in:
Sergey Igushkin
2022-02-01 18:11:07 +04:00
committed by Space
parent 8676685ebc
commit 0f251477e9
3 changed files with 215 additions and 37 deletions
@@ -0,0 +1,100 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util
import org.gradle.api.Project
import org.gradle.api.artifacts.*
import org.gradle.api.attributes.Attribute
import org.gradle.api.attributes.AttributeContainer
import org.gradle.api.capabilities.Capability
import org.jetbrains.kotlin.gradle.plugin.ProjectLocalConfigurations
internal fun copyConfigurationForPublishing(
project: Project,
newName: String,
configuration: Configuration,
overrideDependencies: (DependencySet.() -> Unit)? = null,
overrideArtifacts: (Configuration.(PublishArtifactSet) -> Unit)? = null,
overrideAttributes: (Configuration.(AttributeContainer) -> Unit)? = {
copyAttributes(it, attributes, it.keySet().minus(ProjectLocalConfigurations.ATTRIBUTE))
},
overrideCapabilities: (Configuration.(Collection<Capability>) -> Unit)? = null,
configureAction: Configuration.() -> Unit = { }
) = copyConfiguration(
project,
newName,
configuration,
overrideDependencies,
overrideArtifacts,
overrideAttributes,
overrideCapabilities,
configureAction = {
isCanBeConsumed = false
isCanBeResolved = false
configureAction()
}
)
internal fun copyConfiguration(
project: Project,
newName: String,
configuration: Configuration,
overrideDependencies: (DependencySet.() -> Unit)? = null,
overrideArtifacts: (Configuration.(PublishArtifactSet) -> Unit)? = null,
overrideAttributes: (Configuration.(AttributeContainer) -> Unit)? = null,
overrideCapabilities: (Configuration.(Collection<Capability>) -> Unit)? = null,
configureAction: Configuration.() -> Unit = { }
): Configuration =
project.configurations.create(newName).apply {
isCanBeConsumed = configuration.isCanBeConsumed
isCanBeResolved = configuration.isCanBeResolved
// dependencies:
if (overrideDependencies != null) {
withDependencies { dependencySet ->
overrideDependencies(dependencySet)
}
} else {
dependencies.addAllLater(project.listProperty { configuration.allDependencies })
}
// artifacts:
if (overrideArtifacts != null) {
overrideArtifacts(this, artifacts)
} else {
artifacts.addAllLater(project.listProperty { configuration.allArtifacts })
}
// attributes
if (overrideAttributes != null) {
overrideAttributes(this, attributes)
} else {
copyAttributes(configuration.attributes, attributes)
}
// capabilities
if (overrideCapabilities != null) {
overrideCapabilities(this, configuration.outgoing.capabilities)
} else {
configuration.outgoing.capabilities.forEach(outgoing::capability)
}
configureAction(this)
}
internal fun copyAttributes(from: AttributeContainer, to: AttributeContainer, keys: Iterable<Attribute<*>> = from.keySet()) {
// capture type argument T
fun <T : Any> copyOneAttribute(from: AttributeContainer, to: AttributeContainer, key: Attribute<T>) {
val value = checkNotNull(from.getAttribute(key))
to.attribute(key, value)
}
for (key in keys) {
copyOneAttribute(from, to, key)
}
}
internal inline fun <reified T> Project.listProperty(noinline itemsProvider: () -> Iterable<T>) =
objects.listProperty(T::class.java).apply { set(provider(itemsProvider)) }
@@ -14,12 +14,15 @@ import org.gradle.api.attributes.*
import org.gradle.api.capabilities.Capability
import org.gradle.api.component.AdhocComponentWithVariants
import org.gradle.api.component.SoftwareComponentFactory
import org.gradle.api.provider.Provider
import org.gradle.api.publish.PublishingExtension
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.publish.maven.internal.publication.DefaultMavenPublication
import org.gradle.api.tasks.bundling.AbstractArchiveTask
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.ComputedCapability
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.copyAttributes
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.copyConfigurationForPublishing
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.disambiguateName
import org.jetbrains.kotlin.gradle.plugin.mpp.publishedConfigurationName
import org.jetbrains.kotlin.gradle.plugin.usageByName
@@ -28,17 +31,67 @@ import org.jetbrains.kotlin.gradle.utils.dashSeparatedName
import java.util.*
import javax.inject.Inject
interface PlatformPublicationToMavenRequest {
val componentName: String
val fromModule: KotlinGradleModule
val publicationHolder: SingleMavenPublishedModuleHolder
val variantPublicationRequests: Iterable<VariantPublicationRequest>
}
data class BasicPlatformPublicationToMavenRequest(
override val componentName: String,
override val fromModule: KotlinGradleModule,
override val publicationHolder: SingleMavenPublishedModuleHolder,
override val variantPublicationRequests: Iterable<VariantPublicationRequest>
) : PlatformPublicationToMavenRequest {
init {
check(variantPublicationRequests.all { it.fromVariant.containingModule === fromModule }) {
"Variants for publication should all belong to the fromModule ($fromModule)"
}
}
}
/** TODO: consider also using this class for exposing a KPM variant's configurations for project-to-project dependencies,
* so that a variant may expose an arbitrary set of configurations rather just { API, runtime } or { API } */
interface VariantPublicationRequest {
val fromVariant: KotlinGradleVariant
val publishConfiguration: Configuration
}
data class BasicVariantPublicationRequest(
override val fromVariant: KotlinGradleVariant,
override val publishConfiguration: Configuration
) : VariantPublicationRequest
fun VariantPublishingConfigurator.configureNativeVariantPublication(variant: KotlinNativeVariantInternal) {
val publishConfigurations = listOfNotNull(
variant.apiElementsConfiguration.name,
variant.hostSpecificMetadataElementsConfiguration?.name // host-specific metadata may be absent
variant.apiElementsConfiguration,
variant.hostSpecificMetadataElementsConfiguration // host-specific metadata may be absent
)
configurePublishing(variant, variant, publishConfigurations)
configureSingleVariantPublishing(variant, variant, publishConfigurations)
}
fun VariantPublishingConfigurator.configureSingleVariantPublication(variant: KotlinGradlePublishedVariantWithRuntime) {
val publishConfigurations = listOf(variant.apiElementsConfiguration.name, variant.runtimeElementsConfiguration.name)
configurePublishing(variant, variant, publishConfigurations)
val publishConfigurations = listOf(variant.apiElementsConfiguration, variant.runtimeElementsConfiguration)
configureSingleVariantPublishing(variant, variant, publishConfigurations)
}
fun VariantPublishingConfigurator.configureSingleVariantPublishing(
variant: KotlinGradleVariant,
publishedModuleHolder: SingleMavenPublishedModuleHolder,
publishConfigurations: Iterable<Configuration>
) {
configurePublishing(
BasicPlatformPublicationToMavenRequest(
platformComponentName(variant),
variant.containingModule,
publishedModuleHolder,
publishConfigurations.map {
BasicVariantPublicationRequest(variant, it)
}
)
)
}
open class VariantPublishingConfigurator @Inject constructor(
@@ -60,34 +113,39 @@ open class VariantPublishingConfigurator @Inject constructor(
}
open fun configurePublishing(
variant: KotlinGradleVariant,
publishedModuleHolder: SingleMavenPublishedModuleHolder,
publishConfigurations: Iterable<String>
request: PlatformPublicationToMavenRequest
) {
val componentName = platformComponentName(variant)
val configurationsMap = publishConfigurations.associateWith { inferMavenScope(variant, it) }
val componentName = request.componentName
registerPlatformModulePublication(
componentName,
publishedModuleHolder,
configurationsMap,
variant.containingModule::ifMadePublic
request.publicationHolder,
request.variantPublicationRequests,
request.fromModule::ifMadePublic
)
/** The MPP plugin doesn't publish the source artifacts as variants; keep that behavior for legacymapped variants */
if (variant !is LegacyMappedVariant)
configureSourceElementsPublishing(variant)
val publishFromVariants = request.variantPublicationRequests.mapTo(mutableSetOf()) { it.fromVariant }
// Collecting sources for multiple variants is not yet supported;
// TODO make callers provide the source variants?
// The MPP plugin doesn't publish the source artifacts as variants; keep that behavior for legacy-mapped variants for now
if (
publishFromVariants.size == 1 &&
publishFromVariants.none { it is LegacyMappedVariant }
) {
val singlePublishedVariant = publishFromVariants.single()
configureSourceElementsPublishing(componentName, singlePublishedVariant)
}
registerPlatformVariantsInRootModule(
publishedModuleHolder,
variant.containingModule,
publishConfigurations
request.publicationHolder,
request.fromModule,
request.variantPublicationRequests
)
}
protected open fun configureSourceElementsPublishing(variant: KotlinGradleVariant) {
protected open fun configureSourceElementsPublishing(componentName: String, variant: KotlinGradleVariant) {
val configurationName = variant.disambiguateName("sourceElements")
val componentName = platformComponentName(variant)
val docsVariants = DocumentationVariantConfigurator().createSourcesElementsConfiguration(configurationName, variant)
project.components.withType(AdhocComponentWithVariants::class.java).named(componentName).configure { component ->
component.addVariantsFromConfiguration(docsVariants) { }
@@ -102,13 +160,29 @@ open class VariantPublishingConfigurator @Inject constructor(
protected open fun registerPlatformModulePublication(
componentName: String,
publishedModuleHolder: SingleMavenPublishedModuleHolder,
publishConfigurationsWithMavenScopes: Map<String, String?>,
variantRequests: Iterable<VariantPublicationRequest>,
whenShouldRegisterPublication: (() -> Unit) -> Unit
) {
val platformComponent = softwareComponentFactory.adhoc(componentName)
project.components.add(platformComponent)
publishConfigurationsWithMavenScopes.forEach { (configurationName, mavenScopeOrNull) ->
platformComponent.addVariantsFromConfiguration(project.configurations.getByName(configurationName)) details@{ variantDetails ->
variantRequests.forEach { request ->
val originalConfiguration = request.publishConfiguration
val mavenScopeOrNull = inferMavenScope(request.fromVariant, originalConfiguration.name)
val publishedConfiguration = copyConfigurationForPublishing(
request.fromVariant.project,
newName = publishedConfigurationName(originalConfiguration.name) + "-platform",
configuration = originalConfiguration,
overrideArtifacts = (request as? AdvancedVariantPublicationRequest)
?.overrideConfigurationArtifactsForPublication
?.let { override -> { artifacts -> artifacts.addAllLater(override) } },
overrideAttributes = (request as? AdvancedVariantPublicationRequest)
?.overrideConfigurationAttributesForPublication
?.let { override -> { attributes -> copyAttributes(override, attributes) } }
)
platformComponent.addVariantsFromConfiguration(publishedConfiguration) details@{ variantDetails ->
// TODO: This is temporary for Android! Make sure to only publish AAR
val androidArtifactType = variantDetails.configurationVariant.attributes.getAttribute(AndroidArtifacts.ARTIFACT_TYPE)
if (androidArtifactType != null && androidArtifactType != AndroidArtifacts.ArtifactType.AAR.type) {
@@ -138,7 +212,7 @@ open class VariantPublishingConfigurator @Inject constructor(
protected open fun registerPlatformVariantsInRootModule(
publishedModuleHolder: SingleMavenPublishedModuleHolder,
kotlinModule: KotlinGradleModule,
configurationNames: Iterable<String>
variantRequests: Iterable<VariantPublicationRequest>
) {
val platformModuleDependencyProvider = project.provider {
val coordinates = publishedModuleHolder.publishedMavenModuleCoordinates
@@ -154,28 +228,29 @@ open class VariantPublishingConfigurator @Inject constructor(
.withType(AdhocComponentWithVariants::class.java)
.getByName(rootPublicationComponentName(kotlinModule))
configurationNames.forEach { configurationName ->
fun <T : Any> copyAttribute(from: AttributeContainer, to: AttributeContainer, key: Attribute<T>) {
to.attribute(key, checkNotNull(from.getAttribute(key)))
}
val originalConfiguration = project.configurations.getByName(configurationName)
project.configurations.create(publishedConfigurationName(configurationName)).apply {
variantRequests.forEach { variantRequest ->
val configuration = variantRequest.publishConfiguration
project.configurations.create(publishedConfigurationName(configuration.name)).apply {
isCanBeConsumed = false
isCanBeResolved = false
setModuleCapability(this, kotlinModule)
dependencies.addLater(platformModuleDependencyProvider)
val originalAttributes = originalConfiguration.attributes
originalAttributes.keySet().forEach {
copyAttribute(originalAttributes, attributes, it)
}
copyAttributes(configuration.attributes, this.attributes)
rootSoftwareComponent.addVariantsFromConfiguration(this) { }
}
}
}
}
internal data class AdvancedVariantPublicationRequest(
override val fromVariant: KotlinGradleVariant,
override val publishConfiguration: Configuration,
val overrideConfigurationAttributesForPublication: AttributeContainer?,
val overrideConfigurationArtifactsForPublication: Provider<out Iterable<PublishArtifact>>?,
val includeIntoProjectStructureMetadata: Boolean
) : VariantPublicationRequest
open class DocumentationVariantConfigurator {
open fun createSourcesElementsConfiguration(
project: Project,
@@ -214,4 +289,4 @@ open class DocumentationVariantConfigurator {
ComputedCapability.fromModuleOrNull(variant.containingModule)
)
}
}
}
@@ -97,3 +97,6 @@ internal fun Project.filesProvider(
internal fun <T : Task> T.outputFilesProvider(provider: T.() -> Any): ConfigurableFileCollection {
return project.filesProvider(this) { provider() }
}
internal inline fun <reified T> Project.listProperty(noinline itemsProvider: () -> Iterable<T>) =
objects.listProperty(T::class.java).apply { set(provider(itemsProvider)) }