KT-49780 prepare IC to log cache corruption reason
diff --git a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/IncrementalCompilerRunner.kt b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/IncrementalCompilerRunner.kt index eadf86c..3856a2b 100644 --- a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/IncrementalCompilerRunner.kt +++ b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/IncrementalCompilerRunner.kt
@@ -38,6 +38,7 @@ import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.progress.CompilationCanceledStatus import java.io.File +import java.io.IOException abstract class IncrementalCompilerRunner< Args : CommonCompilerArguments, @@ -81,7 +82,6 @@ providedChangedFiles: ChangedFiles?, projectDir: File? = null ): ExitCode { - var caches = createCacheManager(args, projectDir) if (withAbiSnapshot) { reporter.report { "Incremental compilation with ABI snapshot enabled" } @@ -96,27 +96,32 @@ emptyMap() } + var currentCache: CacheManager? = null + fun rebuild(reason: BuildAttribute): ExitCode { reporter.report { "Non-incremental compilation will be performed: $reason" } - caches.close(false) + currentCache?.close(false) // todo: we can recompile all files incrementally (not cleaning caches), so rebuild won't propagate reporter.measure(BuildTime.CLEAR_OUTPUT_ON_REBUILD) { cleanOutputsAndLocalStateOnRebuild(args) } - caches = createCacheManager(args, projectDir) + + //newCaches variable is used instead to avoid null checks + val newCaches = createCacheManager(args, projectDir).also { currentCache = it } if (providedChangedFiles == null) { - caches.inputsCache.sourceSnapshotMap.compareAndUpdate(allSourceFiles) + newCaches.inputsCache.sourceSnapshotMap.compareAndUpdate(allSourceFiles) } val allKotlinFiles = allSourceFiles.filter { it.isKotlinFile(kotlinSourceFilesExtensions) } return compileIncrementally( - args, caches, allKotlinFiles, CompilationMode.Rebuild(reason), messageCollector, withAbiSnapshot, + args, newCaches, allKotlinFiles, CompilationMode.Rebuild(reason), messageCollector, withAbiSnapshot, classpathAbiSnapshot = classpathAbiSnapshot ) } // If compilation has crashed or we failed to close caches we have to clear them var cachesMayBeCorrupted = true - return try { + + fun compile(providedChangedFiles: ChangedFiles?, caches: CacheManager): ExitCode { val changedFiles = when (providedChangedFiles) { is ChangedFiles.Dependencies -> { val changedSources = caches.inputsCache.sourceSnapshotMap.compareAndUpdate(allSourceFiles) @@ -129,7 +134,6 @@ else -> providedChangedFiles } - @Suppress("MoveVariableDeclarationIntoWhen") val compilationMode = sourcesToCompile(caches, changedFiles, args, messageCollector, classpathAbiSnapshot) val exitCode = when (compilationMode) { @@ -165,12 +169,25 @@ rebuild(compilationMode.reason) } } + return exitCode + } + + return try { + + val caches = try { + createCacheManager(args, projectDir) + } catch (e: IOException) { + reporter.report { "Unable to init caches. Possible caches corruption: $e" } + return rebuild(BuildAttribute.CACHE_CORRUPTION) + }.also { currentCache = it } + + val exitCode = compile(providedChangedFiles, caches) if (exitCode == ExitCode.OK) { - performWorkAfterSuccessfulCompilation(caches) + currentCache?.also { performWorkAfterSuccessfulCompilation(it) } } - if (!caches.close(flush = true)) throw RuntimeException("Could not flush caches") + currentCache?.close(flush = true)?.takeIf { !it }?.also { throw RuntimeException("Could not flush caches") } // Here we should analyze exit code of compiler. E.g. compiler failure should lead to caches rebuild, // but now JsKlib compiler reports invalid exit code. cachesMayBeCorrupted = false @@ -200,6 +217,30 @@ } } + private fun determineCompilationMode( + providedChangedFiles: ChangedFiles?, + caches: CacheManager, + allSourceFiles: List<File>, + args: Args, + messageCollector: MessageCollector, + classpathAbiSnapshot: Map<String, AbiSnapshot> + ): CompilationMode { + val changedFiles = when (providedChangedFiles) { + is ChangedFiles.Dependencies -> { + val changedSources = caches.inputsCache.sourceSnapshotMap.compareAndUpdate(allSourceFiles) + ChangedFiles.Known( + providedChangedFiles.modified + changedSources.modified, + providedChangedFiles.removed + changedSources.removed + ) + } + null -> caches.inputsCache.sourceSnapshotMap.compareAndUpdate(allSourceFiles) + else -> providedChangedFiles + } + + @Suppress("MoveVariableDeclarationIntoWhen") + return sourcesToCompile(caches, changedFiles, args, messageCollector, classpathAbiSnapshot) + } + /** * Deletes output files and contents of output directories on rebuild, including `@LocalState` files/directories. *
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/IncrementalCompilationMultiProjectIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/IncrementalCompilationMultiProjectIT.kt index c65442d..b1e117f 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/IncrementalCompilationMultiProjectIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/IncrementalCompilationMultiProjectIT.kt
@@ -1,5 +1,6 @@ package org.jetbrains.kotlin.gradle +import org.gradle.api.logging.LogLevel import org.gradle.testkit.runner.BuildResult import org.gradle.util.GradleVersion import org.jetbrains.kotlin.gradle.testbase.* @@ -686,6 +687,28 @@ @DisplayName("KT-49780: No need to rebuild invalid code due to cache corruption") @GradleTest + fun testIncrementalBuildWithCompilationError2(gradleVersion: GradleVersion) { + defaultProject(gradleVersion) { + breakCachesAfterCompileKotlinExecution(this) + + build("assemble") + + subProject("lib").kotlinSourcesDir().resolve("bar/B.kt").modify { + it.replace("fun b() {}", "fun bb() = 123") + } + + buildAndFail("assemble") { + printBuildOutput() + assertOutputContains("Unable to init caches. Possible caches corruption") + assertOutputContains("Non-incremental compilation will be performed:") + assertOutputDoesNotContain("Using fallback strategy") + assertOutputDoesNotContain("out-of-process execution strategy") + } + } + } + + @DisplayName("KT-49780: No need to rebuild invalid code due to cache corruption") + @GradleTest open fun testIncrementalBuildWithCompilationError(gradleVersion: GradleVersion) { defaultProject(gradleVersion) { breakCachesAfterCompileKotlinExecution(this) @@ -720,5 +743,19 @@ } } + private fun breakCachesInitialisationAfterCompileKotlinExecution(testProject: TestProject) { + listOf("app", "lib").forEach { + testProject.subProject(it).buildGradle.appendText( + """ + $compileKotlinTaskName { + doLast { + def file = new File(projectDir.path, "/build/kotlin/${compileKotlinTaskName}/cacheable/${compileCacheFolderName}/lookups/counters.tab") + println("Update lookup file " + file.path) + file.write("la-la") + } + } + """.trimIndent() + ) + } + } } -