Unverified Commit f0d0baa1 authored by Sebastian Sellmair's avatar Sebastian Sellmair Committed by GitHub

Publish 'skiko-awt-runtime' (#1119)

This publication creates one variant for each native platform. 
It allows depending (and exposing) a single dependency, which can later
be resolved to platform-specific artifacts.

<details>
<summary>skiko-awt-runtime.module</summary>

```json
{
  "formatVersion": "1.1",
  "component": {
    "group": "org.jetbrains.skiko",
    "module": "skiko-awt-runtime",
    "version": "1.0-SNAPSHOT",
    "attributes": {
      "org.gradle.status": "integration"
    }
  },
  "createdBy": {
    "gradle": {
      "version": "8.13"
    }
  },
  "variants": [
    {
      "name": "jvmRuntimeElements-macos-x64",
      "attributes": {
        "org.gradle.category": "library",
        "org.gradle.dependency.bundling": "external",
        "org.gradle.jvm.environment": "standard-jvm",
        "org.gradle.libraryelements": "jar",
        "org.gradle.native.architecture": "x86-64",
        "org.gradle.native.operatingSystem": "macos",
        "org.gradle.usage": "java-runtime",
        "org.jetbrains.kotlin.platform.type": "jvm"
      },
      "dependencies": [
        {
          "group": "org.jetbrains.skiko",
          "module": "skiko-awt-runtime-macos-x64",
          "version": {
            "requires": "1.0-SNAPSHOT"
          }
        }
      ]
    },
    {
      "name": "jvmRuntimeElements-macos-arm64",
      "attributes": {
        "org.gradle.category": "library",
        "org.gradle.dependency.bundling": "external",
        "org.gradle.jvm.environment": "standard-jvm",
        "org.gradle.libraryelements": "jar",
        "org.gradle.native.architecture": "aarch64",
        "org.gradle.native.operatingSystem": "macos",
        "org.gradle.usage": "java-runtime",
        "org.jetbrains.kotlin.platform.type": "jvm"
      },
      "dependencies": [
        {
          "group": "org.jetbrains.skiko",
          "module": "skiko-awt-runtime-macos-arm64",
          "version": {
            "requires": "1.0-SNAPSHOT"
          }
        }
      ]
    }
  ]
}
```

</details>


<details>
<summary>skiko-awt-runtime.pom</summary>

```xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <!-- This module was also published with a richer model, Gradle metadata,  -->
  <!-- which should be used instead. Do not delete the following line which  -->
  <!-- is to indicate to Gradle or any Gradle module metadata file consumer  -->
  <!-- that they should prefer consuming it instead. -->
  <!-- do_not_remove: published-with-gradle-metadata -->
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.jetbrains.skiko</groupId>
  <artifactId>skiko-awt-runtime</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>pom</packaging>
  <name>Skiko JVM Runtime</name>
  <description>Kotlin Skia bindings</description>
  <url>https://www.github.com/JetBrains/skiko</url>
  <licenses>
    <license>
      <name>The Apache License, Version 2.0</name>
      <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
    </license>
  </licenses>
  <developers>
    <developer>
      <name>Compose Multiplatform Team</name>
      <organization>JetBrains</organization>
      <organizationUrl>https://www.jetbrains.com</organizationUrl>
    </developer>
  </developers>
  <scm>
    <connection>scm:git:https://www.github.com/JetBrains/skiko.git</connection>
    <developerConnection>scm:git:https://www.github.com/JetBrains/skiko.git</developerConnection>
    <url>https://www.github.com/JetBrains/skiko</url>
  </scm>
</project>
```

</details>



Simple example usage:
```kotlin
kotlin {
    target.attributes {
        attribute(ARCHITECTURE_ATTRIBUTE, objects.named(DefaultNativePlatform.getCurrentArchitecture().name))
        attribute(OPERATING_SYSTEM_ATTRIBUTE, objects.named(DefaultNativePlatform.getCurrentOperatingSystem().toFamilyName()))
    }
}

dependencies {
    implementation("org.jetbrains.skiko:skiko-awt:1.0-SNAPSHOT")
    implementation("org.jetbrains.skiko:skiko-awt-runtime:1.0-SNAPSHOT")
}
```

## Motivation
This allows 'intermediate' libraries to express that this dependency
exists (in a single publication) and push down the artifact selection to
consumers. This is an advanced feature and can be leveraged by more
complex builds.

---------
Co-authored-by: 's avatarIgor Demin <igor.demin@jetbrains.com>
parent 40c0ffb0
......@@ -296,120 +296,7 @@ afterEvaluate {
}
}
val emptySourcesJar by tasks.registering(Jar::class) {
archiveClassifier.set("sources")
}
val emptyJavadocJar by tasks.registering(Jar::class) {
archiveClassifier.set("javadoc")
}
publishing {
repositories {
configureEach {
val repoName = name
tasks.register("publishTo${repoName}") {
group = "publishing"
dependsOn(tasks.named("publishAllPublicationsTo${repoName}Repository"))
}
}
maven {
name = "BuildRepo"
url = rootProject.layout.buildDirectory.dir("repo").get().asFile.toURI()
}
maven {
name = "ComposeRepo"
url = uri(skiko.composeRepoUrl)
credentials {
username = skiko.composeRepoUserName
password = skiko.composeRepoKey
}
}
}
publications {
val pomNameForPublication = HashMap<String, String>()
pomNameForPublication["kotlinMultiplatform"] = "Skiko MPP"
kotlin.targets.forEach {
pomNameForPublication[it.name] = "Skiko ${toTitleCase(it.name)}"
}
configureEach {
this as MavenPublication
groupId = SkikoArtifacts.groupId
// Necessary for publishing to Maven Central
artifact(emptyJavadocJar)
pom {
description.set("Kotlin Skia bindings")
licenses {
license {
name.set("The Apache License, Version 2.0")
url.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
}
}
val repoUrl = "https://www.github.com/JetBrains/skiko"
url.set(repoUrl)
scm {
url.set(repoUrl)
val repoConnection = "scm:git:$repoUrl.git"
connection.set(repoConnection)
developerConnection.set(repoConnection)
}
developers {
developer {
name.set("Compose Multiplatform Team")
organization.set("JetBrains")
organizationUrl.set("https://www.jetbrains.com")
}
}
}
}
skikoProjectContext.allJvmRuntimeJars.forEach { entry ->
val os = entry.key.first
val arch = entry.key.second
create<MavenPublication>("skikoJvmRuntime${toTitleCase(os.id)}${toTitleCase(arch.id)}") {
pomNameForPublication[name] = "Skiko JVM Runtime for ${os.name} ${arch.name}"
artifactId = SkikoArtifacts.jvmRuntimeArtifactIdFor(os, arch)
afterEvaluate {
artifact(entry.value.map { it.archiveFile.get() })
artifact(emptySourcesJar)
}
pom.withXml {
asNode().appendNode("dependencies")
.appendNode("dependency").apply {
appendNode("groupId", SkikoArtifacts.groupId)
appendNode("artifactId", SkikoArtifacts.jvmArtifactId)
appendNode("version", skiko.deployVersion)
appendNode("scope", "compile")
}
}
}
}
if (supportWeb) {
create<MavenPublication>("skikoWasmRuntime") {
pomNameForPublication[name] = "Skiko WASM Runtime"
artifactId = SkikoArtifacts.jsWasmArtifactId
artifact(tasks.named("skikoWasmJar").get())
artifact(emptySourcesJar)
}
}
if (supportAndroid) {
pomNameForPublication["androidRelease"] = "Skiko Android Runtime"
}
val publicationsWithoutPomNames = publications.filter { it.name !in pomNameForPublication }
if (publicationsWithoutPomNames.isNotEmpty()) {
error("Publications with unknown POM names: ${publicationsWithoutPomNames.joinToString { "'$it'" }}")
}
configureEach {
this as MavenPublication
pom.name.set(pomNameForPublication[name]!!)
}
}
}
skikoProjectContext.declarePublications()
val mavenCentral = MavenCentralProperties(project)
if (skiko.isTeamcityCIBuild || mavenCentral.signArtifacts) {
......
import org.gradle.api.attributes.Bundling
import org.gradle.api.attributes.Category
import org.gradle.api.attributes.LibraryElements
import org.gradle.api.attributes.Usage
import org.gradle.api.attributes.java.TargetJvmEnvironment
import org.gradle.api.component.SoftwareComponentFactory
import org.gradle.api.publish.PublicationContainer
import org.gradle.api.publish.PublishingExtension
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.jvm.tasks.Jar
import org.gradle.kotlin.dsl.create
import org.gradle.kotlin.dsl.named
import org.gradle.kotlin.dsl.support.serviceOf
import org.gradle.nativeplatform.MachineArchitecture
import org.gradle.nativeplatform.OperatingSystemFamily
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
private val SkikoProjectContext.publishing get() = project.extensions.getByType(PublishingExtension::class.java)
private class SkikoPublishingContext(
val projectContext: SkikoProjectContext,
) {
val project = projectContext.project
val kotlin = projectContext.kotlin
val skiko = projectContext.skiko
val pomNameForPublication: MutableMap<String, String> = HashMap()
fun publishing(configure: PublishingExtension.() -> Unit) {
projectContext.publishing.apply(configure)
}
fun publications(configure: PublicationContainer.() -> Unit) {
projectContext.publishing.publications.apply(configure)
}
}
fun SkikoProjectContext.declarePublications() {
val ctx = SkikoPublishingContext(this)
ctx.configurePublishingRepositories()
ctx.configurePublicationDefaults()
ctx.configureAllJvmRuntimeJarPublications()
ctx.configureAwtRuntimeJarPublication()
ctx.configureWebPublication()
ctx.configureAndroidPublication()
ctx.configurePomNames()
}
private val SkikoPublishingContext.emptySourcesJar
get() = project.tasks.registerOrGetTask<Jar>("emptySourcesJar") {
archiveClassifier.set("sources")
}
private val SkikoPublishingContext.emptyJavadocJar
get() = project.tasks.registerOrGetTask<Jar>("emptyJavadocJar") {
archiveClassifier.set("javadoc")
}
private fun SkikoPublishingContext.configurePublishingRepositories() {
publishing {
repositories {
configureEach {
val repoName = name
project.tasks.register("publishTo${repoName}") {
group = "publishing"
dependsOn(project.tasks.named("publishAllPublicationsTo${repoName}Repository"))
}
}
maven {
name = "BuildRepo"
url = project.rootProject.layout.buildDirectory.dir("repo").get().asFile.toURI()
}
maven {
name = "ComposeRepo"
url = project.uri(skiko.composeRepoUrl)
credentials {
username = skiko.composeRepoUserName
password = skiko.composeRepoKey
}
}
}
}
}
private fun SkikoPublishingContext.configurePublicationDefaults() {
pomNameForPublication["kotlinMultiplatform"] = "Skiko MPP"
kotlin.targets.forEach {
pomNameForPublication[it.name] = "Skiko ${toTitleCase(it.name)}"
}
publishing {
publications.configureEach {
this as MavenPublication
groupId = SkikoArtifacts.groupId
// Necessary for publishing to Maven Central
artifact(emptyJavadocJar)
pom {
description.set("Kotlin Skia bindings")
licenses {
license {
name.set("The Apache License, Version 2.0")
url.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
}
}
val repoUrl = "https://www.github.com/JetBrains/skiko"
url.set(repoUrl)
scm {
url.set(repoUrl)
val repoConnection = "scm:git:$repoUrl.git"
connection.set(repoConnection)
developerConnection.set(repoConnection)
}
developers {
developer {
name.set("Compose Multiplatform Team")
organization.set("JetBrains")
organizationUrl.set("https://www.jetbrains.com")
}
}
}
}
}
}
private fun SkikoPublishingContext.configureAllJvmRuntimeJarPublications() = publications {
projectContext.allJvmRuntimeJars.forEach { entry ->
val os = entry.key.first
val arch = entry.key.second
create("skikoJvmRuntime${toTitleCase(os.id)}${toTitleCase(arch.id)}", MavenPublication::class.java) {
pomNameForPublication[name] = "Skiko JVM Runtime for ${os.name} ${arch.name}"
artifactId = SkikoArtifacts.jvmRuntimeArtifactIdFor(os, arch)
project.afterEvaluate {
artifact(entry.value.map { it.archiveFile.get() })
artifact(emptySourcesJar)
}
pom.withXml {
asNode().appendNode("dependencies")
.appendNode("dependency").apply {
appendNode("groupId", SkikoArtifacts.groupId)
appendNode("artifactId", SkikoArtifacts.jvmArtifactId)
appendNode("version", skiko.deployVersion)
appendNode("scope", "compile")
}
}
}
}
}
/**
* There are several artifacts, providing the native runtime, for each OS and architecture:
* - skiko-awt-runtime-macos-arm64
* - skiko-awt-runtime-macos-x64
* - ...
*
* Each of those artifacts gets published using its own maven coordinates.
* In order to support consumers who would like to express a single dependency on Skiko, this 'uber' publication is created,
* listing each OS and architecture-specific artifact as a dependency to a Gradle variant, distinguised by
* default Gradle attributes.
*
* ```
* org.jetbrains.skiko:skiko-awt-runtime
* - variant awtRuntimeElements-macos-arm64
* - depends on org.jetbrains.skiko:skiko-awt-runtime-macos-arm64
*
* - variant awtRuntimeElements-macos-x64
* - depends on org.jetbrains.skiko:skiko-awt-runtime-macos-x64
* ...
* ```
*
* This allows Gradle consumers to add a single, universal dependency
* ```
* dependencies {
* implementation("org.jetbrains.skiko:skiko-awt-runtime:...")
* }
* ```
*
* which resolves the correct artifact for the current platform.
*/
private fun SkikoPublishingContext.configureAwtRuntimeJarPublication() {
/*
Defines all the child targets that this uber publication can point to
*/
val childAwtRuntimeTargets = listOf(
OS.MacOS to Arch.X64, OS.MacOS to Arch.Arm64,
OS.Linux to Arch.X64, OS.Linux to Arch.Arm64,
OS.Windows to Arch.X64, OS.Windows to Arch.Arm64
)
val allJvmRuntimeVariants = childAwtRuntimeTargets.map { (os, arch) ->
project.configurations.create("awtRuntimeElements-${targetId(os, arch)}").apply {
/* Setup default attributes */
attributes.attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage.JAVA_RUNTIME))
attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category.LIBRARY))
attributes.attribute(Bundling.BUNDLING_ATTRIBUTE, project.objects.named(Bundling.EXTERNAL))
attributes.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, project.objects.named(LibraryElements.JAR))
attributes.attribute(KotlinPlatformType.attribute, KotlinPlatformType.jvm)
attributes.attribute(
TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE,
project.objects.named(TargetJvmEnvironment.STANDARD_JVM)
)
/*
Add OS and architecture attributes to the exposed configuration.
*/
attributes.attribute(
OperatingSystemFamily.OPERATING_SYSTEM_ATTRIBUTE,
project.objects.named(
when (os) {
OS.Linux -> OperatingSystemFamily.LINUX
OS.Windows -> OperatingSystemFamily.WINDOWS
OS.MacOS -> OperatingSystemFamily.MACOS
else -> error("Unsupported OS for awtRuntimeElements: $os")
}
)
)
attributes.attribute(
MachineArchitecture.ARCHITECTURE_ATTRIBUTE,
project.objects.named(
when (arch) {
Arch.X64 -> MachineArchitecture.X86_64
Arch.Arm64 -> MachineArchitecture.ARM64
else -> error("Unsupported arch for awtRuntimeElements: $arch")
}
)
)
/*
Add the dependency to the actual JVM runtime artifact.
*/
dependencies.add(
project.dependencies.create(
SkikoArtifacts.groupId,
SkikoArtifacts.jvmRuntimeArtifactIdFor(os, arch),
skiko.deployVersion
)
)
}
}
/* Create a new software component and add all variants */
val component = project.serviceOf<SoftwareComponentFactory>().adhoc("awtRuntimeElements")
allJvmRuntimeVariants.forEach { variant ->
component.addVariantsFromConfiguration(variant) {
mapToMavenScope("runtime")
}
}
/* Create the actual publication for this */
publications {
create("awtRuntimeElements", MavenPublication::class.java) {
from(component)
pomNameForPublication[name] = "Skiko JVM Runtime"
groupId = SkikoArtifacts.groupId
artifactId = "skiko-awt-runtime"
version = skiko.deployVersion
/*
The entire machinery only works with Gradle attributes;
therefore, we do not add any dependencies to the maven pom file
*/
pom {
withXml {
val dependencyNodes = asElement().getElementsByTagName("dependencies")
for (i in 0 until dependencyNodes.length) {
dependencyNodes.item(i).parentNode.removeChild(dependencyNodes.item(i))
}
}
}
}
}
}
private fun SkikoPublishingContext.configureWebPublication() = publications {
if (!project.supportWeb) return@publications
create("skikoWasmRuntime", MavenPublication::class.java) {
pomNameForPublication[name] = "Skiko WASM Runtime"
artifactId = SkikoArtifacts.jsWasmArtifactId
artifact(project.tasks.named("skikoWasmJar").get())
artifact(emptySourcesJar)
}
}
private fun SkikoPublishingContext.configureAndroidPublication() = publications {
if (!project.supportAndroid) return@publications
pomNameForPublication["androidRelease"] = "Skiko Android Runtime"
}
private fun SkikoPublishingContext.configurePomNames() = publications {
val publicationsWithoutPomNames = this.toList().filter { it.name !in pomNameForPublication }
if (publicationsWithoutPomNames.isNotEmpty()) {
error("Publications with unknown POM names: ${publicationsWithoutPomNames.joinToString { "'${it.name}'" }}")
}
configureEach {
this as MavenPublication
pom.name.set(pomNameForPublication[name]!!)
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment