diff --git a/build-common/src/org/jetbrains/kotlin/build/report/metrics/BuildAttribute.kt b/build-common/src/org/jetbrains/kotlin/build/report/metrics/BuildAttribute.kt index 4ed65b21d7c..9115e6daa35 100644 --- a/build-common/src/org/jetbrains/kotlin/build/report/metrics/BuildAttribute.kt +++ b/build-common/src/org/jetbrains/kotlin/build/report/metrics/BuildAttribute.kt @@ -16,7 +16,6 @@ enum class BuildAttributeKind : Serializable { } enum class BuildAttribute(val kind: BuildAttributeKind, val readableString: String) : Serializable { - CACHE_DIRECTORY_NOT_POPULATED(BuildAttributeKind.REBUILD_REASON, "Cache directory not populated"), NO_BUILD_HISTORY(BuildAttributeKind.REBUILD_REASON, "Build history file not found"), NO_ABI_SNAPSHOT(BuildAttributeKind.REBUILD_REASON, "ABI snapshot not found"), CLASSPATH_SNAPSHOT_NOT_FOUND(BuildAttributeKind.REBUILD_REASON, "Classpath snapshot not found"), diff --git a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/BuildInfo.kt b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/BuildInfo.kt index 856759ad0bd..24b6cf13862 100644 --- a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/BuildInfo.kt +++ b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/BuildInfo.kt @@ -37,19 +37,15 @@ data class BuildInfo(val startTS: Long, val dependencyToAbiSnapshot: Map = emptyList() @@ -129,15 +129,8 @@ abstract class IncrementalCompilerRunner< else -> providedChangedFiles } - // Check whether the cache directory is populated (note that it may be deleted upon a Gradle build cache hit if the directory is - // marked as @LocalState in the Gradle task). - val cacheDirectoryNotPopulated = cacheDirectory.walk().none { it.isFile } - - val compilationMode = if (cacheDirectoryNotPopulated) { - CompilationMode.Rebuild(BuildAttribute.CACHE_DIRECTORY_NOT_POPULATED) - } else { - sourcesToCompile(caches, changedFiles, args, messageCollector, classpathAbiSnapshot) - } + @Suppress("MoveVariableDeclarationIntoWhen") + val compilationMode = sourcesToCompile(caches, changedFiles, args, messageCollector, classpathAbiSnapshot) val exitCode = when (compilationMode) { is CompilationMode.Incremental -> { diff --git a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/IncrementalJsCompilerRunner.kt b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/IncrementalJsCompilerRunner.kt index 23191780058..d511c317b45 100644 --- a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/IncrementalJsCompilerRunner.kt +++ b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/IncrementalJsCompilerRunner.kt @@ -118,8 +118,10 @@ class IncrementalJsCompilerRunner( messageCollector: MessageCollector, classpathAbiSnapshots: Map //Ignore for now ): CompilationMode { + if (!withSnapshot && !buildHistoryFile.isFile) { + return CompilationMode.Rebuild(BuildAttribute.NO_BUILD_HISTORY) + } val lastBuildInfo = BuildInfo.read(lastBuildInfoFile) - ?: return CompilationMode.Rebuild(BuildAttribute.NO_BUILD_HISTORY) val dirtyFiles = DirtyFilesContainer(caches, reporter, kotlinSourceFilesExtensions) initDirtyFiles(dirtyFiles, changedFiles) diff --git a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/IncrementalJvmCompilerRunner.kt b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/IncrementalJvmCompilerRunner.kt index e0bb95cf638..96c30b2c2ed 100644 --- a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/IncrementalJvmCompilerRunner.kt +++ b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/IncrementalJvmCompilerRunner.kt @@ -256,7 +256,14 @@ class IncrementalJvmCompilerRunner( is NotAvailableDueToMissingClasspathSnapshot -> ChangesEither.Unknown(BuildAttribute.CLASSPATH_SNAPSHOT_NOT_FOUND) is NotAvailableForNonIncrementalRun -> ChangesEither.Unknown(BuildAttribute.UNKNOWN_CHANGES_IN_GRADLE_INPUTS) is ClasspathSnapshotDisabled -> reporter.measure(BuildTime.IC_ANALYZE_CHANGES_IN_DEPENDENCIES) { - val lastBuildInfo = BuildInfo.read(lastBuildInfoFile) ?: return CompilationMode.Rebuild(BuildAttribute.NO_BUILD_HISTORY) + if (!withSnapshot && !buildHistoryFile.isFile) { + // If the previous build was a Gradle cache hit, the build history file must have been deleted as it is marked as + // @LocalState in the Gradle task. Therefore, this compilation will need to run non-incrementally. + // (Note that buildHistoryFile is outside workingDir. We don't need to perform the same check for files inside + // 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) reporter.reportVerbose { "Last Kotlin Build info -- $lastBuildInfo" } val scopes = caches.lookupCache.lookupSymbols.map { it.scope.ifBlank { it.name } }.distinct() diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildCacheRelocationIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildCacheRelocationIT.kt index 7975b588ecb..2f7c9d13339 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildCacheRelocationIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildCacheRelocationIT.kt @@ -22,7 +22,6 @@ import org.gradle.util.GradleVersion import org.jetbrains.kotlin.gradle.testbase.* import org.junit.jupiter.api.DisplayName import kotlin.io.path.createDirectory -import kotlin.io.path.pathString @DisplayName("Build cache relocation") @SimpleGradlePluginTests @@ -328,12 +327,25 @@ class BuildCacheRelocationIT : KGPBaseTest() { @DisplayName("Kotlin incremental compilation with `kotlin.incremental.useClasspathSnapshot` feature should work correctly") @GradleTest - fun testKotlinIncrementalCompilation_withClasspathSnapshot(gradleVersion: GradleVersion) { - checkKotlinIncrementalCompilation(gradleVersion, useClasspathSnapshot = true) + fun testKotlinIncrementalCompilation_withGradleClasspathSnapshot(gradleVersion: GradleVersion) { + checkKotlinIncrementalCompilation(gradleVersion, useGradleClasspathSnapshot = true) } - private fun checkKotlinIncrementalCompilation(gradleVersion: GradleVersion, useClasspathSnapshot: Boolean? = null) { - val buildOptions = defaultBuildOptions.copy(useClasspathSnapshot = useClasspathSnapshot) + @DisplayName("Kotlin incremental compilation with `kotlin.incremental.classpath.snapshot.enabled` feature should work correctly") + @GradleTest + fun testKotlinIncrementalCompilation_withICClasspathSnapshot(gradleVersion: GradleVersion) { + checkKotlinIncrementalCompilation(gradleVersion, useICClasspathSnapshot = true) + } + + private fun checkKotlinIncrementalCompilation( + gradleVersion: GradleVersion, + useGradleClasspathSnapshot: Boolean? = null, + useICClasspathSnapshot: Boolean? = null + ) { + val buildOptions = defaultBuildOptions.copy( + useGradleClasspathSnapshot = useGradleClasspathSnapshot, + useICClasspathSnapshot = useICClasspathSnapshot + ) val (firstProject, secondProject) = prepareTestProjects("buildCacheSimple", gradleVersion, buildOptions) // First build, should be stored into the build cache: @@ -346,14 +358,15 @@ class BuildCacheRelocationIT : KGPBaseTest() { assertTasksFromCache(":compileKotlin") } - // Change the return type of foo() from Int to String in foo.kt, and check that fooUsage.kt is recompiled as well: + // Check whether compilation after a cache hit is incremental (KT-34862) val fooKtSourceFile = secondProject.kotlinSourcesDir().resolve("foo.kt") - val fooUsageKtSourceFile = secondProject.kotlinSourcesDir().resolve("fooUsage.kt") fooKtSourceFile.modify { it.replace("Int = 1", "String = \"abc\"") } secondProject.build("assemble", buildOptions = buildOptions.copy(logLevel = LogLevel.DEBUG)) { - assertIncrementalCompilation( - listOf(fooKtSourceFile, fooUsageKtSourceFile).relativizeTo(secondProject.projectPath).map { it.pathString } - ) + if (useGradleClasspathSnapshot == true || useICClasspathSnapshot == true) { + assertIncrementalCompilation(listOf("src/main/kotlin/foo.kt", "src/main/kotlin/fooUsage.kt")) + } else { + assertNonIncrementalCompilation() + } } // Revert the change to the return type of foo(), and check if we get a cache hit diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/BuildOptions.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/BuildOptions.kt index 2eec4a153a1..7f2e86372a3 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/BuildOptions.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/BuildOptions.kt @@ -9,6 +9,7 @@ import org.gradle.api.logging.LogLevel import org.gradle.api.logging.configuration.WarningMode import org.gradle.util.GradleVersion import org.jetbrains.kotlin.cli.common.CompilerSystemProperties.COMPILE_INCREMENTAL_WITH_ARTIFACT_TRANSFORM +import org.jetbrains.kotlin.cli.common.CompilerSystemProperties.COMPILE_INCREMENTAL_WITH_CLASSPATH_SNAPSHOTS import org.jetbrains.kotlin.gradle.BaseGradleIT import org.jetbrains.kotlin.gradle.plugin.KotlinJsCompilerType import org.jetbrains.kotlin.gradle.report.BuildReportType @@ -22,7 +23,8 @@ data class BuildOptions( val configurationCacheProblems: BaseGradleIT.ConfigurationCacheProblems = BaseGradleIT.ConfigurationCacheProblems.FAIL, val parallel: Boolean = true, val incremental: Boolean? = null, - val useClasspathSnapshot: Boolean? = null, + val useGradleClasspathSnapshot: Boolean? = null, + val useICClasspathSnapshot: Boolean? = null, val maxWorkers: Int = (Runtime.getRuntime().availableProcessors() / 4 - 1).coerceAtLeast(2), val fileSystemWatchEnabled: Boolean = false, val buildCacheEnabled: Boolean = false, @@ -83,7 +85,8 @@ data class BuildOptions( arguments.add("-Pkotlin.incremental=$incremental") } - useClasspathSnapshot?.let { arguments.add("-P${COMPILE_INCREMENTAL_WITH_ARTIFACT_TRANSFORM.property}=$it") } + useGradleClasspathSnapshot?.let { arguments.add("-P${COMPILE_INCREMENTAL_WITH_ARTIFACT_TRANSFORM.property}=$it") } + useICClasspathSnapshot?.let { arguments.add("-D${COMPILE_INCREMENTAL_WITH_CLASSPATH_SNAPSHOTS.property}=$it") } if (gradleVersion >= GradleVersion.version("6.5")) { if (fileSystemWatchEnabled) { 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 c9135558332..ddb93638189 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 @@ -84,6 +84,16 @@ fun assertCompiledKotlinSources( assertSameFiles(expectedSources, actualSources, "Compiled Kotlin files differ${errorMessageSuffix}:\n") } +/** + * Asserts that compilation was non-incremental. + * + * Note: Log level of output must be set to [LogLevel.DEBUG]. + */ +@Suppress("unused") +fun BuildResult.assertNonIncrementalCompilation() { + assertOutputContains("Non-incremental compilation will be performed") +} + /** * Asserts that compilation was incremental and the set of compiled .kt files exactly match [expectedCompiledKotlinFiles]. *