From 98349411fb6b98d3187a348e7d893c4f8483fdc1 Mon Sep 17 00:00:00 2001 From: Hung Nguyen Date: Tue, 2 Aug 2022 13:21:55 +0100 Subject: [PATCH] Always compile non-incrementally if incremental state is missing Test: Updated IncrementalCompilationClasspathSnapshotJvmMultiProjectIT. .testMissingIncrementalState ^KT-53231 Fixed --- .../incremental/IncrementalCompilerRunner.kt | 1 + .../jetbrains/kotlin/gradle/BuildCacheIT.kt | 4 +--- .../IncrementalCompilationMultiProjectIT.kt | 15 ++++++------- .../gradle/testbase/compilationAssertions.kt | 21 ++++++++++++++++--- .../jetbrains/kotlin/gradle/tasks/Tasks.kt | 17 ++++++++------- 5 files changed, 38 insertions(+), 20 deletions(-) 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 a427e7a2055..1b5ed92b814 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 @@ -170,6 +170,7 @@ abstract class IncrementalCompilerRunner< when (compilationMode) { is CompilationMode.Incremental -> { try { + reporter.debug { "Performing incremental compilation" } val exitCode = if (withAbiSnapshot) { compileIncrementally( args, caches, allSourceFiles, compilationMode, messageCollector, diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildCacheIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildCacheIT.kt index c265ffc40af..65af7a10f44 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildCacheIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildCacheIT.kt @@ -160,11 +160,9 @@ class BuildCacheIT : KGPBaseTest() { bKtSourceFile.modify { it.replace("fun b() {}", "fun b() {}\nfun b2() {}") } build("assemble", buildOptions = defaultBuildOptions.copy(useICClasspathSnapshot = true, logLevel = LogLevel.DEBUG)) { - assertOutputDoesNotContain(NON_INCREMENTAL_COMPILATION_WILL_BE_PERFORMED) + assertIncrementalCompilation(expectedCompiledKotlinFiles = setOf(bKtSourceFile).map { it.relativeTo(projectPath)}) assertOutputContains("Incremental compilation with ABI snapshot enabled") - assertCompiledKotlinSources(setOf(bKtSourceFile).map { it.relativeTo(projectPath)}, output) } - } } 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 4df2a75825c..fffdb1cb170 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 @@ -2,7 +2,6 @@ package org.jetbrains.kotlin.gradle import org.gradle.testkit.runner.BuildResult import org.gradle.util.GradleVersion -import org.jetbrains.kotlin.build.report.metrics.BuildAttribute import org.jetbrains.kotlin.gradle.tasks.KotlinCompilerExecutionStrategy import org.jetbrains.kotlin.gradle.testbase.* import org.jetbrains.kotlin.gradle.util.checkedReplace @@ -658,7 +657,9 @@ abstract class BaseIncrementalCompilationMultiProjectIT : IncrementalCompilation ":lib:compileKotlin", "-Pkotlin.compiler.execution.strategy=${KotlinCompilerExecutionStrategy.IN_PROCESS.propertyValue}" ) { - assert(projectPath.resolve("lib/build/kotlin/${compileKotlinTaskName}/classpath-snapshot").listDirectoryEntries().isEmpty()) + projectPath.resolve("lib/build/kotlin/${compileKotlinTaskName}/classpath-snapshot").let { + assert(!it.exists() || it.listDirectoryEntries().isEmpty()) + } } // Perform the next build using Kotlin daemon without making a change and check that tasks are up-to-date. This is to ensure @@ -673,7 +674,7 @@ abstract class BaseIncrementalCompilationMultiProjectIT : IncrementalCompilation // In the next build, compilation should be non-incremental as incremental state is missing build(":lib:compileKotlin") { - assertNonIncrementalCompilation(BuildAttribute.INCREMENTAL_COMPILATION_FAILED) + assertNonIncrementalCompilation() } } } @@ -692,11 +693,11 @@ abstract class BaseIncrementalCompilationMultiProjectIT : IncrementalCompilation it.replace("fun a() {}", "fun a() { // Compile error: Missing closing bracket") } - // In the next build, compilation should be incremental and fail, and not fall back to non-incremental compilation + // In the next build, compilation should be incremental and fail (and not fall back to non-incremental compilation) buildAndFail(":lib:compileKotlin") { + assertIncrementalCompilation() assertTasksFailed(":lib:$compileKotlinTaskName") assertOutputContains("Compilation error. See log for more details") - assertOutputDoesNotContain(NON_INCREMENTAL_COMPILATION_WILL_BE_PERFORMED) } // Fix the compile error in the source code @@ -734,7 +735,7 @@ abstract class BaseIncrementalCompilationMultiProjectIT : IncrementalCompilation // In the next build, compilation should be incremental and fail, then fall back to non-incremental compilation and succeed build(":lib:compileKotlin") { - assertNonIncrementalCompilation(BuildAttribute.INCREMENTAL_COMPILATION_FAILED) + assertIncrementalCompilationFellBackToNonIncremental() // Also check that the output is not deleted (regression test for KT-49780) assertFileExists(lookupFile) } @@ -746,7 +747,7 @@ abstract class BaseIncrementalCompilationMultiProjectIT : IncrementalCompilation """ $compileKotlinTaskName { doLast { - new File("$lookupFile").write("Invalid contents") + new File("${lookupFile.toFile().invariantSeparatorsPath}").write("Invalid contents") } } """.trimIndent() diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/compilationAssertions.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/compilationAssertions.kt index d000ae54426..17ce2f3194c 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/compilationAssertions.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/compilationAssertions.kt @@ -96,6 +96,8 @@ fun BuildResult.assertNonIncrementalCompilation(reason: BuildAttribute? = null) } else { assertOutputContains(NON_INCREMENTAL_COMPILATION_WILL_BE_PERFORMED) } + // Also check that incremental compilation was not attempted, failed, and fell back to non-incremental compilation + assertOutputDoesNotContain(PERFORMING_INCREMENTAL_COMPILATION) } /** @@ -105,11 +107,24 @@ fun BuildResult.assertNonIncrementalCompilation(reason: BuildAttribute? = null) * * Note: Log level of output must be set to [LogLevel.DEBUG]. */ -fun BuildResult.assertIncrementalCompilation(expectedCompiledKotlinFiles: Iterable) { +fun BuildResult.assertIncrementalCompilation(expectedCompiledKotlinFiles: Iterable? = null) { + assertOutputContains(PERFORMING_INCREMENTAL_COMPILATION) + // Also check that incremental compilation did not fail and fall back to non-incremental compilation assertOutputDoesNotContain(NON_INCREMENTAL_COMPILATION_WILL_BE_PERFORMED) - val actualCompiledKotlinFiles = extractCompiledKotlinFiles(output) - assertSameFiles(expectedCompiledKotlinFiles, actualCompiledKotlinFiles, "Compiled Kotlin files differ:\n") + expectedCompiledKotlinFiles?.let { + assertSameFiles(expected = it, actual = extractCompiledKotlinFiles(output), "Compiled Kotlin files differ:\n") + } } +/** + * Asserts that incremental compilation was attempted, failed, and fell back to non-incremental compilation. + * + * Note: Log level of output must be set to [LogLevel.DEBUG]. + */ +fun BuildResult.assertIncrementalCompilationFellBackToNonIncremental() { + assertOutputContains("$NON_INCREMENTAL_COMPILATION_WILL_BE_PERFORMED: ${BuildAttribute.INCREMENTAL_COMPILATION_FAILED.name}") +} + +private const val PERFORMING_INCREMENTAL_COMPILATION = "Performing incremental compilation" const val NON_INCREMENTAL_COMPILATION_WILL_BE_PERFORMED = "Non-incremental compilation will be performed" diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt index c700b8fd3f1..a191134a260 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt @@ -844,16 +844,19 @@ abstract class KotlinCompile @Inject constructor( ) when { !inputChanges.isIncremental -> NotAvailableForNonIncrementalRun(classpathSnapshotFiles) - inputChanges.getFileChanges(classpathSnapshotProperties.classpathSnapshot).none() -> NoChanges(classpathSnapshotFiles) + // When `inputChanges.isIncremental == true`, we want to compile incrementally. However, if the incremental state (e.g. + // lookup caches or previous classpath snapshot) is missing, we need to compile non-incrementally. There are a few cases: + // 1. Previous compilation happened using Kotlin daemon and with IC enabled (the usual case) => Incremental state + // including classpath snapshot must have been produced (we have that guarantee in `IncrementalCompilerRunner`). + // 2. Previous compilation happened using Kotlin daemon but with IC disabled (e.g., by setting `kotlin.incremental=false`) + // => This run will have `inputChanges.isIncremental = false` as `isIncrementalCompilationEnabled` is a task input. + // 3. Previous compilation happened without using Kotlin daemon (set by the user or caused by a fallback). + // 4. Previous compilation was skipped because there were no sources to compile (see `AbstractKotlinCompile.executeImpl`). + // In case 3 and 4, it is possible that `inputChanges.isIncremental == true` in this run and incremental state is missing. !classpathSnapshotFiles.shrunkPreviousClasspathSnapshotFile.exists() -> { - // When this happens, it means that the classpath snapshot in the previous run was not saved for some reason. It's - // likely that there were no source files to compile, so the task action was skipped (see - // AbstractKotlinCompile.executeImpl), and therefore the classpath snapshot was not saved. - // Missing classpath snapshot will make this run non-incremental, but because there were no source files to compile in - // the previous run, *all* source files in this run (if there are any) need to be compiled anyway, so being - // non-incremental is actually okay. NotAvailableDueToMissingClasspathSnapshot(classpathSnapshotFiles) } + inputChanges.getFileChanges(classpathSnapshotProperties.classpathSnapshot).none() -> NoChanges(classpathSnapshotFiles) else -> ToBeComputedByIncrementalCompiler(classpathSnapshotFiles) } }