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