• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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