blob: be36677b3555e3dd4b819b4be1f333a1168f62e1 [file] [log] [blame] [edit]
@file:Suppress("UNUSED_VARIABLE", "NAME_SHADOWING")
import org.gradle.api.internal.component.SoftwareComponentInternal
import org.gradle.api.internal.component.UsageContext
import org.gradle.jvm.tasks.Jar
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages
import org.jetbrains.kotlin.gradle.targets.js.d8.D8RootPlugin
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinTargetWithNodeJsDsl
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinWasmTargetDsl
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrLink
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
import org.jetbrains.kotlin.gradle.tasks.UsesKotlinJavaToolchain
import plugins.configureDefaultPublishing
import plugins.configureKotlinPomAttributes
import kotlin.io.path.copyTo
plugins {
id("kotlin-multiplatform")
`maven-publish`
signing
}
description = "Kotlin Standard Library"
configureJvmToolchain(JdkMajorVersion.JDK_1_8)
fun resolvingConfiguration(name: String, configure: Action<Configuration> = Action {}) =
configurations.create(name) {
isCanBeResolved = true
isCanBeConsumed = false
configure(this)
}
fun outgoingConfiguration(name: String, configure: Action<Configuration> = Action {}) =
configurations.create(name) {
isCanBeResolved = false
isCanBeConsumed = true
configure(this)
}
val configurationBuiltins = resolvingConfiguration("builtins") {
attributes.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, project.objects.named(LibraryElements.JAR))
}
dependencies {
configurationBuiltins(project(":core:builtins"))
}
val builtinsDir = "${rootDir}/core/builtins"
val builtinsSrcDir = "${buildDir}/src/builtin-sources"
val jsDir = "${projectDir}/js"
val jsBuiltinsSrcDir = "${buildDir}/src/js-builtin-sources"
val commonOptIns = listOf(
"kotlin.ExperimentalMultiplatform",
"kotlin.contracts.ExperimentalContracts",
)
val commonTestOptIns = listOf(
"kotlin.ExperimentalUnsignedTypes",
"kotlin.ExperimentalStdlibApi",
"kotlin.io.encoding.ExperimentalEncodingApi",
)
kotlin {
val renderDiagnosticNames by extra(project.kotlinBuildProperties.renderDiagnosticNames)
val diagnosticNamesArg = if (renderDiagnosticNames) "-Xrender-internal-diagnostic-names" else null
explicitApi()
metadata {
compilations {
all {
compileTaskProvider.configure {
kotlinOptions {
freeCompilerArgs = listOfNotNull(
"-Xallow-kotlin-package",
"-module-name", "kotlin-stdlib-common",
"-Xexpect-actual-classes",
"-Xexplicit-api=strict",
diagnosticNamesArg,
)
}
// workaround for compiling legacy MPP metadata, remove when this compilation is not needed anymore
// restate the list of opt-ins
compilerOptions.optIn.addAll(commonOptIns)
}
}
}
}
jvm {
withJava()
compilations {
val compileOnlyDeclarations by creating {
compileTaskProvider.configure {
kotlinOptions {
freeCompilerArgs = listOfNotNull("-Xallow-kotlin-package", diagnosticNamesArg)
}
}
}
val main by getting {
compileTaskProvider.configure {
this as UsesKotlinJavaToolchain
kotlinJavaToolchain.toolchain.use(getToolchainLauncherFor(JdkMajorVersion.JDK_1_6))
kotlinOptions {
moduleName = "kotlin-stdlib"
jvmTarget = "1.8"
// providing exhaustive list of args here
freeCompilerArgs = listOfNotNull(
"-Xallow-kotlin-package",
"-Xexpect-actual-classes",
"-Xmultifile-parts-inherit",
"-Xuse-14-inline-classes-mangling-scheme",
"-Xbuiltins-from-sources",
"-Xno-new-java-annotation-targets",
diagnosticNamesArg,
)
}
}
defaultSourceSet {
dependencies {
compileOnly(compileOnlyDeclarations.output.allOutputs)
}
}
}
val mainJdk7 by creating {
associateWith(main)
compileTaskProvider.configure {
this as UsesKotlinJavaToolchain
kotlinJavaToolchain.toolchain.use(getToolchainLauncherFor(JdkMajorVersion.JDK_1_7))
kotlinOptions {
moduleName = "kotlin-stdlib-jdk7"
jvmTarget = "1.8"
freeCompilerArgs = listOfNotNull(
"-Xallow-kotlin-package",
"-Xmultifile-parts-inherit",
"-Xno-new-java-annotation-targets",
"-Xexplicit-api=strict",
diagnosticNamesArg,
)
}
}
}
val mainJdk8 by creating {
associateWith(main)
associateWith(mainJdk7)
compileTaskProvider.configure {
kotlinOptions {
moduleName = "kotlin-stdlib-jdk8"
freeCompilerArgs = listOfNotNull(
"-Xallow-kotlin-package",
"-Xmultifile-parts-inherit",
"-Xno-new-java-annotation-targets",
"-Xexplicit-api=strict",
diagnosticNamesArg,
)
}
}
}
project.sourceSets.create("java9") {
java.srcDir("jvm/java9")
}
configureJava9Compilation("kotlin.stdlib", listOf(
main.output.allOutputs,
mainJdk7.output.allOutputs,
mainJdk8.output.allOutputs,
), main.configurations.compileDependencyConfiguration)
val test by getting {
associateWith(mainJdk7)
associateWith(mainJdk8)
compileTaskProvider.configure {
kotlinOptions {
freeCompilerArgs += listOf(
"-Xallow-kotlin-package", // TODO: maybe rename test packages
"-Xexpect-actual-classes",
)
if (kotlinBuildProperties.useFir) {
freeCompilerArgs += "-Xuse-k2"
}
// This is needed for JavaTypeTest; typeOf for non-reified type parameters doesn't work otherwise, for implementation reasons.
freeCompilerArgs -= "-Xno-optimized-callable-references"
}
}
}
val longRunningTest by creating {
associateWith(main)
associateWith(mainJdk7)
associateWith(mainJdk8)
}
}
}
js(IR) {
if (!kotlinBuildProperties.isTeamcityBuild) {
browser {}
}
nodejs {
testTask {
useMocha {
timeout = "10s"
}
}
}
compilations {
all {
kotlinOptions {
freeCompilerArgs += "-Xallow-kotlin-package"
}
}
val main by getting
main.apply {
kotlinOptions {
freeCompilerArgs += listOfNotNull(
"-Xir-module-name=kotlin",
"-Xexpect-actual-classes",
diagnosticNamesArg,
)
if (!kotlinBuildProperties.disableWerror) {
allWarningsAsErrors = true
}
}
}
}
}
D8RootPlugin.apply(rootProject).version = v8Version
fun KotlinWasmTargetDsl.commonWasmTargetConfiguration() {
(this as KotlinTargetWithNodeJsDsl).nodejs()
compilations {
all {
kotlinOptions.freeCompilerArgs += listOfNotNull(
"-Xallow-kotlin-package",
"-Xexpect-actual-classes",
diagnosticNamesArg
)
}
val main by getting
main.apply {
kotlinOptions.freeCompilerArgs += "-Xir-module-name=kotlin"
kotlinOptions.allWarningsAsErrors = true
}
}
}
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
commonWasmTargetConfiguration()
}
@OptIn(ExperimentalWasmDsl::class)
wasmWasi {
commonWasmTargetConfiguration()
}
if (kotlinBuildProperties.isInIdeaSync) {
val hostOs = System.getProperty("os.name")
val isMingwX64 = hostOs.startsWith("Windows")
val nativeTarget = when {
hostOs == "Mac OS X" -> macosX64("native")
hostOs == "Linux" -> linuxX64("native")
isMingwX64 -> mingwX64("native")
else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
}
nativeTarget.apply {
compilations.all {
kotlinOptions {
freeCompilerArgs += listOf(
"-Xallow-kotlin-package",
"-Xexpect-actual-classes",
"-nostdlib",
)
}
}
}
}
sourceSets {
fun <TP : TaskProvider<*>> TP.requiredForImport(): TP {
tasks.findByName("prepareKotlinIdeaImport")?.dependsOn(this)
return this
}
all {
kotlin.setSrcDirs(emptyList<File>())
}
commonMain {
val prepareCommonSources by tasks.registering {
dependsOn(":prepare:build.version:writeStdlibVersion")
}
kotlin {
srcDir("common/src")
srcDir(files("src").builtBy(prepareCommonSources))
srcDir("unsigned/src")
if (!kotlinBuildProperties.isInIdeaSync) {
srcDir("$builtinsDir/src/kotlin/internal")
}
if (kotlinBuildProperties.isInIdeaSync) {
// required for correct resolution of builtin classes in common code in K2 IDE
srcDir("$builtinsDir/src")
}
}
}
commonTest {
dependencies {
// TODO: use project dependency when kotlin-test is migrated
compileOnly("org.jetbrains.kotlin:kotlin-test-common:$bootstrapKotlinVersion")
compileOnly("org.jetbrains.kotlin:kotlin-test-annotations-common:$bootstrapKotlinVersion")
// compileOnly(project(":kotlin-test:kotlin-test-common"))
// compileOnly(project(":kotlin-test:kotlin-test-annotations-common"))
}
kotlin {
srcDir("common/test")
srcDir("test")
}
}
val jvmCompileOnlyDeclarations by getting {
kotlin.srcDir("jvm/compileOnly")
}
val jvmMain by getting {
project.configurations.getByName("jvmMainCompileOnly").extendsFrom(configurationBuiltins)
dependencies {
api("org.jetbrains:annotations:13.0")
}
val jvmSrcDirs = listOfNotNull(
"jvm/src",
"jvm/runtime",
"$builtinsDir/src".takeUnless { kotlinBuildProperties.isInIdeaSync }
)
project.sourceSets["main"].java.srcDirs(*jvmSrcDirs.toTypedArray())
kotlin.setSrcDirs(jvmSrcDirs)
kotlin.exclude("kotlin/internal/InternalAnnotations.kt")
}
val jvmMainJdk7 by getting {
kotlin.srcDir("jdk7/src")
}
val jvmMainJdk8 by getting {
kotlin.srcDir("jdk8/src")
}
val jvmTest by getting {
languageSettings {
optIn("kotlin.io.path.ExperimentalPathApi")
}
dependencies {
api(project(":kotlin-test:kotlin-test-junit"))
}
kotlin.srcDir("jvm/test")
kotlin.srcDir("jdk7/test")
kotlin.srcDir("jdk8/test")
}
val jvmLongRunningTest by getting {
dependencies {
api(project(":kotlin-test:kotlin-test-junit"))
}
kotlin.srcDir("jvm/testLongRunning")
}
val jsMain by getting {
val prepareJsIrMainSources by tasks.registering(Sync::class)
kotlin {
srcDir(prepareJsIrMainSources.requiredForImport())
srcDir("$jsDir/builtins")
srcDir("$jsDir/runtime")
srcDir("$jsDir/src").apply {
exclude("kotlin/browser")
exclude("kotlin/dom")
exclude("kotlinx")
exclude("org.w3c")
}
}
prepareJsIrMainSources.configure {
val unimplementedNativeBuiltIns =
(file("$builtinsDir/native/kotlin/").list()!!.toSortedSet() - file("$jsDir/builtins/").list()!!)
.map { "core/builtins/native/kotlin/$it" }
// TODO: try to reuse absolute paths defined in the beginning
val sources = listOf(
"core/builtins/src/kotlin/",
) + unimplementedNativeBuiltIns
val excluded = listOf(
// JS-specific optimized version of emptyArray() already defined
"core/builtins/src/kotlin/ArrayIntrinsics.kt",
// included in common
"core/builtins/src/kotlin/internal/**",
)
sources.forEach { path ->
from("$rootDir/$path") {
into(path.dropLastWhile { it != '/' })
excluded.filter { it.startsWith(path) }.forEach {
exclude(it.substring(path.length))
}
}
}
into(jsBuiltinsSrcDir)
// Required to compile native builtins with the rest of runtime
val builtInsHeader = """@file:Suppress(
"NON_ABSTRACT_FUNCTION_WITH_NO_BODY",
"MUST_BE_INITIALIZED_OR_BE_ABSTRACT",
"EXTERNAL_TYPE_EXTENDS_NON_EXTERNAL_TYPE",
"PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED",
"WRONG_MODIFIER_TARGET",
"UNUSED_PARAMETER"
)
"""
doLast {
unimplementedNativeBuiltIns.forEach { path ->
val file = File("$destinationDir/$path")
val sourceCode = builtInsHeader + file.readText()
file.writeText(sourceCode)
}
}
}
}
val jsTest by getting {
dependencies {
api(project(":kotlin-test:kotlin-test-js-ir"))
}
kotlin.srcDir("${jsDir}/test")
}
val nativeWasmMain by creating {
dependsOn(commonMain.get())
kotlin.srcDir("native-wasm/src")
}
val nativeWasmTest by creating {
dependsOn(commonTest.get())
kotlin.srcDir("native-wasm/test")
}
val wasmCommonMain by creating {
dependsOn(nativeWasmMain)
val prepareWasmBuiltinSources by tasks.registering(Sync::class)
kotlin {
srcDir(prepareWasmBuiltinSources.requiredForImport())
srcDir("wasm/builtins")
srcDir("wasm/internal")
srcDir("wasm/runtime")
srcDir("wasm/src")
srcDir("wasm/stubs")
}
prepareWasmBuiltinSources.configure {
val unimplementedNativeBuiltIns =
(file("$rootDir/core/builtins/native/kotlin/").list().toSortedSet() - file("wasm/builtins/kotlin/").list())
.map { "core/builtins/native/kotlin/$it" }
val sources = listOf(
"core/builtins/src/kotlin/"
) + unimplementedNativeBuiltIns
val excluded = listOf(
// included in commonMain
"internal/InternalAnnotations.kt",
// JS-specific optimized version of emptyArray() already defined
"ArrayIntrinsics.kt",
// Included with K/N collections
"Collections.kt", "Iterator.kt", "Iterators.kt"
)
sources.forEach { path ->
from("$rootDir/$path") {
into(path.dropLastWhile { it != '/' })
excluded.forEach {
exclude(it)
}
}
}
into("$buildDir/src/wasm-builtin-sources")
}
}
val wasmCommonTest by creating {
dependsOn(nativeWasmTest)
kotlin {
srcDir("wasm/test")
}
}
val wasmJsMain by getting {
dependsOn(wasmCommonMain)
kotlin {
srcDir("wasm/js/builtins")
srcDir("wasm/js/internal")
srcDir("wasm/js/src")
}
}
val wasmJsTest by getting {
dependsOn(wasmCommonTest)
dependencies {
api(project(":kotlin-test:kotlin-test-wasm-js"))
}
kotlin {
srcDir("wasm/js/test")
}
}
val wasmWasiMain by getting {
dependsOn(wasmCommonMain)
kotlin {
srcDir("wasm/wasi/builtins")
srcDir("wasm/wasi/src")
}
languageSettings {
optIn("kotlin.wasm.unsafe.UnsafeWasmMemoryApi")
}
}
val wasmWasiTest by getting {
dependsOn(wasmCommonTest)
dependencies {
api(project(":kotlin-test:kotlin-test-wasm-wasi"))
}
kotlin {
srcDir("wasm/wasi/test")
}
}
if (kotlinBuildProperties.isInIdeaSync) {
val nativeKotlinTestCommon by creating {
dependsOn(commonMain.get())
val prepareKotlinTestCommonNativeSources by tasks.registering(Sync::class) {
from("../kotlin.test/common/src/main/kotlin")
from("../kotlin.test/annotations-common/src/main/kotlin")
into("$buildDir/src/native-kotlin-test-common-sources")
}
kotlin {
srcDir(prepareKotlinTestCommonNativeSources.requiredForImport())
}
}
val nativeMain by getting {
dependsOn(nativeWasmMain)
dependsOn(nativeKotlinTestCommon)
kotlin {
srcDir("$rootDir/kotlin-native/runtime/src/main/kotlin")
srcDir("$rootDir/kotlin-native/Interop/Runtime/src/main/kotlin")
srcDir("$rootDir/kotlin-native/Interop/Runtime/src/native/kotlin")
}
languageSettings {
optIn("kotlin.native.internal.InternalForKotlinNative")
}
}
val nativeTest by getting {
dependsOn(nativeWasmTest)
kotlin {
srcDir("$rootDir/kotlin-native/runtime/test")
}
languageSettings {
optIn("kotlin.experimental.ExperimentalNativeApi")
optIn("kotlin.native.ObsoleteNativeApi")
optIn("kotlin.native.runtime.NativeRuntimeApi")
optIn("kotlin.native.internal.InternalForKotlinNative")
optIn("kotlinx.cinterop.ExperimentalForeignApi")
optIn("kotlin.native.concurrent.ObsoleteWorkersApi")
}
}
}
all sourceSet@ {
languageSettings {
// TODO: progressiveMode = use build property 'test.progressive.mode'
if (this@sourceSet == jvmCompileOnlyDeclarations) {
return@languageSettings
}
commonOptIns.forEach { optIn(it) }
if (this@sourceSet.name.endsWith("Test")) {
commonTestOptIns.forEach { optIn(it) }
}
}
}
}
}
dependencies {
val jvmMainApi by configurations.getting
val commonMainMetadataElementsWithClassifier by configurations.creating
val metadataApiElements by configurations.getting
val nativeApiElements = configurations.maybeCreate("nativeApiElements")
constraints {
// there is no dependency anymore from kotlin-stdlib to kotlin-stdlib-common,
// but use this constraint to align it if another library brings it transitively
jvmMainApi(project(":kotlin-stdlib-common"))
commonMainMetadataElementsWithClassifier(project(":kotlin-stdlib-common"))
metadataApiElements(project(":kotlin-stdlib-common"))
nativeApiElements(project(":kotlin-stdlib-common"))
// to avoid split package and duplicate classes on classpath after moving them from these artifacts in 1.8.0
jvmMainApi("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.0")
jvmMainApi("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.0")
}
}
tasks {
val metadataJar by existing(Jar::class) {
archiveAppendix.set("metadata")
}
val sourcesJar by existing(Jar::class) {
archiveAppendix.set("metadata")
}
val jvmJar by existing(Jar::class) {
dependsOn(configurationBuiltins)
duplicatesStrategy = DuplicatesStrategy.FAIL
archiveAppendix.set(null as String?)
manifestAttributes(manifest, "Main", multiRelease = true)
manifest.attributes(mapOf("Implementation-Title" to "kotlin-stdlib"))
from { zipTree(configurationBuiltins.singleFile) }
from(kotlin.jvm().compilations["mainJdk7"].output.allOutputs)
from(kotlin.jvm().compilations["mainJdk8"].output.allOutputs)
from(project.sourceSets["java9"].output)
}
val jvmSourcesJar by existing(Jar::class) {
duplicatesStrategy = DuplicatesStrategy.FAIL
archiveAppendix.set(null as String?)
into("jvmMain") {
from("${rootDir}/core/builtins/native")
from(kotlin.sourceSets["jvmMainJdk7"].kotlin) {
into("jdk7")
}
from(kotlin.sourceSets["jvmMainJdk8"].kotlin) {
into("jdk8")
}
}
}
dexMethodCount {
from(jvmJar)
ownPackages.set(listOf("kotlin"))
}
val jsJar by existing(Jar::class) {
manifestAttributes(manifest, "Main")
manifest.attributes(mapOf("Implementation-Title" to "kotlin-stdlib-js"))
}
val jsJarForTests by registering(Copy::class) {
from(jsJar)
rename { _ -> "full-runtime.klib" }
// some tests expect stdlib-js klib in this location
into(rootProject.buildDir.resolve("js-ir-runtime"))
}
val jsRearrangedSourcesJar by registering(Jar::class) {
archiveClassifier.set("js-sources")
archiveVersion.set("")
destinationDirectory.set(file("$buildDir/lib"))
includeEmptyDirs = false
duplicatesStrategy = DuplicatesStrategy.FAIL
into("commonMain") {
from(kotlin.sourceSets.commonMain.get().kotlin)
}
into("jsMain") {
from(kotlin.sourceSets["jsMain"].kotlin) {
// just to depend on source-generating tasks
exclude("**")
}
from("${rootDir}/core/builtins/native/kotlin") {
into("kotlin")
include("Comparable.kt")
include("Enum.kt")
}
from("$jsBuiltinsSrcDir/core/builtins/native") {
exclude("kotlin/Comparable.kt")
}
from("$jsBuiltinsSrcDir/core/builtins/src")
from("$jsBuiltinsSrcDir/libraries/stdlib/js/src")
from("$jsDir/builtins") {
into("kotlin")
exclude("Enum.kt")
}
from("$jsDir/runtime") {
into("runtime")
}
from("$jsDir/src") {
include("**/*.kt")
}
}
}
val jsSourcesJar by existing(Jar::class) {
val jsSourcesJarFile = jsRearrangedSourcesJar.get().archiveFile
inputs.file(jsSourcesJarFile)
doLast {
jsSourcesJarFile.get().asFile.toPath().copyTo(archiveFile.get().asFile.toPath(), overwrite = true)
}
}
val wasmJsJar by existing(Jar::class) {
manifestAttributes(manifest, "Main")
}
val wasmWasiJar by existing(Jar::class) {
manifestAttributes(manifest, "Main")
}
artifacts {
val distJsJar = configurations.create("distJsJar")
val distJsSourcesJar = configurations.create("distJsSourcesJar")
val distJsKlib = configurations.create("distJsKlib")
add(distJsSourcesJar.name, jsSourcesJar)
add(distJsKlib.name, jsJar)
}
val jvmTest by existing(Test::class)
listOf(JdkMajorVersion.JDK_9_0, JdkMajorVersion.JDK_10_0, JdkMajorVersion.JDK_11_0).forEach { jvmVersion ->
val jvmVersionTest = register("jvm${jvmVersion.majorVersion}Test", Test::class) {
group = "verification"
javaLauncher.set(getToolchainLauncherFor(jvmVersion))
// additional test tasks are not configured automatically same as the main test task
// after KMP plugin stopped applying java plugin
classpath = jvmTest.get().classpath
testClassesDirs = jvmTest.get().testClassesDirs
}
check.configure { dependsOn(jvmVersionTest) }
}
val jvmLongRunningTest by registering(Test::class) {
val compilation = kotlin.jvm().compilations["longRunningTest"]
classpath = compilation.compileDependencyFiles + compilation.runtimeDependencyFiles + compilation.output.allOutputs
testClassesDirs = compilation.output.classesDirs
}
if (project.hasProperty("kotlin.stdlib.test.long.running")) {
check.configure { dependsOn(jvmLongRunningTest) }
}
listOf("Js", "Wasi").forEach { wasmTarget ->
named("compileTestKotlinWasm$wasmTarget", AbstractKotlinCompile::class) {
// TODO: fix all warnings, enable -Werror
compilerOptions.suppressWarnings = true
// exclusions due to KT-51647
exclude("generated/minmax/*")
exclude("collections/MapTest.kt")
}
named("compileTestDevelopmentExecutableKotlinWasm$wasmTarget", KotlinJsIrLink::class) {
kotlinOptions.freeCompilerArgs += listOf("-Xwasm-enable-array-range-checks")
}
}
val wasmWasiNodeTest by existing {
if (!kotlinBuildProperties.getBoolean("kotlin.stdlib.wasi.tests")) {
enabled = false
}
}
/*
We are using a custom 'kotlin-project-structure-metadata' to ensure 'nativeApiElements' lists 'commonMain' as source set
*/
val generateProjectStructureMetadata by existing {
val outputFile = file("build/kotlinProjectStructureMetadata/kotlin-project-structure-metadata.json")
val outputTestFile = file("kotlin-project-structure-metadata.beforePatch.json")
val patchedFile = file("kotlin-project-structure-metadata.json")
inputs.file(patchedFile)
inputs.file(outputTestFile)
doLast {
/*
Check that the generated 'outputFile' by default matches our expectations stored in the .beforePatch file
This will fail if the kotlin-project-structure-metadata.json file would change unnoticed (w/o updating our patched file)
*/
run {
val outputFileText = outputFile.readText().trim()
val expectedFileContent = outputTestFile.readText().trim()
if (outputFileText != expectedFileContent)
error(
"${outputFile.path} file content does not match expected content\n\n" +
"expected:\n\n$expectedFileContent\n\nactual:\n\n$outputFileText"
)
}
patchedFile.copyTo(outputFile, overwrite = true)
}
}
}
// region ==== Publishing ====
configureDefaultPublishing()
open class ComponentsFactoryAccess
@javax.inject.Inject
constructor(val factory: SoftwareComponentFactory)
val componentFactory = objects.newInstance<ComponentsFactoryAccess>().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")
artifact(emptyJavadocJar)
}
// creates a variant from existing configuration or creates new one
variant("jvmApiElements")
variant("jvmRuntimeElements")
variant("jvmSourcesElements")
variant("metadataApiElements")
variant("commonMainMetadataElementsWithClassifier") {
name = "commonMainMetadataElements"
configuration {
isCanBeConsumed = false
}
attributes {
copyAttributes(from = project.configurations["commonMainMetadataElements"].attributes, to = this)
}
artifact(tasks["metadataJar"]) {
classifier = "common"
}
}
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)
}
}
}
// we cannot publish legacy common artifact with metadata in kotlin-stdlib-common
// because it will cause problems in explicitly configured stdlib dependencies in project
// val common = module("commonModule") {
// mavenPublication {
// artifactId = "$artifactBaseName-common"
// configureKotlinPomAttributes(project, "Kotlin Common Standard Library (for compatibility with legacy multiplatform)")
// artifact(tasks["sourcesJar"]) // publish sources.jar just for maven, without including it in Gradle metadata
// }
// variant("commonMainMetadataElements")
// }
val js = module("jsModule") {
mavenPublication {
artifactId = "$artifactBaseName-js"
configureKotlinPomAttributes(project, "Kotlin Standard Library for JS", packaging = "klib")
}
variant("jsApiElements")
variant("jsRuntimeElements")
variant("jsSourcesElements")
}
val wasmJs = module("wasmJsModule") {
mavenPublication {
artifactId = "$artifactBaseName-wasm-js"
configureKotlinPomAttributes(project, "Kotlin Standard Library for experimental WebAssembly JS platform", packaging = "klib")
}
variant("wasmJsApiElements")
variant("wasmJsRuntimeElements")
variant("wasmJsSourcesElements")
}
val wasmWasi = module("wasmWasiModule") {
mavenPublication {
artifactId = "$artifactBaseName-wasm-wasi"
configureKotlinPomAttributes(project, "Kotlin Standard Library for experimental WebAssembly WASI platform", packaging = "klib")
}
variant("wasmWasiApiElements")
variant("wasmWasiRuntimeElements")
variant("wasmWasiSourcesElements")
}
// Makes all variants from accompanying artifacts visible through `available-at`
rootModule.include(js, wasmJs, wasmWasi)
}
publications {
val rootModule by existing(MavenPublication::class)
val jsModule by existing(MavenPublication::class)
configureSbom("Main", "kotlin-stdlib", setOf("jvmRuntimeClasspath"), rootModule)
configureSbom("Js", "kotlin-stdlib-js", setOf("jsRuntimeClasspath"), jsModule)
val wasmJsModule by existing(MavenPublication::class)
val wasmWasiModule by existing(MavenPublication::class)
configureSbom("Wasm-Js", "kotlin-stdlib-wasm-js", setOf("wasmJsRuntimeClasspath"), wasmJsModule)
configureSbom("Wasm-Wasi", "kotlin-stdlib-wasm-wasi", setOf("wasmWasiRuntimeClasspath"), wasmWasiModule)
}
}
fun copyAttributes(from: AttributeContainer, to: AttributeContainer,) {
// capture type argument T
fun <T : Any> copyOneAttribute(from: AttributeContainer, to: AttributeContainer, key: Attribute<T>) {
val value = checkNotNull(from.getAttribute(key))
to.attribute(key, value)
}
for (key in from.keySet()) {
copyOneAttribute(from, to, key)
}
}
class MultiModuleMavenPublishingConfiguration() {
val modules = mutableMapOf<String, Module>()
class Module(val name: String) {
val variants = mutableMapOf<String, Variant>()
val includes = mutableSetOf<Module>()
class Variant(
val configurationName: String
) {
var name: String = configurationName
val attributesConfigurations = mutableListOf<AttributeContainer.() -> Unit>()
fun attributes(code: AttributeContainer.() -> Unit) {
attributesConfigurations += code
}
val artifactsWithConfigurations = mutableListOf<Pair<Any, ConfigurablePublishArtifact.() -> Unit>>()
fun artifact(file: Any, code: ConfigurablePublishArtifact.() -> Unit = {}) {
artifactsWithConfigurations += file to code
}
val configurationConfigurations = mutableListOf<Configuration.() -> Unit>()
fun configuration(code: Configuration.() -> Unit) {
configurationConfigurations += code
}
val variantDetailsConfigurations = mutableListOf<ConfigurationVariantDetails.() -> Unit>()
fun configureVariantDetails(code: ConfigurationVariantDetails.() -> Unit) {
variantDetailsConfigurations += code
}
}
val mavenPublicationConfigurations = mutableListOf<MavenPublication.() -> 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<MavenPublication>(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<String, String>,
private val base: SoftwareComponentInternal
): SoftwareComponentInternal by base {
override fun getName(): String = base.name
override fun getUsages(): Set<UsageContext> {
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<SoftwareComponent>
) : ComponentWithVariants, SoftwareComponentInternal {
override fun getName(): String = mainComponent.name
override fun getUsages(): Set<UsageContext> = (mainComponent as SoftwareComponentInternal).usages
override fun getVariants(): Set<SoftwareComponent> = externalComponents
}
// endregion
// for legacy intra-project dependencies
for (name in listOf("sources", "distSources")) {
val sourcesConfiguration = configurations.getOrCreate(name).apply {
isCanBeResolved = false
isCanBeConsumed = true
}
artifacts.add(sourcesConfiguration.name, tasks["jvmSourcesJar"])
}