Incremental KAPT - invalidate obsolete types
Once set of dirty symbols is computed, filter these types when they are requested from the JavaFileManager. This is accomplished by tracking all declared types in the sources and generated sources. It is not necessary track types in generated class files, as these will be removed before the APs are incrementally run. Motivation: APs (e.g. Dagger) may use Elements.getTypeElement(String) to determine if type is already present, and if it is, they will not generate it. Therefore, whenever generated sources is invalidated, types it defines need to be filtered in the JavaFileManager. Issue is https://youtrack.jetbrains.com/issue/KT-23880
This commit is contained in:
committed by
Alexey Tsvetkov
parent
2f3d234516
commit
b2ad82b7d2
+1
-1
@@ -107,7 +107,7 @@ fun BaseGradleIT.Project.setupIncrementalAptProject(procType: String) {
|
||||
|
||||
val updatedContent = content.replace(
|
||||
Regex("^\\s*kapt\\s\"org\\.jetbrain.*$", RegexOption.MULTILINE),
|
||||
" kapt files(\"$processorPath\")"
|
||||
" kapt files(\"${processorPath.invariantSeparatorsPath}\")"
|
||||
)
|
||||
buildFile.writeText(updatedContent)
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ object Kapt {
|
||||
|
||||
logger.info { options.logString("stand-alone mode") }
|
||||
|
||||
val javaSourceFiles = options.collectJavaSourceFiles(kaptContext.cacheManager)
|
||||
val javaSourceFiles = options.collectJavaSourceFiles(kaptContext.sourcesToReprocess)
|
||||
|
||||
val processorLoader = ProcessorLoader(options, logger)
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import com.sun.tools.javac.util.Options
|
||||
import org.jetbrains.kotlin.base.kapt3.KaptFlag
|
||||
import org.jetbrains.kotlin.base.kapt3.KaptOptions
|
||||
import org.jetbrains.kotlin.kapt3.base.incremental.JavaClassCacheManager
|
||||
import org.jetbrains.kotlin.kapt3.base.incremental.SourcesToReprocess
|
||||
import org.jetbrains.kotlin.kapt3.base.javac.KaptJavaCompiler
|
||||
import org.jetbrains.kotlin.kapt3.base.javac.KaptJavaFileManager
|
||||
import org.jetbrains.kotlin.kapt3.base.javac.KaptJavaLog
|
||||
@@ -32,6 +33,8 @@ open class KaptContext(val options: KaptOptions, val withJdk: Boolean, val logge
|
||||
val javaLog: KaptJavaLog
|
||||
val cacheManager: JavaClassCacheManager?
|
||||
|
||||
val sourcesToReprocess: SourcesToReprocess
|
||||
|
||||
protected open fun preregisterTreeMaker(context: Context) {}
|
||||
|
||||
private fun preregisterLog(context: Context) {
|
||||
@@ -53,6 +56,13 @@ open class KaptContext(val options: KaptOptions, val withJdk: Boolean, val logge
|
||||
|
||||
KaptJavaCompiler.preRegister(context)
|
||||
|
||||
cacheManager = options.incrementalCache?.let {
|
||||
JavaClassCacheManager(it, options.classpathFqNamesHistory!!)
|
||||
}
|
||||
sourcesToReprocess = cacheManager?.invalidateAndGetDirtyFiles(
|
||||
options.changedFiles.filter { it.extension == "java" }
|
||||
) ?: SourcesToReprocess.FullRebuild
|
||||
|
||||
javacOptions = Options.instance(context).apply {
|
||||
for ((key, value) in options.processingOptions) {
|
||||
val option = if (value.isEmpty()) "-A$key" else "-A$key=$value"
|
||||
@@ -78,7 +88,7 @@ open class KaptContext(val options: KaptOptions, val withJdk: Boolean, val logge
|
||||
put("accessInternalAPI", "true")
|
||||
}
|
||||
|
||||
val compileClasspath = if (options.changedFiles.isEmpty()) {
|
||||
val compileClasspath = if (sourcesToReprocess is SourcesToReprocess.FullRebuild || options.changedFiles.isEmpty()) {
|
||||
options.compileClasspath
|
||||
} else {
|
||||
options.compileClasspath + options.compiledSources
|
||||
@@ -101,6 +111,10 @@ open class KaptContext(val options: KaptOptions, val withJdk: Boolean, val logge
|
||||
}
|
||||
|
||||
fileManager = context.get(JavaFileManager::class.java) as KaptJavaFileManager
|
||||
if (sourcesToReprocess is SourcesToReprocess.Incremental) {
|
||||
fileManager.typeToIgnore = sourcesToReprocess.dirtyTypes
|
||||
fileManager.rootsToFilter = options.compiledSources.toSet()
|
||||
}
|
||||
|
||||
if (isJava9OrLater()) {
|
||||
for (option in Option.getJavacFileManagerOptions()) {
|
||||
@@ -114,10 +128,6 @@ open class KaptContext(val options: KaptOptions, val withJdk: Boolean, val logge
|
||||
|
||||
ClassReader.instance(context).saveParameterNames = true
|
||||
|
||||
cacheManager = options.incrementalCache?.let {
|
||||
JavaClassCacheManager(it, options.classpathFqNamesHistory!!)
|
||||
}
|
||||
|
||||
javaLog = compiler.log as KaptJavaLog
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.base.kapt3
|
||||
|
||||
import org.jetbrains.kotlin.kapt3.base.incremental.JavaClassCacheManager
|
||||
import org.jetbrains.kotlin.kapt3.base.incremental.SourcesToReprocess
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
@@ -129,7 +128,7 @@ enum class AptMode(override val stringValue: String) : KaptSelector {
|
||||
get() = this != APT_ONLY
|
||||
}
|
||||
|
||||
fun KaptOptions.collectJavaSourceFiles(cacheManager: JavaClassCacheManager? = null): List<File> {
|
||||
fun KaptOptions.collectJavaSourceFiles(sourcesToReprocess: SourcesToReprocess = SourcesToReprocess.FullRebuild): List<File> {
|
||||
fun allSources(): List<File> {
|
||||
return (javaSourceRoots + stubsOutputDir)
|
||||
.sortedBy { Files.isSymbolicLink(it.toPath()) } // Get non-symbolic paths first
|
||||
@@ -138,15 +137,9 @@ fun KaptOptions.collectJavaSourceFiles(cacheManager: JavaClassCacheManager? = nu
|
||||
.distinctBy { it.canonicalPath }
|
||||
}
|
||||
|
||||
return if (cacheManager != null && changedFiles.isNotEmpty()) {
|
||||
val toReprocess = cacheManager.invalidateAndGetDirtyFiles(changedFiles.filter { it.extension == "java" })
|
||||
|
||||
when (toReprocess) {
|
||||
is SourcesToReprocess.FullRebuild -> allSources()
|
||||
is SourcesToReprocess.Incremental -> toReprocess.toReprocess.filter { it.exists() }
|
||||
}
|
||||
} else {
|
||||
allSources()
|
||||
return when (sourcesToReprocess) {
|
||||
is SourcesToReprocess.FullRebuild -> allSources()
|
||||
is SourcesToReprocess.Incremental -> sourcesToReprocess.toReprocess.filter { it.exists() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+13
-7
@@ -12,8 +12,10 @@ import com.sun.tools.javac.processing.JavacFiler
|
||||
import com.sun.tools.javac.processing.JavacProcessingEnvironment
|
||||
import com.sun.tools.javac.tree.JCTree
|
||||
import org.jetbrains.kotlin.base.kapt3.KaptFlag
|
||||
import org.jetbrains.kotlin.base.kapt3.collectJavaSourceFiles
|
||||
import org.jetbrains.kotlin.kapt3.base.incremental.*
|
||||
import org.jetbrains.kotlin.kapt3.base.incremental.DeclaredProcType
|
||||
import org.jetbrains.kotlin.kapt3.base.incremental.GeneratedTypesTaskListener
|
||||
import org.jetbrains.kotlin.kapt3.base.incremental.IncrementalProcessor
|
||||
import org.jetbrains.kotlin.kapt3.base.incremental.MentionedTypesTaskListener
|
||||
import org.jetbrains.kotlin.kapt3.base.util.KaptBaseError
|
||||
import org.jetbrains.kotlin.kapt3.base.util.isJava9OrLater
|
||||
import org.jetbrains.kotlin.kapt3.base.util.measureTimeMillisWithResult
|
||||
@@ -50,8 +52,8 @@ fun KaptContext.doAnnotationProcessing(
|
||||
}
|
||||
val parsedJavaFiles = parseJavaFiles(javaSourceFiles)
|
||||
|
||||
val listener = cacheManager?.let {
|
||||
if (processors.any { it.kind == DeclaredProcType.NON_INCREMENTAL}) return@let null
|
||||
val sourcesStructureListener = cacheManager?.let {
|
||||
if (processors.any { it.kind == DeclaredProcType.NON_INCREMENTAL }) return@let null
|
||||
|
||||
val recordTypesListener = MentionedTypesTaskListener(cacheManager.javaCache, processingEnvironment.elementUtils)
|
||||
compiler.getTaskListeners().add(recordTypesListener)
|
||||
@@ -64,15 +66,19 @@ fun KaptContext.doAnnotationProcessing(
|
||||
CompileState.PARSE, compiler.enterTrees(parsedJavaFiles + additionalSources)
|
||||
)
|
||||
|
||||
listener?.let { compiler.getTaskListeners().remove(it) }
|
||||
val generatedSourcesListener = sourcesStructureListener?.let {
|
||||
compiler.getTaskListeners().remove(it)
|
||||
GeneratedTypesTaskListener(cacheManager!!.javaCache)
|
||||
}?.also { compiler.getTaskListeners().add(it) }
|
||||
|
||||
if (isJava9OrLater()) {
|
||||
val processAnnotationsMethod = compiler.javaClass.getMethod("processAnnotations", JavacList::class.java)
|
||||
processAnnotationsMethod.invoke(compiler, analyzedFiles)
|
||||
compiler
|
||||
} else {
|
||||
val processAnnotations = compiler.processAnnotations(analyzedFiles)
|
||||
processAnnotations
|
||||
compiler.processAnnotations(analyzedFiles).also {
|
||||
generatedSourcesListener?.let { compiler.getTaskListeners().remove(it) }
|
||||
}
|
||||
}
|
||||
} catch (e: AnnotationProcessingError) {
|
||||
throw KaptBaseError(KaptBaseError.Kind.EXCEPTION, e.cause ?: e)
|
||||
|
||||
+13
-4
@@ -49,25 +49,34 @@ class IncrementalAptCache : Serializable {
|
||||
return true
|
||||
}
|
||||
|
||||
fun invalidateAggregatingAndGetAnnotations(): Set<String> {
|
||||
fun getAggregatingClaimedAnnotations(): Set<String> = aggregatingClaimedAnnotations
|
||||
|
||||
/** Returns generated Java sources originating from aggregating APs. */
|
||||
fun invalidateAggregating(): List<File> {
|
||||
val dirtyAggregating = aggregatingGenerated.filter { it.extension == "java" }
|
||||
aggregatingGenerated.forEach { it.delete() }
|
||||
aggregatingGenerated.clear()
|
||||
|
||||
return aggregatingClaimedAnnotations
|
||||
return dirtyAggregating
|
||||
}
|
||||
|
||||
fun invalidateIsolatingGenerated(fromSources: Set<File>) {
|
||||
/** Returns generated Java sources originating from the specified sources, and generated by isloating APs. */
|
||||
fun invalidateIsolatingGenerated(fromSources: Set<File>): List<File> {
|
||||
val allInvalidated = mutableListOf<File>()
|
||||
var changedSources = fromSources.toSet()
|
||||
|
||||
// We need to do it in a loop because mapping could be: [AGenerated.java -> A.java, AGeneratedGenerated.java -> AGenerated.java]
|
||||
while(changedSources.isNotEmpty()) {
|
||||
while (changedSources.isNotEmpty()) {
|
||||
val generated = isolatingMapping.filter { changedSources.contains(it.value) }.keys
|
||||
generated.forEach {
|
||||
if (it.extension == "java") allInvalidated.add(it)
|
||||
|
||||
it.delete()
|
||||
isolatingMapping.remove(it)
|
||||
}
|
||||
changedSources = generated
|
||||
}
|
||||
return allInvalidated
|
||||
}
|
||||
|
||||
private fun invalidateCache() {
|
||||
|
||||
@@ -8,15 +8,15 @@ package org.jetbrains.kotlin.kapt3.base.incremental
|
||||
import java.io.*
|
||||
|
||||
// TODO(gavra): switch away from Java serialization
|
||||
class JavaClassCacheManager(private val file: File, private val classpathFqNamesHistory: File) : Closeable {
|
||||
class JavaClassCacheManager(val file: File, private val classpathFqNamesHistory: File) : Closeable {
|
||||
|
||||
internal val javaCacheFile = file.resolve("java-cache.bin")
|
||||
private val javaCacheFile = file.resolve("java-cache.bin")
|
||||
internal val javaCache = maybeGetJavaCacheFromFile()
|
||||
|
||||
internal val aptCacheFile = file.resolve("apt-cache.bin")
|
||||
internal val aptCache = maybeGetAptCacheFromFile()
|
||||
private val aptCacheFile = file.resolve("apt-cache.bin")
|
||||
private val aptCache = maybeGetAptCacheFromFile()
|
||||
|
||||
internal val lastBuildTimestamp = file.resolve("last-build-ts.bin")
|
||||
private val lastBuildTimestamp = file.resolve("last-build-ts.bin")
|
||||
|
||||
private var closed = false
|
||||
|
||||
@@ -75,15 +75,20 @@ class JavaClassCacheManager(private val file: File, private val classpathFqNames
|
||||
is SourcesToReprocess.Incremental -> {
|
||||
val toReprocess = filesToReprocess.toReprocess.toMutableSet()
|
||||
|
||||
aptCache.invalidateIsolatingGenerated(toReprocess)
|
||||
val isolatingGenerated = aptCache.invalidateIsolatingGenerated(toReprocess)
|
||||
val generatedDirtyTypes = javaCache.invalidateGeneratedTypes(isolatingGenerated).toMutableSet()
|
||||
|
||||
if (!toReprocess.isEmpty()) {
|
||||
// only if there are some files to reprocess we should invalidate the aggregating ones
|
||||
val aggregatingGenerated = aptCache.invalidateAggregating()
|
||||
generatedDirtyTypes.addAll(javaCache.invalidateGeneratedTypes(aggregatingGenerated))
|
||||
|
||||
toReprocess.addAll(
|
||||
javaCache.invalidateEntriesAnnotatedWith(aptCache.invalidateAggregatingAndGetAnnotations())
|
||||
javaCache.invalidateEntriesAnnotatedWith(aptCache.getAggregatingClaimedAnnotations())
|
||||
)
|
||||
}
|
||||
|
||||
SourcesToReprocess.Incremental(toReprocess.toList())
|
||||
SourcesToReprocess.Incremental(toReprocess.toList(), generatedDirtyTypes)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -147,7 +152,7 @@ class JavaClassCacheManager(private val file: File, private val classpathFqNames
|
||||
}
|
||||
|
||||
sealed class SourcesToReprocess {
|
||||
class Incremental(val toReprocess: List<File>): SourcesToReprocess()
|
||||
class Incremental(val toReprocess: List<File>, val dirtyTypes: Set<String>) : SourcesToReprocess()
|
||||
object FullRebuild : SourcesToReprocess()
|
||||
}
|
||||
|
||||
|
||||
+27
-2
@@ -7,12 +7,17 @@ package org.jetbrains.kotlin.kapt3.base.incremental
|
||||
|
||||
import com.sun.tools.javac.processing.JavacProcessingEnvironment
|
||||
import java.io.File
|
||||
import java.io.ObjectInputStream
|
||||
import java.io.ObjectOutputStream
|
||||
import java.io.Serializable
|
||||
import java.net.URI
|
||||
|
||||
class JavaClassCache() : Serializable {
|
||||
private var sourceCache = mutableMapOf<URI, SourceFileStructure>()
|
||||
|
||||
/** Record these separately because we only need to know where each generated type is coming from. */
|
||||
private var generatedTypes = mutableMapOf<File, MutableList<String>>()
|
||||
|
||||
/** Map from types to files they are mentioned in. */
|
||||
@Transient
|
||||
private var dependencyCache = mutableMapOf<String, MutableSet<URI>>()
|
||||
@@ -23,9 +28,21 @@ class JavaClassCache() : Serializable {
|
||||
sourceCache[sourceStructure.sourceFile] = sourceStructure
|
||||
}
|
||||
|
||||
private fun readObject(input: java.io.ObjectInputStream) {
|
||||
fun addGeneratedType(type: String, generatedFile: File) {
|
||||
val typesInFile = generatedTypes[generatedFile] ?: ArrayList(1)
|
||||
typesInFile.add(type)
|
||||
generatedTypes[generatedFile] = typesInFile
|
||||
}
|
||||
|
||||
fun invalidateGeneratedTypes(files: List<File>): Set<String> {
|
||||
return files.mapNotNull { generatedTypes.remove(it) }.flatten().toSet()
|
||||
}
|
||||
|
||||
private fun readObject(input: ObjectInputStream) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
sourceCache = input.readObject() as MutableMap<URI, SourceFileStructure>
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
generatedTypes = input.readObject() as MutableMap<File, MutableList<String>>
|
||||
|
||||
dependencyCache = HashMap(sourceCache.size * 4)
|
||||
for (sourceInfo in sourceCache.values) {
|
||||
@@ -45,6 +62,11 @@ class JavaClassCache() : Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeObject(output: ObjectOutputStream) {
|
||||
output.writeObject(sourceCache)
|
||||
output.writeObject(generatedTypes)
|
||||
}
|
||||
|
||||
fun isAlreadyProcessed(sourceFile: URI) = sourceCache.containsKey(sourceFile)
|
||||
|
||||
/** Used for testing only. */
|
||||
@@ -57,6 +79,7 @@ class JavaClassCache() : Serializable {
|
||||
fun invalidateEntriesForChangedFiles(changes: Changes): SourcesToReprocess {
|
||||
val allDirtyFiles = mutableSetOf<URI>()
|
||||
var currentDirtyFiles = changes.sourceChanges.map { it.toURI() }
|
||||
val allDirtyTypes = mutableSetOf<String>()
|
||||
|
||||
// check for constants first because they cause full rebuilt
|
||||
for (sourceChange in currentDirtyFiles) {
|
||||
@@ -76,6 +99,7 @@ class JavaClassCache() : Serializable {
|
||||
|
||||
val structure = sourceCache.remove(dirtyFile) ?: continue
|
||||
val dirtyTypes = structure.getDeclaredTypes()
|
||||
allDirtyTypes.addAll(dirtyTypes)
|
||||
|
||||
dirtyTypes.forEach { type ->
|
||||
nonTransitiveCache[type]?.let {
|
||||
@@ -91,7 +115,7 @@ class JavaClassCache() : Serializable {
|
||||
currentDirtyFiles = nextRound.filter { !allDirtyFiles.contains(it) }
|
||||
}
|
||||
|
||||
return SourcesToReprocess.Incremental(allDirtyFiles.map { File(it) })
|
||||
return SourcesToReprocess.Incremental(allDirtyFiles.map { File(it) }, allDirtyTypes)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,6 +143,7 @@ class JavaClassCache() : Serializable {
|
||||
|
||||
internal fun invalidateAll() {
|
||||
sourceCache.clear()
|
||||
generatedTypes.clear()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-3
@@ -125,12 +125,13 @@ internal class AnnotationProcessorDependencyCollector(private val runtimeProcTyp
|
||||
}
|
||||
|
||||
private fun getSrcFiles(elements: Array<out Element?>): List<File> {
|
||||
return elements.filterNotNull().mapNotNull {
|
||||
var origin = it
|
||||
return elements.filterNotNull().mapNotNull { elem ->
|
||||
var origin = elem
|
||||
while (origin.enclosingElement != null && origin.enclosingElement !is PackageElement) {
|
||||
origin = origin.enclosingElement
|
||||
}
|
||||
(origin as? Symbol.ClassSymbol)?.sourcefile?.let { src -> File(src.toUri()) }
|
||||
val uri = (origin as? Symbol.ClassSymbol)?.sourcefile?.toUri()?.takeIf { it.isAbsolute }
|
||||
uri?.let { File(it) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+24
@@ -192,4 +192,28 @@ private class TypeTreeVisitor(val elementUtils: Elements, val sourceStructure: S
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
class GeneratedTypesTaskListener(private val cache: JavaClassCache) : TaskListener {
|
||||
|
||||
override fun started(e: TaskEvent) {
|
||||
// do nothing, we just process on finish
|
||||
}
|
||||
|
||||
override fun finished(e: TaskEvent) {
|
||||
if (e.kind != TaskEvent.Kind.ENTER || cache.isAlreadyProcessed(e.sourceFile.toUri())) return
|
||||
|
||||
val treeVisitor = object : SimpleTreeVisitor<Void, Void>() {
|
||||
override fun visitClass(node: ClassTree, p: Void?): Void? {
|
||||
node as JCTree.JCClassDecl
|
||||
cache.addGeneratedType(node.sym.fullname.toString(), File(e.sourceFile.toUri()))
|
||||
|
||||
node.members.forEach { visit(it, null) }
|
||||
return null
|
||||
}
|
||||
}
|
||||
e.compilationUnit.typeDecls.forEach {
|
||||
it.accept(treeVisitor, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
+56
@@ -5,10 +5,15 @@
|
||||
|
||||
package org.jetbrains.kotlin.kapt3.base.javac
|
||||
|
||||
import com.sun.tools.javac.file.BaseFileObject
|
||||
import com.sun.tools.javac.file.JavacFileManager
|
||||
import com.sun.tools.javac.main.Option
|
||||
import com.sun.tools.javac.util.Context
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import javax.tools.JavaFileManager
|
||||
import javax.tools.JavaFileObject
|
||||
import javax.tools.StandardLocation
|
||||
|
||||
class KaptJavaFileManager(context: Context) : JavacFileManager(context, true, null) {
|
||||
fun handleOptionJavac9(option: Option, value: String) {
|
||||
@@ -18,6 +23,57 @@ class KaptJavaFileManager(context: Context) : JavacFileManager(context, true, nu
|
||||
handleOptionMethod.invoke(this, option, value)
|
||||
}
|
||||
|
||||
var rootsToFilter = emptySet<File>()
|
||||
// TODO (gavra): store these more efficiently by package (requires some changes to type collection).
|
||||
var typeToIgnore = emptySet<String>()
|
||||
|
||||
override fun list(
|
||||
location: JavaFileManager.Location?,
|
||||
packageName: String?,
|
||||
kinds: MutableSet<JavaFileObject.Kind>?,
|
||||
recurse: Boolean
|
||||
): MutableIterable<JavaFileObject> {
|
||||
val originalList = super.list(location, packageName, kinds, recurse)
|
||||
|
||||
if (location == null
|
||||
|| location != StandardLocation.CLASS_PATH
|
||||
|| rootsToFilter.isEmpty()
|
||||
|| typeToIgnore.isEmpty()
|
||||
|| !filterThisPath(packageName)
|
||||
) {
|
||||
return originalList
|
||||
}
|
||||
|
||||
val filteredList = LinkedList<JavaFileObject>()
|
||||
for (file in originalList)
|
||||
if (!shouldBeFiltered(packageName, file)) {
|
||||
filteredList.add(file)
|
||||
}
|
||||
|
||||
return filteredList
|
||||
}
|
||||
|
||||
|
||||
private fun filterThisPath(packageName: String?): Boolean {
|
||||
packageName ?: return false
|
||||
|
||||
val relativePath = packageName.replace('.', File.separatorChar)
|
||||
return rootsToFilter.any { it.resolve(relativePath).isDirectory }
|
||||
}
|
||||
|
||||
private fun shouldBeFiltered(packageName: String?, fileObject: JavaFileObject): Boolean {
|
||||
if (fileObject.kind != JavaFileObject.Kind.CLASS) return false
|
||||
return when (fileObject.toUri().scheme) {
|
||||
"jar", "zip" -> false
|
||||
else -> {
|
||||
if (fileObject !is BaseFileObject) return false
|
||||
val typeName = packageName?.let { "$it." } + fileObject.shortName.dropLast(".class".length)
|
||||
|
||||
return typeToIgnore.contains(typeName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
internal fun preRegister(context: Context) {
|
||||
context.put(JavaFileManager::class.java, Context.Factory<JavaFileManager> { KaptJavaFileManager(it) })
|
||||
|
||||
+74
-4
@@ -50,8 +50,9 @@ class IncrementalKaptTest {
|
||||
|
||||
val logger = WriterBackedKaptLogger(isVerbose = true)
|
||||
KaptContext(options, true, logger).use {
|
||||
val toReprocess = it.cacheManager!!.invalidateAndGetDirtyFiles(options.changedFiles)
|
||||
it.doAnnotationProcessing(
|
||||
options.collectJavaSourceFiles(it.cacheManager), listOf(SimpleProcessor().toIsolating())
|
||||
options.collectJavaSourceFiles(toReprocess), listOf(SimpleProcessor().toIsolating())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -77,11 +78,12 @@ class IncrementalKaptTest {
|
||||
}.build()
|
||||
|
||||
KaptContext(optionsForSecondRun, true, logger).use {
|
||||
val sourcesToReprocess = optionsForSecondRun.collectJavaSourceFiles(it.cacheManager)
|
||||
val sourcesToReprocess =
|
||||
it.cacheManager!!.invalidateAndGetDirtyFiles(optionsForSecondRun.changedFiles)
|
||||
assertFalse(outputDir.resolve("test/UserGenerated.java").exists())
|
||||
|
||||
it.doAnnotationProcessing(
|
||||
sourcesToReprocess, listOf(SimpleProcessor().toIsolating())
|
||||
optionsForSecondRun.collectJavaSourceFiles(sourcesToReprocess), listOf(SimpleProcessor().toIsolating())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -90,12 +92,80 @@ class IncrementalKaptTest {
|
||||
|
||||
sourcesDir.resolve("User.java").delete()
|
||||
KaptContext(optionsForSecondRun, true, logger).use {
|
||||
val sourcesToReprocess = it.cacheManager!!.invalidateAndGetDirtyFiles(optionsForSecondRun.changedFiles)
|
||||
|
||||
it.doAnnotationProcessing(
|
||||
optionsForSecondRun.collectJavaSourceFiles(it.cacheManager), listOf(SimpleProcessor().toIsolating())
|
||||
optionsForSecondRun.collectJavaSourceFiles(sourcesToReprocess), listOf(SimpleProcessor().toIsolating())
|
||||
)
|
||||
}
|
||||
|
||||
assertEquals(addressTimestamp, outputDir.resolve("test/AddressGenerated.java").lastModified())
|
||||
assertFalse(outputDir.resolve("test/UserGenerated.java").exists())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGeneratedCompiledAreIgnored() {
|
||||
val sourcesDir = tmp.newFolder().resolve("test").also { base ->
|
||||
base.mkdir()
|
||||
listOf("User.java", "Address.java", "Observable.java").map {
|
||||
TEST_DATA_DIR.resolve(it).copyTo(base.resolve(it))
|
||||
}
|
||||
}
|
||||
|
||||
val outputDir = tmp.newFolder()
|
||||
val incrementalCacheDir = tmp.newFolder()
|
||||
val classpathHistory = tmp.newFolder().also {
|
||||
it.resolve("0").createNewFile()
|
||||
}
|
||||
val options = KaptOptions.Builder().apply {
|
||||
projectBaseDir = tmp.newFolder()
|
||||
javaSourceRoots.add(sourcesDir)
|
||||
|
||||
sourcesOutputDir = outputDir
|
||||
classesOutputDir = outputDir
|
||||
stubsOutputDir = outputDir
|
||||
incrementalDataOutputDir = outputDir
|
||||
|
||||
incrementalCache = incrementalCacheDir
|
||||
classpathFqNamesHistory = classpathHistory
|
||||
}.build()
|
||||
|
||||
val logger = WriterBackedKaptLogger(isVerbose = true)
|
||||
KaptContext(options, true, logger).use {
|
||||
val toReprocess = it.cacheManager!!.invalidateAndGetDirtyFiles(options.changedFiles)
|
||||
it.doAnnotationProcessing(
|
||||
options.collectJavaSourceFiles(toReprocess), listOf(SimpleGeneratingIfTypeDoesNotExist().toIsolating())
|
||||
)
|
||||
}
|
||||
|
||||
val classesOutput = tmp.newFolder()
|
||||
compileSources(sourcesDir.listFiles().asIterable(), classesOutput)
|
||||
compileSources(listOf(outputDir.resolve("test/UserGenerated.java")), classesOutput)
|
||||
|
||||
val optionsForSecondRun = KaptOptions.Builder().apply {
|
||||
projectBaseDir = tmp.newFolder()
|
||||
|
||||
sourcesOutputDir = outputDir
|
||||
classesOutputDir = outputDir
|
||||
stubsOutputDir = outputDir
|
||||
incrementalDataOutputDir = outputDir
|
||||
|
||||
incrementalCache = incrementalCacheDir
|
||||
classpathFqNamesHistory = classpathHistory
|
||||
compiledSources.add(classesOutput)
|
||||
changedFiles.add(sourcesDir.resolve("User.java"))
|
||||
}.build()
|
||||
|
||||
KaptContext(optionsForSecondRun, true, logger).use {
|
||||
val sourcesToReprocess =
|
||||
it.cacheManager!!.invalidateAndGetDirtyFiles(optionsForSecondRun.changedFiles)
|
||||
assertFalse(outputDir.resolve("test/UserGenerated.java").exists())
|
||||
|
||||
it.doAnnotationProcessing(
|
||||
optionsForSecondRun.collectJavaSourceFiles(sourcesToReprocess), listOf(SimpleGeneratingIfTypeDoesNotExist().toIsolating())
|
||||
)
|
||||
}
|
||||
|
||||
assertTrue(outputDir.resolve("test/UserGenerated.java").exists())
|
||||
}
|
||||
}
|
||||
@@ -119,6 +119,30 @@ class SimpleCreatingClassFilesAndResources : SimpleProcessor() {
|
||||
filer.createResource(StandardLocation.SOURCE_OUTPUT, "test", "${it.simpleName}GeneratedResource", it).openWriter().use { it.write("") }
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
class SimpleGeneratingIfTypeDoesNotExist: SimpleProcessor() {
|
||||
override fun process(annotations: MutableSet<out TypeElement>, roundEnv: RoundEnvironment): Boolean {
|
||||
if (annotations.isEmpty()) return false
|
||||
|
||||
roundEnv.getElementsAnnotatedWith(annotations.single()).forEach { element ->
|
||||
element as TypeElement
|
||||
val generatedName = "${element.qualifiedName}Generated"
|
||||
|
||||
if (processingEnv.elementUtils.getTypeElement(generatedName) == null) {
|
||||
filer.createSourceFile(generatedName, element).openWriter().use {
|
||||
it.write(
|
||||
"""
|
||||
package test;
|
||||
public class ${element.simpleName}Generated {}
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -216,7 +216,7 @@ abstract class AbstractKapt3Extension(
|
||||
private fun runAnnotationProcessing(kaptContext: KaptContext, processors: LoadedProcessors) {
|
||||
if (!options.mode.runAnnotationProcessing) return
|
||||
|
||||
val javaSourceFiles = options.collectJavaSourceFiles(kaptContext.cacheManager)
|
||||
val javaSourceFiles = options.collectJavaSourceFiles(kaptContext.sourcesToReprocess)
|
||||
logger.info { "Java source files: " + javaSourceFiles.joinToString { it.canonicalPath } }
|
||||
|
||||
val (annotationProcessingTime) = measureTimeMillis {
|
||||
|
||||
Reference in New Issue
Block a user