<lambda>null1 import groovy.util.*
2 import org.gradle.jvm.tasks.Jar
3 import org.gradle.kotlin.dsl.*
4 import org.gradle.plugins.signing.*
5 import org.jetbrains.kotlin.gradle.dsl.*
6 import org.jetbrains.kotlin.gradle.tasks.*
7 import java.net.*
8
9 /*
10 * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
11 */
12
13 // Configures publishing of Maven artifacts to MavenCentral
14 plugins {
15 `maven-publish`
16 signing
17 }
18
19 val isMultiplatform = name in listOf(
20 "kotlinx-serialization-core",
21 "kotlinx-serialization-json",
22 "kotlinx-serialization-json-okio",
23 "kotlinx-serialization-json-io",
24 "kotlinx-serialization-json-tests",
25 "kotlinx-serialization-protobuf",
26 "kotlinx-serialization-cbor",
27 "kotlinx-serialization-properties"
28 )
29
30 val isBom = name == "kotlinx-serialization-bom"
31
32 if (!isBom) {
<lambda>null33 tasks.register<Jar>("stubJavadoc") {
34 archiveClassifier = "javadoc"
35 }
36 }
37
38 tasks.register<Jar>("emptyJar")
39
<lambda>null40 afterEvaluate {
41 val mainSourcesJar = tasks.register<Jar>("mainSourcesJar") {
42 archiveClassifier = "sources"
43 if (isMultiplatform) {
44 from(kotlinExtension.sourceSets.getByName("commonMain").kotlin)
45 } else if (isBom) {
46 // no-op: sourceSets is [] for BOM, as it does not have sources.
47 } else {
48 from(sourceSets.named("main").get().allSource)
49 }
50 }
51
52 publishing {
53 if (!isMultiplatform && !isBom) {
54 publications.register<MavenPublication>("maven") {
55 artifactId = project.name
56 from(components["java"])
57 artifact(mainSourcesJar)
58 artifact(tasks.named("stubJavadoc"))
59 }
60 } else {
61 // Rename artifacts for backward compatibility
62 publications.withType<MavenPublication>().configureEach {
63 val type = name
64 logger.info("Configuring $type")
65 when (type) {
66 "kotlinMultiplatform" -> {
67 // With Kotlin 1.4.0, the root module ID has no suffix, but for compatibility with
68 // the consumers who can't read Gradle module metadata, we publish the JVM artifacts in it
69 artifactId = project.name
70 reconfigureMultiplatformPublication(publications.getByName("jvm") as MavenPublication)
71 }
72 "metadata", "jvm", "js", "native" -> artifactId = "${project.name}-$type"
73 }
74 logger.info("Artifact id = $artifactId")
75
76 // The 'root' module publishes the JVM module's Javadoc JAR as per reconfigureMultiplatformPublication, and
77 // every other module should publish an empty Javadoc JAR. TODO: provide proper documentation artifacts?
78 if (name != "kotlinMultiplatform" && !isBom) {
79 artifact(tasks.named("stubJavadoc"))
80 }
81 }
82 }
83
84 publications.withType<MavenPublication>().configureEach {
85 pom.configureMavenCentralMetadata()
86 signPublicationIfKeyPresent()
87 }
88 }
89 }
90
91 val testRepositoryDir = project.layout.buildDirectory.dir("testRepository")
92
<lambda>null93 publishing {
94 repositories {
95 addSonatypeRepository()
96
97 /**
98 * Maven repository in build directory to check published artifacts.
99 */
100 maven {
101 setUrl(testRepositoryDir)
102 name = "test"
103 }
104 }
105 }
106
107 interface LocalArtifactAttr : Named {
108 companion object {
109 val ATTRIBUTE = Attribute.of(
110 "kotlinx.kover.gradle-plugin",
111 LocalArtifactAttr::class.java
112 )
113 }
114 }
115
namenull116 val testPublicationTask: TaskCollection<*> = tasks.named { name -> name == "publishAllPublicationsToTestRepository" }
<lambda>null117 configurations.register("testPublication") {
118 isVisible = false
119 isCanBeResolved = false
120 // this configuration produces modules that can be consumed by other projects
121 isCanBeConsumed = true
122 attributes {
123 attribute(Attribute.of("kotlinx.serialization.repository", String::class.java), "test")
124 }
125 outgoing.artifact(testRepositoryDir) {
126 builtBy(testPublicationTask)
127 }
128 }
129
<lambda>null130 tasks.withType<AbstractPublishToMaven>().configureEach {
131 dependsOn(tasks.withType<Sign>())
132 }
133
134 // NOTE: This is a temporary WA, see KT-61313.
135 // Task ':compileTestKotlin<platform>' uses this output of task ':sign<platform>Publication' without declaring an explicit or implicit dependency
<lambda>null136 tasks.withType<KotlinNativeCompile>().matching { it.name.startsWith("compileTestKotlin") }.configureEach {
137 val targetName = name.substringAfter("compileTestKotlin")
<lambda>null138 mustRunAfter(tasks.withType<Sign>().named { it == "sign${targetName}Publication" })
139 }
140
141 // NOTE: This is a temporary WA, see KT-61313.
142 // Task ':linkDebugTest<platform>' uses this output of task ':sign<platform>Publication' without declaring an explicit or implicit dependency
<lambda>null143 tasks.withType<KotlinNativeLink>() {
144 val targetName = name.substringAfter("linkDebugTest")
145 mustRunAfter(tasks.withType<Sign>().named { it == "sign${targetName}Publication" })
146 }
147
configureMavenCentralMetadatanull148 fun MavenPom.configureMavenCentralMetadata() {
149 name = project.name
150 description = "Kotlin multiplatform serialization runtime library"
151 url = "https://github.com/Kotlin/kotlinx.serialization"
152
153 licenses {
154 license {
155 name = "The Apache Software License, Version 2.0"
156 url = "https://www.apache.org/licenses/LICENSE-2.0.txt"
157 distribution = "repo"
158 }
159 }
160
161 developers {
162 developer {
163 id = "JetBrains"
164 name = "JetBrains Team"
165 organization = "JetBrains"
166 organizationUrl = "https://www.jetbrains.com"
167 }
168 }
169
170 scm {
171 url = "https://github.com/Kotlin/kotlinx.serialization"
172 }
173 }
174
175 // utility functions
176
177 /**
178 * Re-configure common publication to depend on JVM artifact only in pom.xml.
179 *
180 * Publish the platform JAR and POM so that consumers who depend on this module and can't read Gradle module
181 * metadata can still get the platform artifact and transitive dependencies from the POM.
182 *
183 * Taken from https://github.com/Kotlin/kotlinx.coroutines
184 */
reconfigureMultiplatformPublicationnull185 public fun Project.reconfigureMultiplatformPublication(jvmPublication: MavenPublication) {
186 val mavenPublications =
187 extensions.getByType<PublishingExtension>().publications.withType<MavenPublication>()
188 val kmpPublication = mavenPublications.getByName("kotlinMultiplatform")
189
190 var jvmPublicationXml: XmlProvider? = null
191 jvmPublication.pom.withXml { jvmPublicationXml = this }
192
193 kmpPublication.pom.withXml {
194 val root = asNode()
195 // Remove the original content and add the content from the platform POM:
196 root.children().toList().forEach { root.remove(it as Node) }
197 jvmPublicationXml!!.asNode().children().forEach { root.append(it as Node) }
198
199 // Adjust the self artifact ID, as it should match the root module's coordinates:
200 ((root["artifactId"] as NodeList).first() as Node).setValue(kmpPublication.artifactId)
201
202 // Set packaging to POM to indicate that there's no artifact:
203 root.appendNode("packaging", "pom")
204
205 // Remove the original platform dependencies and add a single dependency on the platform module:
206 val dependencies = (root["dependencies"] as NodeList).first() as Node
207 dependencies.children().toList().forEach { dependencies.remove(it as Node) }
208 dependencies.appendNode("dependency").apply {
209 appendNode("groupId", jvmPublication.groupId)
210 appendNode("artifactId", jvmPublication.artifactId)
211 appendNode("version", jvmPublication.version)
212 appendNode("scope", "compile")
213 }
214 }
215
216 // TODO verify if this is still relevant
217 tasks.matching { it.name == "generatePomFileForKotlinMultiplatformPublication" }.configureEach {
218 @Suppress("DEPRECATION")
219 dependsOn("generatePomFileFor${jvmPublication.name.capitalize()}Publication")
220 }
221 }
222
signPublicationIfKeyPresentnull223 fun MavenPublication.signPublicationIfKeyPresent() {
224 val keyId = getSensitiveProperty("libs.sign.key.id")
225 val signingKey = getSensitiveProperty("libs.sign.key.private")
226 val signingKeyPassphrase = getSensitiveProperty("libs.sign.passphrase")
227 if (!signingKey.isNullOrBlank()) {
228 extensions.configure<SigningExtension>("signing") {
229 useInMemoryPgpKeys(keyId, signingKey, signingKeyPassphrase)
230 sign(this@signPublicationIfKeyPresent)
231 }
232 }
233 }
234
RepositoryHandlernull235 fun RepositoryHandler.addSonatypeRepository() {
236 maven {
237 url = mavenRepositoryUri()
238 credentials {
239 username = getSensitiveProperty("libs.sonatype.user")
240 password = getSensitiveProperty("libs.sonatype.password")
241 }
242 }
243 }
244
mavenRepositoryUrinull245 fun mavenRepositoryUri(): URI {
246 // TODO -SNAPSHOT detection can be made here as well
247 val repositoryId: String? = System.getenv("libs.repository.id")
248 return if (repositoryId == null) {
249 URI("https://oss.sonatype.org/service/local/staging/deploy/maven2/")
250 } else {
251 URI("https://oss.sonatype.org/service/local/staging/deployByRepositoryId/$repositoryId")
252 }
253 }
254
getSensitivePropertynull255 fun getSensitiveProperty(name: String): String? {
256 return findProperty(name) as? String ?: System.getenv(name)
257 }
258