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
This commit is contained in:
Yahor Berdnikau
2021-06-07 11:00:01 +02:00
committed by Space
parent 3d36e11e80
commit a383c1ce79
5 changed files with 71 additions and 36 deletions
@@ -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()
}
}
}
}
@@ -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) }
)
}
}
@@ -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)
}
}
@@ -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<File>) {
internal sealed class SourceRoots(val kotlinSourceFiles: FileCollection) {
private companion object {
fun dumpPaths(files: Iterable<File>): 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<File>,
private val javaSourceRootsProvider: () -> Set<File>
kotlinSourceFiles: FileCollection,
val javaSourceRoots: FileCollection
) : SourceRoots(kotlinSourceFiles) {
val javaSourceRoots: Set<File>
get() = javaSourceRootsProvider()
constructor(kotlinSourceFiles: List<File>, allSourceRoots: FileCollection, taskSource: FileCollection) : this(kotlinSourceFiles, {
findRootsForSources(allSourceRoots, taskSource.filter(File::isJavaFile)).toSet()
})
companion object {
fun create(taskSource: FileTree, sourceRoots: FilteringSourceRootsContainer, sourceFilesExtensions: List<String>): ForJvm {
val kotlinSourceFiles = (taskSource as Iterable<File>).filter { it.isKotlinFile(sourceFilesExtensions) }
return ForJvm(kotlinSourceFiles, sourceRoots.sourceRoots, taskSource)
fun create(
taskSource: FileTree,
sourceRoots: FilteringSourceRootsContainer,
sourceFilesExtensions: List<String>
): ForJvm {
return ForJvm(
taskSource.filter { it.isKotlinFile(sourceFilesExtensions) },
sourceRoots.sourceRoots.filterJavaRoots(
taskSource.filter { it.isJavaFile() }
)
)
}
private fun findRootsForSources(allSourceRoots: Iterable<File>, sources: Iterable<File>): Set<File> {
val resultRoots = HashSet<File>()
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<File>) : SourceRoots(kotlinSourceFiles) {
class KotlinOnly(kotlinSourceFiles: FileCollection) : SourceRoots(kotlinSourceFiles) {
companion object {
fun create(taskSource: FileTree, sourceFilesExtensions: List<String>) =
KotlinOnly((taskSource as Iterable<File>).filter { it.isKotlinFile(sourceFilesExtensions) })
fun create(
taskSource: FileTree,
sourceFilesExtensions: List<String>
) = KotlinOnly(
taskSource.filter { it.isKotlinFile(sourceFilesExtensions) }
)
}
}
}
@@ -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
)
}
}