[Gradle] Print diagnostics stacktrace only under --stacktrace or higher

Most of the changes in commit are test-related, as our tests launched
with '-full-stacktrace' by defayult + asserting full stacktrace is
flaky and unreliable

^KT-59774 Fixed
This commit is contained in:
Dmitry Savvinov
2023-07-27 14:46:00 +02:00
committed by Space Team
parent e334523fd8
commit 027fdb86a5
19 changed files with 260 additions and 58 deletions
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.gradle
import com.intellij.testFramework.TestDataFile
import org.gradle.api.logging.LogLevel
import org.gradle.api.logging.configuration.WarningMode
import org.gradle.internal.logging.LoggingConfigurationBuildOptions.StacktraceOption
import org.gradle.tooling.GradleConnector
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.cli.common.CompilerSystemProperties.COMPILE_INCREMENTAL_WITH_ARTIFACT_TRANSFORM
@@ -281,6 +282,8 @@ abstract class BaseGradleIT {
val enableKpmModelMapping: Boolean? = null,
val useDaemonFallbackStrategy: Boolean = false,
val useParsableDiagnosticsFormatting: Boolean = true,
val showDiagnosticsStacktrace: Boolean? = false, // false by default to not clutter the testdata + stacktraces change often
val stacktraceMode: String? = StacktraceOption.FULL_STACKTRACE_LONG_OPTION,
) {
val safeAndroidGradlePluginVersion: AGPVersion
get() = androidGradlePluginVersion ?: error("AGP version is expected to be set")
@@ -856,7 +859,6 @@ abstract class BaseGradleIT {
private fun Project.createGradleTailParameters(options: BuildOptions, params: Array<out String> = arrayOf()): List<String> =
params.toMutableList().apply {
add("--stacktrace")
when (minLogLevel) {
// Do not allow to configure Gradle project with `ERROR` log level (error logs visible on all log levels)
LogLevel.ERROR -> error("Log level ERROR is not supported by Gradle command-line")
@@ -941,6 +943,14 @@ abstract class BaseGradleIT {
add("-Pkotlin.internal.diagnostics.useParsableFormatting=true")
}
if (options.showDiagnosticsStacktrace != null) {
add("-Pkotlin.internal.diagnostics.showStacktrace=${options.showDiagnosticsStacktrace}")
}
if (options.stacktraceMode != null) {
add("--${options.stacktraceMode}")
}
// Workaround: override a console type set in the user machine gradle.properties (since Gradle 4.3):
add("--console=plain")
//The feature of failing the build on deprecation warnings is introduced in gradle 5.6
@@ -168,6 +168,26 @@ class MppDiagnosticsIt : KGPBaseTest() {
}
}
@GradleTest
fun testDiagnosticsRenderingWithStacktraceOption(gradleVersion: GradleVersion) {
project("diagnosticsRenderingWithStacktraceOption", gradleVersion) {
// KGP sets showDiagnosticsStacktrace=false and --full-stacktrace by default in tests,
// need to override that to mimic real-life scenarios
val options = buildOptions.copy(showDiagnosticsStacktrace = null, stacktraceMode = null)
build("help", buildOptions = options) {
assertEqualsToFile(expectedOutputFile("without-stacktrace"), extractProjectsAndTheirDiagnostics())
}
build("help", "--stacktrace", buildOptions = options) {
assertEqualsToFile(expectedOutputFile("with-stacktrace"), extractProjectsAndTheirDiagnostics())
}
build("help", "--full-stacktrace", buildOptions = options) {
assertEqualsToFile(expectedOutputFile("with-full-stacktrace"), extractProjectsAndTheirDiagnostics())
}
}
}
private fun TestProject.expectedOutputFile(suffix: String? = null): File {
val suffixIfAny = if (suffix != null) "-$suffix" else ""
return projectPath.resolve("expectedOutput$suffixIfAny.txt").toFile()
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.gradle.testbase
import org.gradle.api.logging.LogLevel
import org.gradle.api.logging.configuration.WarningMode
import org.gradle.internal.logging.LoggingConfigurationBuildOptions.StacktraceOption
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.cli.common.CompilerSystemProperties.COMPILE_INCREMENTAL_WITH_ARTIFACT_TRANSFORM
import org.jetbrains.kotlin.gradle.BaseGradleIT
@@ -18,6 +19,7 @@ import java.util.*
data class BuildOptions(
val logLevel: LogLevel = LogLevel.INFO,
val stacktraceMode: String? = StacktraceOption.FULL_STACKTRACE_LONG_OPTION,
val kotlinVersion: String = TestVersions.Kotlin.CURRENT,
val warningMode: WarningMode = WarningMode.Fail,
val configurationCache: Boolean = false,
@@ -44,6 +46,7 @@ data class BuildOptions(
val keepIncrementalCompilationCachesInMemory: Boolean? = null,
val useDaemonFallbackStrategy: Boolean = false,
val useParsableDiagnosticsFormatting: Boolean = true,
val showDiagnosticsStacktrace: Boolean? = false, // false by default to not clutter the testdata + stacktraces change often
val nativeOptions: NativeOptions = NativeOptions(),
val compilerExecutionStrategy: KotlinCompilerExecutionStrategy? = null,
val runViaBuildToolsApi: Boolean? = null,
@@ -187,6 +190,14 @@ data class BuildOptions(
arguments.add("-Pkotlin.compiler.runViaBuildToolsApi=$runViaBuildToolsApi")
}
if (showDiagnosticsStacktrace != null) {
arguments.add("-Pkotlin.internal.diagnostics.showStacktrace=$showDiagnosticsStacktrace")
}
if (stacktraceMode != null) {
arguments.add("--$stacktraceMode")
}
arguments.addAll(freeArgs)
return arguments.toList()
@@ -7,8 +7,7 @@ package org.jetbrains.kotlin.gradle.testbase
import org.gradle.testkit.runner.BuildResult
import org.jetbrains.kotlin.gradle.BaseGradleIT
import org.jetbrains.kotlin.gradle.internals.ENSURE_NO_KOTLIN_GRADLE_PLUGIN_ERRORS_TASK_NAME
import org.jetbrains.kotlin.gradle.internals.VERBOSE_DIAGNOSTIC_SEPARATOR
import org.jetbrains.kotlin.gradle.internals.*
import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnostics
import org.jetbrains.kotlin.gradle.plugin.diagnostics.ToolingDiagnosticFactory
import kotlin.test.assertNull
@@ -31,7 +30,7 @@ fun BuildResult.assertNoDiagnostic(diagnosticFactory: ToolingDiagnosticFactory,
}
fun String.assertHasDiagnostic(diagnosticFactory: ToolingDiagnosticFactory, withSubstring: String? = null) {
val diagnosticsMessages = extractVerboselyRenderedDiagnostics(diagnosticFactory, this)
val diagnosticsMessages = extractRenderedDiagnostics(diagnosticFactory, this)
assertTrue(diagnosticsMessages.isNotEmpty(), "Diagnostic with id=${diagnosticFactory.id} not found. Full text output:\n\n" + this)
if (withSubstring != null) {
assertTrue(
@@ -46,7 +45,7 @@ fun String.assertHasDiagnostic(diagnosticFactory: ToolingDiagnosticFactory, with
}
fun String.assertNoDiagnostic(diagnosticFactory: ToolingDiagnosticFactory, withSubstring: String? = null) {
val diagnosticMessages = extractVerboselyRenderedDiagnostics(diagnosticFactory, this)
val diagnosticMessages = extractRenderedDiagnostics(diagnosticFactory, this)
if (withSubstring != null) {
val matchedWithSubstring = diagnosticMessages.find { withSubstring in it }
assertNull(
@@ -76,6 +75,7 @@ fun String.assertNoDiagnostic(diagnosticFactory: ToolingDiagnosticFactory, withS
*/
fun BuildResult.extractProjectsAndTheirDiagnostics(): String = buildString {
var diagnosticStarted = false
var stacktraceStarted = false
val currentDiagnostic = mutableListOf<String>()
fun startDiagnostic(line: String, lineIndex: Int) {
@@ -89,7 +89,21 @@ fun BuildResult.extractProjectsAndTheirDiagnostics(): String = buildString {
}
fun continueDiagnostic(line: String) {
currentDiagnostic += line
when {
line == KOTLIN_DIAGNOSTIC_STACKTRACE_START -> {
stacktraceStarted = true
currentDiagnostic += line
currentDiagnostic += DIAGNOSTIC_STACKTRACE_REPLACEMENT_STUB
}
line == KOTLIN_DIAGNOSTIC_STACKTRACE_END_SEPARATOR -> {
stacktraceStarted = false
}
stacktraceStarted -> return // Omit stacktrace lines for tests stability
else -> currentDiagnostic += line
}
}
fun endDiagnostic(line: String, lineIndex: Int) {
@@ -100,12 +114,10 @@ fun BuildResult.extractProjectsAndTheirDiagnostics(): String = buildString {
currentDiagnostic += line
// Suppress InternalKotlinGradlePluginProperties, but only if the single property it complains about is
// 'kotlin.internal.diagnostics.useParsableFormatting'
val offendingProperties = currentDiagnostic.asSequence().filter { it.startsWith("kotlin.internal.") }
if (KotlinToolingDiagnostics.InternalKotlinGradlePluginPropertiesUsed.id !in currentDiagnostic.first() ||
offendingProperties.singleOrNull() != "kotlin.internal.diagnostics.useParsableFormatting"
) {
if (KotlinToolingDiagnostics.InternalKotlinGradlePluginPropertiesUsed.id in currentDiagnostic.first()) {
val cleanedDiagnostic = filterKgpUtilityPropertiesFromDiagnostic(currentDiagnostic)
if (cleanedDiagnostic.isNotEmpty()) appendLine(cleanedDiagnostic.joinToString(separator = "\n", postfix = "\n"))
} else {
appendLine(currentDiagnostic.joinToString(separator = "\n", postfix = "\n"))
}
@@ -131,6 +143,27 @@ fun BuildResult.extractProjectsAndTheirDiagnostics(): String = buildString {
}
}.trim()
/**
* Filters from the report all internal utility-properties that KGP uses in tests.
* If no properties aside from utility-properties were reported, the whole report is hidden
*/
private fun filterKgpUtilityPropertiesFromDiagnostic(diagnosticLines: List<String>): List<String> {
val diagnosticWithUtilityPropertiesFiltered = diagnosticLines.filter { line ->
!utilityInternalProperties.any { line.startsWith(it) }
}
return if (diagnosticWithUtilityPropertiesFiltered.none { it.startsWith("kotlin.internal") }) {
// all reported properties were utility-properties, so don't render this diagnostic at all
emptyList()
} else {
diagnosticWithUtilityPropertiesFiltered
}
}
private val utilityInternalProperties = listOf(
KOTLIN_INTERNAL_DIAGNOSTICS_USE_PARSABLE_FORMATTING,
KOTLIN_INTERNAL_DIAGNOSTICS_SHOW_STACKTRACE,
)
/*
Expected format
w: [DIAGNOSTIC_ID | WARNING] first line of diagnostic's text
@@ -158,18 +191,18 @@ private val DIAGNOSTIC_START_REGEX = """\s*([we]:)?\s*\[\w+ \| \w+].*""".toRegex
Multiline
Text"
*/
private fun extractVerboselyRenderedDiagnostics(diagnostic: ToolingDiagnosticFactory, fromText: String): List<String> {
private fun extractRenderedDiagnostics(diagnostic: ToolingDiagnosticFactory, fromText: String): List<String> {
var parsedPrefix = 0
return generateSequence {
extractNextVerboselyRenderedDiagnosticAndIndex(diagnostic, fromText, startIndex = parsedPrefix)
extractNextDiagnosticAndIndex(diagnostic, fromText, startIndex = parsedPrefix)
?.also { (_, newPrefix) -> parsedPrefix = newPrefix }
?.first
}.toList()
}
// Returns diagnostic substring + index of the first symbol after the diagnostic message
private fun extractNextVerboselyRenderedDiagnosticAndIndex(
private fun extractNextDiagnosticAndIndex(
diagnostic: ToolingDiagnosticFactory,
fromText: String,
startIndex: Int,
@@ -184,9 +217,12 @@ private fun extractNextVerboselyRenderedDiagnosticAndIndex(
// NB: substring's endIndex is exclusive, which gives us exactly the message
val diagnosticMessage = fromText.substring(diagnosticMessageStart, diagnosticSeparatorStartIndex)
.trim { it.isWhitespace() || it == '\n' }
val diagnosticMessageSanitized = diagnosticMessage.replace(DIAGNOSTIC_STACKTRACE_REGEX, DIAGNOSTIC_STACKTRACE_REPLACEMENT_STUB)
val indexOfFirstSymbolAfterSeparator = diagnosticSeparatorStartIndex + VERBOSE_DIAGNOSTIC_SEPARATOR.length
return diagnosticMessage to indexOfFirstSymbolAfterSeparator
return diagnosticMessageSanitized to indexOfFirstSymbolAfterSeparator
}
private const val CONFIGURE_PROJECT_PREFIX = "> Configure project"
private const val TASK_EXECUTION_PREFIX = "> Task"
private val DIAGNOSTIC_STACKTRACE_REGEX = Regex("""$KOTLIN_DIAGNOSTIC_STACKTRACE_START[^#]*$KOTLIN_DIAGNOSTIC_STACKTRACE_END_SEPARATOR""")
private val DIAGNOSTIC_STACKTRACE_REPLACEMENT_STUB = """ <... stackframes are omitted for test stability ...>"""
@@ -428,7 +428,6 @@ private fun commonBuildSetup(
// Required toolchains should be pre-installed via repo. Tests should not download any JDKs
"-Porg.gradle.java.installations.auto-download=false",
"-Porg.gradle.java.installations.paths=$jdkLocations",
"--full-stacktrace",
if (enableBuildCacheDebug) "-Dorg.gradle.caching.debug=true" else null,
if (enableBuildScan) "--scan" else null,
kotlinDaemonDebugPort?.let {
@@ -0,0 +1,18 @@
plugins {
kotlin("multiplatform")
}
repositories {
mavenLocal()
mavenCentral()
}
kotlin {
jvm("customName")
sourceSets {
jvmMain { // should provoke PlatformSourceSetConventionUsedWithCustomTargetName
}
}
}
@@ -0,0 +1,22 @@
> Configure project :
w: [PlatformSourceSetConventionUsedWithCustomTargetName | WARNING] Accessed 'source set jvmMain', but jvm target used a custom name 'customName' (expected 'jvm'):
Replace:
kotlin {
jvm("customName") /* <- custom name used */
}
With:
kotlin {
jvm()
}
Stacktrace:
<... stackframes are omitted for test stability ...>
#diagnostic-end
w: [UnusedSourceSetsWarning | WARNING] The Kotlin source set jvmMain was configured but not added to any Kotlin compilation.
You can add a source set to a target's compilation by connecting it with the compilation's default source set using 'dependsOn'.
See https://kotl.in/connecting-source-sets
#diagnostic-end
@@ -0,0 +1,22 @@
> Configure project :
w: [PlatformSourceSetConventionUsedWithCustomTargetName | WARNING] Accessed 'source set jvmMain', but jvm target used a custom name 'customName' (expected 'jvm'):
Replace:
kotlin {
jvm("customName") /* <- custom name used */
}
With:
kotlin {
jvm()
}
Stacktrace:
<... stackframes are omitted for test stability ...>
#diagnostic-end
w: [UnusedSourceSetsWarning | WARNING] The Kotlin source set jvmMain was configured but not added to any Kotlin compilation.
You can add a source set to a target's compilation by connecting it with the compilation's default source set using 'dependsOn'.
See https://kotl.in/connecting-source-sets
#diagnostic-end
@@ -0,0 +1,19 @@
> Configure project :
w: [PlatformSourceSetConventionUsedWithCustomTargetName | WARNING] Accessed 'source set jvmMain', but jvm target used a custom name 'customName' (expected 'jvm'):
Replace:
kotlin {
jvm("customName") /* <- custom name used */
}
With:
kotlin {
jvm()
}
#diagnostic-end
w: [UnusedSourceSetsWarning | WARNING] The Kotlin source set jvmMain was configured but not added to any Kotlin compilation.
You can add a source set to a target's compilation by connecting it with the compilation's default source set using 'dependsOn'.
See https://kotl.in/connecting-source-sets
#diagnostic-end
@@ -2,9 +2,8 @@
w: [InternalKotlinGradlePluginPropertiesUsed | WARNING] ATTENTION! This build uses the following Kotlin Gradle Plugin properties:
kotlin.internal.suppressGradlePluginErrors
kotlin.internal.diagnostics.useParsableFormatting
kotlin.internal-properties are not recommended for production use.
Internal properties are not recommended for production use.
Stability and future compatibility of the build is not guaranteed.
#diagnostic-end
@@ -294,7 +294,7 @@ private fun Project.setupDiagnosticsChecksAndReporting() {
renderReportedDiagnostics(
collectorProvider.get().getDiagnosticsForProject(project),
logger,
diagnosticRenderingOptions.useParsableFormat
diagnosticRenderingOptions
)
}
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLI
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_COMPILER_USE_PRECISE_COMPILATION_RESULTS_BACKUP
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_CREATE_DEFAULT_MULTIPLATFORM_PUBLICATIONS
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_EXPERIMENTAL_TRY_K2
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_INTERNAL_DIAGNOSTICS_SHOW_STACKTRACE
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_INTERNAL_DIAGNOSTICS_USE_PARSABLE_FORMATTING
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_JS_KARMA_BROWSERS
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_JS_STDLIB_DOM_API_INCLUDED
@@ -524,9 +525,22 @@ internal class PropertiesProvider private constructor(private val project: Proje
}
.orElse(false)
/**
* Outputs diagnostics in a more verbose format with synthetic delimiters, helping to parse
* diagnostics from unstructured input (e.g. build log).
* This mode is meant to be at least somewhat human-readable (as it's used in our tests),
* but is not meant to be pretty (users shouldn't see it)
*/
val internalDiagnosticsUseParsableFormat: Boolean
get() = booleanProperty(KOTLIN_INTERNAL_DIAGNOSTICS_USE_PARSABLE_FORMATTING) ?: false
/**
* *Overrides* the default option for rendering a stacktrace from which a diagnostic has been reported.
* Because it's an override, 'null' is meaningful here and signifies "prefer the choice made without this property"
*/
val internalDiagnosticsShowStacktrace: Boolean?
get() = booleanProperty(KOTLIN_INTERNAL_DIAGNOSTICS_SHOW_STACKTRACE)
val suppressedGradlePluginWarnings: List<String>
get() = property(PropertyNames.KOTLIN_SUPPRESS_GRADLE_PLUGIN_WARNINGS)?.split(",").orEmpty()
@@ -651,6 +665,7 @@ internal class PropertiesProvider private constructor(private val project: Proje
val KOTLIN_MPP_HIERARCHICAL_STRUCTURE_BY_DEFAULT = property("$KOTLIN_INTERNAL_NAMESPACE.mpp.hierarchicalStructureByDefault")
val KOTLIN_CREATE_DEFAULT_MULTIPLATFORM_PUBLICATIONS = property("$KOTLIN_INTERNAL_NAMESPACE.mpp.createDefaultMultiplatformPublications")
val KOTLIN_INTERNAL_DIAGNOSTICS_USE_PARSABLE_FORMATTING = property("$KOTLIN_INTERNAL_NAMESPACE.diagnostics.useParsableFormatting")
val KOTLIN_INTERNAL_DIAGNOSTICS_SHOW_STACKTRACE = property("$KOTLIN_INTERNAL_NAMESPACE.diagnostics.showStacktrace")
val KOTLIN_SUPPRESS_GRADLE_PLUGIN_ERRORS = property("$KOTLIN_INTERNAL_NAMESPACE.suppressGradlePluginErrors")
val MPP_13X_FLAGS_SET_BY_PLUGIN = property("$KOTLIN_INTERNAL_NAMESPACE.mpp.13X.flags.setByPlugin")
}
@@ -29,7 +29,7 @@ internal abstract class CheckKotlinGradlePluginConfigurationErrors : DefaultTask
@TaskAction
fun checkNoErrors() {
if (errorDiagnostics.get().isNotEmpty()) {
renderReportedDiagnostics(errorDiagnostics.get(), logger, renderingOptions.get().useParsableFormat)
renderReportedDiagnostics(errorDiagnostics.get(), logger, renderingOptions.get())
throw InvalidUserCodeException("Kotlin Gradle Plugin reported errors. Check the log for details")
}
}
@@ -610,7 +610,7 @@ object KotlinToolingDiagnostics {
|
|${propertiesUsed.joinToString(separator = "\n")}
|
|kotlin.internal-properties are not recommended for production use.
|Internal properties are not recommended for production use.
|Stability and future compatibility of the build is not guaranteed.
""".trimMargin()
)
@@ -9,7 +9,6 @@ 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
import org.jetbrains.kotlin.gradle.utils.registerClassLoaderScopedBuildService
import java.util.*
import java.util.concurrent.ConcurrentHashMap
@@ -39,7 +38,7 @@ internal abstract class KotlinToolingDiagnosticsCollector : BuildService<BuildSe
fun report(task: UsesKotlinToolingDiagnostics, diagnostic: ToolingDiagnostic) {
val options = task.diagnosticRenderingOptions.get()
if (!diagnostic.isSuppressed(options)) {
renderReportedDiagnostic(diagnostic, task.logger, options.useParsableFormat)
renderReportedDiagnostic(diagnostic, task.logger, options)
}
}
@@ -60,12 +59,11 @@ internal abstract class KotlinToolingDiagnosticsCollector : BuildService<BuildSe
}
private fun handleDiagnostic(project: Project, diagnostic: ToolingDiagnostic) {
if (diagnostic.isSuppressed(ToolingDiagnosticRenderingOptions.forProject(project))) return
val useParsableFormat = project.kotlinPropertiesProvider.internalDiagnosticsUseParsableFormat
val options = ToolingDiagnosticRenderingOptions.forProject(project)
if (diagnostic.isSuppressed(options)) return
if (isTransparent) {
renderReportedDiagnostic(diagnostic, project.logger, useParsableFormat)
renderReportedDiagnostic(diagnostic, project.logger, options)
return
}
@@ -74,7 +72,7 @@ internal abstract class KotlinToolingDiagnosticsCollector : BuildService<BuildSe
}
if (diagnostic.severity == ToolingDiagnostic.Severity.FATAL) {
throw diagnostic.createAnExceptionForFatalDiagnostic(isVerbose)
throw diagnostic.createAnExceptionForFatalDiagnostic(options)
}
}
}
@@ -6,20 +6,28 @@
package org.jetbrains.kotlin.gradle.plugin.diagnostics
import org.gradle.api.Project
import org.gradle.api.logging.configuration.ShowStacktrace
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider
internal class ToolingDiagnosticRenderingOptions(
val useParsableFormat: Boolean,
val suppressedWarningIds: List<String>,
val suppressedErrorIds: List<String>
val suppressedErrorIds: List<String>,
val showStacktrace: Boolean
) {
companion object {
fun forProject(project: Project): ToolingDiagnosticRenderingOptions = with(project.kotlinPropertiesProvider) {
ToolingDiagnosticRenderingOptions(
internalDiagnosticsUseParsableFormat,
suppressedGradlePluginWarnings,
suppressedGradlePluginErrors
)
fun forProject(project: Project): ToolingDiagnosticRenderingOptions {
return with(project.kotlinPropertiesProvider) {
val showStacktrace = internalDiagnosticsShowStacktrace
?: (project.gradle.startParameter.showStacktrace > ShowStacktrace.INTERNAL_EXCEPTIONS)
ToolingDiagnosticRenderingOptions(
internalDiagnosticsUseParsableFormat,
suppressedGradlePluginWarnings,
suppressedGradlePluginErrors,
showStacktrace
)
}
}
}
}
@@ -9,44 +9,60 @@ import org.gradle.api.InvalidUserCodeException
import org.gradle.api.logging.Logger
import org.jetbrains.kotlin.gradle.plugin.diagnostics.ToolingDiagnostic.Severity.*
internal fun renderReportedDiagnostics(diagnostics: Collection<ToolingDiagnostic>, logger: Logger, useParsableFormat: Boolean) {
internal fun renderReportedDiagnostics(
diagnostics: Collection<ToolingDiagnostic>,
logger: Logger,
renderingOptions: ToolingDiagnosticRenderingOptions,
) {
for (diagnostic in diagnostics) {
renderReportedDiagnostic(diagnostic, logger, useParsableFormat)
renderReportedDiagnostic(diagnostic, logger, renderingOptions)
}
}
internal fun renderReportedDiagnostic(
diagnostic: ToolingDiagnostic,
logger: Logger,
useParsableFormat: Boolean,
renderingOptions: ToolingDiagnosticRenderingOptions
) {
when (diagnostic.severity) {
WARNING -> logger.warn("w: ${diagnostic.render(useParsableFormat)}\n")
WARNING -> logger.warn("w: ${diagnostic.render(renderingOptions.useParsableFormat, renderingOptions.showStacktrace)}\n")
ERROR -> logger.error("e: ${diagnostic.render(useParsableFormat)}\n")
ERROR -> logger.error("e: ${diagnostic.render(renderingOptions.useParsableFormat, renderingOptions.showStacktrace)}\n")
FATAL -> throw diagnostic.createAnExceptionForFatalDiagnostic(useParsableFormat)
FATAL -> throw diagnostic.createAnExceptionForFatalDiagnostic(renderingOptions)
}
}
internal fun ToolingDiagnostic.createAnExceptionForFatalDiagnostic(useParsableFormat: Boolean): InvalidUserCodeException =
internal fun ToolingDiagnostic.createAnExceptionForFatalDiagnostic(
renderingOptions: ToolingDiagnosticRenderingOptions
): InvalidUserCodeException {
// NB: override showStacktrace to false, because it will be shown as 'cause' anyways
val message = render(renderingOptions.useParsableFormat, showStacktrace = false)
if (throwable != null)
InvalidUserCodeException(render(useParsableFormat), throwable)
throw InvalidUserCodeException(message, throwable)
else
InvalidUserCodeException(render(useParsableFormat))
throw InvalidUserCodeException(message)
}
private fun ToolingDiagnostic.render(useParsableFormat: Boolean): String = buildString {
if (useParsableFormat) {
appendLine(this@render)
append(DIAGNOSTIC_SEPARATOR)
} else {
append(message)
if (throwable != null) {
appendLine()
appendLine("Stacktrace:")
append(throwable.stackTraceToString().prependIndent(" "))
}
}
private fun ToolingDiagnostic.render(useParsableFormatting: Boolean, showStacktrace: Boolean): String = buildString {
// Main message
if (useParsableFormatting) appendLine(this@render) else append(message)
// Additional stacktrace, if requested
if (showStacktrace) renderStacktrace(this@render.throwable, useParsableFormatting)
// Separator, if in verbose mode
if (useParsableFormatting) appendLine(DIAGNOSTIC_SEPARATOR)
}
private fun StringBuilder.renderStacktrace(throwable: Throwable?, useParsableFormatting: Boolean) {
if (throwable == null) return
appendLine()
appendLine(DIAGNOSTIC_STACKTRACE_START)
appendLine(throwable.stackTraceToString().trim().prependIndent(" "))
if (useParsableFormatting) appendLine(DIAGNOSTIC_STACKTRACE_END_SEPARATOR)
}
internal const val DIAGNOSTIC_SEPARATOR = "#diagnostic-end"
internal const val DIAGNOSTIC_STACKTRACE_START = "Stacktrace:"
internal const val DIAGNOSTIC_STACKTRACE_END_SEPARATOR = "#stacktrace-end"
@@ -2,5 +2,5 @@
kotlin.internal.suppressGradlePluginErrors
kotlin.internal-properties are not recommended for production use.
Internal properties are not recommended for production use.
Stability and future compatibility of the build is not guaranteed.
@@ -7,8 +7,12 @@ package org.jetbrains.kotlin.gradle.internals
import org.jetbrains.kotlin.compilerRunner.asFinishLogMessage
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_NATIVE_IGNORE_DISABLED_TARGETS
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_INTERNAL_DIAGNOSTICS_USE_PARSABLE_FORMATTING
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_INTERNAL_DIAGNOSTICS_SHOW_STACKTRACE
import org.jetbrains.kotlin.gradle.plugin.diagnostics.DIAGNOSTIC_SEPARATOR
import org.jetbrains.kotlin.gradle.plugin.diagnostics.CheckKotlinGradlePluginConfigurationErrors
import org.jetbrains.kotlin.gradle.plugin.diagnostics.DIAGNOSTIC_STACKTRACE_END_SEPARATOR
import org.jetbrains.kotlin.gradle.plugin.diagnostics.DIAGNOSTIC_STACKTRACE_START
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinProjectStructureMetadata
import org.jetbrains.kotlin.gradle.plugin.mpp.MULTIPLATFORM_PROJECT_METADATA_JSON_FILE_NAME
import org.jetbrains.kotlin.gradle.plugin.mpp.parseKotlinSourceSetMetadataFromJson
@@ -26,3 +30,8 @@ const val ENSURE_NO_KOTLIN_GRADLE_PLUGIN_ERRORS_TASK_NAME = CheckKotlinGradlePlu
val KotlinCompilerExecutionStrategy.asFinishLogMessage: String
get() = this.asFinishLogMessage
val KOTLIN_INTERNAL_DIAGNOSTICS_USE_PARSABLE_FORMATTING = KOTLIN_INTERNAL_DIAGNOSTICS_USE_PARSABLE_FORMATTING
val KOTLIN_INTERNAL_DIAGNOSTICS_SHOW_STACKTRACE = KOTLIN_INTERNAL_DIAGNOSTICS_SHOW_STACKTRACE
val KOTLIN_DIAGNOSTIC_STACKTRACE_START = DIAGNOSTIC_STACKTRACE_START
val KOTLIN_DIAGNOSTIC_STACKTRACE_END_SEPARATOR = DIAGNOSTIC_STACKTRACE_END_SEPARATOR