diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinMultiplatformExtension.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinMultiplatformExtension.kt index 31f06f6e6d1..b189caff93f 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinMultiplatformExtension.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinMultiplatformExtension.kt @@ -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 diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginLifecycle.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginLifecycle.kt index c2ef726c098..518d5d98304 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginLifecycle.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginLifecycle.kt @@ -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 Project.newKotlinPluginLifecycleAwareProperty( - finaliseIn: Stage = Stage.FinaliseDsl, initialValue: T? = null, -): LifecycleAwareProperty { - 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() - * myProperty.set("hello") - * //... - * project.launch { - * val myFinalValue = myProperty.awaitFinalValue() // <- suspends until final value is known! - * } - * ``` - */ -internal inline fun KotlinPluginLifecycle.newProperty( - finaliseIn: Stage = Stage.FinaliseDsl, initialValue: T? = null, -): LifecycleAwareProperty { - return newProperty(T::class.java, finaliseIn, initialValue) -} - -/** - * Will return the [LifecycleAwareProperty] instance if the given receiver was created by [newKotlinPluginLifecycleAwareProperty] - */ -internal suspend fun Property.findKotlinPluginLifecycleAwareProperty(): LifecycleAwareProperty? { - 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 Property.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 newProperty( - type: Class, finaliseIn: Stage, initialValue: T?, - ): LifecycleAwareProperty - 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 { - val finaliseIn: Stage - val property: Property - - /** - * See [LifecycleAwareProperty] - */ - suspend fun awaitFinalValue(): T? - operator fun getValue(thisRef: Any?, property: KProperty<*>): Property = 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, WeakReference>>() fun start() { check(!isStarted.getAndSet(true)) { @@ -576,32 +495,6 @@ private class KotlinPluginLifecycleImpl(override val project: Project) : KotlinP } } } - - override fun newProperty(type: Class, finaliseIn: Stage, initialValue: T?): LifecycleAwareProperty { - 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 findLifecycleAwareProperty(property: Property): LifecycleAwareProperty? { - @Suppress("UNCHECKED_CAST") - return properties[property]?.get() as? LifecycleAwareProperty - } - - private class LifecycleAwarePropertyImpl( - override val finaliseIn: Stage, - override val property: Property, - ) : LifecycleAwareProperty { - - override suspend fun awaitFinalValue(): T? { - finaliseIn.await() - return property.orNull - } - } } private class KotlinPluginLifecycleCoroutineContextElement( diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/targetHierarchy/KotlinTargetHierarchyDslImpl.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/targetHierarchy/KotlinTargetHierarchyDslImpl.kt index 1872c34dccc..e48c8ced4f4 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/targetHierarchy/KotlinTargetHierarchyDslImpl.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/targetHierarchy/KotlinTargetHierarchyDslImpl.kt @@ -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, - private val sourceSets: NamedDomainObjectContainer + private val sourceSets: NamedDomainObjectContainer, ) : KotlinTargetHierarchyDsl { private val _appliedDescriptors = mutableListOf() val appliedDescriptors: List 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 by lifecycle.newProperty() +internal class KotlinAndroidVariantHierarchyDslImpl(objects: ObjectFactory) : KotlinAndroidVariantHierarchyDsl { + override val sourceSetTree: Property = objects.property() } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/KotlinAndroidTargetHierarchyDsl.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/KotlinAndroidTargetHierarchyDsl.kt index bd7cfc908d8..98d3c6b0470 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/KotlinAndroidTargetHierarchyDsl.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/KotlinAndroidTargetHierarchyDsl.kt @@ -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 { diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/LifecycleAwaitFinalPropertyValueTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/LifecycleAwaitFinalPropertyValueTest.kt new file mode 100644 index 00000000000..0815564767d --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/LifecycleAwaitFinalPropertyValueTest.kt @@ -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() + + 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() + property.set(1) + + launchInStage(FinaliseDsl.previousOrThrow) { + property.awaitFinalValue() + } + + launchInStage(FinaliseDsl.nextOrThrow) { + assertFailsWith { property.set(2) } + } + } + + @Test + fun `test - creating a property - after finaliseDsl stage already passed`() = project.runLifecycleAwareTest { + launchInStage(KotlinPluginLifecycle.Stage.last) { + val property = project.newProperty() + 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() + assertNull(property.awaitFinalValue()) + assertFails { property.set("") } + } + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/LifecycleAwarePropertyTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/LifecycleAwarePropertyTest.kt deleted file mode 100644 index e56f7b8baf4..00000000000 --- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/LifecycleAwarePropertyTest.kt +++ /dev/null @@ -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(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() - 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(AfterEvaluateBuildscript) - property.set(1) - - launchInStage(AfterEvaluateBuildscript) { - assertFailsWith { 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(Companion.first) - - /* Property was finalised immediately */ - assertFailsWith { 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(FinaliseDsl) - - /* Property was finalised immediately */ - assertFailsWith { property.set("Expected to fail") } - assertNull(property.orNull) - } - } -}