From 02db8f4d0481af25f84b2486fb22431ceb1d450a Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Thu, 18 Aug 2022 16:09:48 +0200 Subject: [PATCH 01/11] Configure sourse-sets and compilations for module merger --- kotlinx-coroutines-core/build.gradle | 62 ++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 12 deletions(-) diff --git a/kotlinx-coroutines-core/build.gradle b/kotlinx-coroutines-core/build.gradle index a415aeec17..9462120ff7 100644 --- a/kotlinx-coroutines-core/build.gradle +++ b/kotlinx-coroutines-core/build.gradle @@ -19,23 +19,41 @@ apply from: rootProject.file('gradle/publish.gradle') /* ========================================================================== Configure source sets structure for kotlinx-coroutines-core: - TARGETS SOURCE SETS - ------- ---------------------------------------------- + TARGETS SOURCE SETS + ------- ---------------------------------------------- js -----------------------------------------------------+ | V - jvm -------------------------------> concurrent ---> common - ^ - ios \ | - macos | ---> nativeDarwin ---> native --+ + jvmCore\ --------> jvm ---------> concurrent -------> common + jdk8 / ^ + | + ios \ | + macos | ---> nativeDarwin ---> native ---+ tvos | ^ watchos / | | linux \ ---> nativeOther -------+ mingw / - ========================================================================== */ + +Explanation of JVM source sets structure: + +The overall structure is just a hack to support the scenario we are interested in: + +* We would like to have two source-sets "core" and "jdk8" +* "jdk8" is allowed to use API from Java 8 and from "core" +* "core" is prohibited to use any API from "jdk8" +* It is okay to have test in a single test source-set +* We want to validate source-sets with animal sniffer separately +* And we want to publish a **single** artifact kotlinx-coroutines-core.jar that contains classes from both source-sets + +For that, we have following compilations: +* jvmMain compilation: [jvmCoreMain, jdk8Main] +* jvmCore compilation: [commonMain] +* jdk8 compilation: [commonMain, jvmCoreMain] + + ========================================================================== */ project.ext.sourceSetSuffixes = ["Main", "Test"] @@ -68,15 +86,12 @@ if (rootProject.ext.native_targets_enabled) { /* ========================================================================== */ + /* * 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 two test runs: * 1) New memory model, Main thread @@ -104,13 +119,36 @@ kotlin { } } + def jvmMain = sourceSets.jvmMain + def jvmCoreMain = sourceSets.create('jvmCoreMain') + def jdk8Main = sourceSets.create('jdk8Main') + jvmCoreMain.dependsOn(jvmMain) + jdk8Main.dependsOn(jvmMain) + + sourceSets.forEach { + SourceSetsKt.configureMultiplatform(it) + } + + jvm { + def main = compilations.main + main.source(jvmCoreMain) + main.source(jdk8Main) + + /* Create compilation for jvmCore to prove that jvmMain does not rely on jdk8 */ + compilations.create('CoreMain') { + /* jvmCore is automatically matched as 'defaultSourceSet' for the compilation */ + tasks.getByName('check').dependsOn(compileKotlinTaskProvider) + } + } +} + +kotlin { jvm { // For animal sniffer withJava() } } - configurations { configureKotlinJvmPlatform(kotlinCompilerPluginClasspath) } From 82af0337edae6e24d3040167ca52376d1dd36265 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Thu, 18 Aug 2022 17:17:05 +0200 Subject: [PATCH 02/11] Extract all API from -jdk8 module into core and tweak animal sniffer --- .../animalsniffer-conventions.gradle.kts | 14 ++++++++++++ .../api/kotlinx-coroutines-jdk8.api | 22 ------------------- .../api/kotlinx-coroutines-core.api | 22 +++++++++++++++++++ .../jdk8}/src/future/Future.kt | 0 .../jdk8}/src/stream/Stream.kt | 0 .../jdk8}/src/time/Time.kt | 0 .../jvm/test/jdk8}/future/AsFutureTest.kt | 0 ...eferredUnhandledCompletionExceptionTest.kt | 4 ++-- .../test/jdk8}/future/FutureExceptionsTest.kt | 0 .../jvm/test/jdk8}/future/FutureTest.kt | 0 .../test/jdk8}/stream/ConsumeAsFlowTest.kt | 0 .../test/jdk8}/time/DurationOverflowTest.kt | 0 .../jvm/test/jdk8}/time/FlowDebounceTest.kt | 0 .../jvm/test/jdk8}/time/FlowSampleTest.kt | 0 .../jvm/test/jdk8}/time/WithTimeoutTest.kt | 0 15 files changed, 38 insertions(+), 24 deletions(-) rename {integration/kotlinx-coroutines-jdk8 => kotlinx-coroutines-core/jdk8}/src/future/Future.kt (100%) rename {integration/kotlinx-coroutines-jdk8 => kotlinx-coroutines-core/jdk8}/src/stream/Stream.kt (100%) rename {integration/kotlinx-coroutines-jdk8 => kotlinx-coroutines-core/jdk8}/src/time/Time.kt (100%) rename {integration/kotlinx-coroutines-jdk8/test => kotlinx-coroutines-core/jvm/test/jdk8}/future/AsFutureTest.kt (100%) rename {integration/kotlinx-coroutines-jdk8/test => kotlinx-coroutines-core/jvm/test/jdk8}/future/FutureAsDeferredUnhandledCompletionExceptionTest.kt (91%) rename {integration/kotlinx-coroutines-jdk8/test => kotlinx-coroutines-core/jvm/test/jdk8}/future/FutureExceptionsTest.kt (100%) rename {integration/kotlinx-coroutines-jdk8/test => kotlinx-coroutines-core/jvm/test/jdk8}/future/FutureTest.kt (100%) rename {integration/kotlinx-coroutines-jdk8/test => kotlinx-coroutines-core/jvm/test/jdk8}/stream/ConsumeAsFlowTest.kt (100%) rename {integration/kotlinx-coroutines-jdk8/test => kotlinx-coroutines-core/jvm/test/jdk8}/time/DurationOverflowTest.kt (100%) rename {integration/kotlinx-coroutines-jdk8/test => kotlinx-coroutines-core/jvm/test/jdk8}/time/FlowDebounceTest.kt (100%) rename {integration/kotlinx-coroutines-jdk8/test => kotlinx-coroutines-core/jvm/test/jdk8}/time/FlowSampleTest.kt (100%) rename {integration/kotlinx-coroutines-jdk8/test => kotlinx-coroutines-core/jvm/test/jdk8}/time/WithTimeoutTest.kt (100%) diff --git a/buildSrc/src/main/kotlin/animalsniffer-conventions.gradle.kts b/buildSrc/src/main/kotlin/animalsniffer-conventions.gradle.kts index f00a0b315f..3dd1f6ac80 100644 --- a/buildSrc/src/main/kotlin/animalsniffer-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/animalsniffer-conventions.gradle.kts @@ -17,6 +17,20 @@ configure(subprojects) { signature("net.sf.androidscents.signature:android-api-level-14:4.0_r4@signature") signature("org.codehaus.mojo.signature:java17:1.0@signature") } + + if (project.name == coreModule) { + // Precise files so nothing from core is accidentally skipepd + tasks.withType().configureEach { + exclude("**/future/FutureKt*") + exclude("**/future/ContinuationConsumer*") + exclude("**/future/CompletableFutureCoroutine*") + + exclude("**/stream/StreamKt*") + exclude("**/stream/StreamFlow*") + + exclude("**/time/TimeKt*") + } + } } } diff --git a/integration/kotlinx-coroutines-jdk8/api/kotlinx-coroutines-jdk8.api b/integration/kotlinx-coroutines-jdk8/api/kotlinx-coroutines-jdk8.api index 4ee57845b2..e69de29bb2 100644 --- a/integration/kotlinx-coroutines-jdk8/api/kotlinx-coroutines-jdk8.api +++ b/integration/kotlinx-coroutines-jdk8/api/kotlinx-coroutines-jdk8.api @@ -1,22 +0,0 @@ -public final class kotlinx/coroutines/future/FutureKt { - public static final fun asCompletableFuture (Lkotlinx/coroutines/Deferred;)Ljava/util/concurrent/CompletableFuture; - public static final fun asCompletableFuture (Lkotlinx/coroutines/Job;)Ljava/util/concurrent/CompletableFuture; - public static final fun asDeferred (Ljava/util/concurrent/CompletionStage;)Lkotlinx/coroutines/Deferred; - public static final fun await (Ljava/util/concurrent/CompletionStage;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun future (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlinx/coroutines/CoroutineStart;Lkotlin/jvm/functions/Function2;)Ljava/util/concurrent/CompletableFuture; - public static synthetic fun future$default (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlinx/coroutines/CoroutineStart;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Ljava/util/concurrent/CompletableFuture; -} - -public final class kotlinx/coroutines/stream/StreamKt { - public static final fun consumeAsFlow (Ljava/util/stream/Stream;)Lkotlinx/coroutines/flow/Flow; -} - -public final class kotlinx/coroutines/time/TimeKt { - public static final fun debounce (Lkotlinx/coroutines/flow/Flow;Ljava/time/Duration;)Lkotlinx/coroutines/flow/Flow; - public static final fun delay (Ljava/time/Duration;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun onTimeout (Lkotlinx/coroutines/selects/SelectBuilder;Ljava/time/Duration;Lkotlin/jvm/functions/Function1;)V - public static final fun sample (Lkotlinx/coroutines/flow/Flow;Ljava/time/Duration;)Lkotlinx/coroutines/flow/Flow; - public static final fun withTimeout (Ljava/time/Duration;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun withTimeoutOrNull (Ljava/time/Duration;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; -} - diff --git a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api index ad04fb9c8e..3a2d08f428 100644 --- a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api +++ b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api @@ -1176,6 +1176,15 @@ public final class kotlinx/coroutines/flow/internal/SendingCollector : kotlinx/c public fun emit (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } +public final class kotlinx/coroutines/future/FutureKt { + public static final fun asCompletableFuture (Lkotlinx/coroutines/Deferred;)Ljava/util/concurrent/CompletableFuture; + public static final fun asCompletableFuture (Lkotlinx/coroutines/Job;)Ljava/util/concurrent/CompletableFuture; + public static final fun asDeferred (Ljava/util/concurrent/CompletionStage;)Lkotlinx/coroutines/Deferred; + public static final fun await (Ljava/util/concurrent/CompletionStage;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun future (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlinx/coroutines/CoroutineStart;Lkotlin/jvm/functions/Function2;)Ljava/util/concurrent/CompletableFuture; + public static synthetic fun future$default (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlinx/coroutines/CoroutineStart;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Ljava/util/concurrent/CompletableFuture; +} + public final class kotlinx/coroutines/intrinsics/CancellableKt { public static final fun startCoroutineCancellable (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)V } @@ -1292,6 +1301,10 @@ public final class kotlinx/coroutines/selects/WhileSelectKt { public static final fun whileSelect (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } +public final class kotlinx/coroutines/stream/StreamKt { + public static final fun consumeAsFlow (Ljava/util/stream/Stream;)Lkotlinx/coroutines/flow/Flow; +} + public abstract interface class kotlinx/coroutines/sync/Mutex { public abstract fun getOnLock ()Lkotlinx/coroutines/selects/SelectClause2; public abstract fun holdsLock (Ljava/lang/Object;)Z @@ -1327,3 +1340,12 @@ public final class kotlinx/coroutines/sync/SemaphoreKt { public static final fun withPermit (Lkotlinx/coroutines/sync/Semaphore;Lkotlin/jvm/functions/Function0;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } +public final class kotlinx/coroutines/time/TimeKt { + public static final fun debounce (Lkotlinx/coroutines/flow/Flow;Ljava/time/Duration;)Lkotlinx/coroutines/flow/Flow; + public static final fun delay (Ljava/time/Duration;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun onTimeout (Lkotlinx/coroutines/selects/SelectBuilder;Ljava/time/Duration;Lkotlin/jvm/functions/Function1;)V + public static final fun sample (Lkotlinx/coroutines/flow/Flow;Ljava/time/Duration;)Lkotlinx/coroutines/flow/Flow; + public static final fun withTimeout (Ljava/time/Duration;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun withTimeoutOrNull (Ljava/time/Duration;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + diff --git a/integration/kotlinx-coroutines-jdk8/src/future/Future.kt b/kotlinx-coroutines-core/jdk8/src/future/Future.kt similarity index 100% rename from integration/kotlinx-coroutines-jdk8/src/future/Future.kt rename to kotlinx-coroutines-core/jdk8/src/future/Future.kt diff --git a/integration/kotlinx-coroutines-jdk8/src/stream/Stream.kt b/kotlinx-coroutines-core/jdk8/src/stream/Stream.kt similarity index 100% rename from integration/kotlinx-coroutines-jdk8/src/stream/Stream.kt rename to kotlinx-coroutines-core/jdk8/src/stream/Stream.kt diff --git a/integration/kotlinx-coroutines-jdk8/src/time/Time.kt b/kotlinx-coroutines-core/jdk8/src/time/Time.kt similarity index 100% rename from integration/kotlinx-coroutines-jdk8/src/time/Time.kt rename to kotlinx-coroutines-core/jdk8/src/time/Time.kt diff --git a/integration/kotlinx-coroutines-jdk8/test/future/AsFutureTest.kt b/kotlinx-coroutines-core/jvm/test/jdk8/future/AsFutureTest.kt similarity index 100% rename from integration/kotlinx-coroutines-jdk8/test/future/AsFutureTest.kt rename to kotlinx-coroutines-core/jvm/test/jdk8/future/AsFutureTest.kt diff --git a/integration/kotlinx-coroutines-jdk8/test/future/FutureAsDeferredUnhandledCompletionExceptionTest.kt b/kotlinx-coroutines-core/jvm/test/jdk8/future/FutureAsDeferredUnhandledCompletionExceptionTest.kt similarity index 91% rename from integration/kotlinx-coroutines-jdk8/test/future/FutureAsDeferredUnhandledCompletionExceptionTest.kt rename to kotlinx-coroutines-core/jvm/test/jdk8/future/FutureAsDeferredUnhandledCompletionExceptionTest.kt index bf810af7aa..9c9c97ecdf 100644 --- a/integration/kotlinx-coroutines-jdk8/test/future/FutureAsDeferredUnhandledCompletionExceptionTest.kt +++ b/kotlinx-coroutines-core/jvm/test/jdk8/future/FutureAsDeferredUnhandledCompletionExceptionTest.kt @@ -1,8 +1,8 @@ /* - * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -package future +package kotlinx.coroutines.future import kotlinx.coroutines.* import kotlinx.coroutines.future.* diff --git a/integration/kotlinx-coroutines-jdk8/test/future/FutureExceptionsTest.kt b/kotlinx-coroutines-core/jvm/test/jdk8/future/FutureExceptionsTest.kt similarity index 100% rename from integration/kotlinx-coroutines-jdk8/test/future/FutureExceptionsTest.kt rename to kotlinx-coroutines-core/jvm/test/jdk8/future/FutureExceptionsTest.kt diff --git a/integration/kotlinx-coroutines-jdk8/test/future/FutureTest.kt b/kotlinx-coroutines-core/jvm/test/jdk8/future/FutureTest.kt similarity index 100% rename from integration/kotlinx-coroutines-jdk8/test/future/FutureTest.kt rename to kotlinx-coroutines-core/jvm/test/jdk8/future/FutureTest.kt diff --git a/integration/kotlinx-coroutines-jdk8/test/stream/ConsumeAsFlowTest.kt b/kotlinx-coroutines-core/jvm/test/jdk8/stream/ConsumeAsFlowTest.kt similarity index 100% rename from integration/kotlinx-coroutines-jdk8/test/stream/ConsumeAsFlowTest.kt rename to kotlinx-coroutines-core/jvm/test/jdk8/stream/ConsumeAsFlowTest.kt diff --git a/integration/kotlinx-coroutines-jdk8/test/time/DurationOverflowTest.kt b/kotlinx-coroutines-core/jvm/test/jdk8/time/DurationOverflowTest.kt similarity index 100% rename from integration/kotlinx-coroutines-jdk8/test/time/DurationOverflowTest.kt rename to kotlinx-coroutines-core/jvm/test/jdk8/time/DurationOverflowTest.kt diff --git a/integration/kotlinx-coroutines-jdk8/test/time/FlowDebounceTest.kt b/kotlinx-coroutines-core/jvm/test/jdk8/time/FlowDebounceTest.kt similarity index 100% rename from integration/kotlinx-coroutines-jdk8/test/time/FlowDebounceTest.kt rename to kotlinx-coroutines-core/jvm/test/jdk8/time/FlowDebounceTest.kt diff --git a/integration/kotlinx-coroutines-jdk8/test/time/FlowSampleTest.kt b/kotlinx-coroutines-core/jvm/test/jdk8/time/FlowSampleTest.kt similarity index 100% rename from integration/kotlinx-coroutines-jdk8/test/time/FlowSampleTest.kt rename to kotlinx-coroutines-core/jvm/test/jdk8/time/FlowSampleTest.kt diff --git a/integration/kotlinx-coroutines-jdk8/test/time/WithTimeoutTest.kt b/kotlinx-coroutines-core/jvm/test/jdk8/time/WithTimeoutTest.kt similarity index 100% rename from integration/kotlinx-coroutines-jdk8/test/time/WithTimeoutTest.kt rename to kotlinx-coroutines-core/jvm/test/jdk8/time/WithTimeoutTest.kt From 09b9da0da5bfa90800623eb993e192b1ca47ca45 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Thu, 18 Aug 2022 17:21:19 +0200 Subject: [PATCH 03/11] Answer the obvious question to current sourse-set structure --- kotlinx-coroutines-core/build.gradle | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/kotlinx-coroutines-core/build.gradle b/kotlinx-coroutines-core/build.gradle index 9462120ff7..b863c0990c 100644 --- a/kotlinx-coroutines-core/build.gradle +++ b/kotlinx-coroutines-core/build.gradle @@ -53,6 +53,15 @@ For that, we have following compilations: * jvmCore compilation: [commonMain] * jdk8 compilation: [commonMain, jvmCoreMain] +Theoretically, "jvmCore" could've been "jvmMain", it is not for technical reasons, +here is the explanation from Seb: + +""" +The jvmCore is theoretically not necessary. All code for jdk6 compatibility can be in jvmMain and jdk8 dependent code can be in jdk8Main. +Effectively there is no reason for ever putting code into jvmCoreMain. +However, when creating a new compilation, we have to take care of creating a defaultSourceSet. Without creating the jvmCoreMain source set, + the creation of the compilation fails. That is the only reason for this source set. +""" ========================================================================== */ project.ext.sourceSetSuffixes = ["Main", "Test"] From 956e2a3d20a63afd03de95a06f13baec93e4dcc5 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Thu, 18 Aug 2022 18:00:00 +0200 Subject: [PATCH 04/11] ~tweak Dokka and Knit, add integration test --- README.md | 6 +- build.gradle | 6 +- buildSrc/src/main/kotlin/Projects.kt | 1 + .../kotlin/Jdk8InCoreIntegration.kt | 21 ++++++ integration/README.md | 1 - integration/kotlinx-coroutines-jdk8/README.md | 69 +------------------ 6 files changed, 30 insertions(+), 74 deletions(-) create mode 100644 integration-testing/src/withGuavaTest/kotlin/Jdk8InCoreIntegration.kt diff --git a/README.md b/README.md index fac4a1a2e7..6262a8fa44 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ suspend fun main() = coroutineScope { * [core/jvm](kotlinx-coroutines-core/jvm/) — additional core features available on Kotlin/JVM: * [Dispatchers.IO] dispatcher for blocking coroutines; * [Executor.asCoroutineDispatcher][asCoroutineDispatcher] extension, custom thread pools, and more. + * Integrations with `CompletableFuture` and JVM-specific extensions. * [core/js](kotlinx-coroutines-core/js/) — additional core features available on Kotlin/JS: * Integration with `Promise` via [Promise.await] and [promise] builder; * Integration with `Window` via [Window.asCoroutineDispatcher], etc. @@ -56,7 +57,7 @@ suspend fun main() = coroutineScope { * [ui](ui/README.md) — modules that provide coroutine dispatchers for various single-threaded UI libraries: * Android, JavaFX, and Swing. * [integration](integration/README.md) — modules that provide integration with various asynchronous callback- and future-based libraries: - * JDK8 [CompletionStage.await], Guava [ListenableFuture.await], and Google Play Services [Task.await]; + Guava [ListenableFuture.await], and Google Play Services [Task.await]; * SLF4J MDC integration via [MDCContext]. ## Documentation @@ -259,9 +260,6 @@ See [Contributing Guidelines](CONTRIBUTING.md). - -[CompletionStage.await]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-jdk8/kotlinx.coroutines.future/await.html - diff --git a/build.gradle b/build.gradle index d5b231d969..4377240625 100644 --- a/build.gradle +++ b/build.gradle @@ -223,7 +223,9 @@ def core_docs_url = "https://kotlinlang.org/api/kotlinx.coroutines/$coreModule/" def core_docs_file = "$projectDir/kotlinx-coroutines-core/build/dokka/htmlPartial/package-list" apply plugin: "org.jetbrains.dokka" -configure(subprojects.findAll { !unpublished.contains(it.name) && it.name != coreModule }) { +configure(subprojects.findAll { !unpublished.contains(it.name) + && it.name != coreModule + && it.name != jdk8ObsoleteModule}) { if (it.name != 'kotlinx-coroutines-bom') { apply from: rootProject.file('gradle/dokka.gradle.kts') } @@ -232,7 +234,7 @@ configure(subprojects.findAll { !unpublished.contains(it.name) && it.name != cor configure(subprojects.findAll { !unpublished.contains(it.name) }) { if (it.name != "kotlinx-coroutines-bom") { - if (it.name != coreModule) { + if (it.name != coreModule && it.name != jdk8ObsoleteModule) { tasks.withType(DokkaTaskPartial.class) { dokkaSourceSets.configureEach { externalDocumentationLink { diff --git a/buildSrc/src/main/kotlin/Projects.kt b/buildSrc/src/main/kotlin/Projects.kt index af7098935d..2442c50934 100644 --- a/buildSrc/src/main/kotlin/Projects.kt +++ b/buildSrc/src/main/kotlin/Projects.kt @@ -8,6 +8,7 @@ fun Project.version(target: String): String = property("${target}_version") as String val coreModule = "kotlinx-coroutines-core" +val jdk8ObsoleteModule = "kotlinx-coroutines-jdk8" val testModule = "kotlinx-coroutines-test" val multiplatform = setOf(coreModule, testModule) diff --git a/integration-testing/src/withGuavaTest/kotlin/Jdk8InCoreIntegration.kt b/integration-testing/src/withGuavaTest/kotlin/Jdk8InCoreIntegration.kt new file mode 100644 index 0000000000..efa484c072 --- /dev/null +++ b/integration-testing/src/withGuavaTest/kotlin/Jdk8InCoreIntegration.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2016-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ +package kotlinx.coroutines + +import kotlinx.coroutines.future.* +import org.junit.Test +import kotlin.test.* + +/* + * Temporary test that verifies both signatures from jdk8 and from core source-sets are used + */ +class Jdk8InCoreIntegration { + + @Test + fun testFuture() = runBlocking { + val future = future { yield(); 42 } + future.whenComplete { r, _ -> assertEquals(42, r) } + assertEquals(42, future.await()) + } +} diff --git a/integration/README.md b/integration/README.md index 89100179a8..54dd96bbc9 100644 --- a/integration/README.md +++ b/integration/README.md @@ -5,7 +5,6 @@ Module name below corresponds to the artifact name in Maven/Gradle. ## Modules -* [kotlinx-coroutines-jdk8](kotlinx-coroutines-jdk8/README.md) -- integration with JDK8 `CompletableFuture` (Android API level 24). * [kotlinx-coroutines-guava](kotlinx-coroutines-guava/README.md) -- integration with Guava [ListenableFuture](https://github.com/google/guava/wiki/ListenableFutureExplained). * [kotlinx-coroutines-slf4j](kotlinx-coroutines-slf4j/README.md) -- integration with SLF4J [MDC](https://logback.qos.ch/manual/mdc.html). * [kotlinx-coroutines-play-services](kotlinx-coroutines-play-services) -- integration with Google Play Services [Tasks API](https://developers.google.com/android/guides/tasks). diff --git a/integration/kotlinx-coroutines-jdk8/README.md b/integration/kotlinx-coroutines-jdk8/README.md index 321e293414..56e145fc4e 100644 --- a/integration/kotlinx-coroutines-jdk8/README.md +++ b/integration/kotlinx-coroutines-jdk8/README.md @@ -1,68 +1,3 @@ -# Module kotlinx-coroutines-jdk8 +# Stub module -Integration with JDK8 [CompletableFuture] (Android API level 24). - -Coroutine builders: - -| **Name** | **Result** | **Scope** | **Description** -| -------- | ------------------- | ---------------- | --------------- -| [future] | [CompletableFuture] | [CoroutineScope] | Returns a single value with the future result - -Extension functions: - -| **Name** | **Description** -| -------- | --------------- -| [CompletionStage.await][java.util.concurrent.CompletionStage.await] | Awaits for completion of the completion stage -| [CompletionStage.asDeferred][java.util.concurrent.CompletionStage.asDeferred] | Converts completion stage to an instance of [Deferred] -| [Deferred.asCompletableFuture][kotlinx.coroutines.Deferred.asCompletableFuture] | Converts a deferred value to the future - -## Example - -Given the following functions defined in some Java API: - -```java -public CompletableFuture loadImageAsync(String name); // starts async image loading -public Image combineImages(Image image1, Image image2); // synchronously combines two images using some algorithm -``` - -We can consume this API from Kotlin coroutine to load two images and combine then asynchronously. -The resulting function returns `CompletableFuture` for ease of use back from Java. - -```kotlin -fun combineImagesAsync(name1: String, name2: String): CompletableFuture = future { - val future1 = loadImageAsync(name1) // start loading first image - val future2 = loadImageAsync(name2) // start loading second image - combineImages(future1.await(), future2.await()) // wait for both, combine, and return result -} -``` - -Note that this module should be used only for integration with existing Java APIs based on `CompletableFuture`. -Writing pure-Kotlin code that uses `CompletableFuture` is highly not recommended, since the resulting APIs based -on the futures are quite error-prone. See the discussion on -[Asynchronous Programming Styles](https://github.com/Kotlin/KEEP/blob/master/proposals/coroutines.md#asynchronous-programming-styles) -for details on general problems pertaining to any future-based API and keep in mind that `CompletableFuture` exposes -a _blocking_ method -[get](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Future.html#get--) -that makes it especially bad choice for coroutine-based Kotlin code. - -# Package kotlinx.coroutines.future - -Integration with JDK8 [CompletableFuture] (Android API level 24). - -[CompletableFuture]: https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html - - - - -[CoroutineScope]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html -[Deferred]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/index.html - - - - -[future]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-jdk8/kotlinx.coroutines.future/future.html -[java.util.concurrent.CompletionStage.await]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-jdk8/kotlinx.coroutines.future/await.html -[java.util.concurrent.CompletionStage.asDeferred]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-jdk8/kotlinx.coroutines.future/as-deferred.html -[kotlinx.coroutines.Deferred.asCompletableFuture]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-jdk8/kotlinx.coroutines.future/as-completable-future.html - - +Stub module for backwards compatibility. Since 1.7.0, this module was merged with core. From aad31fdda187388b87a587a985abde3b83a38595 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Tue, 11 Oct 2022 18:38:14 +0300 Subject: [PATCH 05/11] Apply suggestions from code review Co-authored-by: dkhalanskyjb <52952525+dkhalanskyjb@users.noreply.github.com> --- README.md | 2 +- buildSrc/src/main/kotlin/animalsniffer-conventions.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6262a8fa44..c11d97258a 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ suspend fun main() = coroutineScope { * [ui](ui/README.md) — modules that provide coroutine dispatchers for various single-threaded UI libraries: * Android, JavaFX, and Swing. * [integration](integration/README.md) — modules that provide integration with various asynchronous callback- and future-based libraries: - Guava [ListenableFuture.await], and Google Play Services [Task.await]; + * Guava [ListenableFuture.await], and Google Play Services [Task.await]; * SLF4J MDC integration via [MDCContext]. ## Documentation diff --git a/buildSrc/src/main/kotlin/animalsniffer-conventions.gradle.kts b/buildSrc/src/main/kotlin/animalsniffer-conventions.gradle.kts index 3dd1f6ac80..63af718320 100644 --- a/buildSrc/src/main/kotlin/animalsniffer-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/animalsniffer-conventions.gradle.kts @@ -19,7 +19,7 @@ configure(subprojects) { } if (project.name == coreModule) { - // Precise files so nothing from core is accidentally skipepd + // Specific files so nothing from core is accidentally skipped tasks.withType().configureEach { exclude("**/future/FutureKt*") exclude("**/future/ContinuationConsumer*") From 0ca2fd8e0e6eb3089726653414b1fa4237aa145e Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Tue, 11 Oct 2022 17:55:53 +0200 Subject: [PATCH 06/11] ~update comments --- integration-testing/build.gradle | 1 + .../src/withGuavaTest/kotlin/Jdk8InCoreIntegration.kt | 2 +- kotlinx-coroutines-core/build.gradle | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/integration-testing/build.gradle b/integration-testing/build.gradle index 04be36fac9..6f28cf6484 100644 --- a/integration-testing/build.gradle +++ b/integration-testing/build.gradle @@ -23,6 +23,7 @@ dependencies { sourceSets { // Test that relies on Guava to reflectively check all Throwable subclasses in coroutines + // Also other non-specific tests piggyback on that one withGuavaTest { kotlin compileClasspath += sourceSets.test.runtimeClasspath diff --git a/integration-testing/src/withGuavaTest/kotlin/Jdk8InCoreIntegration.kt b/integration-testing/src/withGuavaTest/kotlin/Jdk8InCoreIntegration.kt index efa484c072..84889f2e9b 100644 --- a/integration-testing/src/withGuavaTest/kotlin/Jdk8InCoreIntegration.kt +++ b/integration-testing/src/withGuavaTest/kotlin/Jdk8InCoreIntegration.kt @@ -8,7 +8,7 @@ import org.junit.Test import kotlin.test.* /* - * Temporary test that verifies both signatures from jdk8 and from core source-sets are used + * Integration test that ensures both signatures from jdk8 and from core source-sets are used */ class Jdk8InCoreIntegration { diff --git a/kotlinx-coroutines-core/build.gradle b/kotlinx-coroutines-core/build.gradle index b863c0990c..9778afd5c7 100644 --- a/kotlinx-coroutines-core/build.gradle +++ b/kotlinx-coroutines-core/build.gradle @@ -44,9 +44,9 @@ The overall structure is just a hack to support the scenario we are interested i * We would like to have two source-sets "core" and "jdk8" * "jdk8" is allowed to use API from Java 8 and from "core" * "core" is prohibited to use any API from "jdk8" -* It is okay to have test in a single test source-set -* We want to validate source-sets with animal sniffer separately +* It is okay to have tests in a single test source-set * And we want to publish a **single** artifact kotlinx-coroutines-core.jar that contains classes from both source-sets +* Current limitation: only classes from "core" are checked with animal-sniffer For that, we have following compilations: * jvmMain compilation: [jvmCoreMain, jdk8Main] @@ -145,7 +145,7 @@ kotlin { /* Create compilation for jvmCore to prove that jvmMain does not rely on jdk8 */ compilations.create('CoreMain') { - /* jvmCore is automatically matched as 'defaultSourceSet' for the compilation */ + /* jvmCore is automatically matched as 'defaultSourceSet' for the compilation, due to its name */ tasks.getByName('check').dependsOn(compileKotlinTaskProvider) } } From b29a67586ac0aadccc58a78db44f42d653e52466 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Wed, 12 Oct 2022 13:18:31 +0300 Subject: [PATCH 07/11] Update integration-testing/src/withGuavaTest/kotlin/Jdk8InCoreIntegration.kt Co-authored-by: dkhalanskyjb <52952525+dkhalanskyjb@users.noreply.github.com> --- .../src/withGuavaTest/kotlin/Jdk8InCoreIntegration.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-testing/src/withGuavaTest/kotlin/Jdk8InCoreIntegration.kt b/integration-testing/src/withGuavaTest/kotlin/Jdk8InCoreIntegration.kt index 84889f2e9b..91eef7e24b 100644 --- a/integration-testing/src/withGuavaTest/kotlin/Jdk8InCoreIntegration.kt +++ b/integration-testing/src/withGuavaTest/kotlin/Jdk8InCoreIntegration.kt @@ -8,7 +8,7 @@ import org.junit.Test import kotlin.test.* /* - * Integration test that ensures both signatures from jdk8 and from core source-sets are used + * Integration test that ensures signatures from both the jdk8 and the core source sets of the kotlinx-coroutines-core subproject are used. */ class Jdk8InCoreIntegration { From cf758f9106401364eb9e1e7979403c9fc47ab30f Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Wed, 12 Oct 2022 12:29:23 +0200 Subject: [PATCH 08/11] ~rename test sourceset [NB: wait for TC to build, I'm unable to check it locally] --- integration-testing/build.gradle | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/integration-testing/build.gradle b/integration-testing/build.gradle index 6f28cf6484..f845816f34 100644 --- a/integration-testing/build.gradle +++ b/integration-testing/build.gradle @@ -22,9 +22,8 @@ dependencies { } sourceSets { - // Test that relies on Guava to reflectively check all Throwable subclasses in coroutines - // Also other non-specific tests piggyback on that one - withGuavaTest { + // An assortment of tests for behavior of the core coroutines module on JVM + jvmCoreTest { kotlin compileClasspath += sourceSets.test.runtimeClasspath runtimeClasspath += sourceSets.test.runtimeClasspath @@ -87,9 +86,9 @@ compileDebugAgentTestKotlin { } } -task withGuavaTest(type: Test) { +task jvmCoreTest(type: Test) { environment "version", coroutines_version - def sourceSet = sourceSets.withGuavaTest + def sourceSet = sourceSets.jvmCoreTest testClassesDirs = sourceSet.output.classesDirs classpath = sourceSet.runtimeClasspath } @@ -129,5 +128,5 @@ compileTestKotlin { } check { - dependsOn([withGuavaTest, debugDynamicAgentTest, mavenTest, debugAgentTest, coreAgentTest, 'smokeTest:build']) + dependsOn([jvmCoreTest, debugDynamicAgentTest, mavenTest, debugAgentTest, coreAgentTest, 'smokeTest:build']) } From 2d4bb438805103fc3070a89f1e8e7a8c358ddf79 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Wed, 12 Oct 2022 12:35:11 +0200 Subject: [PATCH 09/11] ~ --- kotlinx-coroutines-core/build.gradle | 4 ---- 1 file changed, 4 deletions(-) diff --git a/kotlinx-coroutines-core/build.gradle b/kotlinx-coroutines-core/build.gradle index 9778afd5c7..54883ff233 100644 --- a/kotlinx-coroutines-core/build.gradle +++ b/kotlinx-coroutines-core/build.gradle @@ -148,11 +148,7 @@ kotlin { /* jvmCore is automatically matched as 'defaultSourceSet' for the compilation, due to its name */ tasks.getByName('check').dependsOn(compileKotlinTaskProvider) } - } -} -kotlin { - jvm { // For animal sniffer withJava() } From 956ce964b55a3981d7f279e297cdd8f104e039dc Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Wed, 12 Oct 2022 12:46:25 +0200 Subject: [PATCH 10/11] ~rename dir --- .../kotlin/Jdk8InCoreIntegration.kt | 0 .../kotlin/ListAllCoroutineThrowableSubclassesTest.kt | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename integration-testing/src/{withGuavaTest => jvmCoreTest}/kotlin/Jdk8InCoreIntegration.kt (100%) rename integration-testing/src/{withGuavaTest => jvmCoreTest}/kotlin/ListAllCoroutineThrowableSubclassesTest.kt (100%) diff --git a/integration-testing/src/withGuavaTest/kotlin/Jdk8InCoreIntegration.kt b/integration-testing/src/jvmCoreTest/kotlin/Jdk8InCoreIntegration.kt similarity index 100% rename from integration-testing/src/withGuavaTest/kotlin/Jdk8InCoreIntegration.kt rename to integration-testing/src/jvmCoreTest/kotlin/Jdk8InCoreIntegration.kt diff --git a/integration-testing/src/withGuavaTest/kotlin/ListAllCoroutineThrowableSubclassesTest.kt b/integration-testing/src/jvmCoreTest/kotlin/ListAllCoroutineThrowableSubclassesTest.kt similarity index 100% rename from integration-testing/src/withGuavaTest/kotlin/ListAllCoroutineThrowableSubclassesTest.kt rename to integration-testing/src/jvmCoreTest/kotlin/ListAllCoroutineThrowableSubclassesTest.kt From 39181a47aa6c172f14ee8fabd90ccddbd67e46c5 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Wed, 12 Oct 2022 23:42:31 +0200 Subject: [PATCH 11/11] ~update sniffer data --- buildSrc/src/main/kotlin/animalsniffer-conventions.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/animalsniffer-conventions.gradle.kts b/buildSrc/src/main/kotlin/animalsniffer-conventions.gradle.kts index 63af718320..639245b6e5 100644 --- a/buildSrc/src/main/kotlin/animalsniffer-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/animalsniffer-conventions.gradle.kts @@ -22,7 +22,7 @@ configure(subprojects) { // Specific files so nothing from core is accidentally skipped tasks.withType().configureEach { exclude("**/future/FutureKt*") - exclude("**/future/ContinuationConsumer*") + exclude("**/future/ContinuationHandler*") exclude("**/future/CompletableFutureCoroutine*") exclude("**/stream/StreamKt*")