[Gradle] Lifecycle: Remove LifecycleAwareProperty

Replaced by just keeping the 'awaitFinalValue()' function

^KT-58255 Verification Pending
This commit is contained in:
Sebastian Sellmair
2023-05-04 10:01:23 +02:00
committed by Space Team
parent 09dbae5eca
commit 0ab0788763
6 changed files with 92 additions and 220 deletions
@@ -53,7 +53,7 @@ abstract class KotlinMultiplatformExtension(project: Project) :
}
internal val internalKotlinTargetHierarchy by lazy {
KotlinTargetHierarchyDslImpl(project.kotlinPluginLifecycle, targets, sourceSets)
KotlinTargetHierarchyDslImpl(project.objects, targets, sourceSets)
}
@ExperimentalKotlinGradlePluginApi
@@ -12,13 +12,10 @@ import org.jetbrains.kotlin.gradle.utils.CompletableFuture
import org.jetbrains.kotlin.gradle.utils.Future
import org.jetbrains.kotlin.gradle.utils.failures
import org.jetbrains.kotlin.gradle.utils.getOrPut
import java.lang.ref.WeakReference
import java.util.*
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.collections.ArrayDeque
import kotlin.collections.set
import kotlin.coroutines.*
import kotlin.reflect.KProperty
/*
Util functions
@@ -191,68 +188,15 @@ internal suspend fun Stage.await() {
}
/**
* See [newProperty]
*/
internal inline fun <reified T : Any> Project.newKotlinPluginLifecycleAwareProperty(
finaliseIn: Stage = Stage.FinaliseDsl, initialValue: T? = null,
): LifecycleAwareProperty<T> {
return kotlinPluginLifecycle.newProperty(T::class.java, finaliseIn, initialValue)
}
/**
* See [LifecycleAwareProperty]
* Will create a new [LifecycleAwareProperty] which is going to finalise its value in stage [finaliseIn]
* and the initialValue [initialValue]
*
* ## Sample
* ```kotlin
* val myProperty by project.newKotlinPluginLifecycleAwareProperty<String>()
* myProperty.set("hello")
* //...
* project.launch {
* val myFinalValue = myProperty.awaitFinalValue() // <- suspends until final value is known!
* }
* ```
*/
internal inline fun <reified T : Any> KotlinPluginLifecycle.newProperty(
finaliseIn: Stage = Stage.FinaliseDsl, initialValue: T? = null,
): LifecycleAwareProperty<T> {
return newProperty(T::class.java, finaliseIn, initialValue)
}
/**
* Will return the [LifecycleAwareProperty] instance if the given receiver was created by [newKotlinPluginLifecycleAwareProperty]
*/
internal suspend fun <T : Any> Property<T>.findKotlinPluginLifecycleAwareProperty(): LifecycleAwareProperty<T>? {
return (currentKotlinPluginLifecycle() as KotlinPluginLifecycleImpl).findLifecycleAwareProperty(this)
}
/**
* Will suspend until the property finalises its value and therefore a final value can returned.
* Note: This only works on properties that are [isKotlinPluginLifecycleAware]
* (e.g. by being created using [newKotlinPluginLifecycleAwareProperty]).
*
* If a property was not created using 'newKotlinPluginLifecycleAwareProperty' then the execution
* will suspend until 'FinaliseDsl' and calls [Property.finalizeValue] before returnign the actual value
* Will suspend until [Stage.FinaliseDsl], finalise the value using [Property.finalizeValue] and return the
* final value.
*/
internal suspend fun <T : Any> Property<T>.awaitFinalValue(): T? {
val lifecycleAwareProperty = findKotlinPluginLifecycleAwareProperty()
if (lifecycleAwareProperty != null) {
return lifecycleAwareProperty.awaitFinalValue()
}
Stage.FinaliseDsl.await()
finalizeValue()
return orNull
}
/**
* @return true if this property has an associated [LifecycleAwareProperty]
*/
internal suspend fun Property<*>.isKotlinPluginLifecycleAware(): Boolean {
return findKotlinPluginLifecycleAwareProperty() != null
}
/**
* See also [withRestrictedStages]
*
@@ -395,31 +339,7 @@ internal interface KotlinPluginLifecycle {
suspend fun await(stage: Stage)
fun <T : Any> newProperty(
type: Class<T>, finaliseIn: Stage, initialValue: T?,
): LifecycleAwareProperty<T>
class IllegalLifecycleException(message: String) : IllegalStateException(message)
/**
* Wrapper around Gradle's [Property] that is aware of the [KotlinPluginLifecycle] and ensures that
* the given [property] is finalised in [stage] (also calling [Property.finalizeValue]).
*
* A property finalised in a given [stage] will allow to safely get the final value using the
* [awaitFinalValue] function, suspending the execution until the value is indeed finalised.
*
* See [Project.newKotlinPluginLifecycleAwareProperty] to create a new instance
*/
interface LifecycleAwareProperty<T : Any> {
val finaliseIn: Stage
val property: Property<T>
/**
* See [LifecycleAwareProperty]
*/
suspend fun awaitFinalValue(): T?
operator fun getValue(thisRef: Any?, property: KProperty<*>): Property<T> = this.property
}
}
@@ -437,7 +357,6 @@ private class KotlinPluginLifecycleImpl(override val project: Project) : KotlinP
private val isFinishedWithFailures = AtomicBoolean(false)
override var stage: Stage = Stage.values.first()
private val properties = WeakHashMap<Property<*>, WeakReference<LifecycleAwareProperty<*>>>()
fun start() {
check(!isStarted.getAndSet(true)) {
@@ -576,32 +495,6 @@ private class KotlinPluginLifecycleImpl(override val project: Project) : KotlinP
}
}
}
override fun <T : Any> newProperty(type: Class<T>, finaliseIn: Stage, initialValue: T?): LifecycleAwareProperty<T> {
val property = project.objects.property(type)
if (initialValue != null) property.set(initialValue)
if (finaliseIn <= stage) property.finalizeValue()
else enqueue(finaliseIn) { property.finalizeValue() }
val lifecycleAwareProperty = LifecycleAwarePropertyImpl(finaliseIn, property)
properties[property] = WeakReference(lifecycleAwareProperty)
return lifecycleAwareProperty
}
fun <T : Any> findLifecycleAwareProperty(property: Property<T>): LifecycleAwareProperty<T>? {
@Suppress("UNCHECKED_CAST")
return properties[property]?.get() as? LifecycleAwareProperty<T>
}
private class LifecycleAwarePropertyImpl<T : Any>(
override val finaliseIn: Stage,
override val property: Property<T>,
) : LifecycleAwareProperty<T> {
override suspend fun awaitFinalValue(): T? {
finaliseIn.await()
return property.orNull
}
}
}
private class KotlinPluginLifecycleCoroutineContextElement(
@@ -16,19 +16,19 @@ import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.utils.property
internal class KotlinTargetHierarchyDslImpl(
private val lifecycle: KotlinPluginLifecycle,
objects: ObjectFactory,
private val targets: DomainObjectCollection<KotlinTarget>,
private val sourceSets: NamedDomainObjectContainer<KotlinSourceSet>
private val sourceSets: NamedDomainObjectContainer<KotlinSourceSet>,
) : KotlinTargetHierarchyDsl {
private val _appliedDescriptors = mutableListOf<KotlinTargetHierarchyDescriptor>()
val appliedDescriptors: List<KotlinTargetHierarchyDescriptor> get() = _appliedDescriptors
override val android: KotlinAndroidTargetHierarchyDsl = KotlinAndroidTargetHierarchyDslImpl(lifecycle)
override val android: KotlinAndroidTargetHierarchyDsl = KotlinAndroidTargetHierarchyDslImpl(objects)
override fun apply(
hierarchyDescriptor: KotlinTargetHierarchyDescriptor,
describeExtension: (KotlinTargetHierarchyBuilder.Root.() -> Unit)?
describeExtension: (KotlinTargetHierarchyBuilder.Root.() -> Unit)?,
) {
val descriptor = hierarchyDescriptor.extendIfNotNull(describeExtension)
_appliedDescriptors.add(descriptor)
@@ -51,12 +51,12 @@ internal class KotlinTargetHierarchyDslImpl(
private fun KotlinTargetHierarchyDescriptor.extendIfNotNull(describe: (KotlinTargetHierarchyBuilder.Root.() -> Unit)?) =
if (describe == null) this else extend(describe)
internal class KotlinAndroidTargetHierarchyDslImpl(lifecycle: KotlinPluginLifecycle) : KotlinAndroidTargetHierarchyDsl {
override val main: KotlinAndroidVariantHierarchyDsl = KotlinAndroidVariantHierarchyDslImpl(lifecycle)
override val unitTest: KotlinAndroidVariantHierarchyDsl = KotlinAndroidVariantHierarchyDslImpl(lifecycle)
override val instrumentedTest: KotlinAndroidVariantHierarchyDsl = KotlinAndroidVariantHierarchyDslImpl(lifecycle)
internal class KotlinAndroidTargetHierarchyDslImpl(objects: ObjectFactory) : KotlinAndroidTargetHierarchyDsl {
override val main: KotlinAndroidVariantHierarchyDsl = KotlinAndroidVariantHierarchyDslImpl(objects)
override val unitTest: KotlinAndroidVariantHierarchyDsl = KotlinAndroidVariantHierarchyDslImpl(objects)
override val instrumentedTest: KotlinAndroidVariantHierarchyDsl = KotlinAndroidVariantHierarchyDslImpl(objects)
}
internal class KotlinAndroidVariantHierarchyDslImpl(lifecycle: KotlinPluginLifecycle) : KotlinAndroidVariantHierarchyDsl {
override val sourceSetTree: Property<KotlinTargetHierarchy.SourceSetTree> by lifecycle.newProperty()
internal class KotlinAndroidVariantHierarchyDslImpl(objects: ObjectFactory) : KotlinAndroidVariantHierarchyDsl {
override val sourceSetTree: Property<KotlinTargetHierarchy.SourceSetTree> = objects.property()
}
@@ -25,7 +25,7 @@ class KotlinAndroidTargetHierarchyDsl {
@Test
fun `test - module - not set`() = buildProjectWithMPP().runLifecycleAwareTest {
val dsl = KotlinAndroidVariantHierarchyDslImpl(project.kotlinPluginLifecycle)
val dsl = KotlinAndroidVariantHierarchyDslImpl(project.objects)
project.kotlinPluginLifecycle.launch {
assertNull(dsl.sourceSetTree.orNull)
assertNull(dsl.sourceSetTree.awaitFinalValue())
@@ -34,25 +34,13 @@ class KotlinAndroidTargetHierarchyDsl {
@Test
fun `test - module - can be set in users afterEvaluate`() = buildProjectWithMPP().runLifecycleAwareTest {
val dsl = KotlinAndroidVariantHierarchyDslImpl(project.kotlinPluginLifecycle)
val dsl = KotlinAndroidVariantHierarchyDslImpl(project.objects)
afterEvaluate { dsl.sourceSetTree.set(KotlinTargetHierarchy.SourceSetTree("x")) }
dsl.sourceSetTree.set(KotlinTargetHierarchy.SourceSetTree("-set-before-after-evaluate-"))
assertEquals("x", dsl.sourceSetTree.awaitFinalValue()?.name)
assertEquals(KotlinPluginLifecycle.Stage.FinaliseDsl, currentKotlinPluginLifecycle().stage)
}
@Test
fun `test - module - cannot be set after FinaliseDsl`() = buildProjectWithMPP().runLifecycleAwareTest {
val dsl = KotlinAndroidVariantHierarchyDslImpl(project.kotlinPluginLifecycle)
launchInStage(KotlinPluginLifecycle.Stage.FinaliseDsl.previousOrThrow) {
dsl.sourceSetTree.set(KotlinTargetHierarchy.SourceSetTree("x"))
}
launchInStage(KotlinPluginLifecycle.Stage.FinaliseDsl) {
assertFails { dsl.sourceSetTree.set(KotlinTargetHierarchy.SourceSetTree("y")) }
}
}
@Test
fun `test - module - is respected in default refines edges`() {
val project = buildProject {
@@ -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.
*/
@file:Suppress("FunctionName")
package org.jetbrains.kotlin.gradle.unitTests
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle.Stage.EvaluateBuildscript
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle.Stage.FinaliseDsl
import org.jetbrains.kotlin.gradle.plugin.awaitFinalValue
import org.jetbrains.kotlin.gradle.plugin.currentKotlinPluginLifecycle
import org.jetbrains.kotlin.gradle.plugin.launchInStage
import org.jetbrains.kotlin.gradle.util.buildProjectWithMPP
import org.jetbrains.kotlin.gradle.util.runLifecycleAwareTest
import org.jetbrains.kotlin.gradle.utils.newProperty
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertFails
import kotlin.test.assertFailsWith
import kotlin.test.assertNull
class LifecycleAwaitFinalPropertyValueTest {
private val project = buildProjectWithMPP()
@Test
fun `test - awaitFinalValue`() = project.runLifecycleAwareTest {
val property = project.newProperty<Int>()
launchInStage(FinaliseDsl.previousOrThrow.previousOrThrow) {
property.set(1)
}
launchInStage(FinaliseDsl.previousOrThrow.previousOrThrow) {
assertEquals(1, property.get())
property.set(2)
}
assertEquals(EvaluateBuildscript, currentKotlinPluginLifecycle().stage)
assertEquals(2, property.awaitFinalValue())
assertEquals(FinaliseDsl, currentKotlinPluginLifecycle().stage)
}
@Test
fun `test - changing value after finalized`() = project.runLifecycleAwareTest {
val property = project.newProperty<Int>()
property.set(1)
launchInStage(FinaliseDsl.previousOrThrow) {
property.awaitFinalValue()
}
launchInStage(FinaliseDsl.nextOrThrow) {
assertFailsWith<IllegalStateException> { property.set(2) }
}
}
@Test
fun `test - creating a property - after finaliseDsl stage already passed`() = project.runLifecycleAwareTest {
launchInStage(KotlinPluginLifecycle.Stage.last) {
val property = project.newProperty<String>()
assertNull(property.awaitFinalValue())
assertFails { property.set("") }
}
}
@Test
fun `test - creating a property - in finaliseIn stage`() = project.runLifecycleAwareTest {
launchInStage(KotlinPluginLifecycle.Stage.FinaliseDsl) {
val property = project.newProperty<String>()
assertNull(property.awaitFinalValue())
assertFails { property.set("") }
}
}
}
@@ -1,86 +0,0 @@
/*
* 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.*
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle.Stage.*
import org.jetbrains.kotlin.gradle.plugin.awaitFinalValue
import org.jetbrains.kotlin.gradle.plugin.currentKotlinPluginLifecycle
import org.jetbrains.kotlin.gradle.plugin.launchInStage
import org.jetbrains.kotlin.gradle.plugin.newKotlinPluginLifecycleAwareProperty
import org.jetbrains.kotlin.gradle.util.buildProjectWithMPP
import org.jetbrains.kotlin.gradle.util.runLifecycleAwareTest
import org.jetbrains.kotlin.gradle.utils.newProperty
import org.junit.Assert.assertFalse
import org.junit.Test
import kotlin.test.*
class LifecycleAwarePropertyTest {
private val project = buildProjectWithMPP()
@Test
fun `test - awaitFinalValue`() = project.runLifecycleAwareTest {
val property by project.newKotlinPluginLifecycleAwareProperty<Int>(AfterFinaliseRefinesEdges)
assertTrue(property.isKotlinPluginLifecycleAware())
launchInStage(FinaliseRefinesEdges.previousOrThrow) {
property.set(1)
}
launchInStage(FinaliseRefinesEdges) {
assertEquals(1, property.get())
property.set(2)
}
assertEquals(EvaluateBuildscript, currentKotlinPluginLifecycle().stage)
assertEquals(2, property.awaitFinalValue())
assertEquals(AfterFinaliseRefinesEdges, currentKotlinPluginLifecycle().stage)
}
@Test
fun `test - awaitFinalValue - on non lifecycle aware property`() = project.runLifecycleAwareTest {
val property = project.newProperty<String>()
assertFalse(property.isKotlinPluginLifecycleAware())
assertEquals(KotlinPluginLifecycle.Stage.EvaluateBuildscript, currentKotlinPluginLifecycle().stage)
assertNull(property.awaitFinalValue())
assertEquals(KotlinPluginLifecycle.Stage.FinaliseDsl, currentKotlinPluginLifecycle().stage)
assertFails { property.set("x") }
}
@Test
fun `test - changing value after finalized`() = project.runLifecycleAwareTest {
val property by project.newKotlinPluginLifecycleAwareProperty<Int>(AfterEvaluateBuildscript)
property.set(1)
launchInStage(AfterEvaluateBuildscript) {
assertFailsWith<IllegalStateException> { property.set(2) }
}
}
@Test
fun `test - creating a property - after finaliseIn stage already passed`() = project.runLifecycleAwareTest {
launchInStage(KotlinPluginLifecycle.Stage.last) {
val property by project.newKotlinPluginLifecycleAwareProperty<String>(Companion.first)
/* Property was finalised immediately */
assertFailsWith<IllegalStateException> { property.set("Expected to fail") }
assertNull(property.orNull)
}
}
@Test
fun `test - creating a property - in finaliseIn stage`() = project.runLifecycleAwareTest {
launchInStage(KotlinPluginLifecycle.Stage.FinaliseDsl) {
val property by project.newKotlinPluginLifecycleAwareProperty<String>(FinaliseDsl)
/* Property was finalised immediately */
assertFailsWith<IllegalStateException> { property.set("Expected to fail") }
assertNull(property.orNull)
}
}
}