@@ -8,13 +8,8 @@ package org.jetbrains.kotlin.gradle.plugin
8
8
import org.gradle.api.Project
9
9
import org.gradle.api.provider.Property
10
10
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle.*
11
- import org.jetbrains.kotlin.gradle.utils.CompletableFuture
12
11
import org.jetbrains.kotlin.gradle.utils.Future
13
- import org.jetbrains.kotlin.gradle.utils.failures
14
12
import org.jetbrains.kotlin.gradle.utils.getOrPut
15
- import java.util.*
16
- import java.util.concurrent.atomic.AtomicBoolean
17
- import kotlin.collections.ArrayDeque
18
13
import kotlin.coroutines.*
19
14
20
15
/*
@@ -156,11 +151,7 @@ internal val Project.kotlinPluginLifecycle: KotlinPluginLifecycle
156
151
* some later data to be available. In this case, the Future still will only return 'sane' data.
157
152
*/
158
153
internal val Project .configurationResult: Future <ProjectConfigurationResult >
159
- get() = configurationResultImpl
160
-
161
-
162
- private val Project .configurationResultImpl: CompletableFuture <ProjectConfigurationResult >
163
- get() = extraProperties.getOrPut(" org.jetbrains.kotlin.gradle.plugin.configurationResult" ) { CompletableFuture () }
154
+ get() = (kotlinPluginLifecycle as KotlinPluginLifecycleImpl ).configurationResult
164
155
165
156
166
157
/* *
@@ -175,8 +166,7 @@ internal fun Project.startKotlinPluginLifecycle() {
175
166
* the currently running coroutine. Throws if this coroutine was not started using a [KotlinPluginLifecycle]
176
167
*/
177
168
internal suspend fun currentKotlinPluginLifecycle (): KotlinPluginLifecycle {
178
- return coroutineContext[KotlinPluginLifecycleCoroutineContextElement ]?.lifecycle
179
- ? : error(" Missing $KotlinPluginLifecycleCoroutineContextElement in currentCoroutineContext" )
169
+ return coroutineContext.kotlinPluginLifecycle
180
170
}
181
171
182
172
/* *
@@ -224,33 +214,6 @@ internal suspend fun <T> requireCurrentStage(block: suspend () -> T): T {
224
214
return requiredStage(currentKotlinPluginLifecycle().stage, block)
225
215
}
226
216
227
- /* *
228
- * Will ensure that the given [block] cannot leave the specified allowed stages [allowed]
229
- * e.g.
230
- *
231
- * ```kotlin
232
- * project.launchInStage(Stage.BeforeFinaliseDsl) {
233
- * withRestrictedStages(Stage.upTo(Stage.FinaliseDsl)) {
234
- * await(Stage.FinaliseDsl) // <- OK, since still in allowed stages
235
- * await(Stage.AfterFinaliseDsl) // <- fails, since not in allowed stages!
236
- * }
237
- * }
238
- * ```
239
- */
240
- internal suspend fun <T > withRestrictedStages (allowed : Set <Stage >, block : suspend () -> T ): T {
241
- val newCoroutineContext = coroutineContext + RestrictedLifecycleStages (currentKotlinPluginLifecycle(), allowed)
242
- return suspendCoroutine { continuation ->
243
- val newContinuation = object : Continuation <T > {
244
- override val context: CoroutineContext
245
- get() = newCoroutineContext
246
-
247
- override fun resumeWith (result : Result <T >) {
248
- continuation.resumeWith(result)
249
- }
250
- }
251
- block.startCoroutine(newContinuation)
252
- }
253
- }
254
217
255
218
/*
256
219
Definition of the Lifecycle and its stages
@@ -339,195 +302,3 @@ internal interface KotlinPluginLifecycle {
339
302
340
303
class IllegalLifecycleException (message : String ) : IllegalStateException(message)
341
304
}
342
-
343
-
344
- /*
345
- Implementation
346
- */
347
-
348
- internal class KotlinPluginLifecycleImpl (override val project : Project ) : KotlinPluginLifecycle {
349
- private val enqueuedActions: Map <Stage , ArrayDeque <KotlinPluginLifecycle .() - > Unit >> =
350
- Stage .values().associateWith { ArrayDeque () }
351
-
352
- private val loopRunning = AtomicBoolean (false )
353
- private val isStarted = AtomicBoolean (false )
354
- private val isFinishedSuccessfully = AtomicBoolean (false )
355
- private val isFinishedWithFailures = AtomicBoolean (false )
356
-
357
- override var stage: Stage = Stage .values.first()
358
-
359
- fun start () {
360
- check(! isStarted.getAndSet(true )) {
361
- " ${KotlinPluginLifecycle ::class .java.name} already started"
362
- }
363
-
364
- check(! project.state.executed) {
365
- " ${KotlinPluginLifecycle ::class .java.name} cannot be started in ProjectState '${project.state} '"
366
- }
367
-
368
- loopIfNecessary()
369
-
370
- project.whenEvaluated {
371
- /* Check for failures happening during buildscript evaluation */
372
- project.failures.let { failures ->
373
- if (failures.isNotEmpty()) {
374
- finishWithFailures(failures)
375
- return @whenEvaluated
376
- }
377
- }
378
-
379
- assert (enqueuedActions.getValue(stage).isEmpty()) { " Expected empty queue from '$stage '" }
380
- stage = stage.nextOrThrow
381
- executeCurrentStageAndScheduleNext()
382
- }
383
- }
384
-
385
- private fun executeCurrentStageAndScheduleNext () {
386
- stage.previousOrNull?.let { previousStage ->
387
- assert (enqueuedActions.getValue(previousStage).isEmpty()) {
388
- " Actions from previous stage '$previousStage ' have not been executed (stage: '$stage ')"
389
- }
390
- }
391
-
392
- val failures = project.failures
393
- if (failures.isNotEmpty()) {
394
- finishWithFailures(failures)
395
- return
396
- }
397
-
398
- try {
399
- loopIfNecessary()
400
- } catch (t: Throwable ) {
401
- finishWithFailures(listOf (t))
402
- throw t
403
- }
404
-
405
- stage = stage.nextOrNull ? : run {
406
- finishSuccessfully()
407
- return
408
- }
409
-
410
- project.afterEvaluate {
411
- executeCurrentStageAndScheduleNext()
412
- }
413
- }
414
-
415
- private fun loopIfNecessary () {
416
- if (loopRunning.getAndSet(true )) return
417
- try {
418
- val queue = enqueuedActions.getValue(stage)
419
- do {
420
- project.state.rethrowFailure()
421
- val action = queue.removeFirstOrNull()
422
- action?.invoke(this )
423
- } while (action != null )
424
- } finally {
425
- loopRunning.set(false )
426
- }
427
- }
428
-
429
- private fun finishWithFailures (failures : List <Throwable >) {
430
- assert (failures.isNotEmpty())
431
- assert (isStarted.get())
432
- assert (! isFinishedWithFailures.getAndSet(true ))
433
- project.configurationResultImpl.complete(ProjectConfigurationResult .Failure (failures))
434
- }
435
-
436
- private fun finishSuccessfully () {
437
- assert (isStarted.get())
438
- assert (! isFinishedSuccessfully.getAndSet(true ))
439
- project.configurationResultImpl.complete(ProjectConfigurationResult .Success )
440
- }
441
-
442
- fun enqueue (stage : Stage , action : KotlinPluginLifecycle .() -> Unit ) {
443
- if (stage < this .stage) {
444
- throw IllegalLifecycleException (" Cannot enqueue Action for stage '$stage ' in current stage '${this .stage} '" )
445
- }
446
-
447
- /*
448
- Lifecycle finished: action shall not be enqueued, but just executed right away.
449
- This is desirable, so that .enqueue (and .launch) functions that are scheduled in execution phase
450
- will be executed right away (no suspend necessary or wanted)
451
- */
452
- if (isFinishedSuccessfully.get()) {
453
- return action()
454
- }
455
-
456
- /*
457
- Lifecycle finished, but some exceptions have been thrown.
458
- In this case, an enqueue for future Stages is not allowed, since those will not be executed anymore.
459
- Any enqueue in the current stage will be executed right away (no suspend necessary or wanted).
460
- */
461
- if (isFinishedWithFailures.get()) {
462
- return if (stage == this .stage) action()
463
- else Unit
464
- }
465
-
466
- enqueuedActions.getValue(stage).addLast(action)
467
-
468
- if (stage == Stage .EvaluateBuildscript && isStarted.get()) {
469
- loopIfNecessary()
470
- }
471
- }
472
-
473
- override fun launch (block : suspend KotlinPluginLifecycle .() -> Unit ) {
474
- val lifecycle = this
475
-
476
- val coroutine = block.createCoroutine(this , object : Continuation <Unit > {
477
- override val context: CoroutineContext = EmptyCoroutineContext +
478
- KotlinPluginLifecycleCoroutineContextElement (lifecycle)
479
-
480
- override fun resumeWith (result : Result <Unit >) = result.getOrThrow()
481
- })
482
-
483
- enqueue(stage) {
484
- coroutine.resume(Unit )
485
- }
486
- }
487
-
488
- override suspend fun await (stage : Stage ) {
489
- if (this .stage > stage) return
490
- suspendCoroutine<Unit > { continuation ->
491
- enqueue(stage) {
492
- continuation.resume(Unit )
493
- }
494
- }
495
- }
496
- }
497
-
498
- private class KotlinPluginLifecycleCoroutineContextElement (
499
- val lifecycle : KotlinPluginLifecycle ,
500
- ) : CoroutineContext.Element {
501
- companion object Key : CoroutineContext.Key<KotlinPluginLifecycleCoroutineContextElement>
502
-
503
- override val key: CoroutineContext .Key <KotlinPluginLifecycleCoroutineContextElement > = Key
504
- }
505
-
506
- private class RestrictedLifecycleStages (
507
- private val lifecycle : KotlinPluginLifecycle ,
508
- private val allowedStages : Set <Stage >,
509
- ) : CoroutineContext.Element, ContinuationInterceptor {
510
-
511
- override val key: CoroutineContext .Key <* > = ContinuationInterceptor
512
-
513
- override fun <T > interceptContinuation (continuation : Continuation <T >): Continuation <T > = object : Continuation <T > {
514
- override val context: CoroutineContext
515
- get() = continuation.context
516
-
517
- override fun resumeWith (result : Result <T >) = when {
518
- result.isFailure -> continuation.resumeWith(result)
519
- lifecycle.stage !in allowedStages -> continuation.resumeWithException(
520
- IllegalLifecycleException (
521
- " Required stage in '$allowedStages ', but lifecycle switched to '${lifecycle.stage} '"
522
- )
523
- )
524
- else -> continuation.resumeWith(result)
525
- }
526
- }
527
-
528
- init {
529
- if (lifecycle.stage !in allowedStages) {
530
- throw IllegalLifecycleException (" Required stage in '${allowedStages} ' but lifecycle is currently in '${lifecycle.stage} '" )
531
- }
532
- }
533
- }
0 commit comments