-
Notifications
You must be signed in to change notification settings - Fork 47
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Set up multiplatform publishing maintaining jvm artifact name
- Loading branch information
1 parent
bc94c2f
commit 5d7e023
Showing
4 changed files
with
286 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
plugins { | ||
`kotlin-dsl` | ||
} | ||
|
||
repositories { | ||
gradlePluginPortal() | ||
} |
206 changes: 206 additions & 0 deletions
206
buildSrc/src/main/kotlin/plugins/publishing/CustomVariantPublishingDsl.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,206 @@ | ||
/* | ||
* Copyright 2010-2023 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.txt file. | ||
*/ | ||
|
||
package plugins.publishing | ||
|
||
import org.gradle.api.Project | ||
import org.gradle.api.artifacts.ConfigurablePublishArtifact | ||
import org.gradle.api.artifacts.Configuration | ||
import org.gradle.api.artifacts.ConfigurationContainer | ||
import org.gradle.api.attributes.Attribute | ||
import org.gradle.api.attributes.AttributeContainer | ||
import org.gradle.api.component.* | ||
import org.gradle.api.internal.component.SoftwareComponentInternal | ||
import org.gradle.api.internal.component.UsageContext | ||
import org.gradle.api.publish.PublishingExtension | ||
import org.gradle.api.publish.maven.MavenPublication | ||
import org.gradle.kotlin.dsl.create | ||
import org.gradle.kotlin.dsl.extra | ||
import org.gradle.kotlin.dsl.getByType | ||
import org.gradle.kotlin.dsl.newInstance | ||
|
||
private open class ComponentsFactoryAccess | ||
@javax.inject.Inject | ||
constructor(val factory: SoftwareComponentFactory) | ||
|
||
val Project.componentFactory: SoftwareComponentFactory | ||
get() = findProperty("_componentFactory") as SoftwareComponentFactory? | ||
?: objects.newInstance<ComponentsFactoryAccess>().factory | ||
.also { project.extra["_componentFactory"] = it } | ||
|
||
fun copyAttributes(from: AttributeContainer, to: AttributeContainer) { | ||
// 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 from.keySet()) { | ||
copyOneAttribute(from, to, key) | ||
} | ||
} | ||
|
||
class MultiModuleMavenPublishingConfiguration { | ||
val modules = mutableMapOf<String, Module>() | ||
|
||
class Module(val name: String) { | ||
val variants = mutableMapOf<String, Variant>() | ||
val includes = mutableSetOf<Module>() | ||
|
||
class Variant( | ||
val configurationName: String | ||
) { | ||
var name: String = configurationName | ||
val attributesConfigurations = mutableListOf<AttributeContainer.() -> Unit>() | ||
fun attributes(code: AttributeContainer.() -> Unit) { | ||
attributesConfigurations += code | ||
} | ||
|
||
val artifactsWithConfigurations = mutableListOf<Pair<Any, ConfigurablePublishArtifact.() -> Unit>>() | ||
fun artifact(file: Any, code: ConfigurablePublishArtifact.() -> Unit = {}) { | ||
artifactsWithConfigurations += file to code | ||
} | ||
|
||
val configurationConfigurations = mutableListOf<Configuration.() -> Unit>() | ||
fun configuration(code: Configuration.() -> Unit) { | ||
configurationConfigurations += code | ||
} | ||
|
||
val variantDetailsConfigurations = mutableListOf<ConfigurationVariantDetails.() -> Unit>() | ||
fun configureVariantDetails(code: ConfigurationVariantDetails.() -> Unit) { | ||
variantDetailsConfigurations += code | ||
} | ||
|
||
var suppressPomMetadataWarnings: Boolean = false | ||
fun suppressPomMetadataWarnings() { suppressPomMetadataWarnings = true } | ||
} | ||
|
||
val mavenPublicationConfigurations = mutableListOf<MavenPublication.() -> Unit>() | ||
fun mavenPublication(code: MavenPublication.() -> Unit) { | ||
mavenPublicationConfigurations += code | ||
} | ||
|
||
fun variant(fromConfigurationName: String, code: Variant.() -> Unit = {}): Variant { | ||
val variant = variants.getOrPut(fromConfigurationName) { Variant(fromConfigurationName) } | ||
variant.code() | ||
return variant | ||
} | ||
|
||
fun include(vararg modules: Module) { | ||
includes.addAll(modules) | ||
} | ||
} | ||
|
||
fun module(name: String, code: Module.() -> Unit): Module { | ||
val module = modules.getOrPut(name) { Module(name) } | ||
module.code() | ||
return module | ||
} | ||
} | ||
|
||
fun Project.configureMultiModuleMavenPublishing(code: MultiModuleMavenPublishingConfiguration.() -> Unit) { | ||
val publishingConfiguration = MultiModuleMavenPublishingConfiguration() | ||
publishingConfiguration.code() | ||
|
||
val components = publishingConfiguration | ||
.modules | ||
.mapValues { (_, module) -> project.createModulePublication(module) } | ||
|
||
val componentsWithExternals = publishingConfiguration | ||
.modules | ||
.filter { (_, module) -> module.includes.isNotEmpty() } | ||
.mapValues { (moduleName, module) -> | ||
val mainComponent = components[moduleName] ?: error("Component with name $moduleName wasn't created") | ||
val externalComponents = module.includes | ||
.map { components[it.name] ?: error("Component with name ${it.name} wasn't created") } | ||
.toSet() | ||
ComponentWithExternalVariants(mainComponent, externalComponents) | ||
} | ||
|
||
// override some components with items from componentsWithExternals | ||
val mergedComponents = components + componentsWithExternals | ||
|
||
val publicationsContainer = project.extensions.getByType<PublishingExtension>().publications | ||
for ((componentName, component) in mergedComponents) { | ||
publicationsContainer.create<MavenPublication>(componentName) { | ||
from(component) | ||
val module = publishingConfiguration.modules[componentName]!! | ||
module.mavenPublicationConfigurations.forEach { configure -> configure() } | ||
module.variants.values.filter { it.suppressPomMetadataWarnings }.forEach { | ||
suppressPomMetadataWarningsFor(it.name) | ||
} | ||
} | ||
} | ||
} | ||
|
||
|
||
fun Project.createModulePublication(module: MultiModuleMavenPublishingConfiguration.Module): SoftwareComponent { | ||
val component = componentFactory.adhoc(module.name) | ||
module.variants.values.forEach { addVariant(component, it) } | ||
|
||
val newNames = module.variants.map { it.key to it.value.name }.filter { it.first != it.second }.toMap() | ||
return if (newNames.isNotEmpty()) { | ||
ComponentWithRenamedVariants(newNames, component as SoftwareComponentInternal) | ||
} else { | ||
component | ||
} | ||
} | ||
|
||
fun Project.addVariant(component: AdhocComponentWithVariants, variant: MultiModuleMavenPublishingConfiguration.Module.Variant) { | ||
val configuration: Configuration = configurations.getOrCreate(variant.configurationName) | ||
configuration.apply { | ||
isCanBeResolved = false | ||
|
||
variant.attributesConfigurations.forEach { configure -> attributes.configure() } | ||
} | ||
|
||
for ((artifactNotation, configure) in variant.artifactsWithConfigurations) { | ||
artifacts.add(configuration.name, artifactNotation) { | ||
configure() | ||
} | ||
} | ||
|
||
for (configure in variant.configurationConfigurations) { | ||
configuration.apply(configure) | ||
} | ||
|
||
component.addVariantsFromConfiguration(configuration) { | ||
variant.variantDetailsConfigurations.forEach { configure -> configure() } | ||
} | ||
} | ||
|
||
private class RenamedVariant(val newName: String, context: UsageContext) : UsageContext by context { | ||
override fun getName(): String = newName | ||
} | ||
|
||
private class ComponentWithRenamedVariants( | ||
val newNames: Map<String, String>, | ||
private val base: SoftwareComponentInternal | ||
): SoftwareComponentInternal by base { | ||
|
||
override fun getName(): String = base.name | ||
override fun getUsages(): Set<UsageContext> { | ||
return base.usages.map { | ||
val newName = newNames[it.name] | ||
if (newName != null) { | ||
RenamedVariant(newName, it) | ||
} else { | ||
it | ||
} | ||
}.toSet() | ||
} | ||
} | ||
|
||
private class ComponentWithExternalVariants( | ||
private val mainComponent: SoftwareComponent, | ||
private val externalComponents: Set<SoftwareComponent> | ||
) : ComponentWithVariants, SoftwareComponentInternal { | ||
override fun getName(): String = mainComponent.name | ||
|
||
override fun getUsages(): Set<UsageContext> = (mainComponent as SoftwareComponentInternal).usages | ||
|
||
override fun getVariants(): Set<SoftwareComponent> = externalComponents | ||
} | ||
|
||
fun ConfigurationContainer.getOrCreate(name: String): Configuration = findByName(name) ?: create(name) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters