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()
+            )
+        }
+    }
 }
-