1+ /*
2+ * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
3+ * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
4+ */
5+
6+ package plugins.publishing
7+
8+ import org.gradle.api.Project
9+ import org.gradle.api.artifacts.ConfigurablePublishArtifact
10+ import org.gradle.api.artifacts.Configuration
11+ import org.gradle.api.artifacts.ConfigurationContainer
12+ import org.gradle.api.attributes.Attribute
13+ import org.gradle.api.attributes.AttributeContainer
14+ import org.gradle.api.component.*
15+ import org.gradle.api.internal.component.SoftwareComponentInternal
16+ import org.gradle.api.internal.component.UsageContext
17+ import org.gradle.api.publish.PublishingExtension
18+ import org.gradle.api.publish.maven.MavenPublication
19+ import org.gradle.kotlin.dsl.create
20+ import org.gradle.kotlin.dsl.extra
21+ import org.gradle.kotlin.dsl.getByType
22+ import org.gradle.kotlin.dsl.newInstance
23+
24+ private open class ComponentsFactoryAccess
25+ @javax.inject.Inject
26+ constructor (val factory: SoftwareComponentFactory )
27+
28+ val Project .componentFactory: SoftwareComponentFactory
29+ get() = findProperty(" _componentFactory" ) as SoftwareComponentFactory ?
30+ ? : objects.newInstance<ComponentsFactoryAccess >().factory
31+ .also { project.extra[" _componentFactory" ] = it }
32+
33+ fun copyAttributes (from : AttributeContainer , to : AttributeContainer ) {
34+ // capture type argument T
35+ fun <T : Any > copyOneAttribute (from : AttributeContainer , to : AttributeContainer , key : Attribute <T >) {
36+ val value = checkNotNull(from.getAttribute(key))
37+ to.attribute(key, value)
38+ }
39+ for (key in from.keySet()) {
40+ copyOneAttribute(from, to, key)
41+ }
42+ }
43+
44+ class MultiModuleMavenPublishingConfiguration {
45+ val modules = mutableMapOf<String , Module >()
46+
47+ class Module (val name : String ) {
48+ val variants = mutableMapOf<String , Variant >()
49+ val includes = mutableSetOf<Module >()
50+
51+ class Variant (
52+ val configurationName : String
53+ ) {
54+ var name: String = configurationName
55+ val attributesConfigurations = mutableListOf<AttributeContainer .() - > Unit > ()
56+ fun attributes (code : AttributeContainer .() -> Unit ) {
57+ attributesConfigurations + = code
58+ }
59+
60+ val artifactsWithConfigurations = mutableListOf<Pair <Any , ConfigurablePublishArtifact .() - > Unit >> ()
61+ fun artifact (file : Any , code : ConfigurablePublishArtifact .() -> Unit = {}) {
62+ artifactsWithConfigurations + = file to code
63+ }
64+
65+ val configurationConfigurations = mutableListOf<Configuration .() - > Unit > ()
66+ fun configuration (code : Configuration .() -> Unit ) {
67+ configurationConfigurations + = code
68+ }
69+
70+ val variantDetailsConfigurations = mutableListOf<ConfigurationVariantDetails .() - > Unit > ()
71+ fun configureVariantDetails (code : ConfigurationVariantDetails .() -> Unit ) {
72+ variantDetailsConfigurations + = code
73+ }
74+
75+ var suppressPomMetadataWarnings: Boolean = false
76+ fun suppressPomMetadataWarnings () { suppressPomMetadataWarnings = true }
77+ }
78+
79+ val mavenPublicationConfigurations = mutableListOf<MavenPublication .() - > Unit > ()
80+ fun mavenPublication (code : MavenPublication .() -> Unit ) {
81+ mavenPublicationConfigurations + = code
82+ }
83+
84+ fun variant (fromConfigurationName : String , code : Variant .() -> Unit = {}): Variant {
85+ val variant = variants.getOrPut(fromConfigurationName) { Variant (fromConfigurationName) }
86+ variant.code()
87+ return variant
88+ }
89+
90+ fun include (vararg modules : Module ) {
91+ includes.addAll(modules)
92+ }
93+ }
94+
95+ fun module (name : String , code : Module .() -> Unit ): Module {
96+ val module = modules.getOrPut(name) { Module (name) }
97+ module.code()
98+ return module
99+ }
100+ }
101+
102+ fun Project.configureMultiModuleMavenPublishing (code : MultiModuleMavenPublishingConfiguration .() -> Unit ) {
103+ val publishingConfiguration = MultiModuleMavenPublishingConfiguration ()
104+ publishingConfiguration.code()
105+
106+ val components = publishingConfiguration
107+ .modules
108+ .mapValues { (_, module) -> project.createModulePublication(module) }
109+
110+ val componentsWithExternals = publishingConfiguration
111+ .modules
112+ .filter { (_, module) -> module.includes.isNotEmpty() }
113+ .mapValues { (moduleName, module) ->
114+ val mainComponent = components[moduleName] ? : error(" Component with name $moduleName wasn't created" )
115+ val externalComponents = module.includes
116+ .map { components[it.name] ? : error(" Component with name ${it.name} wasn't created" ) }
117+ .toSet()
118+ ComponentWithExternalVariants (mainComponent, externalComponents)
119+ }
120+
121+ // override some components with items from componentsWithExternals
122+ val mergedComponents = components + componentsWithExternals
123+
124+ val publicationsContainer = project.extensions.getByType<PublishingExtension >().publications
125+ for ((componentName, component) in mergedComponents) {
126+ publicationsContainer.create<MavenPublication >(componentName) {
127+ from(component)
128+ val module = publishingConfiguration.modules[componentName]!!
129+ module.mavenPublicationConfigurations.forEach { configure -> configure() }
130+ module.variants.values.filter { it.suppressPomMetadataWarnings }.forEach {
131+ suppressPomMetadataWarningsFor(it.name)
132+ }
133+ }
134+ }
135+ }
136+
137+
138+ fun Project.createModulePublication (module : MultiModuleMavenPublishingConfiguration .Module ): SoftwareComponent {
139+ val component = componentFactory.adhoc(module.name)
140+ module.variants.values.forEach { addVariant(component, it) }
141+
142+ val newNames = module.variants.map { it.key to it.value.name }.filter { it.first != it.second }.toMap()
143+ return if (newNames.isNotEmpty()) {
144+ ComponentWithRenamedVariants (newNames, component as SoftwareComponentInternal )
145+ } else {
146+ component
147+ }
148+ }
149+
150+ fun Project.addVariant (component : AdhocComponentWithVariants , variant : MultiModuleMavenPublishingConfiguration .Module .Variant ) {
151+ val configuration: Configuration = configurations.getOrCreate(variant.configurationName)
152+ configuration.apply {
153+ isCanBeResolved = false
154+
155+ variant.attributesConfigurations.forEach { configure -> attributes.configure() }
156+ }
157+
158+ for ((artifactNotation, configure) in variant.artifactsWithConfigurations) {
159+ artifacts.add(configuration.name, artifactNotation) {
160+ configure()
161+ }
162+ }
163+
164+ for (configure in variant.configurationConfigurations) {
165+ configuration.apply (configure)
166+ }
167+
168+ component.addVariantsFromConfiguration(configuration) {
169+ variant.variantDetailsConfigurations.forEach { configure -> configure() }
170+ }
171+ }
172+
173+ private class RenamedVariant (val newName : String , context : UsageContext ) : UsageContext by context {
174+ override fun getName (): String = newName
175+ }
176+
177+ private class ComponentWithRenamedVariants (
178+ val newNames : Map <String , String >,
179+ private val base : SoftwareComponentInternal
180+ ): SoftwareComponentInternal by base {
181+
182+ override fun getName (): String = base.name
183+ override fun getUsages (): Set <UsageContext > {
184+ return base.usages.map {
185+ val newName = newNames[it.name]
186+ if (newName != null ) {
187+ RenamedVariant (newName, it)
188+ } else {
189+ it
190+ }
191+ }.toSet()
192+ }
193+ }
194+
195+ private class ComponentWithExternalVariants (
196+ private val mainComponent : SoftwareComponent ,
197+ private val externalComponents : Set <SoftwareComponent >
198+ ) : ComponentWithVariants, SoftwareComponentInternal {
199+ override fun getName (): String = mainComponent.name
200+
201+ override fun getUsages (): Set <UsageContext > = (mainComponent as SoftwareComponentInternal ).usages
202+
203+ override fun getVariants (): Set <SoftwareComponent > = externalComponents
204+ }
205+
206+ fun ConfigurationContainer.getOrCreate (name : String ): Configuration = findByName(name) ? : create(name)
0 commit comments