From a383c1ce797e35b89ee4f420e99ba3748783b9b5 Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Mon, 7 Jun 2021 11:00:01 +0200 Subject: [PATCH] Ensure kapt tasks don't capture sources state in configuration cache. Kapt tasks have captured sources state inside SourceRoots container internal collection, that was cached by configuration cache. This lead to the compilation errors whenever sources files were changed (deleted or renamed), because kapt still expected previous file state to exist. Now SourceRoots will not create internal collection, but rather reuse standard Gradle FileCollection methods that perform required calculations dynamically. ^KT-46651 Fixed --- .../org/jetbrains/kotlin/gradle/Kapt3IT.kt | 34 +++++++++++ .../internal/kapt/KaptGenerateStubsTask.kt | 4 +- .../gradle/tasks/KotlinCompileCommon.kt | 2 +- .../kotlin/gradle/tasks/SourceRoots.kt | 58 +++++++++---------- .../jetbrains/kotlin/gradle/tasks/Tasks.kt | 9 ++- 5 files changed, 71 insertions(+), 36 deletions(-) diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kapt3IT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kapt3IT.kt index 13c184b585e..016b2c4418a 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kapt3IT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kapt3IT.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.gradle +import org.gradle.api.logging.LogLevel import org.gradle.api.logging.configuration.WarningMode import org.jetbrains.kotlin.gradle.tasks.USING_JVM_INCREMENTAL_COMPILATION_MESSAGE import org.jetbrains.kotlin.gradle.util.* @@ -783,4 +784,37 @@ open class Kapt3IT : Kapt3BaseIT() { } } + + // https://youtrack.jetbrains.com/issue/KT-46651 + @Test + fun kaptGenerateStubsShouldNotCaptureSourcesStateInConfigurationCache() { + with( + Project( + "incrementalRebuild", + directoryPrefix = "kapt2", + gradleVersionRequirement = GradleVersionRequired.AtLeast("6.7.1"), + minLogLevel = LogLevel.INFO + ) + ) { + setupWorkingDir() + val buildOptions = defaultBuildOptions().copy( + configurationCache = true + ) + + build("assemble", options = buildOptions) { + assertSuccessful() + } + + projectDir.resolve("src/main/java/bar/UseBar.kt").apply { + modify { + it.replace("UseBar", "UseBar1") + } + renameTo(parentFile.resolve("UseBar1.kt")) + } + + build("assemble", options = buildOptions) { + assertSuccessful() + } + } + } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptGenerateStubsTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptGenerateStubsTask.kt index 9ea51cfcee0..989c990e7ce 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptGenerateStubsTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptGenerateStubsTask.kt @@ -57,8 +57,8 @@ abstract class KaptGenerateStubsTask : KotlinCompile(KotlinJvmOptionsImpl()) { providerFactory.provider { kotlinCompileTask.getSourceRoots().let { compileTaskSourceRoots -> SourceRoots.ForJvm( - compileTaskSourceRoots.kotlinSourceFiles.filterTo(mutableListOf()) { task.isSourceRootAllowed(it) }, - javaSourceRootsProvider = { compileTaskSourceRoots.javaSourceRoots.filterTo(mutableSetOf()) { task.isSourceRootAllowed(it) } } + compileTaskSourceRoots.kotlinSourceFiles.filter { task.isSourceRootAllowed(it) }, + compileTaskSourceRoots.javaSourceRoots.filter { task.isSourceRootAllowed(it) } ) } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompileCommon.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompileCommon.kt index 392d180b8c1..b0ae22e52bb 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompileCommon.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompileCommon.kt @@ -127,6 +127,6 @@ abstract class KotlinCompileCommon @Inject constructor( reportingSettings = reportingSettings, outputFiles = allOutputFiles() ) - compilerRunner.runMetadataCompilerAsync(sourceRoots.kotlinSourceFiles, args, environment) + compilerRunner.runMetadataCompilerAsync(sourceRoots.kotlinSourceFiles.files.toList(), args, environment) } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/SourceRoots.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/SourceRoots.kt index c22970c3463..59cf73033bb 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/SourceRoots.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/SourceRoots.kt @@ -11,62 +11,58 @@ import org.jetbrains.kotlin.gradle.utils.isJavaFile import org.jetbrains.kotlin.gradle.utils.isKotlinFile import org.jetbrains.kotlin.gradle.utils.isParentOf import java.io.File -import java.util.* import java.util.concurrent.Callable -internal sealed class SourceRoots(val kotlinSourceFiles: List) { +internal sealed class SourceRoots(val kotlinSourceFiles: FileCollection) { private companion object { fun dumpPaths(files: Iterable): String = "[${files.map { it.canonicalPath }.sorted().joinToString(prefix = "\n\t", separator = ",\n\t")}]" } open fun log(taskName: String, logger: Logger) { - logger.kotlinDebug { "$taskName source roots: ${dumpPaths(kotlinSourceFiles)}" } + logger.kotlinDebug { "$taskName source roots: ${dumpPaths(kotlinSourceFiles.files)}" } } class ForJvm constructor( - kotlinSourceFiles: List, - private val javaSourceRootsProvider: () -> Set + kotlinSourceFiles: FileCollection, + val javaSourceRoots: FileCollection ) : SourceRoots(kotlinSourceFiles) { - val javaSourceRoots: Set - get() = javaSourceRootsProvider() - - constructor(kotlinSourceFiles: List, allSourceRoots: FileCollection, taskSource: FileCollection) : this(kotlinSourceFiles, { - findRootsForSources(allSourceRoots, taskSource.filter(File::isJavaFile)).toSet() - }) companion object { - fun create(taskSource: FileTree, sourceRoots: FilteringSourceRootsContainer, sourceFilesExtensions: List): ForJvm { - val kotlinSourceFiles = (taskSource as Iterable).filter { it.isKotlinFile(sourceFilesExtensions) } - return ForJvm(kotlinSourceFiles, sourceRoots.sourceRoots, taskSource) + fun create( + taskSource: FileTree, + sourceRoots: FilteringSourceRootsContainer, + sourceFilesExtensions: List + ): ForJvm { + return ForJvm( + taskSource.filter { it.isKotlinFile(sourceFilesExtensions) }, + sourceRoots.sourceRoots.filterJavaRoots( + taskSource.filter { it.isJavaFile() } + ) + ) } - private fun findRootsForSources(allSourceRoots: Iterable, sources: Iterable): Set { - val resultRoots = HashSet() - val sourceDirs = sources.mapTo(HashSet()) { it.parentFile } - - for (sourceDir in sourceDirs) { - for (sourceRoot in allSourceRoots) { - if (sourceRoot.isParentOf(sourceDir)) { - resultRoots.add(sourceRoot) - } - } - } - - return resultRoots + private fun FileCollection.filterJavaRoots( + sourceDirs: FileCollection + ): FileCollection = filter { sourceRoot -> + sourceDirs.asSequence().map { it.parentFile }.any { sourceRoot.isParentOf(it) } } } override fun log(taskName: String, logger: Logger) { super.log(taskName, logger) - logger.kotlinDebug { "$taskName java source roots: ${dumpPaths(javaSourceRoots)}" } + logger.kotlinDebug { "$taskName java source roots: ${dumpPaths(javaSourceRoots.files)}" } } } - class KotlinOnly(kotlinSourceFiles: List) : SourceRoots(kotlinSourceFiles) { + class KotlinOnly(kotlinSourceFiles: FileCollection) : SourceRoots(kotlinSourceFiles) { companion object { - fun create(taskSource: FileTree, sourceFilesExtensions: List) = - KotlinOnly((taskSource as Iterable).filter { it.isKotlinFile(sourceFilesExtensions) }) + fun create( + taskSource: FileTree, + sourceFilesExtensions: List + ) = KotlinOnly( + taskSource.filter { it.isKotlinFile(sourceFilesExtensions) } + ) } } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt index 4a199d24deb..dc37cf1bd15 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt @@ -490,7 +490,7 @@ abstract class KotlinCompile @Inject constructor( kotlinScriptExtensions = sourceFilesExtensions.get().toTypedArray() ) compilerRunner.runJvmCompilerAsync( - sourceRoots.kotlinSourceFiles, + sourceRoots.kotlinSourceFiles.files.toList(), commonSourceSet.toList(), sourceRoots.javaSourceRoots, javaPackagePrefix, @@ -799,7 +799,12 @@ abstract class Kotlin2JsCompile @Inject constructor( reportingSettings = reportingSettings, incrementalCompilationEnvironment = icEnv ) - compilerRunner.runJsCompilerAsync(sourceRoots.kotlinSourceFiles, commonSourceSet.toList(), args, environment) + compilerRunner.runJsCompilerAsync( + sourceRoots.kotlinSourceFiles.files.toList(), + commonSourceSet.toList(), + args, + environment + ) } }