blob: 001df3a055384e8e0dc5bd8268006bae8d7d35f2 [file] [log] [blame]
/*
* Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
apply plugin: 'org.jetbrains.kotlin.multiplatform'
apply plugin: 'org.jetbrains.dokka'
// apply plugin to use autocomplete for Kover DSL
apply plugin: 'org.jetbrains.kotlinx.kover'
apply from: rootProject.file("gradle/compile-jvm-multiplatform.gradle")
apply from: rootProject.file("gradle/compile-common.gradle")
if (rootProject.ext.native_targets_enabled) {
apply from: rootProject.file("gradle/compile-native-multiplatform.gradle")
}
apply from: rootProject.file("gradle/compile-js-multiplatform.gradle")
apply from: rootProject.file('gradle/publish-npm-js.gradle')
apply from: rootProject.file('gradle/dokka.gradle.kts')
apply from: rootProject.file('gradle/publish.gradle')
/* ==========================================================================
Configure source sets structure for kotlinx-coroutines-core:
TARGETS SOURCE SETS
------- ----------------------------------------------
js -----------------------------------------------------+
|
V
jvmCore\ --------> jvm ---------> concurrent -------> common
jdk8 / ^
|
ios \ |
macos | ---> nativeDarwin ---> native ---+
tvos | ^
watchos / |
|
linux \ ---> nativeOther -------+
mingw /
Explanation of JVM source sets structure:
The overall structure is just a hack to support the scenario we are interested in:
* We would like to have two source-sets "core" and "jdk8"
* "jdk8" is allowed to use API from Java 8 and from "core"
* "core" is prohibited to use any API from "jdk8"
* It is okay to have tests in a single test source-set
* And we want to publish a **single** artifact kotlinx-coroutines-core.jar that contains classes from both source-sets
* Current limitation: only classes from "core" are checked with animal-sniffer
For that, we have following compilations:
* jvmMain compilation: [jvmCoreMain, jdk8Main]
* jvmCore compilation: [commonMain]
* jdk8 compilation: [commonMain, jvmCoreMain]
Theoretically, "jvmCore" could've been "jvmMain", it is not for technical reasons,
here is the explanation from Seb:
"""
The jvmCore is theoretically not necessary. All code for jdk6 compatibility can be in jvmMain and jdk8 dependent code can be in jdk8Main.
Effectively there is no reason for ever putting code into jvmCoreMain.
However, when creating a new compilation, we have to take care of creating a defaultSourceSet. Without creating the jvmCoreMain source set,
the creation of the compilation fails. That is the only reason for this source set.
"""
========================================================================== */
project.ext.sourceSetSuffixes = ["Main", "Test"]
void defineSourceSet(newName, dependsOn, includedInPred) {
for (suffix in project.ext.sourceSetSuffixes) {
def newSS = kotlin.sourceSets.maybeCreate(newName + suffix)
for (dep in dependsOn) {
newSS.dependsOn(kotlin.sourceSets[dep + suffix])
}
for (curSS in kotlin.sourceSets) {
def curName = curSS.name
if (curName.endsWith(suffix)) {
def prefix = curName.substring(0, curName.length() - suffix.length())
if (includedInPred(prefix)) curSS.dependsOn(newSS)
}
}
}
}
static boolean isNativeDarwin(String name) { return ["ios", "macos", "tvos", "watchos"].any { name.startsWith(it) } }
static boolean isNativeOther(String name) { return ["linux", "mingw", "androidNative"].any { name.startsWith(it) } }
defineSourceSet("concurrent", ["common"]) { it in ["jvm", "native"] }
if (rootProject.ext.native_targets_enabled) {
defineSourceSet("nativeDarwin", ["native"]) { isNativeDarwin(it) }
defineSourceSet("nativeOther", ["native"]) { isNativeOther(it) }
}
/* ========================================================================== */
/*
* All platform plugins and configuration magic happens here instead of build.gradle
* because JMV-only projects depend on core, thus core should always be initialized before configuration.
*/
kotlin {
/*
* Configure two test runs:
* 1) New memory model, Main thread
* 2) New memory model, BG thread (required for Dispatchers.Main tests on Darwin)
*
* All new MM targets are build with optimize = true to have stress tests properly run.
*/
targets.withType(org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetWithTests.class).configureEach {
binaries.getTest("DEBUG").with {
optimized = true
// Test for memory leaks using a special entry point that does not exit but returns from main
freeCompilerArgs += ["-e", "kotlinx.coroutines.mainNoExit"]
binaryOptions["memoryModel"] = "experimental"
}
binaries.test("workerWithNewMM", [DEBUG]) {
def thisTest = it
optimized = true
freeCompilerArgs += ["-e", "kotlinx.coroutines.mainBackground"]
binaryOptions["memoryModel"] = "experimental"
testRuns.create("workerWithNewMM") {
setExecutionSourceFrom(thisTest)
executionTask.configure { targetName = "$targetName worker with new MM" }
}
}
}
def jvmMain = sourceSets.jvmMain
def jvmCoreMain = sourceSets.create('jvmCoreMain')
def jdk8Main = sourceSets.create('jdk8Main')
jvmCoreMain.dependsOn(jvmMain)
jdk8Main.dependsOn(jvmMain)
sourceSets.forEach {
SourceSetsKt.configureMultiplatform(it)
}
jvm {
def main = compilations.main
main.source(jvmCoreMain)
main.source(jdk8Main)
/* Create compilation for jvmCore to prove that jvmMain does not rely on jdk8 */
compilations.create('CoreMain') {
/* jvmCore is automatically matched as 'defaultSourceSet' for the compilation, due to its name */
tasks.getByName('check').dependsOn(compileKotlinTaskProvider)
}
// For animal sniffer
withJava()
}
}
configurations {
configureKotlinJvmPlatform(kotlinCompilerPluginClasspath)
}
// Update module name for metadata artifact to avoid conflicts
// see https://github.com/Kotlin/kotlinx.coroutines/issues/1797
compileKotlinMetadata {
kotlinOptions {
freeCompilerArgs += ["-module-name", "kotlinx-coroutines-core-common"]
}
}
// :KLUDGE: Idea.active: This is needed to workaround resolve problems after importing this project to IDEA
def configureNativeSourceSetPreset(name, preset) {
def hostMainCompilation = project.kotlin.targetFromPreset(preset).compilations.main
// Look for platform libraries in "implementation" for default source set
def implementationConfiguration = configurations[hostMainCompilation.defaultSourceSet.implementationMetadataConfigurationName]
// Now find the libraries: Finds platform libs & stdlib, but platform declarations are still not resolved due to IDE bugs
def hostNativePlatformLibs = files(
provider {
implementationConfiguration.findAll {
it.path.endsWith(".klib") || it.absolutePath.contains("klib${File.separator}platform") || it.absolutePath.contains("stdlib")
}
}
)
// Add all those dependencies
for (suffix in sourceSetSuffixes) {
configure(kotlin.sourceSets[name + suffix]) {
dependencies.add(implementationMetadataConfigurationName, hostNativePlatformLibs)
}
}
}
// :KLUDGE: Idea.active: Configure platform libraries for native source sets when working in IDEA
if (Idea.active && rootProject.ext.native_targets_enabled) {
def manager = project.ext.hostManager
def linuxPreset = kotlin.presets.linuxX64
def macosPreset = kotlin.presets.macosX64
// linux should be always available (cross-compilation capable) -- use it as default
assert manager.isEnabled(linuxPreset.konanTarget)
// use macOS libs for nativeDarwin if available
def macosAvailable = manager.isEnabled(macosPreset.konanTarget)
// configure source sets
configureNativeSourceSetPreset("native", linuxPreset)
configureNativeSourceSetPreset("nativeOther", linuxPreset)
configureNativeSourceSetPreset("nativeDarwin", macosAvailable ? macosPreset : linuxPreset)
}
kotlin.sourceSets {
jvmMain.dependencies {
compileOnly "com.google.android:annotations:4.1.1.4"
}
jvmTest.dependencies {
api "org.jetbrains.kotlinx:lincheck:$lincheck_version"
api "org.jetbrains.kotlinx:kotlinx-knit-test:$knit_version"
implementation project(":android-unit-tests")
implementation "org.openjdk.jol:jol-core:0.16"
}
}
kotlin.sourceSets.configureEach {
// Do not apply 'ExperimentalForeignApi' where we have allWarningsAsErrors set
if (it.name in ["jvmMain", "jsMain", "concurrentMain", "commonMain"]) return
languageSettings {
optIn('kotlinx.cinterop.ExperimentalForeignApi')
optIn('kotlin.experimental.ExperimentalNativeApi')
}
}
jvmTest {
minHeapSize = '1g'
maxHeapSize = '1g'
enableAssertions = true
if (!Idea.active) {
// We should not set this security manager when `jvmTest`
// is invoked by IntelliJ IDEA since we need to pass
// system properties for Lincheck and stress tests.
// TODO Remove once IDEA is smart enough to select between `jvmTest`/`jvmStressTest`/`jvmLincheckTest` #KTIJ-599
systemProperty 'java.security.manager', 'kotlinx.coroutines.TestSecurityManager'
}
// 'stress' is required to be able to run all subpackage tests like ":jvmTests --tests "*channels*" -Pstress=true"
if (!Idea.active && rootProject.properties['stress'] == null) {
exclude '**/*LincheckTest*'
exclude '**/*StressTest.*'
}
if (Idea.active) {
// Configure the IDEA runner for Lincheck
configureJvmForLincheck(jvmTest)
}
}
// Setup manifest for kotlinx-coroutines-core-jvm.jar
jvmJar { setupManifest(it) }
/*
* Setup manifest for kotlinx-coroutines-core.jar
* This is convenient for users that pass -javaagent arg manually and also is a workaround #2619 and KTIJ-5659.
* This manifest contains reference to AgentPremain that belongs to
* kotlinx-coroutines-core-jvm, but our resolving machinery guarantees that
* any JVM project that depends on -core artifact also depends on -core-jvm one.
*/
metadataJar { setupManifest(it) }
static def setupManifest(Jar jar) {
jar.manifest {
attributes "Premain-Class": "kotlinx.coroutines.debug.AgentPremain"
attributes "Can-Retransform-Classes": "true"
}
}
task jvmStressTest(type: Test, dependsOn: compileTestKotlinJvm) {
classpath = files { jvmTest.classpath }
testClassesDirs = files { jvmTest.testClassesDirs }
minHeapSize = '1g'
maxHeapSize = '1g'
include '**/*StressTest.*'
enableAssertions = true
testLogging.showStandardStreams = true
systemProperty 'kotlinx.coroutines.scheduler.keep.alive.sec', '100000' // any unpark problem hangs test
// Adjust internal algorithmic parameters to increase the testing quality instead of performance.
systemProperty 'kotlinx.coroutines.semaphore.segmentSize', '1'
systemProperty 'kotlinx.coroutines.semaphore.maxSpinCycles', '10'
systemProperty 'kotlinx.coroutines.bufferedChannel.segmentSize', '2'
systemProperty 'kotlinx.coroutines.bufferedChannel.expandBufferCompletionWaitIterations', '1'
}
task jvmLincheckTest(type: Test, dependsOn: compileTestKotlinJvm) {
classpath = files { jvmTest.classpath }
testClassesDirs = files { jvmTest.testClassesDirs }
include '**/*LincheckTest*'
enableAssertions = true
testLogging.showStandardStreams = true
configureJvmForLincheck(jvmLincheckTest)
}
// Additional Lincheck tests with `segmentSize = 2`.
// Some bugs cannot be revealed when storing one request per segment,
// and some are hard to detect when storing multiple requests.
task jvmLincheckTestAdditional(type: Test, dependsOn: compileTestKotlinJvm) {
classpath = files { jvmTest.classpath }
testClassesDirs = files { jvmTest.testClassesDirs }
include '**/RendezvousChannelLincheckTest*'
include '**/Buffered1ChannelLincheckTest*'
include '**/Semaphore*LincheckTest*'
enableAssertions = true
testLogging.showStandardStreams = true
configureJvmForLincheck(jvmLincheckTestAdditional, true)
}
static void configureJvmForLincheck(task, additional = false) {
task.minHeapSize = '1g'
task.maxHeapSize = '4g' // we may need more space for building an interleaving tree in the model checking mode
task.jvmArgs = ['--add-opens', 'java.base/jdk.internal.misc=ALL-UNNAMED', // required for transformation
'--add-exports', 'java.base/jdk.internal.util=ALL-UNNAMED'] // in the model checking mode
// Adjust internal algorithmic parameters to increase the testing quality instead of performance.
var segmentSize = additional ? '2' : '1'
task.systemProperty 'kotlinx.coroutines.semaphore.segmentSize', segmentSize
task.systemProperty 'kotlinx.coroutines.semaphore.maxSpinCycles', '1' // better for the model checking mode
task.systemProperty 'kotlinx.coroutines.bufferedChannel.segmentSize', segmentSize
task.systemProperty 'kotlinx.coroutines.bufferedChannel.expandBufferCompletionWaitIterations', '1'
}
// Always check additional test sets
task moreTest(dependsOn: [jvmStressTest, jvmLincheckTest, jvmLincheckTestAdditional])
check.dependsOn moreTest
def commonKoverExcludes =
["kotlinx.coroutines.debug.*", // Tested by debug module
"kotlinx.coroutines.channels.ChannelsKt__DeprecatedKt.*", // Deprecated
"kotlinx.coroutines.scheduling.LimitingDispatcher", // Deprecated
"kotlinx.coroutines.scheduling.ExperimentalCoroutineDispatcher" // Deprecated
]
kover {
excludeTests {
// Always disabled, lincheck doesn't really support coverage
tasks("jvmLincheckTest")
}
excludeInstrumentation {
// lincheck has NPE error on `ManagedStrategyStateHolder` class
classes("org.jetbrains.kotlinx.lincheck.*")
}
}
koverReport {
filters {
excludes {
classes(
"kotlinx.coroutines.debug.*", // Tested by debug module
"kotlinx.coroutines.channels.ChannelsKt__DeprecatedKt.*", // Deprecated
"kotlinx.coroutines.scheduling.LimitingDispatcher", // Deprecated
"kotlinx.coroutines.scheduling.ExperimentalCoroutineDispatcher" // Deprecated
)
}
}
}
task testsJar(type: Jar, dependsOn: jvmTestClasses) {
classifier = 'tests'
from(compileTestKotlinJvm.destinationDirectory)
}
artifacts {
archives testsJar
}