[Gradle] Support rendering diagnostics reported in execution phase
- Introduce 'UsesKotlinDiagnostics' marker-interface for tasks that want to report diagnostics during their execution - Introduce 'transparent mode' to collector. When all projects are evaluated, Collector switches to it and will render all received diagnostics right away. This allows reporting and rendering diagnostics that are reported not from tasks, but after evaluation phase (e.g. diagnostics on resolved dependencies) ^KT-58353 Fixed
This commit is contained in:
+20
-2
@@ -257,7 +257,7 @@ abstract class KotlinBasePluginWrapper : DefaultKotlinBasePlugin() {
|
||||
|
||||
project.registerBuildKotlinToolingMetadataTask()
|
||||
|
||||
project.launchDiagnosticChecksAndReporting()
|
||||
project.setupDiagnosticsChecksAndReporting()
|
||||
}
|
||||
|
||||
internal open fun createTestRegistry(project: Project) = KotlinTestsRegistry(project)
|
||||
@@ -267,8 +267,19 @@ abstract class KotlinBasePluginWrapper : DefaultKotlinBasePlugin() {
|
||||
): Plugin<Project>
|
||||
}
|
||||
|
||||
private fun Project.launchDiagnosticChecksAndReporting() {
|
||||
private fun Project.setupDiagnosticsChecksAndReporting() {
|
||||
// Setup reporting from tasks
|
||||
tasks.withType(UsesKotlinToolingDiagnostics::class.java).configureEach {
|
||||
it.usesService(kotlinToolingDiagnosticsCollectorProvider)
|
||||
it.toolingDiagnosticsCollector.value(kotlinToolingDiagnosticsCollectorProvider)
|
||||
it.diagnosticRenderingOptions.set(ToolingDiagnosticRenderingOptions.forProject(this))
|
||||
}
|
||||
|
||||
// Launch checkers. Note that they are invoked eagerly to give them a fine-grained
|
||||
// control over the lifecycle
|
||||
project.launchKotlinGradleProjectCheckers()
|
||||
|
||||
// Schedule diagnostics rendering
|
||||
project.launch {
|
||||
project.configurationResult.await()
|
||||
renderReportedDiagnostics(
|
||||
@@ -277,6 +288,13 @@ private fun Project.launchDiagnosticChecksAndReporting() {
|
||||
project.kotlinPropertiesProvider.internalVerboseDiagnostics
|
||||
)
|
||||
}
|
||||
|
||||
// Schedule switching of Collector to transparent mode, so that any diagnostics reported
|
||||
// after projects are evaluated will be transparently rendered right away instead of being
|
||||
// silently swallowed
|
||||
gradle.projectsEvaluated {
|
||||
kotlinToolingDiagnosticsCollector.switchToTransparentMode()
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AbstractKotlinPluginWrapper(
|
||||
|
||||
+26
-1
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.gradle.plugin.diagnostics
|
||||
|
||||
import org.gradle.api.InvalidUserCodeException
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.services.BuildService
|
||||
import org.gradle.api.services.BuildServiceParameters
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider
|
||||
@@ -20,6 +21,13 @@ private typealias ToolingDiagnosticId = String
|
||||
private typealias GradleProjectPath = String
|
||||
|
||||
internal abstract class KotlinToolingDiagnosticsCollector : BuildService<BuildServiceParameters.None> {
|
||||
/**
|
||||
* When collector is in transparent mode, any diagnostics received will be immediately rendered
|
||||
* instead of collected
|
||||
*/
|
||||
@Volatile
|
||||
private var isTransparent: Boolean = false
|
||||
|
||||
private val rawDiagnosticsFromProject: MutableMap<GradleProjectPath, MutableList<ToolingDiagnostic>> = ConcurrentHashMap()
|
||||
private val reportedIds: MutableSet<ToolingDiagnosticId> = Collections.newSetFromMap(ConcurrentHashMap())
|
||||
|
||||
@@ -39,6 +47,10 @@ internal abstract class KotlinToolingDiagnosticsCollector : BuildService<BuildSe
|
||||
saveDiagnostic(project, diagnostic)
|
||||
}
|
||||
|
||||
fun report(task: UsesKotlinToolingDiagnostics, diagnostic: ToolingDiagnostic) {
|
||||
renderReportedDiagnostic(diagnostic, task.logger, isVerbose = false) // TODO: wire suppression/verbosity
|
||||
}
|
||||
|
||||
fun reportOncePerGradleProject(fromProject: Project, diagnostic: ToolingDiagnostic, key: ToolingDiagnosticId = diagnostic.id) {
|
||||
if (reportedIds.add("${fromProject.path}#$key")) {
|
||||
saveDiagnostic(fromProject, diagnostic)
|
||||
@@ -51,7 +63,16 @@ internal abstract class KotlinToolingDiagnosticsCollector : BuildService<BuildSe
|
||||
}
|
||||
}
|
||||
|
||||
fun switchToTransparentMode() {
|
||||
isTransparent = true
|
||||
}
|
||||
|
||||
private fun saveDiagnostic(project: Project, diagnostic: ToolingDiagnostic) {
|
||||
if (isTransparent) {
|
||||
renderReportedDiagnostic(diagnostic, project.logger, project.kotlinPropertiesProvider.internalVerboseDiagnostics)
|
||||
return
|
||||
}
|
||||
|
||||
if (diagnostic.severity == ToolingDiagnostic.Severity.FATAL) {
|
||||
throw InvalidUserCodeException(diagnostic.message)
|
||||
}
|
||||
@@ -61,8 +82,12 @@ internal abstract class KotlinToolingDiagnosticsCollector : BuildService<BuildSe
|
||||
}
|
||||
}
|
||||
|
||||
internal val Project.kotlinToolingDiagnosticsCollectorProvider: Provider<KotlinToolingDiagnosticsCollector>
|
||||
get() = gradle.registerClassLoaderScopedBuildService(KotlinToolingDiagnosticsCollector::class)
|
||||
|
||||
|
||||
internal val Project.kotlinToolingDiagnosticsCollector: KotlinToolingDiagnosticsCollector
|
||||
get() = gradle.registerClassLoaderScopedBuildService(KotlinToolingDiagnosticsCollector::class).get()
|
||||
get() = kotlinToolingDiagnosticsCollectorProvider.get()
|
||||
|
||||
internal fun Project.reportDiagnostic(diagnostic: ToolingDiagnostic) {
|
||||
kotlinToolingDiagnosticsCollector.report(this, diagnostic)
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.Project
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider
|
||||
|
||||
internal class ToolingDiagnosticRenderingOptions(
|
||||
val isVerbose: Boolean,
|
||||
val suppressedWarningIds: List<String>,
|
||||
val suppressedErrorIds: List<String>
|
||||
) {
|
||||
companion object {
|
||||
fun forProject(project: Project): ToolingDiagnosticRenderingOptions = with(project.kotlinPropertiesProvider) {
|
||||
ToolingDiagnosticRenderingOptions(
|
||||
internalVerboseDiagnostics,
|
||||
suppressedGradlePluginWarnings,
|
||||
suppressedGradlePluginErrors
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun Collection<ToolingDiagnostic>.withoutSuppressed(options: ToolingDiagnosticRenderingOptions): Collection<ToolingDiagnostic> =
|
||||
filter { !it.isSuppressed(options) }
|
||||
|
||||
internal fun ToolingDiagnostic.isSuppressed(options: ToolingDiagnosticRenderingOptions): Boolean =
|
||||
severity == ToolingDiagnostic.Severity.WARNING && id in options.suppressedWarningIds
|
||||
|| severity == ToolingDiagnostic.Severity.ERROR && id in options.suppressedErrorIds
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.Task
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.Internal
|
||||
|
||||
internal interface UsesKotlinToolingDiagnostics : Task {
|
||||
@get:Internal
|
||||
val toolingDiagnosticsCollector: Property<KotlinToolingDiagnosticsCollector>
|
||||
|
||||
@get:Internal
|
||||
val diagnosticRenderingOptions: Property<ToolingDiagnosticRenderingOptions>
|
||||
|
||||
fun reportDiagnostic(diagnostic: ToolingDiagnostic) {
|
||||
toolingDiagnosticsCollector.get().report(this, diagnostic)
|
||||
}
|
||||
}
|
||||
+14
-6
@@ -10,14 +10,22 @@ import org.jetbrains.kotlin.gradle.plugin.diagnostics.ToolingDiagnostic.Severity
|
||||
|
||||
internal fun renderReportedDiagnostics(diagnostics: Collection<ToolingDiagnostic>, logger: Logger, isVerbose: Boolean) {
|
||||
for (diagnostic in diagnostics) {
|
||||
when (diagnostic.severity) {
|
||||
WARNING -> logger.warn("w: ${diagnostic.render(isVerbose)}\n")
|
||||
renderReportedDiagnostic(diagnostic, logger, isVerbose)
|
||||
}
|
||||
}
|
||||
|
||||
ERROR -> logger.error("e: ${diagnostic.render(isVerbose)}\n")
|
||||
internal fun renderReportedDiagnostic(
|
||||
diagnostic: ToolingDiagnostic,
|
||||
logger: Logger,
|
||||
isVerbose: Boolean
|
||||
) {
|
||||
when (diagnostic.severity) {
|
||||
WARNING -> logger.warn("w: ${diagnostic.render(isVerbose)}\n")
|
||||
|
||||
FATAL ->
|
||||
error("Internal error: FATAL diagnostics throw an exception immediately in KotlinToolingDiagnosticsCollector")
|
||||
}
|
||||
ERROR -> logger.error("e: ${diagnostic.render(isVerbose)}\n")
|
||||
|
||||
FATAL ->
|
||||
error("Internal error: FATAL diagnostics throw an exception immediately in KotlinToolingDiagnosticsCollector")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user