xref: /aosp_15_r20/external/kotlinx.coroutines/kotlinx-coroutines-core/build.gradle.kts (revision 7a7160fed73afa6648ef8aa100d4a336fe921d9a)
1 import org.gradle.api.tasks.testing.*
2 import org.gradle.kotlin.dsl.*
3 import org.jetbrains.kotlin.gradle.plugin.mpp.*
4 import org.jetbrains.kotlin.gradle.targets.native.tasks.*
5 import org.jetbrains.kotlin.gradle.tasks.*
6 import org.jetbrains.kotlin.gradle.testing.*
7 
<lambda>null8 plugins {
9     kotlin("multiplatform")
10     id("org.jetbrains.kotlinx.benchmark")
11     id("org.jetbrains.dokka")
12     id("org.jetbrains.kotlinx.kover")
13 }
14 
15 apply(plugin = "pub-conventions")
16 
17 /* ==========================================================================
18   Configure source sets structure for kotlinx-coroutines-core:
19 
20      TARGETS                            SOURCE SETS
21      -------         ----------------------------------------------
22      wasmJs \----------> jsAndWasmShared --------------------+
23      js     /                                                |
24                                                              V
25      jvmCore\ --------> jvm ---------> concurrent -------> common
26      jdk8   /                           ^
27                                         |
28      ios     \                          |
29      macos   | ---> nativeDarwin ---> native ---+
30      tvos    |                         ^
31      watchos /                         |
32                                        |
33      linux  \  ---> nativeOther -------+
34      mingw  /
35 
36 
37 Explanation of JVM source sets structure:
38 
39 The overall structure is just a hack to support the scenario we are interested in:
40 
41 * We would like to have two source-sets "core" and "jdk8"
42 * "jdk8" is allowed to use API from Java 8 and from "core"
43 * "core" is prohibited to use any API from "jdk8"
44 * It is okay to have tests in a single test source-set
45 * And we want to publish a **single** artifact kotlinx-coroutines-core.jar that contains classes from both source-sets
46 * Current limitation: only classes from "core" are checked with animal-sniffer
47 
48 For that, we have following compilations:
49 * jvmMain compilation: [jvmCoreMain, jdk8Main]
50 * jvmCore compilation: [commonMain]
51 * jdk8    compilation: [commonMain, jvmCoreMain]
52 
53 Theoretically, "jvmCore" could've been "jvmMain", it is not for technical reasons,
54 here is the explanation from Seb:
55 
56 """
57 The jvmCore is theoretically not necessary. All code for jdk6 compatibility can be in jvmMain and jdk8 dependent code can be in jdk8Main.
58 Effectively there is no reason for ever putting code into jvmCoreMain.
59 However, when creating a new compilation, we have to take care of creating a defaultSourceSet. Without creating the jvmCoreMain source set,
60  the creation of the compilation fails. That is the only reason for this source set.
61 """
62  ========================================================================== */
63 
<lambda>null64 kotlin {
65     sourceSets {
66         // using the source set names from <https://kotlinlang.org/docs/multiplatform-hierarchy.html#see-the-full-hierarchy-template>
67         groupSourceSets("concurrent", listOf("jvm", "native"), listOf("common"))
68         if (project.nativeTargetsAreEnabled) {
69             // TODO: 'nativeDarwin' behaves exactly like 'apple', we can remove it
70             groupSourceSets("nativeDarwin", listOf("apple"), listOf("native"))
71             groupSourceSets("nativeOther", listOf("linux", "mingw", "androidNative"), listOf("native"))
72         }
73         jvmMain {
74             dependencies {
75                 compileOnly("com.google.android:annotations:4.1.1.4")
76             }
77         }
78         jvmTest {
79             dependencies {
80                 api("org.jetbrains.kotlinx:lincheck:${version("lincheck")}")
81                 api("org.jetbrains.kotlinx:kotlinx-knit-test:${version("knit")}")
82                 implementation(project(":android-unit-tests"))
83                 implementation("org.openjdk.jol:jol-core:0.16")
84             }
85         }
86     }
87     /*
88      * Configure two test runs for Native:
89      * 1) Main thread
90      * 2) BG thread (required for Dispatchers.Main tests on Darwin)
91      *
92      * All new MM targets are build with optimize = true to have stress tests properly run.
93      */
94     targets.withType(KotlinNativeTargetWithTests::class).configureEach {
95         binaries.getTest(DEBUG).apply {
96             optimized = true
97         }
98 
99         binaries.test("workerTest", listOf(DEBUG)) {
100             val thisTest = this
101             optimized = true
102             freeCompilerArgs = freeCompilerArgs + listOf("-e", "kotlinx.coroutines.mainBackground")
103             testRuns.create("workerTest") {
104                 this as KotlinTaskTestRun<*, *>
105                 setExecutionSourceFrom(thisTest)
106                 executionTask.configure {
107                     this as KotlinNativeTest
108                     targetName = "$targetName worker with new MM"
109                 }
110             }
111         }
112     }
113 
114     /**
115      * See: https://youtrack.jetbrains.com/issue/KTIJ-25959
116      * The introduction of jvmCore is only for CLI builds and not intended for the IDE.
117      * In the current setup there are two tooling unfriendly configurations used:
118      * 1: - jvmMain, despite being a platform source set, is not a leaf (jvmCoreMain and jdk8Main dependOn it)
119      * 2: - jvmMain effectively becomes a 'shared jvm' source set
120      *
121      * Using this kludge here, will prevent issue 2 from being visible to the IDE.
122      * Therefore jvmMain will resolve using the 'single' compilation it participates in (from the perspective of the IDE)
123      */
124     val jvmCoreMain = if (Idea.active) null else sourceSets.create("jvmCoreMain") {
125         dependsOn(sourceSets.jvmMain.get())
126     }
127     val jdk8Main = sourceSets.create("jdk8Main") {
128         dependsOn(sourceSets.jvmMain.get())
129     }
130 
131     jvm {
132         compilations.named("main") {
133             jvmCoreMain?.let { source(it) }
134             source(jdk8Main)
135         }
136 
137         /* Create compilation for jvmCore to prove that jvmMain does not rely on jdk8 */
138         compilations.create("CoreMain") {
139             /* jvmCore is automatically matched as 'defaultSourceSet' for the compilation, due to its name */
140             tasks.getByName("check").dependsOn(compileTaskProvider)
141         }
142 
143         // For animal sniffer
144         withJava()
145         compilations.create("benchmark") { associateWith(this@jvm.compilations.getByName("main")) }
146     }
147 }
148 
<lambda>null149 benchmark {
150     targets {
151         register("jvmBenchmark")
152     }
153 }
154 
155 // Update module name for metadata artifact to avoid conflicts
156 // see https://github.com/Kotlin/kotlinx.coroutines/issues/1797
<lambda>null157 val compileKotlinMetadata by tasks.getting(KotlinCompilationTask::class) {
158     compilerOptions {
159         freeCompilerArgs.addAll("-module-name", "kotlinx-coroutines-core-common")
160     }
161 }
162 
<lambda>null163 val jvmTest by tasks.getting(Test::class) {
164     minHeapSize = "1g"
165     maxHeapSize = "1g"
166     enableAssertions = true
167     if (!Idea.active) {
168         // We should not set this security manager when `jvmTest`
169         // is invoked by IntelliJ IDEA since we need to pass
170         // system properties for Lincheck and stress tests.
171         // TODO Remove once IDEA is smart enough to select between `jvmTest`/`jvmStressTest`/`jvmLincheckTest` #KTIJ-599
172         systemProperty("java.security.manager", "kotlinx.coroutines.TestSecurityManager")
173     }
174     // 'stress' is required to be able to run all subpackage tests like ":jvmTests --tests "*channels*" -Pstress=true"
175     if (!Idea.active && rootProject.properties["stress"] == null) {
176         exclude("**/*LincheckTest*")
177         exclude("**/*StressTest.*")
178     }
179     if (Idea.active) {
180         // Configure the IDEA runner for Lincheck
181         configureJvmForLincheck()
182     }
183 }
184 
185 // Setup manifest for kotlinx-coroutines-core-jvm.jar
<lambda>null186 val jvmJar by tasks.getting(Jar::class) { setupManifest(this) }
187 
188 /*
189  * Setup manifest for kotlinx-coroutines-core.jar
190  * This is convenient for users that pass -javaagent arg manually and also is a workaround #2619 and KTIJ-5659.
191  * This manifest contains reference to AgentPremain that belongs to
192  * kotlinx-coroutines-core-jvm, but our resolving machinery guarantees that
193  * any JVM project that depends on -core artifact also depends on -core-jvm one.
194  */
<lambda>null195 val allMetadataJar by tasks.getting(Jar::class) { setupManifest(this) }
196 
setupManifestnull197 fun setupManifest(jar: Jar) {
198     jar.manifest {
199         attributes(mapOf(
200             "Premain-Class" to "kotlinx.coroutines.debug.AgentPremain",
201             "Can-Retransform-Classes" to "true",
202         ))
203     }
204 }
205 
206 val compileTestKotlinJvm by tasks.getting(KotlinJvmCompile::class)
207 val jvmTestClasses by tasks.getting
208 
<lambda>null209 val jvmStressTest by tasks.registering(Test::class) {
210     dependsOn(compileTestKotlinJvm)
211     classpath = jvmTest.classpath
212     testClassesDirs = jvmTest.testClassesDirs
213     minHeapSize = "1g"
214     maxHeapSize = "1g"
215     include("**/*StressTest.*")
216     enableAssertions = true
217     testLogging.showStandardStreams = true
218     systemProperty("kotlinx.coroutines.scheduler.keep.alive.sec", 100000) // any unpark problem hangs test
219     // Adjust internal algorithmic parameters to increase the testing quality instead of performance.
220     systemProperty("kotlinx.coroutines.semaphore.segmentSize", 1)
221     systemProperty("kotlinx.coroutines.semaphore.maxSpinCycles", 10)
222     systemProperty("kotlinx.coroutines.bufferedChannel.segmentSize", 2)
223     systemProperty("kotlinx.coroutines.bufferedChannel.expandBufferCompletionWaitIterations", 1)
224 }
225 
<lambda>null226 val jvmLincheckTest by tasks.registering(Test::class) {
227     dependsOn(compileTestKotlinJvm)
228     classpath = jvmTest.classpath
229     testClassesDirs = jvmTest.testClassesDirs
230     include("**/*LincheckTest*")
231     enableAssertions = true
232     testLogging.showStandardStreams = true
233     configureJvmForLincheck()
234 }
235 
236 // Additional Lincheck tests with `segmentSize = 2`.
237 // Some bugs cannot be revealed when storing one request per segment,
238 // and some are hard to detect when storing multiple requests.
<lambda>null239 val jvmLincheckTestAdditional by tasks.registering(Test::class) {
240     dependsOn(compileTestKotlinJvm)
241     classpath = jvmTest.classpath
242     testClassesDirs = jvmTest.testClassesDirs
243     include("**/RendezvousChannelLincheckTest*")
244     include("**/Buffered1ChannelLincheckTest*")
245     include("**/Semaphore*LincheckTest*")
246     enableAssertions = true
247     testLogging.showStandardStreams = true
248     configureJvmForLincheck(segmentSize = 2)
249 }
250 
Testnull251 fun Test.configureJvmForLincheck(segmentSize: Int = 1) {
252     minHeapSize = "1g"
253     maxHeapSize = "4g" // we may need more space for building an interleaving tree in the model checking mode
254     // https://github.com/JetBrains/lincheck#java-9
255     jvmArgs = listOf("--add-opens", "java.base/jdk.internal.misc=ALL-UNNAMED",   // required for transformation
256         "--add-exports", "java.base/sun.security.action=ALL-UNNAMED",
257         "--add-exports", "java.base/jdk.internal.util=ALL-UNNAMED") // in the model checking mode
258     // Adjust internal algorithmic parameters to increase the testing quality instead of performance.
259     systemProperty("kotlinx.coroutines.semaphore.segmentSize", segmentSize)
260     systemProperty("kotlinx.coroutines.semaphore.maxSpinCycles", 1) // better for the model checking mode
261     systemProperty("kotlinx.coroutines.bufferedChannel.segmentSize", segmentSize)
262     systemProperty("kotlinx.coroutines.bufferedChannel.expandBufferCompletionWaitIterations", 1)
263 }
264 
265 // Always check additional test sets
<lambda>null266 val moreTest by tasks.registering {
267     dependsOn(listOf(jvmStressTest, jvmLincheckTest, jvmLincheckTestAdditional))
268 }
269 
<lambda>null270 val check by tasks.getting {
271     dependsOn(moreTest)
272 }
273 
<lambda>null274 kover {
275     currentProject {
276         instrumentation {
277             // Always disabled, lincheck doesn't really support coverage
278             disabledForTestTasks.addAll("jvmLincheckTest")
279 
280             // lincheck has NPE error on `ManagedStrategyStateHolder` class
281             excludedClasses.addAll("org.jetbrains.kotlinx.lincheck.*")
282         }
283     }
284 
285     reports {
286         filters {
287             excludes {
288                 classes(
289                     "kotlinx.coroutines.debug.*", // Tested by debug module
290                     "kotlinx.coroutines.channels.ChannelsKt__DeprecatedKt*", // Deprecated
291                     "kotlinx.coroutines.scheduling.LimitingDispatcher", // Deprecated
292                     "kotlinx.coroutines.scheduling.ExperimentalCoroutineDispatcher", // Deprecated
293                     "kotlinx.coroutines.flow.FlowKt__MigrationKt*", // Migrations
294                     "kotlinx.coroutines.flow.LintKt*", // Migrations
295                     "kotlinx.coroutines.internal.WeakMapCtorCache", // Fallback implementation that we never test
296                     "_COROUTINE._CREATION", // For IDE navigation
297                     "_COROUTINE._BOUNDARY", // For IDE navigation
298                 )
299             }
300         }
301     }
302 }
303 
<lambda>null304 val testsJar by tasks.registering(Jar::class) {
305     dependsOn(jvmTestClasses)
306     archiveClassifier = "tests"
307     from(compileTestKotlinJvm.destinationDirectory)
308 }
309 
<lambda>null310 artifacts {
311     archives(testsJar)
312 }
313 
314 // Workaround for https://github.com/Kotlin/dokka/issues/1833: make implicit dependency explicit
<lambda>null315 tasks.named("dokkaHtmlPartial") {
316     dependsOn(jvmJar)
317 }
318