diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropCommonizerTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropCommonizerTask.kt index 08c9e9c7ca9..c952e5fdcf0 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropCommonizerTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropCommonizerTask.kt @@ -125,7 +125,7 @@ internal open class CInteropCommonizerTask * For Gradle Configuration Cache support the Group-to-Dependencies relation should be pre-cached. * It is used during execution phase. */ - private val groupedCommonizerDependencies: Future by project.lazyFuture { + private val groupedCommonizerDependencies: Future = project.lazyFuture { val multiplatformExtension = project.multiplatformExtensionOrNull ?: return@lazyFuture emptyMap() val sourceSetsByTarget = multiplatformExtension.sourceSets.groupBy { sourceSet -> sourceSet.commonizerTarget.getOrThrow() } @@ -232,7 +232,7 @@ internal open class CInteropCommonizerTask } @get:Internal - internal val allInteropGroups: Future> by project.lazyFuture { + internal val allInteropGroups: Future> = project.lazyFuture { val dependents = allDependents.await() val allScopeSets = dependents.map { it.scopes }.toSet() val rootScopeSets = allScopeSets.filter { scopeSet -> @@ -267,7 +267,7 @@ internal open class CInteropCommonizerTask return suitableGroups.firstOrNull() } - private val allDependents: Future> by project.lazyFuture { + private val allDependents: Future> = project.lazyFuture { val multiplatformExtension = project.multiplatformExtensionOrNull ?: return@lazyFuture emptySet() val fromSharedNativeCompilations = multiplatformExtension diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/Future.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/Future.kt index 2bb3b2286bf..d2bc69c4d16 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/Future.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/Future.kt @@ -85,7 +85,7 @@ internal val Future.lenient: LenientFuture get() = LenientFutureImpl(t * * basically creating a future, which is launched lazily */ -internal fun Project.lazyFuture(block: suspend Project.() -> T): Lazy> = lazy { future(block) } +internal fun Project.lazyFuture(block: suspend Project.() -> T): Future = LazyFutureImpl(lazy { future(block) }) internal fun KotlinPluginLifecycle.future(block: suspend () -> T): Future { return FutureImpl(lifecycle = this).also { future -> @@ -158,4 +158,24 @@ private class LenientFutureImpl( return LenientFutureImpl(FutureImpl(CompletableDeferred(value))) } } +} + +private class LazyFutureImpl(private val future: Lazy>) : Future, Serializable { + override suspend fun await(): T { + return future.value.await() + } + + override fun getOrThrow(): T { + return future.value.getOrThrow() + } + + private fun writeReplace(): Any { + return Surrogate(getOrThrow()) + } + + private class Surrogate(private val value: T) : Serializable { + private fun readResolve(): Any { + return FutureImpl(CompletableDeferred(value)) + } + } } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/FutureTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/FutureTest.kt index 4c3d7531de4..0924837fc6b 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/FutureTest.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/FutureTest.kt @@ -9,19 +9,15 @@ package org.jetbrains.kotlin.gradle.unitTests import org.jetbrains.kotlin.gradle.idea.testFixtures.utils.deserialize import org.jetbrains.kotlin.gradle.idea.testFixtures.utils.serialize +import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle.IllegalLifecycleException import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle.Stage.FinaliseDsl -import org.jetbrains.kotlin.gradle.plugin.await -import org.jetbrains.kotlin.gradle.plugin.currentKotlinPluginLifecycle -import org.jetbrains.kotlin.gradle.plugin.startKotlinPluginLifecycle import org.jetbrains.kotlin.gradle.util.buildProject import org.jetbrains.kotlin.gradle.util.runLifecycleAwareTest -import org.jetbrains.kotlin.gradle.utils.CompletableFuture -import org.jetbrains.kotlin.gradle.utils.LenientFuture -import org.jetbrains.kotlin.gradle.utils.future -import org.jetbrains.kotlin.gradle.utils.lenient +import org.jetbrains.kotlin.gradle.utils.* import org.junit.Test import org.junit.jupiter.api.assertThrows +import java.util.concurrent.atomic.AtomicInteger import kotlin.test.assertEquals import kotlin.test.assertFailsWith import kotlin.test.assertNull @@ -104,4 +100,18 @@ class FutureTest { assertEquals(42, deserializedFuture.getOrNull()) } } + + @Test + fun `test - lazy future`() = project.runLifecycleAwareTest { + val futureInvocations = AtomicInteger(0) + val future = project.lazyFuture { + assertEquals(1, futureInvocations.incrementAndGet()) + } + + project.launchInStage(KotlinPluginLifecycle.Stage.last) { + assertEquals(0, futureInvocations.get()) + future.await() + assertEquals(1, futureInvocations.get()) + } + } }