Skip to content

Commit 5d7e023

Browse files
committed
Set up multiplatform publishing maintaining jvm artifact name
1 parent bc94c2f commit 5d7e023

File tree

4 files changed

+286
-24
lines changed

4 files changed

+286
-24
lines changed

build.gradle.kts

Lines changed: 71 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import jetbrains.sign.GpgSignSignatoryProvider
22
import org.gradle.jvm.tasks.Jar
33
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
44
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
5+
import plugins.publishing.*
56

67
/*
78
* Copyright 2000-2021 JetBrains s.r.o.
@@ -37,6 +38,7 @@ plugins {
3738
id("io.github.gradle-nexus.publish-plugin") version "1.1.0"
3839
`maven-publish`
3940
signing
41+
java
4042
}
4143

4244
var projectVersion = project.findProperty("projectVersion") as String
@@ -180,6 +182,15 @@ tasks {
180182
into("META-INF/versions/9/")
181183
}
182184
}
185+
186+
val allMetadataJar by existing(Jar::class) {
187+
archiveClassifier.set("all")
188+
}
189+
190+
val javadocJar by creating(Jar::class) {
191+
from(javadoc)
192+
archiveClassifier.set("javadoc")
193+
}
183194
}
184195

185196
nexusPublishing {
@@ -192,33 +203,69 @@ nexusPublishing {
192203
}
193204

194205
publishing {
195-
publications.withType(MavenPublication::class) {
196-
group = "org.jetbrains"
197-
version = rootProject.version as String
198-
199-
pom {
200-
name.set("JetBrains Java Annotations")
201-
description.set("A set of annotations used for code inspection support and code documentation.")
202-
url.set("https://github.com/JetBrains/java-annotations")
203-
scm {
204-
url.set("https://github.com/JetBrains/java-annotations")
205-
connection.set("scm:git:git://github.com/JetBrains/java-annotations.git")
206-
developerConnection.set("scm:git:ssh://github.com:JetBrains/java-annotations.git")
206+
val artifactBaseName = base.archivesName.get()
207+
configureMultiModuleMavenPublishing {
208+
val rootModule = module("rootModule") {
209+
mavenPublication {
210+
artifactId = artifactBaseName
211+
groupId = "org.jetbrains"
212+
configureKotlinPomAttributes(packaging = "jar")
213+
artifact(tasks.getByName("javadocJar"))
207214
}
208-
licenses {
209-
license {
210-
name.set("The Apache Software License, Version 2.0")
211-
url.set("https://www.apache.org/licenses/LICENSE-2.0.txt")
212-
distribution.set("repo")
213-
}
215+
variant("metadataApiElements") { suppressPomMetadataWarnings() }
216+
variant("jvmApiElements")
217+
variant("jvmRuntimeElements") {
218+
configureVariantDetails { mapToMavenScope("runtime") }
214219
}
215-
developers {
216-
developer {
217-
id.set("JetBrains")
218-
name.set("JetBrains Team")
219-
organization.set("JetBrains")
220-
organizationUrl.set("https://www.jetbrains.com")
220+
variant("jvmSourcesElements")
221+
}
222+
val targetModules = kotlin.targets.filter { it.targetName != "jvm" && it.targetName != "metadata" }.map { target ->
223+
val targetName = target.targetName
224+
module("${targetName}Module") {
225+
mavenPublication {
226+
artifactId = "$artifactBaseName-$targetName"
227+
groupId = "org.jetbrains"
228+
configureKotlinPomAttributes(packaging = "klib")
221229
}
230+
variant("${targetName}ApiElements")
231+
if (configurations.findByName("${targetName}RuntimeElements") != null) {
232+
variant("${targetName}RuntimeElements")
233+
}
234+
variant("${targetName}SourcesElements")
235+
}
236+
}
237+
238+
// Makes all variants from accompanying artifacts visible through `available-at`
239+
rootModule.include(*targetModules.toTypedArray())
240+
}
241+
}
242+
243+
fun MavenPublication.configureKotlinPomAttributes(
244+
packaging: String,
245+
) {
246+
pom {
247+
this.packaging = packaging
248+
name.set("JetBrains Java Annotations")
249+
description.set("A set of annotations used for code inspection support and code documentation.")
250+
url.set("https://github.com/JetBrains/java-annotations")
251+
scm {
252+
url.set("https://github.com/JetBrains/java-annotations")
253+
connection.set("scm:git:git://github.com/JetBrains/java-annotations.git")
254+
developerConnection.set("scm:git:ssh://github.com:JetBrains/java-annotations.git")
255+
}
256+
licenses {
257+
license {
258+
name.set("The Apache Software License, Version 2.0")
259+
url.set("https://www.apache.org/licenses/LICENSE-2.0.txt")
260+
distribution.set("repo")
261+
}
262+
}
263+
developers {
264+
developer {
265+
id.set("JetBrains")
266+
name.set("JetBrains Team")
267+
organization.set("JetBrains")
268+
organizationUrl.set("https://www.jetbrains.com")
222269
}
223270
}
224271
}

buildSrc/build.gradle.kts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
plugins {
2+
`kotlin-dsl`
3+
}
4+
5+
repositories {
6+
gradlePluginPortal()
7+
}
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
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)

gradle.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,5 @@ projectVersion=25.0.0
1818
kotlin.code.style=official
1919
kotlin.mpp.stability.nowarn=true
2020
kotlin.stdlib.default.dependency=false
21+
kotlin.internal.mpp.createDefaultMultiplatformPublications=false
22+
kotlin.native.ignoreIncorrectDependencies=true

0 commit comments

Comments
 (0)