Files
kotlin-fork/libraries/kotlin.test/build.gradle.kts
T
Yahor Berdnikau d662f72d07 [Gradle] Deprecate KotlinCompilation.compilerOptions
Users quite often confuse that compilerOptions in KotlinCompilation is
actually the same object in the Kotlin compilation task. We want to make
 our API more straightforward and remove this ambiguity by proposing to
 use task compiler options DSL.

^KT-65568 Verification Pending
2024-02-15 18:27:31 +00:00

571 lines
24 KiB
Kotlin

@file:Suppress("UNUSED_VARIABLE")
import com.google.gson.GsonBuilder
import com.google.gson.JsonObject
import org.gradle.api.publish.internal.PublicationInternal
import org.gradle.jvm.tasks.Jar
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.mpp.GenerateProjectStructureMetadata
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
import plugins.configureDefaultPublishing
import plugins.configureKotlinPomAttributes
import plugins.publishing.*
plugins {
kotlin("multiplatform")
`maven-publish`
signing
}
description = "Kotlin Test Library"
base.archivesName = "kotlin-test"
configureJvmToolchain(JdkMajorVersion.JDK_1_8)
val kotlinTestCapability = "$group:${base.archivesName.get()}:$version" // add to variants with explicit capabilities when the default one is needed, too
val baseCapability = "$group:kotlin-test-framework:$version"
val implCapability = "$group:kotlin-test-framework-impl:$version"
enum class JvmTestFramework {
JUnit,
JUnit5,
TestNG;
fun lowercase() = name.lowercase()
}
val jvmTestFrameworks = JvmTestFramework.values().toList()
kotlin {
explicitApi()
jvm {
compilations {
all {
compileTaskProvider.configure {
compilerOptions {
freeCompilerArgs.empty() // avoid common options set from the root project
}
}
}
val main by getting
val test by getting
configureJava9Compilation(
"kotlin.test",
listOf(main.output.allOutputs),
main.configurations.compileDependencyConfiguration,
project.sourceSets.create("jvmJava9") {
java.srcDir("jvm/src/java9/java")
}.name,
)
jvmTestFrameworks.forEach { framework ->
val frameworkMain = create("$framework") {
associateWith(main)
compileTaskProvider.configure {
compilerOptions {
freeCompilerArgs.add("-Xexplicit-api=strict")
}
}
}
create("${framework}Test") {
associateWith(frameworkMain)
}
val frameworkJava9SourceSet = project.sourceSets.create("jvm${framework}Java9") {
java.srcDir("${framework.lowercase()}/src/java9/java")
}
configureJava9Compilation(
"kotlin.test.${framework.lowercase()}",
listOf(frameworkMain.output.allOutputs),
frameworkMain.configurations.compileDependencyConfiguration,
frameworkJava9SourceSet.name,
)
val java9CompileOnly = configurations[frameworkJava9SourceSet.compileOnlyConfigurationName]
project.dependencies {
java9CompileOnly(project)
}
}
test.associateWith(getByName("JUnit"))
}
}
js {
if (!kotlinBuildProperties.isTeamcityBuild) {
browser {}
}
nodejs {}
compilations["main"].compileTaskProvider.configure {
compilerOptions.freeCompilerArgs.add("-Xir-module-name=kotlin-test")
}
}
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
nodejs()
compilations["main"].compileTaskProvider.configure {
compilerOptions.freeCompilerArgs.add("-Xir-module-name=kotlin-test")
}
}
@OptIn(ExperimentalWasmDsl::class)
wasmWasi {
nodejs()
compilations["main"].compileTaskProvider.configure {
compilerOptions.freeCompilerArgs.add("-Xir-module-name=kotlin-test")
}
}
targets.all {
compilations.all {
compileTaskProvider.configure {
compilerOptions {
optIn.add("kotlin.contracts.ExperimentalContracts")
freeCompilerArgs.addAll(
"-Xallow-kotlin-package",
"-Xexpect-actual-classes",
)
}
}
}
}
sourceSets {
val commonMain by getting {
dependencies {
api(kotlinStdlib())
}
}
val annotationsCommonMain by creating {
dependsOn(commonMain)
kotlin.srcDir("annotations-common/src/main/kotlin")
}
val assertionsCommonMain by creating {
dependsOn(commonMain)
kotlin.srcDir("common/src/main/kotlin")
}
val commonTest by getting {
kotlin.srcDir("annotations-common/src/test/kotlin")
kotlin.srcDir("common/src/test/kotlin")
}
val jvmMain by getting {
dependsOn(assertionsCommonMain)
kotlin.srcDir("jvm/src/main/kotlin")
}
val jvmTest by getting {
kotlin.srcDir("jvm/src/test/kotlin")
}
val jvmJUnit by getting {
dependsOn(annotationsCommonMain)
kotlin.srcDir("junit/src/main/kotlin")
resources.srcDir("junit/src/main/resources")
dependencies {
api("junit:junit:4.13.2")
}
}
val jvmJUnitTest by getting {
dependsOn(commonTest)
kotlin.srcDir("junit/src/test/kotlin")
}
val jvmJUnit5 by getting {
dependsOn(annotationsCommonMain)
kotlin.srcDir("junit5/src/main/kotlin")
resources.srcDir("junit5/src/main/resources")
dependencies {
compileOnly("org.junit.jupiter:junit-jupiter-api:5.0.0")
}
}
val jvmJUnit5Test by getting {
dependsOn(commonTest)
kotlin.srcDir("junit5/src/test/kotlin")
dependencies {
runtimeOnly(libs.junit.jupiter.engine)
runtimeOnly(libs.junit.platform.launcher)
}
}
val jvmTestNG by getting {
dependsOn(annotationsCommonMain)
kotlin.srcDir("testng/src/main/kotlin")
resources.srcDir("testng/src/main/resources")
dependencies {
api("org.testng:testng:7.0.0")
}
}
val jvmTestNGTest by getting {
dependsOn(commonTest)
kotlin.srcDir("testng/src/test/kotlin")
dependencies {
implementation("org.testng:testng:7.5.1")
}
}
val jsMain by getting {
dependsOn(assertionsCommonMain)
dependsOn(annotationsCommonMain)
kotlin.srcDir("js/src/main/kotlin")
}
val jsTest by getting {
kotlin.srcDir("js/src/test/kotlin")
}
val wasmCommonMain by creating {
dependsOn(assertionsCommonMain)
dependsOn(annotationsCommonMain)
kotlin.srcDir("wasm/src/main/kotlin")
}
val wasmJsMain by getting {
dependsOn(wasmCommonMain)
kotlin.srcDir("wasm/js/src/main/kotlin")
}
val wasmJsTest by getting {
kotlin.srcDir("wasm/js/src/test/kotlin")
}
val wasmWasiMain by getting {
dependsOn(wasmCommonMain)
kotlin.srcDir("wasm/wasi/src/main/kotlin")
}
}
}
tasks {
val allMetadataJar by existing(Jar::class) {
archiveClassifier = "all"
}
val jvmJar by existing(Jar::class) {
archiveAppendix = null
from(project.sourceSets["jvmJava9"].output)
manifestAttributes(manifest, "Test", multiRelease = true)
}
val jvmSourcesJar by existing(Jar::class) {
archiveAppendix = null
kotlin.sourceSets["annotationsCommonMain"].let { sourceSet ->
into(sourceSet.name) {
from(sourceSet.kotlin)
}
}
}
val jvmJarTasks = jvmTestFrameworks.map { framework ->
named("jvm${framework}Jar", Jar::class) {
archiveBaseName = base.archivesName
archiveAppendix = framework.lowercase()
from(project.sourceSets["jvm${framework}Java9"].output)
manifestAttributes(manifest, "Test", multiRelease = true)
manifest.attributes("Implementation-Title" to "${archiveBaseName.get()}-${archiveAppendix.get()}")
}
}
val jvmSourcesJarTasks = jvmTestFrameworks.map { framework ->
register("jvm${framework}SourcesJar", Jar::class) {
archiveAppendix = framework.lowercase()
archiveClassifier = "sources"
kotlin.jvm().compilations[framework.name].allKotlinSourceSets.forEach {
from(it.kotlin.sourceDirectories) { into(it.name) }
from(it.resources.sourceDirectories) { into(it.name) }
}
}
}
val jsJar by existing(Jar::class) {
manifestAttributes(manifest, "Test")
manifest.attributes("Implementation-Title" to "${archiveBaseName.get()}-${archiveAppendix.get()}")
}
val wasmJsJar by existing(Jar::class) {
manifestAttributes(manifest, "Test")
manifest.attributes("Implementation-Title" to "${archiveBaseName.get()}-${archiveAppendix.get()}")
}
val wasmWasiJar by existing(Jar::class) {
manifestAttributes(manifest, "Test")
manifest.attributes("Implementation-Title" to "${archiveBaseName.get()}-${archiveAppendix.get()}")
}
val assemble by existing {
dependsOn(jvmJarTasks)
}
val jvmTestTasks = jvmTestFrameworks.flatMap { framework ->
listOf(false, true).map { excludeAsserterContributor ->
register("jvm${framework}${if (excludeAsserterContributor) "NoAsserter" else ""}Test", Test::class) {
group = "verification"
val testCompilation = kotlin.jvm().compilations["${framework}Test"]
classpath = testCompilation.runtimeDependencyFiles + testCompilation.output.allOutputs
if (excludeAsserterContributor) {
val mainCompilation = kotlin.jvm().compilations["$framework"]
classpath -= mainCompilation.output.allOutputs
filter.excludePatterns += "*ContributorTest"
}
testClassesDirs = testCompilation.output.classesDirs
when (framework) {
JvmTestFramework.JUnit -> useJUnit()
JvmTestFramework.JUnit5 -> useJUnitPlatform()
JvmTestFramework.TestNG -> useTestNG()
}
}
}
}
val allTests by existing {
dependsOn(jvmTestTasks)
}
val generateProjectStructureMetadata by existing(GenerateProjectStructureMetadata::class) {
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 = resultFile.readText().trim()
val expectedFileContent = outputTestFile.readText().trim()
if (outputFileText != expectedFileContent)
error(
"${resultFile.path} file content does not match expected content\n\n" +
"expected:\n\n$expectedFileContent\n\nactual:\n\n$outputFileText"
)
}
patchedFile.copyTo(resultFile, overwrite = true)
}
}
}
configurations {
val metadataApiElements by getting
metadataApiElements.outgoing.capability(kotlinTestCapability)
for (framework in jvmTestFrameworks) {
val frameworkCapability = "$group:kotlin-test-framework-${framework.lowercase()}:$version"
metadataApiElements.outgoing.capability(frameworkCapability)
val runtimeDeps = create("jvm${framework}RuntimeDependencies") {
isCanBeResolved = true
isCanBeConsumed = false
}
val (apiElements, runtimeElements, sourcesElements) = listOf(KotlinUsages.KOTLIN_API, KotlinUsages.KOTLIN_RUNTIME, KotlinUsages.KOTLIN_SOURCES).map { usage ->
val name = "jvm$framework${usage.substringAfter("kotlin-").replaceFirstChar { it.uppercase() }}Elements"
create(name) {
isCanBeResolved = false
isCanBeConsumed = true
outgoing.capability(baseCapability)
outgoing.capability(frameworkCapability)
attributes {
attribute(TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE, objects.named(TargetJvmEnvironment.STANDARD_JVM))
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.JAR))
attribute(KotlinPlatformType.attribute, KotlinPlatformType.jvm)
when (usage) {
KotlinUsages.KOTLIN_SOURCES -> {
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.DOCUMENTATION))
attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named(DocsType.SOURCES))
attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL))
}
KotlinUsages.KOTLIN_API -> {
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_API))
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY))
extendsFrom(getByName("jvm${framework}Api"))
}
KotlinUsages.KOTLIN_RUNTIME -> {
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY))
runtimeDeps.extendsFrom(getByName("jvm${framework}Api"))
runtimeDeps.extendsFrom(getByName("jvm${framework}Implementation"))
runtimeDeps.extendsFrom(getByName("jvm${framework}RuntimeOnly"))
extendsFrom(runtimeDeps)
}
else -> error(usage)
}
}
}
}
dependencies {
apiElements(project)
runtimeDeps(project)
when (framework) {
JvmTestFramework.JUnit -> {}
JvmTestFramework.JUnit5 -> {
apiElements("org.junit.jupiter:junit-jupiter-api:5.10.1")
runtimeDeps("org.junit.jupiter:junit-jupiter-engine:5.10.1")
runtimeDeps("org.junit.platform:junit-platform-launcher:1.10.1")
}
JvmTestFramework.TestNG -> {
apiElements("org.testng:testng:7.5.1")
}
}
}
artifacts {
add(apiElements.name, tasks.named<Jar>("jvm${framework}Jar"))
add(runtimeElements.name, tasks.named<Jar>("jvm${framework}Jar"))
add(sourcesElements.name, tasks.named<Jar>("jvm${framework}SourcesJar"))
}
}
for (configurationName in listOf("kotlinTestCommon", "kotlinTestAnnotationsCommon")) {
val legacyConfigurationDeps = create("${configurationName}Dependencies") {
isCanBeResolved = true
isCanBeConsumed = false
}
val legacyConfiguration = create("${configurationName}Elements") {
isCanBeResolved = false
isCanBeConsumed = false
attributes {
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY))
}
extendsFrom(legacyConfigurationDeps)
}
dependencies {
legacyConfigurationDeps(project)
}
}
val jvmMainApi by getting
val nativeApiElements by creating
for (artifactName in listOf("kotlin-test-common", "kotlin-test-annotations-common")) {
dependencies {
constraints {
val artifactCoordinates = "$group:$artifactName:$version"
// there is no dependency anymore from kotlin-test to kotlin-test-common and -annotations-common,
// but use this constraint to align it if another library brings it transitively
jvmMainApi(artifactCoordinates)
metadataApiElements(artifactCoordinates)
nativeApiElements(artifactCoordinates)
}
}
}
}
configureDefaultPublishing()
fun emptyJavadocJar(appendix: String? = null): TaskProvider<Jar> =
tasks.register<Jar>("emptyJavadocJar${appendix.orEmpty().capitalize()}") {
archiveAppendix = appendix
archiveClassifier = "javadoc"
}
publishing {
val artifactBaseName = base.archivesName.get()
configureMultiModuleMavenPublishing {
val rootModule = module("rootModule") {
mavenPublication {
artifactId = artifactBaseName
configureKotlinPomAttributes(project, "Kotlin Test Multiplatform library")
artifact(emptyJavadocJar())
}
variant("metadataApiElements") { suppressPomMetadataWarnings() }
variant("jvmApiElements")
variant("jvmRuntimeElements") {
configureVariantDetails { mapToMavenScope("runtime") }
}
variant("jvmSourcesElements")
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)
}
}
}
val frameworkModules = jvmTestFrameworks.map { framework ->
module("${framework.lowercase()}Module") {
mavenPublication {
artifactId = "$artifactBaseName-${framework.lowercase()}"
configureKotlinPomAttributes(project, "Kotlin Test library support for ${framework}")
artifact(emptyJavadocJar(framework.lowercase()))
}
variant("jvm${framework}ApiElements") { suppressPomMetadataWarnings() }
variant("jvm${framework}RuntimeElements") {
suppressPomMetadataWarnings()
configureVariantDetails { mapToMavenScope("runtime") }
}
variant("jvm${framework}SourcesElements") { suppressPomMetadataWarnings() }
}
}
val js = module("jsModule") {
mavenPublication {
artifactId = "$artifactBaseName-js"
configureKotlinPomAttributes(project, "Kotlin Test library for JS", packaging = "klib")
}
variant("jsApiElements")
variant("jsRuntimeElements")
variant("jsSourcesElements")
}
val wasmJs = module("wasmJsModule") {
mavenPublication {
artifactId = "$artifactBaseName-wasm-js"
configureKotlinPomAttributes(project, "Kotlin Test 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 Test library for experimental WebAssembly WASI platform", packaging = "klib")
}
variant("wasmWasiApiElements")
variant("wasmWasiRuntimeElements")
variant("wasmWasiSourcesElements")
}
module("testCommonModule") {
mavenPublication {
artifactId = "$artifactBaseName-common"
configureKotlinPomAttributes(project, "Legacy artifact of Kotlin Test library. Use kotlin-test instead", packaging = "pom")
(this as PublicationInternal<*>).isAlias = true
}
variant("kotlinTestCommonElements")
}
module("testAnnotationsCommonModule") {
mavenPublication {
artifactId = "$artifactBaseName-annotations-common"
configureKotlinPomAttributes(project, "Legacy artifact of Kotlin Test library. Use kotlin-test instead", packaging = "pom")
(this as PublicationInternal<*>).isAlias = true
}
variant("kotlinTestAnnotationsCommonElements")
}
// Makes all variants from accompanying artifacts visible through `available-at`
rootModule.include(js, *frameworkModules.toTypedArray(), wasmJs, wasmWasi)
}
publications {
(listOf(
listOf("rootModule", "Main", "kotlin-test", "jvmRuntimeClasspath"),
listOf("jsModule", "Js", "kotlin-test-js", "jsRuntimeClasspath"),
listOf("wasmJsModule", "Wasm-Js", "kotlin-test-wasm-js", "wasmJsRuntimeClasspath"),
listOf("wasmWasiModule", "Wasm-Wasi", "kotlin-test-wasm-wasi", "wasmWasiRuntimeClasspath"),
listOf("testCommonModule", "Common", "kotlin-test-common", "kotlinTestCommonDependencies"),
listOf("testAnnotationsCommonModule", "AnnotationsCommon", "kotlin-test-annotations-common", "kotlinTestAnnotationsCommonDependencies"),
) + jvmTestFrameworks.map { framework ->
listOf("${framework.lowercase()}Module", "$framework", "kotlin-test-${framework.lowercase()}", "jvm${framework}RuntimeDependencies")
}).forEach { (module, sbomTarget, sbomDocument, classpath) ->
configureSbom(sbomTarget, sbomDocument, setOf(classpath), named<MavenPublication>(module))
}
}
}
tasks.withType<GenerateModuleMetadata> {
val publication = publication.get() as MavenPublication
// alter capabilities of leaf JVM framework artifacts published by "available-at" coordinates
if (jvmTestFrameworks.map { it.lowercase() }.any { publication.artifactId.endsWith(it) }) {
fun capability(group: String, name: String, version: String) =
mapOf("group" to group, "name" to name, "version" to version)
val defaultCapability = publication.let { capability(it.groupId, it.artifactId, it.version) }
val implCapability = implCapability.split(":").let { (g, n, v) -> capability(g, n, v) }
val capabilities = listOf(defaultCapability, implCapability)
doLast {
val output = outputFile.get().asFile
val gson = GsonBuilder().setPrettyPrinting().create()
val moduleJson = output.bufferedReader().use { gson.fromJson(it, JsonObject::class.java) }
val variants = moduleJson.getAsJsonArray("variants")
variants.forEach { variant ->
variant as JsonObject
variant.remove("capabilities")
variant.add("capabilities", gson.toJsonTree(capabilities))
}
output.bufferedWriter().use { writer -> gson.toJson(moduleJson, writer) }
}
}
}