Warn in Gradle log when incremental compilation fails

When incremental compilation fails, we currently log it at the `debug`
level (and fall back to non-incremental compilation). This commit will
change it to `warning` so that we can get more user reports, which will
allow us to fix the root cause.

Also make sure the warning includes a stack trace.

Additionally, let ReportSeverity.fromCode() return a non-null value
or throw an exception otherwise as that case is not expected.

^KT-52839 In Progress
This commit is contained in:
Hung Nguyen
2022-06-16 15:56:41 +01:00
committed by teamcity
parent 623f832bfd
commit e01c2bc651
23 changed files with 358 additions and 118 deletions
@@ -63,7 +63,6 @@ fun MessageCollector.reportFromDaemon(outputsCollector: ((File, List<File>) -> U
ReportSeverity.WARNING -> CompilerMessageSeverity.WARNING
ReportSeverity.INFO -> CompilerMessageSeverity.INFO
ReportSeverity.DEBUG -> CompilerMessageSeverity.LOGGING
else -> throw IllegalStateException("Unexpected compiler message report severity $severity")
}
if (message != null) {
report(compilerSeverity, message, attachment as? CompilerMessageSourceLocation)
@@ -92,7 +91,7 @@ private fun MessageCollector.reportUnexpected(category: Int, severity: Int, mess
ReportSeverity.ERROR -> CompilerMessageSeverity.ERROR
ReportSeverity.WARNING -> CompilerMessageSeverity.WARNING
ReportSeverity.INFO -> CompilerMessageSeverity.INFO
else -> CompilerMessageSeverity.LOGGING
ReportSeverity.DEBUG -> CompilerMessageSeverity.LOGGING
}
report(compilerMessageSeverity, "Unexpected message: category=$category; severity=$severity; message='$message'; attachment=$attachment")
@@ -48,8 +48,9 @@ enum class ReportSeverity(val code: Int) {
DEBUG(3);
companion object {
fun fromCode(code: Int): ReportSeverity? =
ReportSeverity.values().firstOrNull { it.code == code }
fun fromCode(code: Int): ReportSeverity {
return values().firstOrNull { it.code == code } ?: error("Can't find a matching ReportSeverity with code = $code")
}
}
}
@@ -0,0 +1,51 @@
/*
* Copyright 2010-2022 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.daemon.report
import org.jetbrains.kotlin.build.report.debug
import org.jetbrains.kotlin.build.report.info
import org.jetbrains.kotlin.build.report.warn
import org.jetbrains.kotlin.daemon.testfixtures.FakeCompilationResults
import org.junit.Test
import java.io.File
import kotlin.test.assertEquals
class BuildReportICReporterTest {
@Test
fun testVerboseMode() {
val compilationResults = FakeCompilationResults()
val reporter = BuildReportICReporter(compilationResults, File("unusedRootDir"), isVerbose = true)
reporter.warn { WARNING_MESSAGE }
reporter.info { INFO_MESSAGE }
reporter.debug { DEBUG_MESSAGE }
reporter.flush()
assertEquals(
expected = listOf(WARNING_MESSAGE, INFO_MESSAGE, DEBUG_MESSAGE),
actual = compilationResults.results.single() as List<*>
)
}
@Test
fun testNonVerboseMode() {
val compilationResults = FakeCompilationResults()
val reporter = BuildReportICReporter(compilationResults, File("unusedRootDir"), isVerbose = false)
reporter.warn { WARNING_MESSAGE }
reporter.info { INFO_MESSAGE }
reporter.debug { DEBUG_MESSAGE }
reporter.flush()
assertEquals(
expected = listOf(WARNING_MESSAGE, INFO_MESSAGE),
actual = compilationResults.results.single() as List<*>
)
}
}
internal const val WARNING_MESSAGE = "Warning message"
internal const val INFO_MESSAGE = "Info message"
internal const val DEBUG_MESSAGE = "Debug message"
@@ -0,0 +1,71 @@
/*
* Copyright 2010-2022 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.daemon.report
import org.jetbrains.kotlin.build.report.ICReporter
import org.jetbrains.kotlin.build.report.debug
import org.jetbrains.kotlin.build.report.info
import org.jetbrains.kotlin.build.report.warn
import org.jetbrains.kotlin.daemon.common.ReportCategory
import org.jetbrains.kotlin.daemon.common.ReportSeverity
import org.jetbrains.kotlin.daemon.testfixtures.FakeCompilerServicesFacadeBase
import org.junit.Test
import java.io.File
import kotlin.test.assertEquals
class DebugMessagesICReporterTest {
@Test
fun testWarningLevel() {
val compilerServices = FakeCompilerServicesFacadeBase()
val reporter = DebugMessagesICReporter(compilerServices, File("unusedRootDir"), ICReporter.ReportSeverity.WARNING)
reporter.warn { WARNING_MESSAGE }
reporter.info { INFO_MESSAGE }
reporter.debug { DEBUG_MESSAGE }
assertEquals(
expected = mapOf(
WARNING_MESSAGE to Pair(ReportCategory.IC_MESSAGE, ReportSeverity.WARNING),
),
actual = compilerServices.messages
)
}
@Test
fun testInfoLevel() {
val compilerServices = FakeCompilerServicesFacadeBase()
val reporter = DebugMessagesICReporter(compilerServices, File("unusedRootDir"), ICReporter.ReportSeverity.INFO)
reporter.warn { WARNING_MESSAGE }
reporter.info { INFO_MESSAGE }
reporter.debug { DEBUG_MESSAGE }
assertEquals(
expected = mapOf(
WARNING_MESSAGE to Pair(ReportCategory.IC_MESSAGE, ReportSeverity.WARNING),
INFO_MESSAGE to Pair(ReportCategory.IC_MESSAGE, ReportSeverity.INFO),
),
actual = compilerServices.messages
)
}
@Test
fun testDebugLevel() {
val compilerServices = FakeCompilerServicesFacadeBase()
val reporter = DebugMessagesICReporter(compilerServices, File("unusedRootDir"), ICReporter.ReportSeverity.DEBUG)
reporter.warn { WARNING_MESSAGE }
reporter.info { INFO_MESSAGE }
reporter.debug { DEBUG_MESSAGE }
assertEquals(
mapOf(
WARNING_MESSAGE to Pair(ReportCategory.IC_MESSAGE, ReportSeverity.WARNING),
INFO_MESSAGE to Pair(ReportCategory.IC_MESSAGE, ReportSeverity.INFO),
DEBUG_MESSAGE to Pair(ReportCategory.IC_MESSAGE, ReportSeverity.DEBUG)
),
actual = compilerServices.messages
)
}
}
@@ -0,0 +1,21 @@
/*
* Copyright 2010-2022 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.daemon.testfixtures
import org.jetbrains.kotlin.daemon.common.CompilationResults
import java.io.Serializable
class FakeCompilationResults : CompilationResults {
val results: List<Serializable>
get() = resultList.toList()
private val resultList = mutableListOf<Serializable>()
override fun add(compilationResultCategory: Int, value: Serializable) {
resultList.add(value)
}
}
@@ -0,0 +1,23 @@
/*
* Copyright 2010-2022 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.daemon.testfixtures
import org.jetbrains.kotlin.daemon.common.CompilerServicesFacadeBase
import org.jetbrains.kotlin.daemon.common.ReportCategory
import org.jetbrains.kotlin.daemon.common.ReportSeverity
import java.io.Serializable
class FakeCompilerServicesFacadeBase : CompilerServicesFacadeBase {
val messages: Map<String, Pair<ReportCategory, ReportSeverity>>
get() = messageMap.toMap()
private val messageMap = mutableMapOf<String, Pair<ReportCategory, ReportSeverity>>()
override fun report(category: Int, severity: Int, message: String?, attachment: Serializable?) {
messageMap[message!!] = ReportCategory.fromCode(category)!! to ReportSeverity.fromCode(severity)
}
}
@@ -5,16 +5,19 @@
package org.jetbrains.kotlin.daemon.report
import com.google.common.annotations.VisibleForTesting
import org.jetbrains.kotlin.build.report.ICReporter.ReportSeverity
import org.jetbrains.kotlin.build.report.ICReporter.ReportSeverity.DEBUG
import org.jetbrains.kotlin.build.report.ICReporterBase
import org.jetbrains.kotlin.build.report.RemoteICReporter
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.daemon.common.CompilationResultCategory
import org.jetbrains.kotlin.daemon.common.CompilationResults
import org.jetbrains.kotlin.build.report.ICReporterBase
import org.jetbrains.kotlin.build.report.RemoteICReporter
import java.io.File
import java.util.*
// todo: sync BuildReportICReporterAsync
internal class BuildReportICReporter(
@VisibleForTesting
class BuildReportICReporter(
private val compilationResults: CompilationResults,
rootDir: File,
private val isVerbose: Boolean = false
@@ -22,14 +25,10 @@ internal class BuildReportICReporter(
private val icLogLines = arrayListOf<String>()
private val recompilationReason = HashMap<File, String>()
override fun report(message: () -> String) {
icLogLines.add(message())
}
override fun report(message: () -> String, severity: ReportSeverity) {
if (severity == DEBUG && !isVerbose) return
override fun reportVerbose(message: () -> String) {
if (isVerbose) {
report(message)
}
icLogLines.add(message())
}
override fun reportCompileIteration(incremental: Boolean, sourceFiles: Collection<File>, exitCode: ExitCode) {
@@ -5,12 +5,13 @@
package org.jetbrains.kotlin.daemon.report
import org.jetbrains.kotlin.build.report.ICReporter.ReportSeverity
import org.jetbrains.kotlin.build.report.ICReporterBase
import org.jetbrains.kotlin.build.report.RemoteICReporter
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.daemon.common.CompilationResultCategory
import org.jetbrains.kotlin.daemon.common.CompilationResults
import org.jetbrains.kotlin.daemon.common.CompileIterationResult
import org.jetbrains.kotlin.build.report.ICReporterBase
import org.jetbrains.kotlin.build.report.RemoteICReporter
import java.io.File
internal class CompileIterationICReporter(
@@ -23,10 +24,7 @@ internal class CompileIterationICReporter(
)
}
override fun report(message: () -> String) {
}
override fun reportVerbose(message: () -> String) {
override fun report(message: () -> String, severity: ReportSeverity) {
}
override fun flush() {
@@ -5,17 +5,14 @@
package org.jetbrains.kotlin.daemon.report
import org.jetbrains.kotlin.build.report.ICReporter.ReportSeverity
import org.jetbrains.kotlin.build.report.RemoteICReporter
import org.jetbrains.kotlin.cli.common.ExitCode
import java.io.File
internal class CompositeICReporter(private val reporters: Iterable<RemoteICReporter>) : RemoteICReporter {
override fun report(message: () -> String) {
reporters.forEach { it.report(message) }
}
override fun reportVerbose(message: () -> String) {
reporters.forEach { it.reportVerbose(message) }
override fun report(message: () -> String, severity: ReportSeverity) {
reporters.forEach { it.report(message, severity) }
}
override fun reportCompileIteration(incremental: Boolean, sourceFiles: Collection<File>, exitCode: ExitCode) {
@@ -28,7 +28,7 @@ fun DaemonMessageReporter(
compilationOptions: CompilationOptions
): DaemonMessageReporter =
if (ReportCategory.DAEMON_MESSAGE.code in compilationOptions.reportCategories) {
val mySeverity = ReportSeverity.fromCode(compilationOptions.reportSeverity)!!
val mySeverity = ReportSeverity.fromCode(compilationOptions.reportSeverity)
DaemonMessageReporterImpl(servicesFacade, mySeverity)
}
else {
@@ -5,28 +5,28 @@
package org.jetbrains.kotlin.daemon.report
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.daemon.common.*
import com.google.common.annotations.VisibleForTesting
import org.jetbrains.kotlin.build.report.ICReporter
import org.jetbrains.kotlin.build.report.ICReporterBase
import org.jetbrains.kotlin.build.report.RemoteICReporter
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.daemon.common.CompilerServicesFacadeBase
import org.jetbrains.kotlin.daemon.common.ReportCategory
import org.jetbrains.kotlin.daemon.common.ReportSeverity
import org.jetbrains.kotlin.daemon.common.report
import java.io.File
internal class DebugMessagesICReporter(
@VisibleForTesting
class DebugMessagesICReporter(
private val servicesFacade: CompilerServicesFacadeBase,
rootDir: File,
private val isVerbose: Boolean
private val reportSeverity: ICReporter.ReportSeverity
) : ICReporterBase(rootDir), RemoteICReporter {
override fun report(message: () -> String) {
servicesFacade.report(
ReportCategory.IC_MESSAGE,
ReportSeverity.DEBUG, message()
)
}
override fun reportVerbose(message: () -> String) {
if (isVerbose) {
report(message)
}
override fun report(message: () -> String, severity: ICReporter.ReportSeverity) {
if (severity.level < reportSeverity.level) return
servicesFacade.report(ReportCategory.IC_MESSAGE, severity.getSeverity(), message())
}
override fun reportCompileIteration(incremental: Boolean, sourceFiles: Collection<File>, exitCode: ExitCode) {
@@ -34,4 +34,12 @@ internal class DebugMessagesICReporter(
override fun flush() {
}
}
}
internal fun ICReporter.ReportSeverity.getSeverity(): ReportSeverity {
return when (this) {
ICReporter.ReportSeverity.WARNING -> ReportSeverity.WARNING
ICReporter.ReportSeverity.INFO -> ReportSeverity.INFO
ICReporter.ReportSeverity.DEBUG -> ReportSeverity.DEBUG
}
}
@@ -18,7 +18,7 @@ internal fun DaemonMessageReporterAsync(
compilationOptions: CompilationOptions
): DaemonMessageReporter =
if (ReportCategory.DAEMON_MESSAGE.code in compilationOptions.reportCategories) {
val mySeverity = ReportSeverity.fromCode(compilationOptions.reportSeverity)!!
val mySeverity = ReportSeverity.fromCode(compilationOptions.reportSeverity)
DaemonMessageReporterAsyncAsyncImpl(servicesFacade, mySeverity)
} else {
DummyDaemonMessageReporterAsync
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.daemon.report.experimental
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import org.jetbrains.kotlin.build.report.ICReporter
import org.jetbrains.kotlin.build.report.ICReporterBase
import org.jetbrains.kotlin.build.report.RemoteBuildReporter
import org.jetbrains.kotlin.build.report.RemoteICReporter
@@ -16,25 +17,20 @@ import org.jetbrains.kotlin.build.report.metrics.RemoteBuildMetricsReporter
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.daemon.common.*
import org.jetbrains.kotlin.daemon.report.CompositeICReporter
import org.jetbrains.kotlin.daemon.report.getSeverity
import java.io.File
internal class DebugMessagesICReporterAsync(
private val servicesFacade: CompilerServicesFacadeBaseAsync,
rootDir: File,
private val isVerbose: Boolean
private val reportSeverity: ICReporter.ReportSeverity
) : ICReporterBase(rootDir), RemoteICReporter {
override fun report(message: () -> String) {
GlobalScope.async {
servicesFacade.report(
ReportCategory.IC_MESSAGE,
ReportSeverity.DEBUG, message()
)
}
}
override fun reportVerbose(message: () -> String) {
if (isVerbose) {
report(message)
override fun report(message: () -> String, severity: ICReporter.ReportSeverity) {
if (severity.level < reportSeverity.level) return
GlobalScope.async {
servicesFacade.report(ReportCategory.IC_MESSAGE, severity.getSeverity(), message())
}
}
@@ -57,10 +53,7 @@ internal class CompileIterationICReporterAsync(
}
}
override fun report(message: () -> String) {
}
override fun reportVerbose(message: () -> String) {
override fun report(message: () -> String, severity: ICReporter.ReportSeverity) {
}
override fun flush() {
@@ -75,14 +68,10 @@ internal class BuildReportICReporterAsync(
private val icLogLines = arrayListOf<String>()
private val recompilationReason = HashMap<File, String>()
override fun report(message: () -> String) {
icLogLines.add(message())
}
override fun report(message: () -> String, severity: ICReporter.ReportSeverity) {
if (severity == ICReporter.ReportSeverity.DEBUG && !isVerbose) return
override fun reportVerbose(message: () -> String) {
if (isVerbose) {
report(message)
}
icLogLines.add(message())
}
override fun reportCompileIteration(incremental: Boolean, sourceFiles: Collection<File>, exitCode: ExitCode) {
@@ -116,8 +105,14 @@ fun getICReporterAsync(
val reporters = ArrayList<RemoteICReporter>()
if (ReportCategory.IC_MESSAGE.code in compilationOptions.reportCategories) {
val isVerbose = compilationOptions.reportSeverity == ReportSeverity.DEBUG.code
reporters.add(DebugMessagesICReporterAsync(servicesFacade, root, isVerbose = isVerbose))
reporters.add(
DebugMessagesICReporterAsync(
servicesFacade = servicesFacade,
rootDir = root,
reportSeverity = ReportSeverity.fromCode(compilationOptions.reportSeverity)
.getSeverity(mapErrorToWarning = true, mapInfoToWarning = true)
)
)
}
val requestedResults = compilationOptions
@@ -16,12 +16,12 @@
package org.jetbrains.kotlin.daemon.report
import org.jetbrains.kotlin.build.report.ICReporter
import org.jetbrains.kotlin.build.report.RemoteBuildReporter
import org.jetbrains.kotlin.build.report.RemoteICReporter
import org.jetbrains.kotlin.build.report.metrics.BuildMetricsReporterImpl
import org.jetbrains.kotlin.build.report.metrics.DoNothingBuildMetricsReporter
import org.jetbrains.kotlin.daemon.common.*
import java.util.*
fun getBuildReporter(
servicesFacade: CompilerServicesFacadeBase,
@@ -32,8 +32,14 @@ fun getBuildReporter(
val reporters = ArrayList<RemoteICReporter>()
if (ReportCategory.IC_MESSAGE.code in compilationOptions.reportCategories) {
val isVerbose = compilationOptions.reportSeverity == ReportSeverity.DEBUG.code
reporters.add(DebugMessagesICReporter(servicesFacade, root, isVerbose = isVerbose))
reporters.add(
DebugMessagesICReporter(
servicesFacade = servicesFacade,
rootDir = root,
reportSeverity = ReportSeverity.fromCode(compilationOptions.reportSeverity)
.getSeverity(mapErrorToWarning = true, mapInfoToWarning = true)
)
)
}
val requestedResults = compilationOptions
@@ -63,3 +69,43 @@ fun getBuildReporter(
return RemoteBuildReporter(CompositeICReporter(reporters), metricsReporter)
}
internal fun ReportSeverity.getSeverity(
/**
* If true, [ReportSeverity.ERROR] will be mapped to [ICReporter.ReportSeverity.WARNING] (i.e., when [ReportSeverity.ERROR] is
* specified, [ICReporter.ReportSeverity.WARNING] messages will also be shown).
*
* NOTE: This parameter exists only because we don't have `ICReporter.ReportSeverity.ERROR` yet. Once that we create that enum, we can
* remove this parameter.
*/
mapErrorToWarning: Boolean,
/**
* If true, [ReportSeverity.INFO] will be mapped to [ICReporter.ReportSeverity.WARNING] (i.e., when [ReportSeverity.INFO] is specified,
* only messages with severity [ICReporter.ReportSeverity.WARNING] and above will be shown).
*
* NOTE: This parameter exists only to preserve the previous behavior (`mapInfoToWarning = true`). If `mapInfoToWarning` is never set
* to `true`, we can remove this parameter.
*/
mapInfoToWarning: Boolean
): ICReporter.ReportSeverity {
return when (this) {
ReportSeverity.ERROR -> if (mapErrorToWarning) {
ICReporter.ReportSeverity.WARNING
} else {
throw IllegalArgumentException(
"No mapping exists for `ReportSeverity.ERROR`." +
" Add `ICReporter.ReportSeverity.ERROR` and remove mapErrorToWarning, or set mapErrorToWarning = true."
)
}
ReportSeverity.WARNING -> ICReporter.ReportSeverity.WARNING
ReportSeverity.INFO -> if (mapInfoToWarning) {
ICReporter.ReportSeverity.WARNING
} else {
ICReporter.ReportSeverity.INFO
}
ReportSeverity.DEBUG -> ICReporter.ReportSeverity.DEBUG
}
}