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 publishable: Boolean
|
||||||
|
|
||||||
val components: Set<KotlinTargetComponent>
|
val components: Set<SoftwareComponent>
|
||||||
|
|
||||||
fun mavenPublication(action: Closure<Unit>)
|
fun mavenPublication(action: Closure<Unit>)
|
||||||
fun mavenPublication(action: Action<MavenPublication>)
|
fun mavenPublication(action: Action<MavenPublication>)
|
||||||
|
|||||||
+3
-3
@@ -359,7 +359,7 @@ internal abstract class AbstractKotlinPlugin(
|
|||||||
registry.register(KotlinModelBuilder(kotlinPluginVersion, null))
|
registry.register(KotlinModelBuilder(kotlinPluginVersion, null))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun rewriteMppDependenciesInPom(target: KotlinTarget) {
|
private fun rewriteMppDependenciesInPom(target: AbstractKotlinTarget) {
|
||||||
val project = target.project
|
val project = target.project
|
||||||
|
|
||||||
fun shouldRewritePoms(): Boolean =
|
fun shouldRewritePoms(): Boolean =
|
||||||
@@ -370,7 +370,7 @@ internal abstract class AbstractKotlinPlugin(
|
|||||||
publishing.publications.withType(MavenPublication::class.java).all { publication ->
|
publishing.publications.withType(MavenPublication::class.java).all { publication ->
|
||||||
publication.pom.withXml { xml ->
|
publication.pom.withXml { xml ->
|
||||||
if (shouldRewritePoms())
|
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 ->
|
uploadTask.repositories.withType(MavenResolver::class.java).all { mavenResolver ->
|
||||||
mavenResolver.pom.withXml { xml ->
|
mavenResolver.pom.withXml { xml ->
|
||||||
if (shouldRewritePoms())
|
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) {
|
private fun AbstractKotlinTarget.createMavenPublications(publications: PublicationContainer) {
|
||||||
components
|
components
|
||||||
.filter { it.publishable }
|
.map { gradleComponent -> gradleComponent to kotlinComponents.single { it.name == gradleComponent.name } }
|
||||||
.forEach { variant ->
|
.filter { (_, kotlinComponent) -> kotlinComponent.publishable }
|
||||||
val variantPublication = publications.create(variant.name, MavenPublication::class.java).apply {
|
.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:
|
// do this in whenEvaluated since older Gradle versions seem to check the files in the variant eagerly:
|
||||||
project.whenEvaluated {
|
project.whenEvaluated {
|
||||||
from(variant)
|
from(gradleComponent)
|
||||||
variant.sourcesArtifacts.forEach { sourceArtifact ->
|
kotlinComponent.sourcesArtifacts.forEach { sourceArtifact ->
|
||||||
artifact(sourceArtifact)
|
artifact(sourceArtifact)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(this as MavenPublicationInternal).publishWithOriginalFileName()
|
(this as MavenPublicationInternal).publishWithOriginalFileName()
|
||||||
artifactId = variant.defaultArtifactId
|
artifactId = kotlinComponent.defaultArtifactId
|
||||||
|
|
||||||
pom.withXml { xml ->
|
pom.withXml { xml ->
|
||||||
if (PropertiesProvider(project).keepMppDependenciesIntactInPoms != true)
|
if (PropertiesProvider(project).keepMppDependenciesIntactInPoms != true)
|
||||||
project.rewritePomMppDependenciesToActualTargetModules(xml, variant)
|
project.rewritePomMppDependenciesToActualTargetModules(xml, kotlinComponent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(variant as? KotlinTargetComponentWithPublication)?.publicationDelegate = variantPublication
|
(kotlinComponent as? KotlinTargetComponentWithPublication)?.publicationDelegate = componentPublication
|
||||||
publicationConfigureActions.all { it.execute(variantPublication) }
|
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.capabilities.Capability
|
||||||
import org.gradle.api.component.ComponentWithCoordinates
|
import org.gradle.api.component.ComponentWithCoordinates
|
||||||
import org.gradle.api.component.ComponentWithVariants
|
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.SoftwareComponentInternal
|
||||||
import org.gradle.api.internal.component.UsageContext
|
import org.gradle.api.internal.component.UsageContext
|
||||||
import org.gradle.api.publish.maven.MavenPublication
|
import org.gradle.api.publish.maven.MavenPublication
|
||||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
||||||
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
|
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
|
||||||
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetComponent
|
|
||||||
import org.jetbrains.kotlin.gradle.plugin.ProjectLocalConfigurations
|
import org.jetbrains.kotlin.gradle.plugin.ProjectLocalConfigurations
|
||||||
|
|
||||||
open class KotlinSoftwareComponent(
|
open class KotlinSoftwareComponent(
|
||||||
@@ -28,7 +28,7 @@ open class KotlinSoftwareComponent(
|
|||||||
|
|
||||||
override fun getUsages(): Set<UsageContext> = emptySet()
|
override fun getUsages(): Set<UsageContext> = emptySet()
|
||||||
|
|
||||||
override fun getVariants(): Set<KotlinTargetComponent> =
|
override fun getVariants(): Set<SoftwareComponent> =
|
||||||
kotlinTargets.flatMap { it.components }.toSet()
|
kotlinTargets.flatMap { it.components }.toSet()
|
||||||
|
|
||||||
override fun getName(): String = name
|
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.NamedDomainObjectContainer
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
import org.gradle.api.artifacts.ConfigurablePublishArtifact
|
import org.gradle.api.artifacts.ConfigurablePublishArtifact
|
||||||
|
import org.gradle.api.artifacts.Configuration
|
||||||
import org.gradle.api.artifacts.PublishArtifact
|
import org.gradle.api.artifacts.PublishArtifact
|
||||||
|
import org.gradle.api.attributes.Attribute
|
||||||
import org.gradle.api.attributes.AttributeContainer
|
import org.gradle.api.attributes.AttributeContainer
|
||||||
import org.gradle.api.attributes.Usage.JAVA_API
|
import org.gradle.api.attributes.Usage.JAVA_API
|
||||||
import org.gradle.api.attributes.Usage.JAVA_RUNTIME_JARS
|
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.api.publish.maven.MavenPublication
|
||||||
import org.gradle.util.ConfigureUtil
|
import org.gradle.util.ConfigureUtil
|
||||||
import org.gradle.util.WrapUtil
|
import org.gradle.util.WrapUtil
|
||||||
@@ -48,7 +56,7 @@ abstract class AbstractKotlinTarget(
|
|||||||
override val publishable: Boolean
|
override val publishable: Boolean
|
||||||
get() = true
|
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 mainCompilation = compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
|
||||||
val usageContexts = createUsageContexts(mainCompilation)
|
val usageContexts = createUsageContexts(mainCompilation)
|
||||||
setOf(
|
setOf(
|
||||||
@@ -65,7 +73,86 @@ abstract class AbstractKotlinTarget(
|
|||||||
} else {
|
} else {
|
||||||
KotlinVariant(mainCompilation, usageContexts)
|
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(
|
protected fun createKotlinVariant(
|
||||||
@@ -80,7 +167,8 @@ abstract class AbstractKotlinTarget(
|
|||||||
if (targetName == KotlinMultiplatformPlugin.METADATA_TARGET_NAME)
|
if (targetName == KotlinMultiplatformPlugin.METADATA_TARGET_NAME)
|
||||||
return KotlinVariantWithCoordinates(compilation, usageContexts)
|
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) {
|
val result = if (kotlinExtension.isGradleMetadataAvailable) {
|
||||||
KotlinVariantWithMetadataVariant(compilation, usageContexts, separateMetadataTarget)
|
KotlinVariantWithMetadataVariant(compilation, usageContexts, separateMetadataTarget)
|
||||||
|
|||||||
+3
-3
@@ -91,7 +91,7 @@ open class KotlinVariantWithCoordinates(
|
|||||||
class KotlinVariantWithMetadataVariant(
|
class KotlinVariantWithMetadataVariant(
|
||||||
producingCompilation: KotlinCompilation<*>,
|
producingCompilation: KotlinCompilation<*>,
|
||||||
usages: Set<DefaultKotlinUsageContext>,
|
usages: Set<DefaultKotlinUsageContext>,
|
||||||
private val metadataTarget: KotlinTarget
|
internal val metadataTarget: AbstractKotlinTarget
|
||||||
) : KotlinVariantWithCoordinates(producingCompilation, usages), ComponentWithVariants {
|
) : KotlinVariantWithCoordinates(producingCompilation, usages), ComponentWithVariants {
|
||||||
override fun getVariants() = metadataTarget.components
|
override fun getVariants() = metadataTarget.components
|
||||||
}
|
}
|
||||||
@@ -99,14 +99,14 @@ class KotlinVariantWithMetadataVariant(
|
|||||||
class KotlinVariantWithMetadataDependency(
|
class KotlinVariantWithMetadataDependency(
|
||||||
producingCompilation: KotlinCompilation<*>,
|
producingCompilation: KotlinCompilation<*>,
|
||||||
val originalUsages: Set<DefaultKotlinUsageContext>,
|
val originalUsages: Set<DefaultKotlinUsageContext>,
|
||||||
private val metadataTarget: KotlinTarget
|
private val metadataTarget: AbstractKotlinTarget
|
||||||
) : KotlinVariantWithCoordinates(producingCompilation, originalUsages) {
|
) : KotlinVariantWithCoordinates(producingCompilation, originalUsages) {
|
||||||
override fun getUsages(): Set<UsageContext> = originalUsages.mapTo(mutableSetOf()) { usageContext ->
|
override fun getUsages(): Set<UsageContext> = originalUsages.mapTo(mutableSetOf()) { usageContext ->
|
||||||
KotlinUsageContextWithAdditionalDependencies(usageContext, setOf(metadataDependency()))
|
KotlinUsageContextWithAdditionalDependencies(usageContext, setOf(metadataDependency()))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun metadataDependency(): ModuleDependency {
|
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 metadataGroupId = metadataPublication.groupId
|
||||||
val metadataArtifactId = metadataPublication.artifactId
|
val metadataArtifactId = metadataPublication.artifactId
|
||||||
val metadataVersion = metadataPublication.version
|
val metadataVersion = metadataPublication.version
|
||||||
|
|||||||
+2
-2
@@ -124,8 +124,8 @@ private fun associateDependenciesWithActualModuleDependencies(
|
|||||||
|
|
||||||
val resolvedToConfiguration = resolved.configuration
|
val resolvedToConfiguration = resolved.configuration
|
||||||
val dependencyTargetComponent: KotlinTargetComponent = run {
|
val dependencyTargetComponent: KotlinTargetComponent = run {
|
||||||
dependencyProjectKotlinExtension.targets.forEach { target ->
|
dependencyProjectKotlinExtension.targets.withType(AbstractKotlinTarget::class.java).forEach { target ->
|
||||||
target.components.forEach { component ->
|
target.kotlinComponents.forEach { component ->
|
||||||
if (component.findUsageContext(resolvedToConfiguration) != null)
|
if (component.findUsageContext(resolvedToConfiguration) != null)
|
||||||
return@run component
|
return@run component
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -86,7 +86,7 @@ open class KotlinAndroidTarget(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val components by lazy {
|
override val kotlinComponents by lazy {
|
||||||
checkPublishLibraryVariantsExist()
|
checkPublishLibraryVariantsExist()
|
||||||
|
|
||||||
KotlinAndroidPlugin.androidTargetHandler(project.getKotlinPluginVersion()!!, this).doCreateComponents()
|
KotlinAndroidPlugin.androidTargetHandler(project.getKotlinPluginVersion()!!, this).doCreateComponents()
|
||||||
|
|||||||
Reference in New Issue
Block a user