Store errors into separate files

This commit is contained in:
nataliya.valtman
2022-09-13 12:39:22 +02:00
parent 4cbee3cde7
commit 4863e5d47b
14 changed files with 150 additions and 20 deletions
@@ -6,7 +6,10 @@
package org.jetbrains.kotlin.build.report
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import java.io.File
import kotlin.reflect.KFunction1
interface ICReporter {
@@ -24,6 +27,7 @@ interface ICReporter {
fun reportMarkDirty(affectedFiles: Iterable<File>, reason: String)
}
//TODO check and remove?
fun ICReporter.warn(message: () -> String) = report(message, severity = ICReporter.ReportSeverity.WARNING)
fun ICReporter.info(message: () -> String) = report(message, severity = ICReporter.ReportSeverity.INFO)
fun ICReporter.debug(message: () -> String) = report(message, severity = ICReporter.ReportSeverity.DEBUG)
@@ -35,3 +39,4 @@ object DoNothingICReporter : ICReporter {
override fun reportMarkDirtyMember(affectedFiles: Iterable<File>, scope: String, name: String) {}
override fun reportMarkDirty(affectedFiles: Iterable<File>, reason: String) {}
}
@@ -18,6 +18,8 @@ enum class BuildAttributeKind : Serializable {
enum class BuildAttribute(val kind: BuildAttributeKind, val readableString: String) : Serializable {
NO_BUILD_HISTORY(BuildAttributeKind.REBUILD_REASON, "Build history file not found"),
NO_ABI_SNAPSHOT(BuildAttributeKind.REBUILD_REASON, "ABI snapshot not found"),
NO_LAST_BUILD_INFO(BuildAttributeKind.REBUILD_REASON, "Last build info not found"),
INVALID_LAST_BUILD_INFO(BuildAttributeKind.REBUILD_REASON, "Last build info corrupted"),
CLASSPATH_SNAPSHOT_NOT_FOUND(BuildAttributeKind.REBUILD_REASON, "Classpath snapshot not found"),
IC_FAILED_TO_GET_CHANGED_FILES(BuildAttributeKind.REBUILD_REASON, "Failed to get changed files"),
IC_FAILED_TO_COMPUTE_FILES_TO_RECOMPILE(BuildAttributeKind.REBUILD_REASON, "Failed to compute files to recompile"),
@@ -16,8 +16,10 @@
package org.jetbrains.kotlin.incremental
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.incremental.AbiSnapshotImpl.Companion.readAbiSnapshot
import org.jetbrains.kotlin.incremental.AbiSnapshotImpl.Companion.writeAbiSnapshot
import org.jetbrains.kotlin.incremental.util.reportException
import java.io.*
data class BuildInfo(val startTS: Long, val dependencyToAbiSnapshot: Map<String, AbiSnapshot> = mapOf()) : Serializable {
@@ -43,10 +45,16 @@ data class BuildInfo(val startTS: Long, val dependencyToAbiSnapshot: Map<String,
}
}
fun read(file: File): BuildInfo =
ObjectInputStream(FileInputStream(file)).use {
it.readBuildInfo()
fun read(file: File, messageCollector: MessageCollector): BuildInfo? {
return try {
ObjectInputStream(FileInputStream(file)).use {
it.readBuildInfo()
}
} catch (e: Exception) {
messageCollector.reportException(e)
null
}
}
fun write(buildInfo: BuildInfo, file: File) {
ObjectOutputStream(FileOutputStream(file)).use {
@@ -18,15 +18,12 @@ package org.jetbrains.kotlin.incremental
import org.jetbrains.kotlin.build.DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS
import org.jetbrains.kotlin.build.GeneratedFile
import org.jetbrains.kotlin.build.report.BuildReporter
import org.jetbrains.kotlin.build.report.debug
import org.jetbrains.kotlin.build.report.info
import org.jetbrains.kotlin.build.report.*
import org.jetbrains.kotlin.build.report.metrics.BuildAttribute
import org.jetbrains.kotlin.build.report.metrics.BuildAttribute.*
import org.jetbrains.kotlin.build.report.metrics.BuildPerformanceMetric
import org.jetbrains.kotlin.build.report.metrics.BuildTime
import org.jetbrains.kotlin.build.report.metrics.measure
import org.jetbrains.kotlin.build.report.warn
import org.jetbrains.kotlin.cli.common.*
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
@@ -39,6 +36,7 @@ import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.incremental.parsing.classesFqNames
import org.jetbrains.kotlin.incremental.util.BufferingMessageCollector
import org.jetbrains.kotlin.incremental.util.reportException
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.progress.CompilationCanceledStatus
import org.jetbrains.kotlin.util.removeSuffixIfPresent
@@ -100,6 +98,7 @@ abstract class IncrementalCompilerRunner<
)
}
is ICResult.Failed -> {
messageCollector.reportException(result.cause)
reporter.warn {
// The indentation after the first line is intentional (so that this message is distinct from next message)
"""
@@ -126,7 +126,7 @@ class IncrementalJsCompilerRunner(
if (!withAbiSnapshot && !buildHistoryFile.isFile) {
return CompilationMode.Rebuild(BuildAttribute.NO_BUILD_HISTORY)
}
val lastBuildInfo = BuildInfo.read(lastBuildInfoFile)
val lastBuildInfo = BuildInfo.read(lastBuildInfoFile, messageCollector) ?: return CompilationMode.Rebuild(BuildAttribute.INVALID_LAST_BUILD_INFO)
val dirtyFiles = DirtyFilesContainer(caches, reporter, kotlinSourceFilesExtensions)
initDirtyFiles(dirtyFiles, changedFiles)
@@ -195,7 +195,7 @@ open class IncrementalJvmCompilerRunner(
classpathAbiSnapshots: Map<String, AbiSnapshot>
): CompilationMode {
return try {
calculateSourcesToCompileImpl(caches, changedFiles, args, classpathAbiSnapshots, withAbiSnapshot)
calculateSourcesToCompileImpl(caches, changedFiles, args, messageCollector, classpathAbiSnapshots, withAbiSnapshot)
} finally {
psiFileProvider.messageCollector.flush(messageCollector)
psiFileProvider.messageCollector.clear()
@@ -238,6 +238,7 @@ open class IncrementalJvmCompilerRunner(
caches: IncrementalJvmCachesManager,
changedFiles: ChangedFiles.Known,
args: K2JVMCompilerArguments,
messageCollector: MessageCollector,
abiSnapshots: Map<String, AbiSnapshot>,
withAbiSnapshot: Boolean
): CompilationMode {
@@ -273,7 +274,10 @@ open class IncrementalJvmCompilerRunner(
// workingDir as workingDir is an @OutputDirectory, so the files must be present in an incremental build.)
return CompilationMode.Rebuild(BuildAttribute.NO_BUILD_HISTORY)
}
val lastBuildInfo = BuildInfo.read(lastBuildInfoFile)
if (!lastBuildInfoFile.exists()) {
return CompilationMode.Rebuild(BuildAttribute.NO_LAST_BUILD_INFO)
}
val lastBuildInfo = BuildInfo.read(lastBuildInfoFile, messageCollector) ?: return CompilationMode.Rebuild(BuildAttribute.INVALID_LAST_BUILD_INFO)
reporter.debug { "Last Kotlin Build info -- $lastBuildInfo" }
val scopes = caches.lookupCache.lookupSymbols.map { it.scope.ifBlank { it.name } }.distinct()
@@ -0,0 +1,14 @@
/*
* 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.incremental.util
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
internal fun MessageCollector.reportException(e: Throwable) {
report(severity = CompilerMessageSeverity.EXCEPTION, message = "Incremental compilation failed:${e.message}\n${e.stackTraceToString()}")
}
@@ -12,6 +12,10 @@ import org.jetbrains.kotlin.gradle.report.BuildReportType
import org.jetbrains.kotlin.gradle.testbase.*
import org.junit.jupiter.api.DisplayName
import java.io.ObjectInputStream
import kotlin.io.path.exists
import kotlin.io.path.listDirectoryEntries
import kotlin.io.path.name
import kotlin.io.path.notExists
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
@@ -140,4 +144,22 @@ class BuildReportsIT : KGPBaseTest() {
}
}
}
@DisplayName("Error file is created")
@GradleTest
fun testErrorsFileSmokeTest(gradleVersion: GradleVersion) {
project("simpleProject", gradleVersion) {
build("compileKotlin") {
assertTrue { projectPath.resolve(".gradle/build_errors").listDirectoryEntries().isEmpty() }
}
val kotlinFile = kotlinSourcesDir().resolve("helloWorld.kt")
kotlinFile.modify { it.replace("ArrayList","skjfghsjk") }
buildAndFail("compileKotlin") {
val buildErrorDir = projectPath.resolve(".gradle/build_errors").toFile()
val files = buildErrorDir.listFiles()
assertTrue { files?.first()?.exists() ?: false }
}
}
}
}
@@ -6,13 +6,13 @@
package org.jetbrains.kotlin.compilerRunner
import org.jetbrains.kotlin.config.Services
import org.jetbrains.kotlin.gradle.logging.GradlePrintingMessageCollector
import org.jetbrains.kotlin.gradle.logging.GradleErrorMessageCollector
import org.jetbrains.kotlin.gradle.report.ReportingSettings
import java.io.File
internal class GradleCompilerEnvironment(
val compilerClasspath: Iterable<File>,
messageCollector: GradlePrintingMessageCollector,
messageCollector: GradleErrorMessageCollector,
outputItemsCollector: OutputItemsCollector,
val outputFiles: List<File>,
val reportingSettings: ReportingSettings,
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.daemon.common.filterExtractProps
import org.jetbrains.kotlin.gradle.dsl.KotlinJsProjectExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
import org.jetbrains.kotlin.gradle.logging.GradleErrorMessageCollector
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
import org.jetbrains.kotlin.gradle.logging.kotlinInfo
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
@@ -67,6 +68,7 @@ internal open class GradleCompilerRunner(
internal val sessionDirProvider = taskProvider.sessionsDir.get()
internal val projectNameProvider = taskProvider.projectName.get()
internal val incrementalModuleInfoProvider = taskProvider.buildModulesInfo
internal val errorsFile = taskProvider.errorsFile.get()
/**
* Compiler might be executed asynchronously. Do not do anything requiring end of compilation after this function is called.
@@ -203,6 +205,7 @@ internal open class GradleCompilerRunner(
kotlinScriptExtensions = environment.kotlinScriptExtensions,
allWarningsAsErrors = compilerArgs.allWarningsAsErrors,
compilerExecutionSettings = compilerExecutionSettings,
errorsFile = errorsFile
)
TaskLoggers.put(pathProvider, loggerProvider)
return runCompilerAsync(
@@ -65,6 +65,7 @@ internal class GradleKotlinCompilerWorkArguments(
val kotlinScriptExtensions: Array<String>,
val allWarningsAsErrors: Boolean,
val compilerExecutionSettings: CompilerExecutionSettings,
val errorsFile: File?,
) : Serializable {
companion object {
const val serialVersionUID: Long = 1
@@ -100,6 +101,7 @@ internal class GradleKotlinCompilerWork @Inject constructor(
private val metrics = if (reportingSettings.buildReportOutputs.isNotEmpty()) BuildMetricsReporterImpl() else DoNothingBuildMetricsReporter
private var icLogLines: List<String> = emptyList()
private val compilerExecutionSettings = config.compilerExecutionSettings
private val errorsFile = config.errorsFile
private val log: KotlinLogger =
TaskLoggers.get(taskPath)?.let { GradleKotlinLogger(it).apply { debug("Using '$taskPath' logger") } }
@@ -119,11 +121,13 @@ internal class GradleKotlinCompilerWork @Inject constructor(
override fun run() {
try {
val messageCollector = GradlePrintingMessageCollector(log, allWarningsAsErrors)
val (exitCode, executionStrategy) = compileWithDaemonOrFallbackImpl(messageCollector)
val gradlePrintingMessageCollector = GradlePrintingMessageCollector(log, allWarningsAsErrors)
val gradleMessageCollector = GradleErrorMessageCollector(gradlePrintingMessageCollector)
val (exitCode, executionStrategy) = compileWithDaemonOrFallbackImpl(gradleMessageCollector)
if (incrementalCompilationEnvironment?.disableMultiModuleIC == true) {
incrementalCompilationEnvironment.multiModuleICSettings.buildHistoryFile.delete()
}
errorsFile?.also { gradleMessageCollector.flush(it) }
throwExceptionIfCompilationFailed(exitCode, executionStrategy)
} finally {
@@ -0,0 +1,54 @@
/*
* 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.gradle.logging
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSourceLocation
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import java.io.File
import java.io.FileWriter
class GradleErrorMessageCollector(private val delegate: MessageCollector? = null) : MessageCollector {
private val errors = ArrayList<String>()
override fun clear() {
delegate?.clear()
errors.clear()
}
fun report(error: Throwable, location: CompilerMessageSourceLocation?) {
report(CompilerMessageSeverity.EXCEPTION, "${error.message}\n${error.stackTraceToString()}", location)
}
override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageSourceLocation?) {
delegate?.report(severity, message, location)
if (severity in listOf(CompilerMessageSeverity.ERROR, CompilerMessageSeverity.EXCEPTION)) {
synchronized(errors) {
errors.add(message)
}
}
}
override fun hasErrors(): Boolean {
return errors.isNotEmpty()
}
fun flush(file: File) {
if (!hasErrors()) {
return
}
file.createNewFile()
println("Errors were stored into ${file.absolutePath}")
FileWriter(file).use {
for (error in errors) {
it.append("error message: $error\n\n")
}
it.flush()
}
clear()
}
}
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.compilerRunner.GradleCompilerEnvironment
import org.jetbrains.kotlin.compilerRunner.OutputItemsCollectorImpl
import org.jetbrains.kotlin.gradle.dsl.*
import org.jetbrains.kotlin.gradle.internal.tasks.allOutputFiles
import org.jetbrains.kotlin.gradle.logging.GradleErrorMessageCollector
import org.jetbrains.kotlin.gradle.logging.GradlePrintingMessageCollector
import org.jetbrains.kotlin.gradle.tasks.internal.KotlinMultiplatformCommonOptionsCompat
import java.io.File
@@ -109,11 +110,12 @@ abstract class KotlinCompileCommon @Inject constructor(
inputChanges: InputChanges,
taskOutputsBackup: TaskOutputsBackup?
) {
val messageCollector = GradlePrintingMessageCollector(logger, args.allWarningsAsErrors)
val gradlePrintingMessageCollector = GradlePrintingMessageCollector(logger, args.allWarningsAsErrors)
val gradleMessageCollector = GradleErrorMessageCollector(gradlePrintingMessageCollector)
val outputItemCollector = OutputItemsCollectorImpl()
val compilerRunner = compilerRunner.get()
val environment = GradleCompilerEnvironment(
defaultCompilerClasspath, messageCollector, outputItemCollector,
defaultCompilerClasspath, gradleMessageCollector, outputItemCollector,
reportingSettings = reportingSettings(),
outputFiles = allOutputFiles()
)
@@ -123,5 +125,6 @@ abstract class KotlinCompileCommon @Inject constructor(
args,
environment
)
compilerRunner.errorsFile?.also { gradleMessageCollector.flush(it) }
}
}
@@ -44,6 +44,7 @@ import org.jetbrains.kotlin.gradle.incremental.*
import org.jetbrains.kotlin.gradle.internal.*
import org.jetbrains.kotlin.gradle.internal.tasks.TaskWithLocalState
import org.jetbrains.kotlin.gradle.internal.tasks.allOutputFiles
import org.jetbrains.kotlin.gradle.logging.GradleErrorMessageCollector
import org.jetbrains.kotlin.gradle.logging.GradleKotlinLogger
import org.jetbrains.kotlin.gradle.logging.GradlePrintingMessageCollector
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
@@ -232,6 +233,12 @@ abstract class GradleCompileTaskProvider @Inject constructor(
}
}
)
@get:Internal
val errorsFile: Provider<File?> = objectFactory
.property(
gradle.rootProject.rootDir.resolve(".gradle/build_errors/").also { it.mkdirs() }
.resolve("errors-${System.currentTimeMillis()}.log"))
}
abstract class AbstractKotlinCompile<T : CommonCompilerArguments> @Inject constructor(
@@ -723,7 +730,8 @@ abstract class KotlinCompile @Inject constructor(
validateKotlinAndJavaHasSameTargetCompatibility(args, kotlinSources)
val scriptSources = scriptSources.asFileTree.files
val messageCollector = GradlePrintingMessageCollector(logger, args.allWarningsAsErrors)
val gradlePrintingMessageCollector = GradlePrintingMessageCollector(logger, args.allWarningsAsErrors,)
val gradleMessageCollector = GradleErrorMessageCollector(gradlePrintingMessageCollector)
val outputItemCollector = OutputItemsCollectorImpl()
val compilerRunner = compilerRunner.get()
@@ -742,7 +750,7 @@ abstract class KotlinCompile @Inject constructor(
@Suppress("ConvertArgumentToSet", "DEPRECATION")
val environment = GradleCompilerEnvironment(
defaultCompilerClasspath, messageCollector, outputItemCollector,
defaultCompilerClasspath, gradleMessageCollector, outputItemCollector,
// In the incremental compiler, outputFiles will be cleaned on rebuild. However, because classpathSnapshotDir is not included in
// TaskOutputsBackup, we don't want classpathSnapshotDir to be cleaned immediately on rebuild, and therefore we exclude it from
// outputFiles here. (See TaskOutputsBackup's kdoc for more info.)
@@ -765,6 +773,7 @@ abstract class KotlinCompile @Inject constructor(
defaultKotlinJavaToolchain.get().providedJvm.get().javaHome,
taskOutputsBackup
)
compilerRunner.errorsFile?.also { gradleMessageCollector.flush(it) }
}
private fun validateKotlinAndJavaHasSameTargetCompatibility(
@@ -1152,7 +1161,8 @@ abstract class Kotlin2JsCompile @Inject constructor(
logger.kotlinDebug("compiling with args ${ArgumentUtils.convertArgumentsToStringList(args)}")
val messageCollector = GradlePrintingMessageCollector(logger, args.allWarningsAsErrors)
val gradlePrintingMessageCollector = GradlePrintingMessageCollector(logger, args.allWarningsAsErrors)
val gradleMessageCollector = GradleErrorMessageCollector(gradlePrintingMessageCollector)
val outputItemCollector = OutputItemsCollectorImpl()
val compilerRunner = compilerRunner.get()
@@ -1167,7 +1177,7 @@ abstract class Kotlin2JsCompile @Inject constructor(
} else null
val environment = GradleCompilerEnvironment(
defaultCompilerClasspath, messageCollector, outputItemCollector,
defaultCompilerClasspath, gradleMessageCollector, outputItemCollector,
outputFiles = allOutputFiles(),
reportingSettings = reportingSettings(),
incrementalCompilationEnvironment = icEnv
@@ -1180,6 +1190,8 @@ abstract class Kotlin2JsCompile @Inject constructor(
environment,
taskOutputsBackup
)
compilerRunner.errorsFile?.also { gradleMessageCollector.flush(it) }
}
private val projectRootDir = project.rootDir