/* * 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 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 jvm -------------------------------> concurrent ---> common ^ ios \ | macos | ---> nativeDarwin ---> native --+ tvos | ^ watchos / | | linux \ ---> nativeOther -------+ mingw / ========================================================================== */ 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"].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 { sourceSets.forEach { SourceSetsKt.configureMultiplatform(it) } /* * Configure four test runs: * 1) Old memory model, Main thread * 2) New memory model, Main thread * 3) Old memory model, BG thread * 4) 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 { // Test for memory leaks using a special entry point that does not exit but returns from main binaries.getTest("DEBUG").freeCompilerArgs += ["-e", "kotlinx.coroutines.mainNoExit"] } binaries.test("newMM", [DEBUG]) { def thisTest = it freeCompilerArgs += ["-e", "kotlinx.coroutines.mainNoExit"] optimized = true binaryOptions["memoryModel"] = "experimental" testRuns.create("newMM") { setExecutionSourceFrom(thisTest) // A hack to get different suffixes in the aggregated report. executionTask.configure { targetName = "$targetName new MM" } } } binaries.test("worker", [DEBUG]) { def thisTest = it freeCompilerArgs += ["-e", "kotlinx.coroutines.mainBackground"] testRuns.create("worker") { setExecutionSourceFrom(thisTest) executionTask.configure { targetName = "$targetName worker" } } } 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" } } } } jvm { // 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") } } 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 systemProperty 'kotlinx.coroutines.semaphore.segmentSize', '2' systemProperty 'kotlinx.coroutines.semaphore.maxSpinCycles', '10' } task jvmLincheckTest(type: Test, dependsOn: compileTestKotlinJvm) { classpath = files { jvmTest.classpath } testClassesDirs = files { jvmTest.testClassesDirs } include '**/*LincheckTest.*' enableAssertions = true testLogging.showStandardStreams = true configureJvmForLincheck(jvmLincheckTest) } static void configureJvmForLincheck(task) { 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 task.systemProperty 'kotlinx.coroutines.semaphore.segmentSize', '2' task.systemProperty 'kotlinx.coroutines.semaphore.maxSpinCycles', '1' // better for the model checking mode } // Always check additional test sets task moreTest(dependsOn: [jvmStressTest, jvmLincheckTest]) check.dependsOn moreTest tasks.jvmLincheckTest { kover { enabled = false // Always disabled, lincheck doesn't really support coverage } } 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 ] tasks.koverHtmlReport { excludes = commonKoverExcludes } tasks.koverVerify { excludes = commonKoverExcludes } task testsJar(type: Jar, dependsOn: jvmTestClasses) { classifier = 'tests' from compileTestKotlinJvm.destinationDir } artifacts { archives testsJar }