diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KaptIncrementalWithIsolatingApt.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KaptIncrementalWithIsolatingApt.kt index 06e01eadb15..5a2c29c2c06 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KaptIncrementalWithIsolatingApt.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KaptIncrementalWithIsolatingApt.kt @@ -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) } diff --git a/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/Kapt.kt b/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/Kapt.kt index c6a5b8134d3..e3a6368e2a9 100644 --- a/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/Kapt.kt +++ b/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/Kapt.kt @@ -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) diff --git a/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/KaptContext.kt b/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/KaptContext.kt index 43329afc6b8..7d602713f86 100644 --- a/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/KaptContext.kt +++ b/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/KaptContext.kt @@ -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 } diff --git a/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/KaptOptions.kt b/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/KaptOptions.kt index f0bbaef8f7d..e12ce239527 100644 --- a/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/KaptOptions.kt +++ b/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/KaptOptions.kt @@ -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 { +fun KaptOptions.collectJavaSourceFiles(sourcesToReprocess: SourcesToReprocess = SourcesToReprocess.FullRebuild): List { fun allSources(): List { 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() } } } diff --git a/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/annotationProcessing.kt b/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/annotationProcessing.kt index 37e3cb8ddac..73b6086afde 100644 --- a/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/annotationProcessing.kt +++ b/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/annotationProcessing.kt @@ -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) diff --git a/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/incremental/IncrementalAptCache.kt b/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/incremental/IncrementalAptCache.kt index 2a4dea1795c..f4f3f3095f1 100644 --- a/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/incremental/IncrementalAptCache.kt +++ b/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/incremental/IncrementalAptCache.kt @@ -49,25 +49,34 @@ class IncrementalAptCache : Serializable { return true } - fun invalidateAggregatingAndGetAnnotations(): Set { + fun getAggregatingClaimedAnnotations(): Set = aggregatingClaimedAnnotations + + /** Returns generated Java sources originating from aggregating APs. */ + fun invalidateAggregating(): List { + val dirtyAggregating = aggregatingGenerated.filter { it.extension == "java" } aggregatingGenerated.forEach { it.delete() } aggregatingGenerated.clear() - return aggregatingClaimedAnnotations + return dirtyAggregating } - fun invalidateIsolatingGenerated(fromSources: Set) { + /** Returns generated Java sources originating from the specified sources, and generated by isloating APs. */ + fun invalidateIsolatingGenerated(fromSources: Set): List { + val allInvalidated = mutableListOf() 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() { diff --git a/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/incremental/cache.kt b/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/incremental/cache.kt index 4e866c0f12c..e427480e047 100644 --- a/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/incremental/cache.kt +++ b/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/incremental/cache.kt @@ -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): SourcesToReprocess() + class Incremental(val toReprocess: List, val dirtyTypes: Set) : SourcesToReprocess() object FullRebuild : SourcesToReprocess() } diff --git a/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/incremental/classStructureCache.kt b/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/incremental/classStructureCache.kt index de8770186aa..cca53b0fe0c 100644 --- a/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/incremental/classStructureCache.kt +++ b/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/incremental/classStructureCache.kt @@ -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() + /** Record these separately because we only need to know where each generated type is coming from. */ + private var generatedTypes = mutableMapOf>() + /** Map from types to files they are mentioned in. */ @Transient private var dependencyCache = mutableMapOf>() @@ -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): Set { + return files.mapNotNull { generatedTypes.remove(it) }.flatten().toSet() + } + + private fun readObject(input: ObjectInputStream) { @Suppress("UNCHECKED_CAST") sourceCache = input.readObject() as MutableMap + @Suppress("UNCHECKED_CAST") + generatedTypes = input.readObject() as MutableMap> 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() var currentDirtyFiles = changes.sourceChanges.map { it.toURI() } + val allDirtyTypes = mutableSetOf() // 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() } } diff --git a/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/incremental/incrementalProcessors.kt b/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/incremental/incrementalProcessors.kt index 88ba03ec61d..4aee664ea0a 100644 --- a/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/incremental/incrementalProcessors.kt +++ b/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/incremental/incrementalProcessors.kt @@ -125,12 +125,13 @@ internal class AnnotationProcessorDependencyCollector(private val runtimeProcTyp } private fun getSrcFiles(elements: Array): List { - 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) } } } diff --git a/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/incremental/javacVisitors.kt b/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/incremental/javacVisitors.kt index 9d953b6c67e..19544cfb107 100644 --- a/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/incremental/javacVisitors.kt +++ b/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/incremental/javacVisitors.kt @@ -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() { + 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) + } + } } \ No newline at end of file diff --git a/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/javac/KaptJavaFileManager.kt b/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/javac/KaptJavaFileManager.kt index 3683e6cd87e..e18da197b33 100644 --- a/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/javac/KaptJavaFileManager.kt +++ b/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/javac/KaptJavaFileManager.kt @@ -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() + // TODO (gavra): store these more efficiently by package (requires some changes to type collection). + var typeToIgnore = emptySet() + + override fun list( + location: JavaFileManager.Location?, + packageName: String?, + kinds: MutableSet?, + recurse: Boolean + ): MutableIterable { + 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() + 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 { KaptJavaFileManager(it) }) diff --git a/plugins/kapt3/kapt3-base/test/org/jetbrains/kotlin/kapt3/base/incremental/IncrementalKaptTest.kt b/plugins/kapt3/kapt3-base/test/org/jetbrains/kotlin/kapt3/base/incremental/IncrementalKaptTest.kt index 92f04c930b6..f9c4e44930d 100644 --- a/plugins/kapt3/kapt3-base/test/org/jetbrains/kotlin/kapt3/base/incremental/IncrementalKaptTest.kt +++ b/plugins/kapt3/kapt3-base/test/org/jetbrains/kotlin/kapt3/base/incremental/IncrementalKaptTest.kt @@ -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()) + } } \ No newline at end of file diff --git a/plugins/kapt3/kapt3-base/test/org/jetbrains/kotlin/kapt3/base/incremental/fixtures.kt b/plugins/kapt3/kapt3-base/test/org/jetbrains/kotlin/kapt3/base/incremental/fixtures.kt index 86e6194ecf6..934f37bf8f7 100644 --- a/plugins/kapt3/kapt3-base/test/org/jetbrains/kotlin/kapt3/base/incremental/fixtures.kt +++ b/plugins/kapt3/kapt3-base/test/org/jetbrains/kotlin/kapt3/base/incremental/fixtures.kt @@ -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, 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 } } \ No newline at end of file diff --git a/plugins/kapt3/kapt3-compiler/src/org/jetbrains/kotlin/kapt3/Kapt3Extension.kt b/plugins/kapt3/kapt3-compiler/src/org/jetbrains/kotlin/kapt3/Kapt3Extension.kt index 31e083c3796..bc4729377c5 100644 --- a/plugins/kapt3/kapt3-compiler/src/org/jetbrains/kotlin/kapt3/Kapt3Extension.kt +++ b/plugins/kapt3/kapt3-compiler/src/org/jetbrains/kotlin/kapt3/Kapt3Extension.kt @@ -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 {