[Gradle] KGP Diagnostics: Update KotlinGradleProjectChecker documentation

KT-58275
This commit is contained in:
Sebastian Sellmair
2023-05-03 18:54:20 +02:00
committed by Space Team
parent 287c431bc4
commit 08055b6188
2 changed files with 76 additions and 6 deletions
@@ -5,8 +5,10 @@
package org.jetbrains.kotlin.gradle.plugin.diagnostics
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.diagnostics.checkers.CommonMainWithDependsOnChecker
import org.jetbrains.kotlin.gradle.plugin.diagnostics.checkers.DeprecatedKotlinNativeTargetsChecker
@@ -14,14 +16,81 @@ import org.jetbrains.kotlin.gradle.plugin.diagnostics.checkers.MissingNativeStdl
import org.jetbrains.kotlin.gradle.plugin.diagnostics.checkers.UnusedSourceSetsChecker
/**
* Interface for generic checks of a Gradle Project with Kotlin Plugin applied
* Interface for generic checks of a Gradle Project with any Kotlin Gradle Plugin applied.
* Checkers are launched within the [KotlinPluginLifecycle] during the [Plugin.apply] function.
* The respective [runChecks] method is marked as suspending and should await for necessary data to be finally available.
*
* All checkers are guaranteed to be launched in [org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle.Stage.ReadyForExecution],
* meaning that all changes to DSL are expected to be received, and all structures of KGP are finalised their state.
* This ensures that the checker can emit the warning as soon as possible, while still only running on healthy projects
* (removing noisy induced errors).
*
* #### Why prefer suspend functions over generic `KotlinPluginLifecycle.Stage.ReadyForExecution.await()`?
* Fine-grained suspension calls will improve the checker by enabling the checker to emit the Diagnostic as soon as it really becomes
* relevant. Let's consider the example where an exception is thrown during [KotlinPluginLifecycle.Stage.FinaliseRefinesEdges].
* (this might happen on ill-defined projects having ill-defined target hierarchies declared). The checker in the example however,
* only verifies the user input, so it only requires some properties final value:
*
*```kotlin
*
* // correct implementation
* override suspend fun KotlinGradleProjectCheckerContext.runChecks(collector: KotlinToolingDiagnosticsCollector) {
* ↪ val myFinalValue = getUserProperty().awaitFinalValue()
* if(!myFinalValue.isAllowed) {
* // emit diagnostic
* }
* }
*
* // non-ideal / lazy implementation
* override suspend fun KotlinGradleProjectCheckerContext.runChecks(collector: KotlinToolingDiagnosticsCollector) {
* ↪ Stage.ReadyForExecution.await()
* val myFinalValue = getUserProperty().orNull
* if(!myFinalValue.isAllowed) {
* // emit diagnostic
* }
* }
* ```
*
* In this example the diagnostic will not be emitted in the non-ideal implementation as the exception thrown in previous Stage
* will never cause the 'isAllowed' check to never execute. Maybe the illegal property value was causing the exception? User will never know!
*
* However, the correct implementation can unsuspend as soon as the relevant data is available. This will run before the exception,
* the diagnostic will be emitted and user can be warned about the issue!
*
* #### Example: Checker requiring all source sets to be there:
* ```kotlin
* override suspend fun KotlinGradleProjectCheckerContext.runChecks(collector: KotlinToolingDiagnosticsCollector) {
* ↪ project.multiplatformExtension.awaitSourceSets() // <- instead of .sourceSets | suspending until finally available
* }
* ```
*
* #### Example: Checker that requires platform compilations of a given SourceSet
* ```kotlin
* override suspend fun KotlinGradleProjectCheckerContext.runChecks(collector: KotlinToolingDiagnosticsCollector) {
* ↪ project.multiplatformExtension.awaitSourceSets().getByName("commonMain") // suspending until all source sets available
* ↪ .internal.awaitPlatformCompilations() // suspending until all platform compilations are finally known
* }
* ```
*
* #### Example: Checker that just wants to run in a given [KotlinPluginLifecycle.Stage]
* ```kotlin
* override suspend fun KotlinGradleProjectCheckerContext.runChecks(collector: KotlinToolingDiagnosticsCollector) {
* ↪ KotlinPluginLifecycle.Stage.FinaliseDsl.await() // suspending until FinaliseDsl stage is reached
* }
*
* ```
*
* #### Example: Very simple checker without fine-grained of data requirements
* If building a simple project checker which requires a fully successfully configured project, but the data requirements
* cannot (yet) be implemented by calling into respective suspend functions, then awaiting the [KotlinPluginLifecycle.Stage.ReadyForExecution]
* stage will be sufficient.
* ```kotlin
* override suspend fun KotlinGradleProjectCheckerContext.runChecks(collector: KotlinToolingDiagnosticsCollector) {
* ↪ KotlinPluginLifecycle.Stage.ReadyForExecution.await()
* // ... run my check on fully configured project
* }
* ```
* However, this approach is discouraged and shall only be used if there are no suspend functions available to express the
* requirements of this checker.
*
* Checkers **will not** be launched if the project's configuration has finished with failure (e.g. some plugin or a build script
* threw an exception). If your check aims to prevent some exception, then code that check as soon as possible in the `apply`-block
* of the [org.jetbrains.kotlin.gradle.plugin.KotlinBasePluginWrapper] or appropriate inheritor
*/
internal interface KotlinGradleProjectChecker {
suspend fun KotlinGradleProjectCheckerContext.runChecks(collector: KotlinToolingDiagnosticsCollector)
@@ -29,6 +29,7 @@ internal fun Project.launchKotlinGradleProjectCheckers() {
internal val Project.kotlinGradleProjectCheckersOverride: Collection<KotlinGradleProjectChecker>?
get() {
return if (extraProperties.has(KOTLIN_GRADLE_PROJECT_CHECKERS_OVERRIDE))
@Suppress("unchecked_cast")
extraProperties.get(KOTLIN_GRADLE_PROJECT_CHECKERS_OVERRIDE) as Collection<KotlinGradleProjectChecker>?
else
null