Fix publishing with Gradle 5.3 (KT-30379)
In Gradle 5.3, there was a change to the internal logic of publishing `SoftwareComponent`s with the 'maven-publish' plugin, and, simultaneously, a new public API was introduced for user-defined software components, see https://github.com/gradle/gradle/pull/8399 Earlier, the 'maven-publish' plugin used the Gradle `Usage` attribute values to arrange the dependencies of a software component's `UsageContext`s into the Maven scopes. Now that mechanism doesn't work and a `SoftwareComponent` must provide an explicit mapping of the variants to POMs by using the `SoftwareComponentFactory.adhoc` API. However, the new API lacks some features we need to properly publish an MPP, such as adding a component to another one as a variant, so we still create old-style software components under the hood and then wrap them into the new API (and then wrap the adhoc components once again). We also continue to use the old-style components for Kotlin-specific logic like finding a target in another project that a dependency resolved to, as the components produced by the new API lack crucial information that is used in the logic, like references to `KotlinCompilation`s. Issue #KT-30379 Fixed
This commit is contained in:
+1
-1
@@ -43,7 +43,7 @@ interface KotlinTarget : Named, HasAttributes {
|
||||
|
||||
val publishable: Boolean
|
||||
|
||||
val components: Set<KotlinTargetComponent>
|
||||
val components: Set<SoftwareComponent>
|
||||
|
||||
fun mavenPublication(action: Closure<Unit>)
|
||||
fun mavenPublication(action: Action<MavenPublication>)
|
||||
|
||||
+3
-3
@@ -359,7 +359,7 @@ internal abstract class AbstractKotlinPlugin(
|
||||
registry.register(KotlinModelBuilder(kotlinPluginVersion, null))
|
||||
}
|
||||
|
||||
private fun rewriteMppDependenciesInPom(target: KotlinTarget) {
|
||||
private fun rewriteMppDependenciesInPom(target: AbstractKotlinTarget) {
|
||||
val project = target.project
|
||||
|
||||
fun shouldRewritePoms(): Boolean =
|
||||
@@ -370,7 +370,7 @@ internal abstract class AbstractKotlinPlugin(
|
||||
publishing.publications.withType(MavenPublication::class.java).all { publication ->
|
||||
publication.pom.withXml { xml ->
|
||||
if (shouldRewritePoms())
|
||||
project.rewritePomMppDependenciesToActualTargetModules(xml, target.components.single())
|
||||
project.rewritePomMppDependenciesToActualTargetModules(xml, target.kotlinComponents.single())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -381,7 +381,7 @@ internal abstract class AbstractKotlinPlugin(
|
||||
uploadTask.repositories.withType(MavenResolver::class.java).all { mavenResolver ->
|
||||
mavenResolver.pom.withXml { xml ->
|
||||
if (shouldRewritePoms())
|
||||
project.rewritePomMppDependenciesToActualTargetModules(xml, target.components.single())
|
||||
project.rewritePomMppDependenciesToActualTargetModules(xml, target.kotlinComponents.single())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-9
@@ -186,27 +186,28 @@ class KotlinMultiplatformPlugin(
|
||||
|
||||
private fun AbstractKotlinTarget.createMavenPublications(publications: PublicationContainer) {
|
||||
components
|
||||
.filter { it.publishable }
|
||||
.forEach { variant ->
|
||||
val variantPublication = publications.create(variant.name, MavenPublication::class.java).apply {
|
||||
.map { gradleComponent -> gradleComponent to kotlinComponents.single { it.name == gradleComponent.name } }
|
||||
.filter { (_, kotlinComponent) -> kotlinComponent.publishable }
|
||||
.forEach { (gradleComponent, kotlinComponent) ->
|
||||
val componentPublication = publications.create(kotlinComponent.name, MavenPublication::class.java).apply {
|
||||
// do this in whenEvaluated since older Gradle versions seem to check the files in the variant eagerly:
|
||||
project.whenEvaluated {
|
||||
from(variant)
|
||||
variant.sourcesArtifacts.forEach { sourceArtifact ->
|
||||
from(gradleComponent)
|
||||
kotlinComponent.sourcesArtifacts.forEach { sourceArtifact ->
|
||||
artifact(sourceArtifact)
|
||||
}
|
||||
}
|
||||
(this as MavenPublicationInternal).publishWithOriginalFileName()
|
||||
artifactId = variant.defaultArtifactId
|
||||
artifactId = kotlinComponent.defaultArtifactId
|
||||
|
||||
pom.withXml { xml ->
|
||||
if (PropertiesProvider(project).keepMppDependenciesIntactInPoms != true)
|
||||
project.rewritePomMppDependenciesToActualTargetModules(xml, variant)
|
||||
project.rewritePomMppDependenciesToActualTargetModules(xml, kotlinComponent)
|
||||
}
|
||||
}
|
||||
|
||||
(variant as? KotlinTargetComponentWithPublication)?.publicationDelegate = variantPublication
|
||||
publicationConfigureActions.all { it.execute(variantPublication) }
|
||||
(kotlinComponent as? KotlinTargetComponentWithPublication)?.publicationDelegate = componentPublication
|
||||
publicationConfigureActions.all { it.execute(componentPublication) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -13,12 +13,12 @@ import org.gradle.api.attributes.Usage
|
||||
import org.gradle.api.capabilities.Capability
|
||||
import org.gradle.api.component.ComponentWithCoordinates
|
||||
import org.gradle.api.component.ComponentWithVariants
|
||||
import org.gradle.api.component.SoftwareComponent
|
||||
import org.gradle.api.internal.component.SoftwareComponentInternal
|
||||
import org.gradle.api.internal.component.UsageContext
|
||||
import org.gradle.api.publish.maven.MavenPublication
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetComponent
|
||||
import org.jetbrains.kotlin.gradle.plugin.ProjectLocalConfigurations
|
||||
|
||||
open class KotlinSoftwareComponent(
|
||||
@@ -28,7 +28,7 @@ open class KotlinSoftwareComponent(
|
||||
|
||||
override fun getUsages(): Set<UsageContext> = emptySet()
|
||||
|
||||
override fun getVariants(): Set<KotlinTargetComponent> =
|
||||
override fun getVariants(): Set<SoftwareComponent> =
|
||||
kotlinTargets.flatMap { it.components }.toSet()
|
||||
|
||||
override fun getName(): String = name
|
||||
|
||||
+91
-3
@@ -10,10 +10,18 @@ import org.gradle.api.DomainObjectSet
|
||||
import org.gradle.api.NamedDomainObjectContainer
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.ConfigurablePublishArtifact
|
||||
import org.gradle.api.artifacts.Configuration
|
||||
import org.gradle.api.artifacts.PublishArtifact
|
||||
import org.gradle.api.attributes.Attribute
|
||||
import org.gradle.api.attributes.AttributeContainer
|
||||
import org.gradle.api.attributes.Usage.JAVA_API
|
||||
import org.gradle.api.attributes.Usage.JAVA_RUNTIME_JARS
|
||||
import org.gradle.api.component.ComponentWithCoordinates
|
||||
import org.gradle.api.component.ComponentWithVariants
|
||||
import org.gradle.api.component.SoftwareComponent
|
||||
import org.gradle.api.internal.component.SoftwareComponentInternal
|
||||
import org.gradle.api.internal.component.UsageContext
|
||||
import org.gradle.api.internal.project.ProjectInternal
|
||||
import org.gradle.api.publish.maven.MavenPublication
|
||||
import org.gradle.util.ConfigureUtil
|
||||
import org.gradle.util.WrapUtil
|
||||
@@ -48,7 +56,7 @@ abstract class AbstractKotlinTarget(
|
||||
override val publishable: Boolean
|
||||
get() = true
|
||||
|
||||
override val components: Set<KotlinTargetComponent> by lazy {
|
||||
internal open val kotlinComponents: Set<KotlinTargetComponent> by lazy {
|
||||
val mainCompilation = compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
|
||||
val usageContexts = createUsageContexts(mainCompilation)
|
||||
setOf(
|
||||
@@ -65,7 +73,86 @@ abstract class AbstractKotlinTarget(
|
||||
} else {
|
||||
KotlinVariant(mainCompilation, usageContexts)
|
||||
}
|
||||
).also { project.components.addAll(it) }
|
||||
)
|
||||
}
|
||||
|
||||
override val components: Set<SoftwareComponent> by lazy {
|
||||
val kotlinVariants = kotlinComponents
|
||||
if (isGradleVersionAtLeast(5, 3)) {
|
||||
buildAdhocComponentsFromKotlinVariants(kotlinVariants)
|
||||
} else {
|
||||
kotlinVariants.also { project.components.addAll(it) }
|
||||
}
|
||||
}
|
||||
|
||||
// This API is introduced in Gradle 5.3. TODO when we build against Gradle 5.3+, rewrite this function
|
||||
private fun buildAdhocComponentsFromKotlinVariants(kotlinVariants: Set<KotlinTargetComponent>): Set<SoftwareComponent> {
|
||||
val softwareComponentFactoryClass = Class.forName("org.gradle.api.component.SoftwareComponentFactory")
|
||||
// TODO replace internal API access with injection (not possible until we have this class on the compile classpath)
|
||||
val softwareComponentFactory = (project as ProjectInternal).services.get(softwareComponentFactoryClass)
|
||||
|
||||
val adhocMethod = softwareComponentFactoryClass.getMethod("adhoc", String::class.java)
|
||||
val adhocSoftwareComponentClass = Class.forName("org.gradle.api.component.AdhocComponentWithVariants")
|
||||
val addVariantsFromConfigurationMethod = adhocSoftwareComponentClass.getMethod(
|
||||
"addVariantsFromConfiguration", Configuration::class.java, org.gradle.api.Action::class.java
|
||||
)
|
||||
val configurationVariantDetailsClass = Class.forName("org.gradle.api.component.ConfigurationVariantDetails")
|
||||
val mapToMavenScopeMethod = configurationVariantDetailsClass.getMethod(
|
||||
"mapToMavenScope", String::class.java
|
||||
)
|
||||
|
||||
return kotlinVariants.map { kotlinVariant ->
|
||||
val adhocVariant = adhocMethod(softwareComponentFactory, kotlinVariant.name)
|
||||
|
||||
project.whenEvaluated {
|
||||
(kotlinVariant as SoftwareComponentInternal).usages.filterIsInstance<KotlinUsageContext>().forEach { kotlinUsageContext ->
|
||||
val configuration = project.configurations.findByName(kotlinUsageContext.name)
|
||||
?: project.configurations.create(kotlinUsageContext.name).also { configuration ->
|
||||
configuration.isCanBeConsumed = false
|
||||
configuration.isCanBeResolved = false
|
||||
configuration.dependencies.addAll(kotlinUsageContext.dependencies)
|
||||
configuration.dependencyConstraints.addAll(kotlinUsageContext.dependencyConstraints)
|
||||
configuration.artifacts.addAll(kotlinUsageContext.artifacts)
|
||||
|
||||
kotlinUsageContext.attributes.keySet().forEach {
|
||||
// capture type parameter T
|
||||
fun <T> copyAttribute(key: Attribute<T>, from: AttributeContainer, to: AttributeContainer) {
|
||||
to.attribute(key, from.getAttribute(key)!!)
|
||||
}
|
||||
copyAttribute(it, kotlinUsageContext.attributes, configuration.attributes)
|
||||
}
|
||||
}
|
||||
|
||||
val chooseMavenScopeAction = Action<Any> { configurationVariantDetails ->
|
||||
val mavenScope = when (kotlinUsageContext.usage.name) {
|
||||
"java-api-jars" -> "compile"
|
||||
JAVA_RUNTIME_JARS -> "runtime"
|
||||
else -> error("unexpected usage value '${kotlinUsageContext.usage.name}'")
|
||||
}
|
||||
mapToMavenScopeMethod(configurationVariantDetails, mavenScope)
|
||||
}
|
||||
|
||||
addVariantsFromConfigurationMethod(adhocVariant, configuration, chooseMavenScopeAction)
|
||||
}
|
||||
}
|
||||
|
||||
adhocVariant as SoftwareComponent
|
||||
|
||||
if (kotlinVariant is KotlinVariantWithMetadataVariant) {
|
||||
object : ComponentWithVariants, ComponentWithCoordinates, SoftwareComponentInternal {
|
||||
override fun getCoordinates() = kotlinVariant.coordinates
|
||||
override fun getVariants(): Set<out SoftwareComponent> = kotlinVariant.variants
|
||||
override fun getName(): String = adhocVariant.name
|
||||
override fun getUsages(): MutableSet<out UsageContext> = (adhocVariant as SoftwareComponentInternal).usages
|
||||
}
|
||||
} else {
|
||||
object : ComponentWithCoordinates, SoftwareComponentInternal {
|
||||
override fun getCoordinates() = (kotlinVariant as? ComponentWithCoordinates)?.coordinates
|
||||
override fun getName(): String = adhocVariant.name
|
||||
override fun getUsages(): MutableSet<out UsageContext> = (adhocVariant as SoftwareComponentInternal).usages
|
||||
}
|
||||
}
|
||||
}.toSet()
|
||||
}
|
||||
|
||||
protected fun createKotlinVariant(
|
||||
@@ -80,7 +167,8 @@ abstract class AbstractKotlinTarget(
|
||||
if (targetName == KotlinMultiplatformPlugin.METADATA_TARGET_NAME)
|
||||
return KotlinVariantWithCoordinates(compilation, usageContexts)
|
||||
|
||||
val separateMetadataTarget = kotlinExtension.targets.getByName(KotlinMultiplatformPlugin.METADATA_TARGET_NAME)
|
||||
val separateMetadataTarget =
|
||||
kotlinExtension.targets.getByName(KotlinMultiplatformPlugin.METADATA_TARGET_NAME) as AbstractKotlinTarget
|
||||
|
||||
val result = if (kotlinExtension.isGradleMetadataAvailable) {
|
||||
KotlinVariantWithMetadataVariant(compilation, usageContexts, separateMetadataTarget)
|
||||
|
||||
+3
-3
@@ -91,7 +91,7 @@ open class KotlinVariantWithCoordinates(
|
||||
class KotlinVariantWithMetadataVariant(
|
||||
producingCompilation: KotlinCompilation<*>,
|
||||
usages: Set<DefaultKotlinUsageContext>,
|
||||
private val metadataTarget: KotlinTarget
|
||||
internal val metadataTarget: AbstractKotlinTarget
|
||||
) : KotlinVariantWithCoordinates(producingCompilation, usages), ComponentWithVariants {
|
||||
override fun getVariants() = metadataTarget.components
|
||||
}
|
||||
@@ -99,14 +99,14 @@ class KotlinVariantWithMetadataVariant(
|
||||
class KotlinVariantWithMetadataDependency(
|
||||
producingCompilation: KotlinCompilation<*>,
|
||||
val originalUsages: Set<DefaultKotlinUsageContext>,
|
||||
private val metadataTarget: KotlinTarget
|
||||
private val metadataTarget: AbstractKotlinTarget
|
||||
) : KotlinVariantWithCoordinates(producingCompilation, originalUsages) {
|
||||
override fun getUsages(): Set<UsageContext> = originalUsages.mapTo(mutableSetOf()) { usageContext ->
|
||||
KotlinUsageContextWithAdditionalDependencies(usageContext, setOf(metadataDependency()))
|
||||
}
|
||||
|
||||
private fun metadataDependency(): ModuleDependency {
|
||||
val metadataPublication = (metadataTarget.components.single() as KotlinTargetComponentWithPublication).publicationDelegate!!
|
||||
val metadataPublication = (metadataTarget.kotlinComponents.single() as KotlinTargetComponentWithPublication).publicationDelegate!!
|
||||
val metadataGroupId = metadataPublication.groupId
|
||||
val metadataArtifactId = metadataPublication.artifactId
|
||||
val metadataVersion = metadataPublication.version
|
||||
|
||||
+2
-2
@@ -124,8 +124,8 @@ private fun associateDependenciesWithActualModuleDependencies(
|
||||
|
||||
val resolvedToConfiguration = resolved.configuration
|
||||
val dependencyTargetComponent: KotlinTargetComponent = run {
|
||||
dependencyProjectKotlinExtension.targets.forEach { target ->
|
||||
target.components.forEach { component ->
|
||||
dependencyProjectKotlinExtension.targets.withType(AbstractKotlinTarget::class.java).forEach { target ->
|
||||
target.kotlinComponents.forEach { component ->
|
||||
if (component.findUsageContext(resolvedToConfiguration) != null)
|
||||
return@run component
|
||||
}
|
||||
|
||||
+1
-1
@@ -86,7 +86,7 @@ open class KotlinAndroidTarget(
|
||||
)
|
||||
}
|
||||
|
||||
override val components by lazy {
|
||||
override val kotlinComponents by lazy {
|
||||
checkPublishLibraryVariantsExist()
|
||||
|
||||
KotlinAndroidPlugin.androidTargetHandler(project.getKotlinPluginVersion()!!, this).doCreateComponents()
|
||||
|
||||
Reference in New Issue
Block a user