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