Incremental annotation processing with KAPT

Add support for incremental annotation processors in KAPT. These
processors conform to https://docs.gradle.org/current/userguide/java_plugin.html#sec:incremental_annotation_processing
specification.

Support is provided by using javac compiler APIs and
recording the source file structure. At runtime, processors
are instrumented with custom Filer that is used to keep track of generated
files. In order to support classpath changes, stub generation task is
used to generated a list of changed FQCNs, and this is simply used by KAPT.
Both worker and non-worker mode are supported.

 #KT-23880
This commit is contained in:
Ivan Gavrilovic
2019-03-02 12:58:45 +00:00
committed by Alexey Tsvetkov
parent 600a955a51
commit 9f14daa682
56 changed files with 2649 additions and 58 deletions
@@ -313,7 +313,7 @@ abstract class IncrementalCompilerRunner<
open fun runWithNoDirtyKotlinSources(caches: CacheManager): Boolean = false
private fun processChangesAfterBuild(
protected open fun processChangesAfterBuild(
compilationMode: CompilationMode,
currentBuildInfo: BuildInfo,
dirtyData: DirtyData
@@ -47,6 +47,7 @@ import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import java.io.File
import java.io.ObjectOutputStream
fun makeIncrementally(
cachesDir: File,
@@ -109,7 +110,8 @@ class IncrementalJvmCompilerRunner(
buildHistoryFile: File,
outputFiles: Collection<File>,
private val modulesApiHistory: ModulesApiHistory,
override val kotlinSourceFilesExtensions: List<String> = DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS
override val kotlinSourceFilesExtensions: List<String> = DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS,
private val classpathFqNamesHistory: File? = null
) : IncrementalCompilerRunner<K2JVMCompilerArguments, IncrementalJvmCachesManager>(
workingDir,
"caches-jvm",
@@ -126,6 +128,8 @@ class IncrementalJvmCompilerRunner(
override fun destinationDir(args: K2JVMCompilerArguments): File =
args.destinationAsFile
private var dirtyClasspathChanges: Collection<FqName> = emptySet<FqName>()
private val psiFileFactory: PsiFileFactory by lazy {
val rootDisposable = Disposer.newDisposable()
val configuration = CompilerConfiguration()
@@ -163,6 +167,7 @@ class IncrementalJvmCompilerRunner(
}
is ChangesEither.Known -> {
dirtyFiles.addByDirtySymbols(classpathChanges.lookupSymbols)
dirtyClasspathChanges = classpathChanges.fqNames
dirtyFiles.addByDirtyClasses(classpathChanges.fqNames)
}
}
@@ -253,6 +258,28 @@ class IncrementalJvmCompilerRunner(
}
}
override fun processChangesAfterBuild(compilationMode: CompilationMode, currentBuildInfo: BuildInfo, dirtyData: DirtyData) {
super.processChangesAfterBuild(compilationMode, currentBuildInfo, dirtyData)
classpathFqNamesHistory ?: return
classpathFqNamesHistory.mkdirs()
val historyFiles = classpathFqNamesHistory.listFiles()
if (dirtyClasspathChanges.isEmpty() && historyFiles.isNotEmpty()) {
// Don't write an empty file. We check there is at least one file so that downstream task can mark what it has processed.
return
}
if (historyFiles.size > 10) {
historyFiles.minBy { it.lastModified() }!!.delete()
}
val newHistoryFile = classpathFqNamesHistory.resolve(System.currentTimeMillis().toString())
ObjectOutputStream(newHistoryFile.outputStream().buffered()).use {
val listOfNames = dirtyClasspathChanges.map { it.toString() }.toList()
it.writeObject(listOfNames)
}
}
override fun postCompilationHook(exitCode: ExitCode) {}
override fun updateCaches(