From fac620df0baac79c0a38e012d7f63b02751ba5b8 Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Thu, 23 Mar 2023 06:07:58 +0100 Subject: [PATCH] [stdlib-mpp] Setup custom publishing, disable default publications KT-56106, KT-53791 Co-authored-by: Anton Lakotka --- libraries/stdlib/build.gradle.kts | 280 ++++++++++++++++++++++++++++- libraries/stdlib/gradle.properties | 1 + 2 files changed, 280 insertions(+), 1 deletion(-) diff --git a/libraries/stdlib/build.gradle.kts b/libraries/stdlib/build.gradle.kts index ee1f9e6517c..6e3a74323b4 100644 --- a/libraries/stdlib/build.gradle.kts +++ b/libraries/stdlib/build.gradle.kts @@ -1,3 +1,5 @@ +import org.gradle.api.internal.component.SoftwareComponentInternal +import org.gradle.api.internal.component.UsageContext import java.nio.file.* import org.gradle.kotlin.dsl.support.serviceOf import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsTargetDsl @@ -5,6 +7,9 @@ import org.jetbrains.kotlin.gradle.tasks.UsesKotlinJavaToolchain import plugins.configureDefaultPublishing import org.gradle.jvm.tasks.Jar import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile +import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages +import plugins.configureKotlinPomAttributes plugins { id("kotlin-multiplatform") @@ -702,4 +707,277 @@ tasks { } } -configureDefaultPublishing() \ No newline at end of file + +// region ==== Publishing ==== + +configureDefaultPublishing() + +open class ComponentsFactoryAccess +@javax.inject.Inject +constructor(val factory: SoftwareComponentFactory) + +val componentFactory = objects.newInstance().factory + +val emptyJavadocJar by tasks.creating(org.gradle.api.tasks.bundling.Jar::class) { + archiveClassifier.set("javadoc") +} + +publishing { + val artifactBaseName = base.archivesName.get() + configureMultiModuleMavenPublishing { + val rootModule = module("rootModule") { + mavenPublication { + artifactId = artifactBaseName + configureKotlinPomAttributes(project, "Kotlin Standard Library") + } + + // creates a variant from existing configuration or creates new one + variant("jvmApiElements") + variant("jvmRuntimeElements") + variant("jvmSourcesElements") + + variant("metadataApiElements") + variant("metadataSourcesElementsFromJvm") { + name = "metadataSourcesElements" + configuration { + // to avoid clash in Gradle 8+ with metadataSourcesElements configuration with the same attributes + isCanBeConsumed = false + } + attributes { + copyAttributes(from = project.configurations["metadataSourcesElements"].attributes, to = this) + } + artifact(tasks["sourcesJar"]) { + classifier = "common-sources" + } + } + variant("nativeApiElements") { + attributes { + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY)) + attribute(TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE, objects.named("non-jvm")) + attribute(Usage.USAGE_ATTRIBUTE, objects.named(KotlinUsages.KOTLIN_API)) + attribute(KotlinPlatformType.attribute, KotlinPlatformType.native) + } + } + // empty variant for wasm + // TODO: replace with wasm target + variant("wasmApiElements") { + attributes { + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY)) + attribute(TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE, objects.named("non-jvm")) + attribute(Usage.USAGE_ATTRIBUTE, objects.named(KotlinUsages.KOTLIN_API)) + attribute(KotlinPlatformType.attribute, KotlinPlatformType.wasm) + } + } + variant("wasmRuntimeElements") { + attributes { + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY)) + attribute(TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE, objects.named("non-jvm")) + attribute(Usage.USAGE_ATTRIBUTE, objects.named(KotlinUsages.KOTLIN_RUNTIME)) + attribute(KotlinPlatformType.attribute, KotlinPlatformType.wasm) + } + } + } + + // TODO: decide what should be published in kotlin-stdlib-common +// val common = module("commonModule") { +// mavenPublication { +// groupId = "org.example" +// artifactId = "sample-lib-common" +// } +// variant("commonMainMetadataElements") { +// // Multiplatform KGP already added klib artifact to metadataApiElements +// // attributes { kotlinLegacyMetadataAttributes() } +// } +// } + val js = module("jsModule") { + mavenPublication { + artifactId = "$artifactBaseName-js" + configureKotlinPomAttributes(project, "Kotlin Standard Library for JS") + } + variant("jsApiElements") + variant("jsRuntimeElements") + variant("jsV1ApiElements") + variant("jsV1RuntimeElements") + variant("jsSourcesElements") + } + + // Makes all variants from accompanying artifacts visible through `available-at` + rootModule.include(js) + } + + publications { + withType { + artifact(emptyJavadocJar) + } + } +} + +fun copyAttributes(from: AttributeContainer, to: AttributeContainer,) { + // capture type argument T + fun copyOneAttribute(from: AttributeContainer, to: AttributeContainer, key: Attribute) { + val value = checkNotNull(from.getAttribute(key)) + to.attribute(key, value) + } + for (key in from.keySet()) { + copyOneAttribute(from, to, key) + } +} + +class MultiModuleMavenPublishingConfiguration() { + val modules = mutableMapOf() + + class Module(val name: String) { + val variants = mutableMapOf() + val includes = mutableSetOf() + + class Variant( + val configurationName: String + ) { + var name: String = configurationName + val attributesConfigurations = mutableListOf Unit>() + fun attributes(code: AttributeContainer.() -> Unit) { + attributesConfigurations += code + } + + val artifactsWithConfigurations = mutableListOf Unit>>() + fun artifact(file: Any, code: ConfigurablePublishArtifact.() -> Unit = {}) { + artifactsWithConfigurations += file to code + } + + val configurationConfigurations = mutableListOf Unit>() + fun configuration(code: Configuration.() -> Unit) { + configurationConfigurations += code + } + + val variantDetailsConfigurations = mutableListOf Unit>() + fun configureVariantDetails(code: ConfigurationVariantDetails.() -> Unit) { + variantDetailsConfigurations += code + } + } + + val mavenPublicationConfigurations = mutableListOf Unit>() + fun mavenPublication(code: MavenPublication.() -> Unit) { + mavenPublicationConfigurations += code + } + + fun variant(fromConfigurationName: String, code: Variant.() -> Unit = {}): Variant { + val variant = variants.getOrPut(fromConfigurationName) { Variant(fromConfigurationName) } + variant.code() + return variant + } + + fun include(vararg modules: Module) { + includes.addAll(modules) + } + } + + fun module(name: String, code: Module.() -> Unit): Module { + val module = modules.getOrPut(name) { Module(name) } + module.code() + return module + } +} + +fun configureMultiModuleMavenPublishing(code: MultiModuleMavenPublishingConfiguration.() -> Unit) { + val publishingConfiguration = MultiModuleMavenPublishingConfiguration() + publishingConfiguration.code() + + val components = publishingConfiguration + .modules + .mapValues { (_, module) -> project.createModulePublication(module) } + + val componentsWithExternals = publishingConfiguration + .modules + .filter { (_, module) -> module.includes.isNotEmpty() } + .mapValues { (moduleName, module) -> + val mainComponent = components[moduleName] ?: error("Component with name $moduleName wasn't created") + val externalComponents = module.includes + .map { components[it.name] ?: error("Component with name ${it.name} wasn't created") } + .toSet() + ComponentWithExternalVariants(mainComponent, externalComponents) + } + + // override some components wih items from componentsWithExternals + val mergedComponents = components + componentsWithExternals + + val publicationsContainer = publishing.publications + for ((componentName, component) in mergedComponents) { + publicationsContainer.create(componentName) { + from(component) + val module = publishingConfiguration.modules[componentName]!! + module.mavenPublicationConfigurations.forEach { configure -> configure() } + } + } +} + + +fun Project.createModulePublication(module: MultiModuleMavenPublishingConfiguration.Module): SoftwareComponent { + val component = componentFactory.adhoc(module.name) + module.variants.values.forEach { addVariant(component, it) } + + val newNames = module.variants.map { it.key to it.value.name }.filter { it.first != it.second }.toMap() + return if (newNames.isNotEmpty()) { + ComponentWithRenamedVariants(newNames, component as SoftwareComponentInternal) + } else { + component + } +} + +fun Project.addVariant(component: AdhocComponentWithVariants, variant: MultiModuleMavenPublishingConfiguration.Module.Variant) { + val configuration = configurations.getOrCreate(variant.configurationName) + configuration.apply { + isCanBeResolved = false + isCanBeConsumed = true + + variant.attributesConfigurations.forEach { configure -> attributes.configure() } + } + + for ((artifactNotation, configure) in variant.artifactsWithConfigurations) { + artifacts.add(configuration.name, artifactNotation) { + configure() + } + } + + for (configure in variant.configurationConfigurations) { + configuration.apply(configure) + } + + component.addVariantsFromConfiguration(configuration) { + variant.variantDetailsConfigurations.forEach { configure -> configure() } + } +} + +private class RenamedVariant(val newName: String, context: UsageContext) : UsageContext by context { + override fun getName(): String = newName +} + +private class ComponentWithRenamedVariants( + val newNames: Map, + private val base: SoftwareComponentInternal +): SoftwareComponentInternal by base { + + override fun getName(): String = base.name + override fun getUsages(): Set { + return base.usages.map { + val newName = newNames[it.name] + if (newName != null) { + RenamedVariant(newName, it) + } else { + it + } + }.toSet() + } +} + +private class ComponentWithExternalVariants( + private val mainComponent: SoftwareComponent, + private val externalComponents: Set +) : ComponentWithVariants, SoftwareComponentInternal { + override fun getName(): String = mainComponent.name + + override fun getUsages(): Set = (mainComponent as SoftwareComponentInternal).usages + + override fun getVariants(): Set = externalComponents +} + +// endregion \ No newline at end of file diff --git a/libraries/stdlib/gradle.properties b/libraries/stdlib/gradle.properties index 0a6a346cf1b..39509d3f536 100644 --- a/libraries/stdlib/gradle.properties +++ b/libraries/stdlib/gradle.properties @@ -1,2 +1,3 @@ kotlin.mpp.enableGranularSourceSetsMetadata=true kotlin.mpp.enableCompatibilityMetadataVariant=true +kotlin.internal.mpp.createDefaultMultiplatformPublications=false \ No newline at end of file