1/* 2 * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. 3 */ 4 5apply plugin: 'org.jetbrains.kotlin.multiplatform' 6apply plugin: 'org.jetbrains.dokka' 7 8// apply plugin to use autocomplete for Kover DSL 9apply plugin: 'org.jetbrains.kotlinx.kover' 10 11apply from: rootProject.file("gradle/compile-jvm-multiplatform.gradle") 12apply from: rootProject.file("gradle/compile-common.gradle") 13 14if (rootProject.ext.native_targets_enabled) { 15 apply from: rootProject.file("gradle/compile-native-multiplatform.gradle") 16} 17 18apply from: rootProject.file("gradle/compile-js-multiplatform.gradle") 19apply from: rootProject.file('gradle/publish-npm-js.gradle') 20 21apply from: rootProject.file('gradle/dokka.gradle.kts') 22apply from: rootProject.file('gradle/publish.gradle') 23/* ========================================================================== 24 Configure source sets structure for kotlinx-coroutines-core: 25 26 TARGETS SOURCE SETS 27 ------- ---------------------------------------------- 28 29 js -----------------------------------------------------+ 30 | 31 V 32 jvmCore\ --------> jvm ---------> concurrent -------> common 33 jdk8 / ^ 34 | 35 ios \ | 36 macos | ---> nativeDarwin ---> native ---+ 37 tvos | ^ 38 watchos / | 39 | 40 linux \ ---> nativeOther -------+ 41 mingw / 42 43 44Explanation of JVM source sets structure: 45 46The overall structure is just a hack to support the scenario we are interested in: 47 48* We would like to have two source-sets "core" and "jdk8" 49* "jdk8" is allowed to use API from Java 8 and from "core" 50* "core" is prohibited to use any API from "jdk8" 51* It is okay to have tests in a single test source-set 52* And we want to publish a **single** artifact kotlinx-coroutines-core.jar that contains classes from both source-sets 53* Current limitation: only classes from "core" are checked with animal-sniffer 54 55For that, we have following compilations: 56* jvmMain compilation: [jvmCoreMain, jdk8Main] 57* jvmCore compilation: [commonMain] 58* jdk8 compilation: [commonMain, jvmCoreMain] 59 60Theoretically, "jvmCore" could've been "jvmMain", it is not for technical reasons, 61here is the explanation from Seb: 62 63""" 64The jvmCore is theoretically not necessary. All code for jdk6 compatibility can be in jvmMain and jdk8 dependent code can be in jdk8Main. 65Effectively there is no reason for ever putting code into jvmCoreMain. 66However, when creating a new compilation, we have to take care of creating a defaultSourceSet. Without creating the jvmCoreMain source set, 67 the creation of the compilation fails. That is the only reason for this source set. 68""" 69 ========================================================================== */ 70 71project.ext.sourceSetSuffixes = ["Main", "Test"] 72 73void defineSourceSet(newName, dependsOn, includedInPred) { 74 for (suffix in project.ext.sourceSetSuffixes) { 75 def newSS = kotlin.sourceSets.maybeCreate(newName + suffix) 76 for (dep in dependsOn) { 77 newSS.dependsOn(kotlin.sourceSets[dep + suffix]) 78 } 79 for (curSS in kotlin.sourceSets) { 80 def curName = curSS.name 81 if (curName.endsWith(suffix)) { 82 def prefix = curName.substring(0, curName.length() - suffix.length()) 83 if (includedInPred(prefix)) curSS.dependsOn(newSS) 84 } 85 } 86 } 87} 88 89static boolean isNativeDarwin(String name) { return ["ios", "macos", "tvos", "watchos"].any { name.startsWith(it) } } 90 91static boolean isNativeOther(String name) { return ["linux", "mingw", "androidNative"].any { name.startsWith(it) } } 92 93defineSourceSet("concurrent", ["common"]) { it in ["jvm", "native"] } 94 95if (rootProject.ext.native_targets_enabled) { 96 defineSourceSet("nativeDarwin", ["native"]) { isNativeDarwin(it) } 97 defineSourceSet("nativeOther", ["native"]) { isNativeOther(it) } 98} 99 100/* ========================================================================== */ 101 102 103/* 104 * All platform plugins and configuration magic happens here instead of build.gradle 105 * because JMV-only projects depend on core, thus core should always be initialized before configuration. 106 */ 107kotlin { 108 /* 109 * Configure two test runs: 110 * 1) New memory model, Main thread 111 * 2) New memory model, BG thread (required for Dispatchers.Main tests on Darwin) 112 * 113 * All new MM targets are build with optimize = true to have stress tests properly run. 114 */ 115 targets.withType(org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetWithTests.class).configureEach { 116 binaries.getTest("DEBUG").with { 117 optimized = true 118 // Test for memory leaks using a special entry point that does not exit but returns from main 119 freeCompilerArgs += ["-e", "kotlinx.coroutines.mainNoExit"] 120 binaryOptions["memoryModel"] = "experimental" 121 } 122 123 binaries.test("workerWithNewMM", [DEBUG]) { 124 def thisTest = it 125 optimized = true 126 freeCompilerArgs += ["-e", "kotlinx.coroutines.mainBackground"] 127 binaryOptions["memoryModel"] = "experimental" 128 testRuns.create("workerWithNewMM") { 129 setExecutionSourceFrom(thisTest) 130 executionTask.configure { targetName = "$targetName worker with new MM" } 131 } 132 } 133 } 134 135 def jvmMain = sourceSets.jvmMain 136 def jvmCoreMain = sourceSets.create('jvmCoreMain') 137 def jdk8Main = sourceSets.create('jdk8Main') 138 jvmCoreMain.dependsOn(jvmMain) 139 jdk8Main.dependsOn(jvmMain) 140 141 sourceSets.forEach { 142 SourceSetsKt.configureMultiplatform(it) 143 } 144 145 jvm { 146 def main = compilations.main 147 main.source(jvmCoreMain) 148 main.source(jdk8Main) 149 150 /* Create compilation for jvmCore to prove that jvmMain does not rely on jdk8 */ 151 compilations.create('CoreMain') { 152 /* jvmCore is automatically matched as 'defaultSourceSet' for the compilation, due to its name */ 153 tasks.getByName('check').dependsOn(compileKotlinTaskProvider) 154 } 155 156 // For animal sniffer 157 withJava() 158 } 159} 160 161configurations { 162 configureKotlinJvmPlatform(kotlinCompilerPluginClasspath) 163} 164 165// Update module name for metadata artifact to avoid conflicts 166// see https://github.com/Kotlin/kotlinx.coroutines/issues/1797 167compileKotlinMetadata { 168 kotlinOptions { 169 freeCompilerArgs += ["-module-name", "kotlinx-coroutines-core-common"] 170 } 171} 172 173// :KLUDGE: Idea.active: This is needed to workaround resolve problems after importing this project to IDEA 174def configureNativeSourceSetPreset(name, preset) { 175 def hostMainCompilation = project.kotlin.targetFromPreset(preset).compilations.main 176 // Look for platform libraries in "implementation" for default source set 177 def implementationConfiguration = configurations[hostMainCompilation.defaultSourceSet.implementationMetadataConfigurationName] 178 // Now find the libraries: Finds platform libs & stdlib, but platform declarations are still not resolved due to IDE bugs 179 def hostNativePlatformLibs = files( 180 provider { 181 implementationConfiguration.findAll { 182 it.path.endsWith(".klib") || it.absolutePath.contains("klib${File.separator}platform") || it.absolutePath.contains("stdlib") 183 } 184 } 185 ) 186 // Add all those dependencies 187 for (suffix in sourceSetSuffixes) { 188 configure(kotlin.sourceSets[name + suffix]) { 189 dependencies.add(implementationMetadataConfigurationName, hostNativePlatformLibs) 190 } 191 } 192} 193 194// :KLUDGE: Idea.active: Configure platform libraries for native source sets when working in IDEA 195if (Idea.active && rootProject.ext.native_targets_enabled) { 196 def manager = project.ext.hostManager 197 def linuxPreset = kotlin.presets.linuxX64 198 def macosPreset = kotlin.presets.macosX64 199 // linux should be always available (cross-compilation capable) -- use it as default 200 assert manager.isEnabled(linuxPreset.konanTarget) 201 // use macOS libs for nativeDarwin if available 202 def macosAvailable = manager.isEnabled(macosPreset.konanTarget) 203 // configure source sets 204 configureNativeSourceSetPreset("native", linuxPreset) 205 configureNativeSourceSetPreset("nativeOther", linuxPreset) 206 configureNativeSourceSetPreset("nativeDarwin", macosAvailable ? macosPreset : linuxPreset) 207} 208 209kotlin.sourceSets { 210 jvmMain.dependencies { 211 compileOnly "com.google.android:annotations:4.1.1.4" 212 } 213 214 jvmTest.dependencies { 215 api "org.jetbrains.kotlinx:lincheck:$lincheck_version" 216 api "org.jetbrains.kotlinx:kotlinx-knit-test:$knit_version" 217 implementation project(":android-unit-tests") 218 implementation "org.openjdk.jol:jol-core:0.16" 219 } 220} 221 222kotlin.sourceSets.configureEach { 223 // Do not apply 'ExperimentalForeignApi' where we have allWarningsAsErrors set 224 if (it.name in ["jvmMain", "jsMain", "concurrentMain", "commonMain"]) return 225 languageSettings { 226 optIn('kotlinx.cinterop.ExperimentalForeignApi') 227 optIn('kotlin.experimental.ExperimentalNativeApi') 228 } 229} 230 231jvmTest { 232 minHeapSize = '1g' 233 maxHeapSize = '1g' 234 enableAssertions = true 235 if (!Idea.active) { 236 // We should not set this security manager when `jvmTest` 237 // is invoked by IntelliJ IDEA since we need to pass 238 // system properties for Lincheck and stress tests. 239 // TODO Remove once IDEA is smart enough to select between `jvmTest`/`jvmStressTest`/`jvmLincheckTest` #KTIJ-599 240 systemProperty 'java.security.manager', 'kotlinx.coroutines.TestSecurityManager' 241 } 242 // 'stress' is required to be able to run all subpackage tests like ":jvmTests --tests "*channels*" -Pstress=true" 243 if (!Idea.active && rootProject.properties['stress'] == null) { 244 exclude '**/*LincheckTest*' 245 exclude '**/*StressTest.*' 246 } 247 if (Idea.active) { 248 // Configure the IDEA runner for Lincheck 249 configureJvmForLincheck(jvmTest) 250 } 251} 252 253// Setup manifest for kotlinx-coroutines-core-jvm.jar 254jvmJar { setupManifest(it) } 255 256/* 257 * Setup manifest for kotlinx-coroutines-core.jar 258 * This is convenient for users that pass -javaagent arg manually and also is a workaround #2619 and KTIJ-5659. 259 * This manifest contains reference to AgentPremain that belongs to 260 * kotlinx-coroutines-core-jvm, but our resolving machinery guarantees that 261 * any JVM project that depends on -core artifact also depends on -core-jvm one. 262 */ 263metadataJar { setupManifest(it) } 264 265static def setupManifest(Jar jar) { 266 jar.manifest { 267 attributes "Premain-Class": "kotlinx.coroutines.debug.AgentPremain" 268 attributes "Can-Retransform-Classes": "true" 269 } 270} 271 272task jvmStressTest(type: Test, dependsOn: compileTestKotlinJvm) { 273 classpath = files { jvmTest.classpath } 274 testClassesDirs = files { jvmTest.testClassesDirs } 275 minHeapSize = '1g' 276 maxHeapSize = '1g' 277 include '**/*StressTest.*' 278 enableAssertions = true 279 testLogging.showStandardStreams = true 280 systemProperty 'kotlinx.coroutines.scheduler.keep.alive.sec', '100000' // any unpark problem hangs test 281 // Adjust internal algorithmic parameters to increase the testing quality instead of performance. 282 systemProperty 'kotlinx.coroutines.semaphore.segmentSize', '1' 283 systemProperty 'kotlinx.coroutines.semaphore.maxSpinCycles', '10' 284 systemProperty 'kotlinx.coroutines.bufferedChannel.segmentSize', '2' 285 systemProperty 'kotlinx.coroutines.bufferedChannel.expandBufferCompletionWaitIterations', '1' 286} 287 288task jvmLincheckTest(type: Test, dependsOn: compileTestKotlinJvm) { 289 classpath = files { jvmTest.classpath } 290 testClassesDirs = files { jvmTest.testClassesDirs } 291 include '**/*LincheckTest*' 292 enableAssertions = true 293 testLogging.showStandardStreams = true 294 configureJvmForLincheck(jvmLincheckTest) 295} 296 297// Additional Lincheck tests with `segmentSize = 2`. 298// Some bugs cannot be revealed when storing one request per segment, 299// and some are hard to detect when storing multiple requests. 300task jvmLincheckTestAdditional(type: Test, dependsOn: compileTestKotlinJvm) { 301 classpath = files { jvmTest.classpath } 302 testClassesDirs = files { jvmTest.testClassesDirs } 303 include '**/RendezvousChannelLincheckTest*' 304 include '**/Buffered1ChannelLincheckTest*' 305 include '**/Semaphore*LincheckTest*' 306 enableAssertions = true 307 testLogging.showStandardStreams = true 308 configureJvmForLincheck(jvmLincheckTestAdditional, true) 309} 310 311static void configureJvmForLincheck(task, additional = false) { 312 task.minHeapSize = '1g' 313 task.maxHeapSize = '4g' // we may need more space for building an interleaving tree in the model checking mode 314 task.jvmArgs = ['--add-opens', 'java.base/jdk.internal.misc=ALL-UNNAMED', // required for transformation 315 '--add-exports', 'java.base/jdk.internal.util=ALL-UNNAMED'] // in the model checking mode 316 // Adjust internal algorithmic parameters to increase the testing quality instead of performance. 317 var segmentSize = additional ? '2' : '1' 318 task.systemProperty 'kotlinx.coroutines.semaphore.segmentSize', segmentSize 319 task.systemProperty 'kotlinx.coroutines.semaphore.maxSpinCycles', '1' // better for the model checking mode 320 task.systemProperty 'kotlinx.coroutines.bufferedChannel.segmentSize', segmentSize 321 task.systemProperty 'kotlinx.coroutines.bufferedChannel.expandBufferCompletionWaitIterations', '1' 322} 323 324// Always check additional test sets 325task moreTest(dependsOn: [jvmStressTest, jvmLincheckTest, jvmLincheckTestAdditional]) 326check.dependsOn moreTest 327 328def commonKoverExcludes = 329 ["kotlinx.coroutines.debug.*", // Tested by debug module 330 "kotlinx.coroutines.channels.ChannelsKt__DeprecatedKt.*", // Deprecated 331 "kotlinx.coroutines.scheduling.LimitingDispatcher", // Deprecated 332 "kotlinx.coroutines.scheduling.ExperimentalCoroutineDispatcher" // Deprecated 333 ] 334 335kover { 336 excludeTests { 337 // Always disabled, lincheck doesn't really support coverage 338 tasks("jvmLincheckTest") 339 } 340 341 excludeInstrumentation { 342 // lincheck has NPE error on `ManagedStrategyStateHolder` class 343 classes("org.jetbrains.kotlinx.lincheck.*") 344 } 345} 346 347koverReport { 348 filters { 349 excludes { 350 classes( 351 "kotlinx.coroutines.debug.*", // Tested by debug module 352 "kotlinx.coroutines.channels.ChannelsKt__DeprecatedKt.*", // Deprecated 353 "kotlinx.coroutines.scheduling.LimitingDispatcher", // Deprecated 354 "kotlinx.coroutines.scheduling.ExperimentalCoroutineDispatcher" // Deprecated 355 ) 356 } 357 } 358} 359 360task testsJar(type: Jar, dependsOn: jvmTestClasses) { 361 classifier = 'tests' 362 from(compileTestKotlinJvm.destinationDirectory) 363} 364 365artifacts { 366 archives testsJar 367} 368