[Gradle] Fail compileKotlin-tasks if ERROR-diagnostics were reported
KT-60237
This commit is contained in:
committed by
Space Team
parent
c544d68a64
commit
6c88b92b88
+22
-12
@@ -32,17 +32,21 @@ import org.jetbrains.kotlin.gradle.internal.KOTLIN_COMPILER_EMBEDDABLE
|
||||
import org.jetbrains.kotlin.gradle.internal.KOTLIN_MODULE_GROUP
|
||||
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformAndroidGradlePluginCompatibilityHealthCheck.runMultiplatformAndroidGradlePluginCompatibilityHealthCheckWhenAndroidIsApplied
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider
|
||||
import org.jetbrains.kotlin.gradle.plugin.diagnostics.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.diagnostics.EnsureNoKotlinGradlePluginErrors
|
||||
import org.jetbrains.kotlin.gradle.plugin.diagnostics.ToolingDiagnosticRenderingOptions
|
||||
import org.jetbrains.kotlin.gradle.plugin.diagnostics.UsesKotlinToolingDiagnostics
|
||||
import org.jetbrains.kotlin.gradle.plugin.diagnostics.kotlinToolingDiagnosticsCollectorProvider
|
||||
import org.jetbrains.kotlin.gradle.plugin.diagnostics.launchKotlinGradleProjectCheckers
|
||||
import org.jetbrains.kotlin.gradle.plugin.internal.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMultiplatformPlugin
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20GradlePlugin
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSetFactory
|
||||
import org.jetbrains.kotlin.gradle.plugin.statistics.BuildFlowService
|
||||
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService
|
||||
import org.jetbrains.kotlin.gradle.report.BuildMetricsService
|
||||
import org.jetbrains.kotlin.gradle.plugin.statistics.BuildFlowService
|
||||
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsCompilerAttribute
|
||||
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsPlugin
|
||||
import org.jetbrains.kotlin.gradle.targets.js.KotlinWasmTargetAttribute
|
||||
@@ -268,24 +272,30 @@ abstract class KotlinBasePluginWrapper : DefaultKotlinBasePlugin() {
|
||||
}
|
||||
|
||||
private fun Project.setupDiagnosticsChecksAndReporting() {
|
||||
val collectorProvider = kotlinToolingDiagnosticsCollectorProvider
|
||||
val diagnosticRenderingOptions = ToolingDiagnosticRenderingOptions.forProject(this)
|
||||
|
||||
// Setup reporting from tasks
|
||||
tasks.withType(UsesKotlinToolingDiagnostics::class.java).configureEach {
|
||||
it.usesService(kotlinToolingDiagnosticsCollectorProvider)
|
||||
it.toolingDiagnosticsCollector.value(kotlinToolingDiagnosticsCollectorProvider)
|
||||
it.diagnosticRenderingOptions.set(ToolingDiagnosticRenderingOptions.forProject(this))
|
||||
it.usesService(collectorProvider)
|
||||
it.toolingDiagnosticsCollector.value(collectorProvider)
|
||||
it.diagnosticRenderingOptions.set(diagnosticRenderingOptions)
|
||||
}
|
||||
|
||||
// Launch checkers. Note that they are invoked eagerly to give them a fine-grained
|
||||
// control over the lifecycle
|
||||
project.launchKotlinGradleProjectCheckers()
|
||||
launchKotlinGradleProjectCheckers()
|
||||
|
||||
// Setup a task that will abort the build if errors will be reported. This task should be the first in the taskgraph
|
||||
project.locateOrRegisterEnsureNoKotlinGradlePluginErrors()
|
||||
|
||||
// Schedule diagnostics rendering
|
||||
project.launch {
|
||||
project.configurationResult.await()
|
||||
launch {
|
||||
configurationResult.await()
|
||||
renderReportedDiagnostics(
|
||||
project.kotlinToolingDiagnosticsCollector.getDiagnosticsForProject(project),
|
||||
project.logger,
|
||||
project.kotlinPropertiesProvider.internalVerboseDiagnostics
|
||||
collectorProvider.get().getDiagnosticsForProject(project),
|
||||
logger,
|
||||
diagnosticRenderingOptions.isVerbose
|
||||
)
|
||||
}
|
||||
|
||||
@@ -293,7 +303,7 @@ private fun Project.setupDiagnosticsChecksAndReporting() {
|
||||
// after projects are evaluated will be transparently rendered right away instead of being
|
||||
// silently swallowed
|
||||
gradle.projectsEvaluated {
|
||||
kotlinToolingDiagnosticsCollector.switchToTransparentMode()
|
||||
collectorProvider.get().switchToTransparentMode()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.gradle.plugin.diagnostics
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.InvalidUserCodeException
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.language.base.plugins.LifecycleBasePlugin
|
||||
import org.gradle.work.DisableCachingByDefault
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompileTool
|
||||
import org.jetbrains.kotlin.gradle.tasks.withType
|
||||
|
||||
@DisableCachingByDefault(
|
||||
because = "This task renders reported diagnostics; caching this task will hide diagnostics and obscure issues in the build"
|
||||
)
|
||||
internal abstract class EnsureNoKotlinGradlePluginErrors : DefaultTask() {
|
||||
@get:Input
|
||||
abstract val errorDiagnostics: ListProperty<ToolingDiagnostic>
|
||||
|
||||
@get:Internal
|
||||
abstract val renderingOptions: Property<ToolingDiagnosticRenderingOptions>
|
||||
|
||||
@TaskAction
|
||||
fun checkNoErrors() {
|
||||
if (errorDiagnostics.get().isNotEmpty()) {
|
||||
renderReportedDiagnostics(errorDiagnostics.get(), logger, renderingOptions.get().isVerbose)
|
||||
throw InvalidUserCodeException("Kotlin Gradle Plugin reported errors. Check the log for details")
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
internal const val TASK_NAME = "ensureNoKotlinGradlePluginErrors"
|
||||
}
|
||||
}
|
||||
|
||||
private const val DESCRIPTION =
|
||||
"Checks that Kotlin Gradle Plugin hasn't reported project configuration errors, failing otherwise. " +
|
||||
"This task always runs before compileKotlin* or similar tasks."
|
||||
|
||||
internal fun Project.locateOrRegisterEnsureNoKotlinGradlePluginErrors(): TaskProvider<EnsureNoKotlinGradlePluginErrors> {
|
||||
val taskProvider = tasks.register(
|
||||
EnsureNoKotlinGradlePluginErrors.TASK_NAME,
|
||||
EnsureNoKotlinGradlePluginErrors::class.java
|
||||
) { task ->
|
||||
task.errorDiagnostics.set(
|
||||
provider {
|
||||
kotlinToolingDiagnosticsCollector
|
||||
.getDiagnosticsForProject(this).asSequence()
|
||||
.filter { it.severity == ToolingDiagnostic.Severity.ERROR }
|
||||
.toList()
|
||||
}
|
||||
)
|
||||
task.renderingOptions.set(ToolingDiagnosticRenderingOptions.forProject(this))
|
||||
task.description = DESCRIPTION
|
||||
task.group = LifecycleBasePlugin.VERIFICATION_GROUP
|
||||
}
|
||||
|
||||
taskProvider.addDependsOnFromTasksThatShouldFailWhenErrorsReported(tasks)
|
||||
|
||||
return taskProvider
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds dependsOn from some selection of the [tasks] to the [this]-task, effectively causing them to fail
|
||||
* if the ERROR-diagnostics were reported.
|
||||
*
|
||||
* Currently, we're doing it conservatively and instrumenting only [KotlinCompileTool]-tasks.
|
||||
* The intuition here is that if the build manages to do something useful for a user without compiling any .kt-sources,
|
||||
* then it's OK for KGP to let that build pass even if it reported ERROR-diagnostics.
|
||||
*/
|
||||
private fun TaskProvider<EnsureNoKotlinGradlePluginErrors>.addDependsOnFromTasksThatShouldFailWhenErrorsReported(tasks: TaskContainer) {
|
||||
tasks.withType<KotlinCompileTool>().configureEach { it.dependsOn(this) }
|
||||
}
|
||||
+21
-4
@@ -12,11 +12,24 @@ data class ToolingDiagnostic(
|
||||
val id: String, val message: String, val severity: Severity, val throwable: Throwable? = null,
|
||||
) {
|
||||
enum class Severity {
|
||||
/**
|
||||
* More visible than most of the output (intuition: yellow-highlighting).
|
||||
* Doesn't prevent the build from running.
|
||||
*
|
||||
* Use for non-critical misconfigurations with low rate of false-positives
|
||||
*/
|
||||
WARNING,
|
||||
|
||||
/**
|
||||
* Stronger highlighting than WARNING, but doesn't prevent further actions (e.g. further
|
||||
* tasks) from being executed
|
||||
* Heavily emphasized in the output (intuition: bold red highlighting).
|
||||
*
|
||||
* ATTENTION. If a diagnostic with this severity is reported, Kotlin compiler
|
||||
* will _not_ be invoked (build will appear failed, as with compilation error)
|
||||
*
|
||||
* However, Gradle IDE Sync and other tasks that are not connected with
|
||||
* any of the Kotlin Compiler and tools (e.g. 'help', 'clean'), will run successfully.
|
||||
*
|
||||
* Use for critical misconfigurations that need immediate addressing
|
||||
*/
|
||||
ERROR,
|
||||
|
||||
@@ -27,8 +40,12 @@ data class ToolingDiagnostic(
|
||||
* - mask further errors (forcing users to make multiple runs before fixing all issues)
|
||||
*
|
||||
* - lead to unpleasant UX in IDE (if the failure happens during import, then depending
|
||||
* on when it happened users might not have even basic IDE assistance, which makes it
|
||||
* hard to fix the issue)
|
||||
* on when it happened users might not have even basic IDE assistance, which makes fixing
|
||||
* the root cause very annoying)
|
||||
*
|
||||
* Use for irreconcilable misconfigurations / malformed input which prevent further
|
||||
* configuration _and_ when the graceful degradation (allowing configuration phase to finish)
|
||||
* is too expensive.
|
||||
*/
|
||||
FATAL,
|
||||
}
|
||||
|
||||
+9
-5
@@ -24,9 +24,13 @@ internal class ToolingDiagnosticRenderingOptions(
|
||||
}
|
||||
}
|
||||
|
||||
internal fun Collection<ToolingDiagnostic>.withoutSuppressed(options: ToolingDiagnosticRenderingOptions): Collection<ToolingDiagnostic> =
|
||||
filter { !it.isSuppressed(options) }
|
||||
internal fun ToolingDiagnostic.isSuppressed(options: ToolingDiagnosticRenderingOptions): Boolean {
|
||||
return when {
|
||||
severity == ToolingDiagnostic.Severity.WARNING -> id in options.suppressedWarningIds
|
||||
|
||||
internal fun ToolingDiagnostic.isSuppressed(options: ToolingDiagnosticRenderingOptions): Boolean =
|
||||
severity == ToolingDiagnostic.Severity.WARNING && id in options.suppressedWarningIds
|
||||
|| severity == ToolingDiagnostic.Severity.ERROR && id in options.suppressedErrorIds
|
||||
severity == ToolingDiagnostic.Severity.ERROR -> id in options.suppressedErrorIds
|
||||
|
||||
// NB: FATALs can not be suppressed
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user