[Gradle] Introduce 'Future' to represent deferred values/calculations
^KT-34662 Verification Pending
This commit is contained in:
committed by
Space Team
parent
028780ae97
commit
02120a6822
+2
-2
@@ -426,7 +426,7 @@ private class KotlinPluginLifecycleImpl(override val project: Project) : KotlinP
|
||||
|
||||
enqueuedActions.getValue(stage).addLast(action)
|
||||
|
||||
if (stage == Stage.Configure) {
|
||||
if (stage == Stage.Configure || isFinished.get()) {
|
||||
loopIfNecessary()
|
||||
}
|
||||
}
|
||||
@@ -434,7 +434,6 @@ private class KotlinPluginLifecycleImpl(override val project: Project) : KotlinP
|
||||
override fun launch(block: suspend KotlinPluginLifecycle.() -> Unit) {
|
||||
val lifecycle = this
|
||||
check(isStarted.get()) { "Cannot launch when ${KotlinPluginLifecycle::class.simpleName} is not started" }
|
||||
check(!isFinished.get()) { "Cannot launch when ${KotlinPluginLifecycle::class.simpleName} is already finished" }
|
||||
|
||||
val coroutine = block.createCoroutine(this, object : Continuation<Unit> {
|
||||
override val context: CoroutineContext = EmptyCoroutineContext +
|
||||
@@ -475,6 +474,7 @@ private class KotlinPluginLifecycleImpl(override val project: Project) : KotlinP
|
||||
override val finaliseIn: Stage,
|
||||
override val property: Property<T>
|
||||
) : LifecycleAwareProperty<T> {
|
||||
|
||||
override suspend fun awaitFinalValue(): T? {
|
||||
await(finaliseIn)
|
||||
return property.orNull
|
||||
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.gradle.utils
|
||||
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.completeWith
|
||||
import org.gradle.api.Project
|
||||
import org.jetbrains.kotlin.gradle.plugin.HasProject
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle.IllegalLifecycleException
|
||||
import org.jetbrains.kotlin.gradle.plugin.kotlinPluginLifecycle
|
||||
import org.jetbrains.kotlin.gradle.plugin.launch
|
||||
import org.jetbrains.kotlin.tooling.core.ExtrasLazyProperty
|
||||
import org.jetbrains.kotlin.tooling.core.HasMutableExtras
|
||||
import org.jetbrains.kotlin.tooling.core.extrasLazyProperty
|
||||
|
||||
/**
|
||||
* See [KotlinPluginLifecycle]:
|
||||
* This [Future] represents a value that will be available in some 'future' time.
|
||||
*
|
||||
*
|
||||
* #### Simple use case example: Deferring the value of a property to a given [KotlinPluginLifecycle.Stage]:
|
||||
*
|
||||
* ```kotlin
|
||||
* val myFutureProperty: Future<Int> = project.future {
|
||||
* await(FinaliseDsl) // <- suspends
|
||||
* 42
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Futures can also be used to dynamically extend entities implementing [HasMutableExtras] and [HasProject]
|
||||
* #### Example Usage: Extending KotlinSourceSet with a property that relies on the final refines edges.
|
||||
*
|
||||
* ```kotlin
|
||||
* internal val KotlinSourceSet.dependsOnCommonMain: Future<Boolean> by lazyFuture("dependsOnCommonMain") {
|
||||
* await(AfterFinaliseRefinesEdges)
|
||||
* return dependsOn.contains { it.name == "commonMain") }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
internal interface Future<T> {
|
||||
suspend fun await(): T
|
||||
fun getOrThrow(): T
|
||||
}
|
||||
|
||||
internal inline fun <Receiver, reified T> lazyFuture(
|
||||
name: String? = null, noinline block: suspend Receiver.() -> T
|
||||
): ExtrasLazyProperty<Receiver, Future<T>> where Receiver : HasMutableExtras, Receiver : HasProject {
|
||||
return extrasLazyProperty<Receiver, Future<T>>(name) {
|
||||
project.future { block() }
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
internal fun <T> Project.future(block: suspend Project.() -> T): Future<T> {
|
||||
val deferred = CompletableDeferred<T>()
|
||||
|
||||
launch {
|
||||
deferred.completeWith(runCatching { block() })
|
||||
}
|
||||
|
||||
return object : Future<T> {
|
||||
override suspend fun await(): T {
|
||||
return deferred.await()
|
||||
}
|
||||
|
||||
override fun getOrThrow(): T {
|
||||
return if (deferred.isCompleted) deferred.getCompleted() else throw IllegalLifecycleException(
|
||||
"Future was not completed yet. Stage: '${kotlinPluginLifecycle.stage}'"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("FunctionName")
|
||||
|
||||
package org.jetbrains.kotlin.gradle.unitTests
|
||||
|
||||
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.future
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
|
||||
class FutureTest {
|
||||
|
||||
private val project = buildProject().also { project ->
|
||||
project.startKotlinPluginLifecycle()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - simple deferred future`() = project.runLifecycleAwareTest {
|
||||
val future = project.future {
|
||||
await(FinaliseDsl)
|
||||
42
|
||||
}
|
||||
|
||||
assertFailsWith<IllegalLifecycleException> { future.getOrThrow() }
|
||||
assertEquals(42, future.await())
|
||||
assertEquals(42, future.getOrThrow())
|
||||
assertEquals(FinaliseDsl, currentKotlinPluginLifecycle().stage)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - future depending on another future`() = project.runLifecycleAwareTest {
|
||||
val futureA = project.future {
|
||||
await(FinaliseDsl)
|
||||
42
|
||||
}
|
||||
|
||||
val futureB = project.future {
|
||||
futureA.await().toString()
|
||||
}
|
||||
|
||||
assertEquals("42", futureB.await())
|
||||
assertEquals("42", futureB.getOrThrow())
|
||||
assertEquals(FinaliseDsl, currentKotlinPluginLifecycle().stage)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - after lifecycle finished`() {
|
||||
val project = buildProject()
|
||||
project.startKotlinPluginLifecycle()
|
||||
project.evaluate()
|
||||
|
||||
val future = project.future {
|
||||
await(FinaliseDsl)
|
||||
42
|
||||
}
|
||||
|
||||
assertEquals(42, future.getOrThrow())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user