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 bcbb7399d44..ed97be4876d 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 @@ -62,6 +62,8 @@ Util functions * It can be executed right away, effectively executing the [block] before this launch function returns * However, when called inside an already existing coroutine (or once Gradle has started executing afterEvaluate listeners), * then this block executed after this launch function returns and put at the end of the execution queue + * + * If the lifecycle already finished and Gradle moved to its execution phase, then the block will be invoked right away. */ internal fun Project.launch(block: suspend KotlinPluginLifecycle.() -> Unit) { kotlinPluginLifecycle.launch(block) @@ -431,9 +433,19 @@ private class KotlinPluginLifecycleImpl(override val project: Project) : KotlinP throw IllegalLifecycleException("Cannot enqueue Action for stage '${this.stage}' in current stage '${this.stage}'") } + /* + Lifecycle finished: action shall not be enqueued, but just executed right away. + This is desirable, so that .enqueue (and .launch) functions that are scheduled in execution phase + will be executed right away (no suspend necessary or wanted) + */ + if (isFinished.get()) { + action() + return + } + enqueuedActions.getValue(stage).addLast(action) - if (stage == Stage.Configure || isFinished.get()) { + if (stage == Stage.Configure) { loopIfNecessary() } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/KotlinPluginLifecycleTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/KotlinPluginLifecycleTest.kt index cfdab39e4e8..30363be17e4 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/KotlinPluginLifecycleTest.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/KotlinPluginLifecycleTest.kt @@ -376,6 +376,28 @@ class KotlinPluginLifecycleTest { await(Stage.values.last()) } + /** + * This requirement is important to safely support project.future { }.getOrThrow() patterns (when the lifecycle is finished), + */ + @Test + fun `test - launching after Lifecycle finished - will execute code right away`() { + project.evaluate() + val actionAInvocations = AtomicInteger(0) + val actionBInvocations = AtomicInteger(0) + project.launch actionB@{ + project.launch actionA@{ + assertEquals(0, actionBInvocations.get()) + assertEquals(1, actionAInvocations.incrementAndGet()) + } + + assertEquals(1, actionAInvocations.get()) + assertEquals(1, actionBInvocations.incrementAndGet()) + + } + assertEquals(1, actionAInvocations.get()) + assertEquals(1, actionBInvocations.get()) + } + @Test fun `test - Stage - previous next utils`() { assertNull(Stage.values.first().previousOrNull)