Use types are origins for incremental KAPT and track generated source
This change introduces tracking of generated sources structure in order to e.g track classpath changes impacting generated sources. This fixes KT-42182. Also, origin tracking for isolating processors is now using types, allowing for origin elements from classpath. This fixes KT-34340. However, classpath origin is used only to invalidate generated files when the type changes and processing will not be requested for that type. This is in line with the incap spec.
This commit is contained in:
committed by
Mikhael Bogdanov
parent
d512158c25
commit
c7e5beece5
+58
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.gradle.incapt;
|
||||||
|
|
||||||
|
import javax.annotation.processing.AbstractProcessor;
|
||||||
|
import javax.annotation.processing.RoundEnvironment;
|
||||||
|
import javax.lang.model.element.Element;
|
||||||
|
import javax.lang.model.element.ExecutableElement;
|
||||||
|
import javax.lang.model.element.TypeElement;
|
||||||
|
import javax.lang.model.element.VariableElement;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/** Simple processor that generates a class for every annotated element (class, field, method). */
|
||||||
|
public class IncrementalProcessorReferencingClasspath extends AbstractProcessor {
|
||||||
|
|
||||||
|
// Type that all generated sources will extend.
|
||||||
|
public static final String CLASSPATH_TYPE = "com.example.FromClasspath";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> getSupportedAnnotationTypes() {
|
||||||
|
return Collections.singleton("example.ExampleAnnotation");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
|
||||||
|
if (annotations.isEmpty()) return true;
|
||||||
|
|
||||||
|
for (Element element : roundEnv.getElementsAnnotatedWith(annotations.iterator().next())) {
|
||||||
|
if (element instanceof TypeElement || element instanceof ExecutableElement || element instanceof VariableElement) {
|
||||||
|
String name = element.getSimpleName().toString();
|
||||||
|
name = name.substring(0, 1).toUpperCase() + name.substring(1) + "Generated";
|
||||||
|
|
||||||
|
String packageName;
|
||||||
|
if (element instanceof TypeElement) {
|
||||||
|
packageName = element.getEnclosingElement().getSimpleName().toString();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
packageName = element.getEnclosingElement().getEnclosingElement().getSimpleName().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
try (Writer writer = processingEnv.getFiler().createSourceFile(packageName + "." + name, element).openWriter()) {
|
||||||
|
writer.append("package ").append(packageName).append(";");
|
||||||
|
writer.append("\npublic class ").append(name).append(" extends ").append(CLASSPATH_TYPE).append(" {}");
|
||||||
|
}
|
||||||
|
catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
+35
-11
@@ -27,6 +27,7 @@ class KaptIncrementalWithAggregatingApt : KaptIncrementalIT() {
|
|||||||
override fun defaultBuildOptions(): BuildOptions =
|
override fun defaultBuildOptions(): BuildOptions =
|
||||||
super.defaultBuildOptions().copy(
|
super.defaultBuildOptions().copy(
|
||||||
incremental = true,
|
incremental = true,
|
||||||
|
debug=false,
|
||||||
kaptOptions = KaptOptions(
|
kaptOptions = KaptOptions(
|
||||||
verbose = true,
|
verbose = true,
|
||||||
useWorkers = true,
|
useWorkers = true,
|
||||||
@@ -171,7 +172,7 @@ class KaptIncrementalWithAggregatingApt : KaptIncrementalIT() {
|
|||||||
}
|
}
|
||||||
project.build("build") {
|
project.build("build") {
|
||||||
assertSuccessful()
|
assertSuccessful()
|
||||||
assertTrue(getProcessedSources(output).isEmpty())
|
assertTrue(output.contains("Skipping annotation processing as all sources are up-to-date."))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,8 +276,9 @@ class KaptIncrementalWithAggregatingApt : KaptIncrementalIT() {
|
|||||||
assertEquals(
|
assertEquals(
|
||||||
setOf(
|
setOf(
|
||||||
fileInWorkingDir("build/tmp/kapt3/stubs/main/bar/NoAnnotationsKt.java").canonicalPath,
|
fileInWorkingDir("build/tmp/kapt3/stubs/main/bar/NoAnnotationsKt.java").canonicalPath,
|
||||||
fileInWorkingDir("build/tmp/kapt3/stubs/main/error/NonExistentClass.java").canonicalPath
|
fileInWorkingDir("build/tmp/kapt3/stubs/main/error/NonExistentClass.java").canonicalPath,
|
||||||
), getProcessedSources(output)
|
fileInWorkingDir("build/generated/source/kapt/main/bar/WithAnnotationGenerated.java").canonicalPath.takeUnless { isBinary },
|
||||||
|
).filterNotNull().toSet(), getProcessedSources(output)
|
||||||
)
|
)
|
||||||
|
|
||||||
checkAggregatingResource { lines ->
|
checkAggregatingResource { lines ->
|
||||||
@@ -302,9 +304,9 @@ class KaptIncrementalWithAggregatingApt : KaptIncrementalIT() {
|
|||||||
assertEquals(
|
assertEquals(
|
||||||
setOf(
|
setOf(
|
||||||
fileInWorkingDir("build/tmp/kapt3/stubs/main/baz/BazClass.java").canonicalPath,
|
fileInWorkingDir("build/tmp/kapt3/stubs/main/baz/BazClass.java").canonicalPath,
|
||||||
fileInWorkingDir("build/tmp/kapt3/stubs/main/error/NonExistentClass.java").canonicalPath
|
fileInWorkingDir("build/tmp/kapt3/stubs/main/error/NonExistentClass.java").canonicalPath,
|
||||||
),
|
fileInWorkingDir("build/generated/source/kapt/main/bar/WithAnnotationGenerated.java").canonicalPath.takeUnless { isBinary },
|
||||||
getProcessedSources(output)
|
).filterNotNull().toSet(), getProcessedSources(output)
|
||||||
)
|
)
|
||||||
|
|
||||||
checkAggregatingResource { lines ->
|
checkAggregatingResource { lines ->
|
||||||
@@ -330,9 +332,9 @@ class KaptIncrementalWithAggregatingApt : KaptIncrementalIT() {
|
|||||||
assertEquals(
|
assertEquals(
|
||||||
setOf(
|
setOf(
|
||||||
fileInWorkingDir("build/tmp/kapt3/stubs/main/baz/BazClass.java").canonicalPath,
|
fileInWorkingDir("build/tmp/kapt3/stubs/main/baz/BazClass.java").canonicalPath,
|
||||||
fileInWorkingDir("build/tmp/kapt3/stubs/main/error/NonExistentClass.java").canonicalPath
|
fileInWorkingDir("build/tmp/kapt3/stubs/main/error/NonExistentClass.java").canonicalPath,
|
||||||
),
|
fileInWorkingDir("build/generated/source/kapt/main/bar/WithAnnotationGenerated.java").canonicalPath.takeUnless { isBinary },
|
||||||
getProcessedSources(output)
|
).filterNotNull().toSet(), getProcessedSources(output)
|
||||||
)
|
)
|
||||||
|
|
||||||
checkAggregatingResource { lines ->
|
checkAggregatingResource { lines ->
|
||||||
@@ -351,8 +353,30 @@ class KaptIncrementalWithAggregatingApt : KaptIncrementalIT() {
|
|||||||
assertEquals(
|
assertEquals(
|
||||||
setOf(
|
setOf(
|
||||||
fileInWorkingDir("build/tmp/kapt3/stubs/main/bar/NoAnnotationsKt.java").canonicalPath,
|
fileInWorkingDir("build/tmp/kapt3/stubs/main/bar/NoAnnotationsKt.java").canonicalPath,
|
||||||
fileInWorkingDir("build/tmp/kapt3/stubs/main/error/NonExistentClass.java").canonicalPath
|
fileInWorkingDir("build/tmp/kapt3/stubs/main/error/NonExistentClass.java").canonicalPath,
|
||||||
), getProcessedSources(output)
|
fileInWorkingDir("build/generated/source/kapt/main/bar/WithAnnotationGenerated.java").canonicalPath.takeUnless { isBinary },
|
||||||
|
fileInWorkingDir("build/generated/source/kapt/main/BazClass/BazNestedGenerated.java").canonicalPath.takeUnless { isBinary },
|
||||||
|
).filterNotNull().toSet(), getProcessedSources(output)
|
||||||
|
)
|
||||||
|
|
||||||
|
checkAggregatingResource { lines ->
|
||||||
|
assertEquals(2, lines.size)
|
||||||
|
assertTrue(lines.contains("WithAnnotationGenerated"))
|
||||||
|
assertTrue(lines.contains("BazNestedGenerated"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure that changing the origin of isolating that produced
|
||||||
|
project.projectFile("withAnnotation.kt").modify { current -> current.substringBeforeLast("}") + "\nfun otherFunction() {} }" }
|
||||||
|
project.build("build", options = buildOptions) {
|
||||||
|
assertSuccessful()
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
setOf(
|
||||||
|
fileInWorkingDir("build/tmp/kapt3/stubs/main/bar/WithAnnotation.java").canonicalPath,
|
||||||
|
fileInWorkingDir("build/tmp/kapt3/stubs/main/error/NonExistentClass.java").canonicalPath,
|
||||||
|
fileInWorkingDir("build/generated/source/kapt/main/BazClass/BazNestedGenerated.java").canonicalPath.takeUnless { isBinary },
|
||||||
|
).filterNotNull().toSet(), getProcessedSources(output)
|
||||||
)
|
)
|
||||||
|
|
||||||
checkAggregatingResource { lines ->
|
checkAggregatingResource { lines ->
|
||||||
|
|||||||
+57
@@ -6,10 +6,14 @@
|
|||||||
package org.jetbrains.kotlin.gradle
|
package org.jetbrains.kotlin.gradle
|
||||||
|
|
||||||
import org.jetbrains.kotlin.gradle.incapt.IncrementalProcessor
|
import org.jetbrains.kotlin.gradle.incapt.IncrementalProcessor
|
||||||
|
import org.jetbrains.kotlin.gradle.incapt.IncrementalProcessorReferencingClasspath
|
||||||
import org.jetbrains.kotlin.gradle.util.modify
|
import org.jetbrains.kotlin.gradle.util.modify
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Assume
|
import org.junit.Assume
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import org.objectweb.asm.ClassWriter
|
||||||
|
import org.objectweb.asm.Opcodes
|
||||||
|
import org.objectweb.asm.Type
|
||||||
import test.kt33617.MyClass
|
import test.kt33617.MyClass
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.zip.ZipEntry
|
import java.util.zip.ZipEntry
|
||||||
@@ -192,6 +196,59 @@ class KaptIncrementalWithIsolatingApt : KaptIncrementalIT() {
|
|||||||
assertSuccessful()
|
assertSuccessful()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Regression test for https://youtrack.jetbrains.com/issue/KT-42182. */
|
||||||
|
@Test
|
||||||
|
fun testGeneratedSourcesImpactedByClasspathChanges() {
|
||||||
|
val project = Project(
|
||||||
|
"kaptIncrementalCompilationProject",
|
||||||
|
GradleVersionRequired.None
|
||||||
|
).apply {
|
||||||
|
setupIncrementalAptProject("ISOLATING", procClass = IncrementalProcessorReferencingClasspath::class.java)
|
||||||
|
}
|
||||||
|
project.gradleSettingsScript().writeText("include ':', ':lib'")
|
||||||
|
val classpathTypeSource = project.projectDir.resolve("lib").run {
|
||||||
|
mkdirs()
|
||||||
|
resolve("build.gradle").writeText("apply plugin: 'java'")
|
||||||
|
val source = resolve("src/main/java/" + IncrementalProcessorReferencingClasspath.CLASSPATH_TYPE.replace(".", "/") + ".java")
|
||||||
|
source.parentFile.mkdirs()
|
||||||
|
|
||||||
|
source.writeText(
|
||||||
|
"""
|
||||||
|
package ${IncrementalProcessorReferencingClasspath.CLASSPATH_TYPE.substringBeforeLast(".")};
|
||||||
|
public class ${IncrementalProcessorReferencingClasspath.CLASSPATH_TYPE.substringAfterLast(".")} {}
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
return@run source
|
||||||
|
}
|
||||||
|
project.gradleBuildScript().appendText(
|
||||||
|
"""
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation project(':lib')
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
project.build("clean", "kaptKotlin") {
|
||||||
|
assertSuccessful()
|
||||||
|
}
|
||||||
|
|
||||||
|
// change type that all generated sources reference
|
||||||
|
classpathTypeSource.writeText(classpathTypeSource.readText().replace("}", "int i = 10;\n}"))
|
||||||
|
project.build("build") {
|
||||||
|
assertSuccessful()
|
||||||
|
assertEquals(
|
||||||
|
setOf(
|
||||||
|
fileInWorkingDir("build/tmp/kapt3/stubs/main/foo/A.java").canonicalPath,
|
||||||
|
fileInWorkingDir("build/tmp/kapt3/stubs/main/bar/B.java").canonicalPath,
|
||||||
|
fileInWorkingDir("build/tmp/kapt3/stubs/main/bar/UseBKt.java").canonicalPath,
|
||||||
|
fileInWorkingDir("build/tmp/kapt3/stubs/main/baz/UtilKt.java").canonicalPath,
|
||||||
|
fileInWorkingDir("build/tmp/kapt3/stubs/main/error/NonExistentClass.java").canonicalPath
|
||||||
|
),
|
||||||
|
getProcessedSources(output)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private const val patternApt = "Processing java sources with annotation processors:"
|
private const val patternApt = "Processing java sources with annotation processors:"
|
||||||
|
|||||||
@@ -60,10 +60,16 @@ open class KaptContext(val options: KaptOptions, val withJdk: Boolean, val logge
|
|||||||
JavaClassCacheManager(it)
|
JavaClassCacheManager(it)
|
||||||
}
|
}
|
||||||
if (options.flags[KaptFlag.INCREMENTAL_APT]) {
|
if (options.flags[KaptFlag.INCREMENTAL_APT]) {
|
||||||
sourcesToReprocess =
|
sourcesToReprocess = run {
|
||||||
|
val start = System.currentTimeMillis()
|
||||||
cacheManager?.invalidateAndGetDirtyFiles(
|
cacheManager?.invalidateAndGetDirtyFiles(
|
||||||
options.changedFiles, options.classpathChanges
|
options.changedFiles, options.classpathChanges
|
||||||
) ?: SourcesToReprocess.FullRebuild
|
).also {
|
||||||
|
if (logger.isVerbose) {
|
||||||
|
logger.info("Computing sources to reprocess took ${System.currentTimeMillis() - start}[ms].")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}?: SourcesToReprocess.FullRebuild
|
||||||
|
|
||||||
if (sourcesToReprocess == SourcesToReprocess.FullRebuild) {
|
if (sourcesToReprocess == SourcesToReprocess.FullRebuild) {
|
||||||
// remove all generated sources and classes
|
// remove all generated sources and classes
|
||||||
|
|||||||
+10
-8
@@ -14,6 +14,7 @@ import com.sun.tools.javac.processing.JavacProcessingEnvironment
|
|||||||
import com.sun.tools.javac.tree.JCTree
|
import com.sun.tools.javac.tree.JCTree
|
||||||
import org.jetbrains.kotlin.base.kapt3.KaptFlag
|
import org.jetbrains.kotlin.base.kapt3.KaptFlag
|
||||||
import org.jetbrains.kotlin.kapt3.base.incremental.*
|
import org.jetbrains.kotlin.kapt3.base.incremental.*
|
||||||
|
import org.jetbrains.kotlin.kapt3.base.javac.KaptJavaCompiler
|
||||||
import org.jetbrains.kotlin.kapt3.base.util.KaptBaseError
|
import org.jetbrains.kotlin.kapt3.base.util.KaptBaseError
|
||||||
import org.jetbrains.kotlin.kapt3.base.util.KaptLogger
|
import org.jetbrains.kotlin.kapt3.base.util.KaptLogger
|
||||||
import org.jetbrains.kotlin.kapt3.base.util.isJava9OrLater
|
import org.jetbrains.kotlin.kapt3.base.util.isJava9OrLater
|
||||||
@@ -40,6 +41,13 @@ fun KaptContext.doAnnotationProcessing(
|
|||||||
|
|
||||||
val compilerAfterAP: JavaCompiler
|
val compilerAfterAP: JavaCompiler
|
||||||
try {
|
try {
|
||||||
|
if (javaSourceFiles.isEmpty() && aggregatedTypes.isEmpty() && additionalSources.isEmpty()) {
|
||||||
|
if (logger.isVerbose) {
|
||||||
|
logger.info("Skipping annotation processing as all sources are up-to-date.")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (isJava9OrLater()) {
|
if (isJava9OrLater()) {
|
||||||
val initProcessAnnotationsMethod = JavaCompiler::class.java.declaredMethods.single { it.name == "initProcessAnnotations" }
|
val initProcessAnnotationsMethod = JavaCompiler::class.java.declaredMethods.single { it.name == "initProcessAnnotations" }
|
||||||
initProcessAnnotationsMethod.invoke(compiler, wrappedProcessors, emptyList<JavaFileObject>(), emptyList<String>())
|
initProcessAnnotationsMethod.invoke(compiler, wrappedProcessors, emptyList<JavaFileObject>(), emptyList<String>())
|
||||||
@@ -66,11 +74,6 @@ fun KaptContext.doAnnotationProcessing(
|
|||||||
CompileState.PARSE, compiler.enterTrees(parsedJavaFiles + additionalSources)
|
CompileState.PARSE, compiler.enterTrees(parsedJavaFiles + additionalSources)
|
||||||
)
|
)
|
||||||
|
|
||||||
val generatedSourcesListener = sourcesStructureListener?.let {
|
|
||||||
compiler.getTaskListeners().remove(it)
|
|
||||||
GeneratedTypesTaskListener(cacheManager!!.javaCache)
|
|
||||||
}?.also { compiler.getTaskListeners().add(it) }
|
|
||||||
|
|
||||||
val additionalClassNames = JavacList.from(aggregatedTypes)
|
val additionalClassNames = JavacList.from(aggregatedTypes)
|
||||||
if (isJava9OrLater()) {
|
if (isJava9OrLater()) {
|
||||||
val processAnnotationsMethod =
|
val processAnnotationsMethod =
|
||||||
@@ -78,13 +81,12 @@ fun KaptContext.doAnnotationProcessing(
|
|||||||
processAnnotationsMethod.invoke(compiler, analyzedFiles, additionalClassNames)
|
processAnnotationsMethod.invoke(compiler, analyzedFiles, additionalClassNames)
|
||||||
compiler
|
compiler
|
||||||
} else {
|
} else {
|
||||||
compiler.processAnnotations(analyzedFiles, additionalClassNames).also {
|
compiler.processAnnotations(analyzedFiles, additionalClassNames)
|
||||||
generatedSourcesListener?.let { compiler.getTaskListeners().remove(it) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (e: AnnotationProcessingError) {
|
} catch (e: AnnotationProcessingError) {
|
||||||
throw KaptBaseError(KaptBaseError.Kind.EXCEPTION, e.cause ?: e)
|
throw KaptBaseError(KaptBaseError.Kind.EXCEPTION, e.cause ?: e)
|
||||||
}
|
}
|
||||||
|
sourcesStructureListener?.let { compiler.getTaskListeners().remove(it) }
|
||||||
|
|
||||||
cacheManager?.updateCache(processors, sourcesStructureListener?.failureReason != null)
|
cacheManager?.updateCache(processors, sourcesStructureListener?.failureReason != null)
|
||||||
|
|
||||||
|
|||||||
+84
-40
@@ -10,16 +10,19 @@ import java.io.Serializable
|
|||||||
|
|
||||||
class IncrementalAptCache : Serializable {
|
class IncrementalAptCache : Serializable {
|
||||||
|
|
||||||
private val aggregatingGenerated: MutableMap<File, String?> = mutableMapOf()
|
private val aggregatingGenerated: MutableSet<File> = mutableSetOf()
|
||||||
private val aggregatedTypes: MutableSet<String> = linkedSetOf()
|
private val aggregatedTypes: MutableSet<String> = linkedSetOf()
|
||||||
private val isolatingMapping: MutableMap<File, Pair<String?, File>> = mutableMapOf()
|
private val isolatingMapping: MutableMap<File, String> = mutableMapOf()
|
||||||
// Annotations claimed by aggregating annotation processors
|
|
||||||
private val aggregatingClaimedAnnotations: MutableSet<String> = mutableSetOf()
|
|
||||||
|
|
||||||
var isIncremental = true
|
var isIncremental = true
|
||||||
private set
|
private set
|
||||||
|
|
||||||
fun updateCache(processors: List<IncrementalProcessor>): Boolean {
|
fun updateCache(processors: List<IncrementalProcessor>, failedToAnalyzeSources: Boolean): Boolean {
|
||||||
|
if (failedToAnalyzeSources) {
|
||||||
|
invalidateCache()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
val aggregating = mutableListOf<IncrementalProcessor>()
|
val aggregating = mutableListOf<IncrementalProcessor>()
|
||||||
val isolating = mutableListOf<IncrementalProcessor>()
|
val isolating = mutableListOf<IncrementalProcessor>()
|
||||||
val nonIncremental = mutableListOf<IncrementalProcessor>()
|
val nonIncremental = mutableListOf<IncrementalProcessor>()
|
||||||
@@ -38,60 +41,68 @@ class IncrementalAptCache : Serializable {
|
|||||||
|
|
||||||
aggregatingGenerated.clear()
|
aggregatingGenerated.clear()
|
||||||
aggregating.forEach {
|
aggregating.forEach {
|
||||||
it.getGeneratedToSourcesAll().mapValuesTo(aggregatingGenerated) { (_, value) ->
|
aggregatingGenerated.addAll(it.getGeneratedToSources().keys)
|
||||||
value?.first
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
aggregatingClaimedAnnotations.clear()
|
|
||||||
aggregatingClaimedAnnotations.addAll(aggregating.flatMap { it.supportedAnnotationTypes })
|
|
||||||
|
|
||||||
aggregatedTypes.clear()
|
aggregatedTypes.clear()
|
||||||
aggregatedTypes.addAll(aggregating.flatMap { it.getAggregatedTypes() })
|
aggregatedTypes.addAll(aggregating.flatMap { it.getAggregatedTypes() })
|
||||||
|
|
||||||
for (isolatingProcessor in isolating) {
|
isolating.forEach {
|
||||||
isolatingProcessor.getGeneratedToSourcesAll().forEach {
|
it.getGeneratedToSources().forEach { (file, type) ->
|
||||||
isolatingMapping[it.key] = it.value!!.first to it.value!!.second!!
|
isolatingMapping[file] = type!!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAggregatingClaimedAnnotations(): Set<String> = aggregatingClaimedAnnotations
|
/**
|
||||||
|
* Invalidates all data collected about aggregating APs, making the cache ready for the next round of data collection. Also,
|
||||||
/** Returns generated Java sources originating from aggregating APs. */
|
* all files generated by aggregating APs are deleted.
|
||||||
fun invalidateAggregating(): Pair<List<File>, List<String>> {
|
*/
|
||||||
val dirtyAggregating = aggregatingGenerated.keys.filter { it.isJavaFileOrClass() }
|
fun invalidateAggregating() {
|
||||||
aggregatingGenerated.forEach { it.key.delete() }
|
aggregatingGenerated.forEach { it.delete() }
|
||||||
aggregatingGenerated.clear()
|
aggregatingGenerated.clear()
|
||||||
|
|
||||||
val dirtyAggregated = ArrayList(aggregatedTypes)
|
|
||||||
aggregatedTypes.clear()
|
aggregatedTypes.clear()
|
||||||
|
|
||||||
return dirtyAggregating to dirtyAggregated
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns generated Java sources originating from the specified sources, and generated by isloating APs. */
|
/**
|
||||||
fun invalidateIsolatingGenerated(fromSources: Set<File>): Pair<List<File>, Set<String>> {
|
* Prepares isolating processors for incremental compilation. The specified generated files are removed, and mapping
|
||||||
val allInvalidated = mutableListOf<File>()
|
* information is deleted for them. The invalidation is non-transitive.
|
||||||
val invalidatedClassIds = mutableSetOf<String>()
|
*/
|
||||||
var changedSources = fromSources.toSet()
|
fun invalidateIsolatingForOriginTypes(originatingTypes: Set<String>) {
|
||||||
|
val isolatingGenerated = mutableSetOf<File>()
|
||||||
|
isolatingMapping.forEach { (file, type) ->
|
||||||
|
if (type in originatingTypes) {
|
||||||
|
isolatingGenerated.add(file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isolatingGenerated.forEach {
|
||||||
|
isolatingMapping.remove(it)
|
||||||
|
it.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getIsolatingGeneratedTypesForOrigins(
|
||||||
|
originatingTypes: Set<String>,
|
||||||
|
typeInfoProvider: (Collection<File>) -> Set<String>
|
||||||
|
): List<String> {
|
||||||
|
val allGeneratedTypes = mutableListOf<String>()
|
||||||
|
var currentOrigins = originatingTypes.toSet()
|
||||||
|
|
||||||
// We need to do it in a loop because mapping could be: [AGenerated.java -> A.java, AGeneratedGenerated.java -> AGenerated.java]
|
// We need to do it in a loop because mapping could be: [AGenerated.java -> A.java, AGeneratedGenerated.java -> AGenerated.java]
|
||||||
while (changedSources.isNotEmpty()) {
|
while (currentOrigins.isNotEmpty()) {
|
||||||
val generated = isolatingMapping.filter { changedSources.contains(it.value.second) }.keys
|
val generated = mutableSetOf<File>()
|
||||||
generated.forEach {
|
isolatingMapping.forEach { (file, origin) ->
|
||||||
if (it.isJavaFileOrClass()) {
|
if (origin in currentOrigins) {
|
||||||
allInvalidated.add(it)
|
generated.add(file)
|
||||||
isolatingMapping[it]?.first?.let { invalidatedClassIds.add(it) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
it.delete()
|
|
||||||
isolatingMapping.remove(it)
|
|
||||||
}
|
}
|
||||||
changedSources = generated
|
currentOrigins = typeInfoProvider(generated)
|
||||||
|
allGeneratedTypes.addAll(currentOrigins)
|
||||||
}
|
}
|
||||||
return allInvalidated to invalidatedClassIds
|
return allGeneratedTypes
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun File.isJavaFileOrClass() = extension == "java" || extension == "class"
|
private fun File.isJavaFileOrClass() = extension == "java" || extension == "class"
|
||||||
@@ -100,5 +111,38 @@ class IncrementalAptCache : Serializable {
|
|||||||
isIncremental = false
|
isIncremental = false
|
||||||
aggregatingGenerated.clear()
|
aggregatingGenerated.clear()
|
||||||
isolatingMapping.clear()
|
isolatingMapping.clear()
|
||||||
|
aggregatedTypes.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets the originating type for the specified type generated by isolating AP. */
|
||||||
|
fun getOriginForGeneratedIsolatingType(generatedType: String, sourceFileProvider: (String) -> File?): String {
|
||||||
|
val generatedFile = checkNotNull(sourceFileProvider(generatedType)) { "Unable to find source for $generatedType" }
|
||||||
|
return isolatingMapping.getValue(generatedFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns types that were processed by aggregating APs. */
|
||||||
|
fun getAggregatingOrigins(): Set<String> = aggregatedTypes
|
||||||
|
|
||||||
|
/** Returns all types generated by aggregating APs. */
|
||||||
|
fun getAggregatingGeneratedTypes(typeInfoProvider: (Collection<File>) -> Set<String>): Set<String> {
|
||||||
|
val generatedAggregating: MutableSet<File> = HashSet(aggregatingGenerated.size)
|
||||||
|
aggregatingGenerated.forEach {
|
||||||
|
if (it.isJavaFileOrClass()) {
|
||||||
|
generatedAggregating.add(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return typeInfoProvider(generatedAggregating)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns all types generated by isolating APs. */
|
||||||
|
fun getIsolatingGeneratedTypes(typeInfoProvider: (Collection<File>) -> Set<String>): Set<String> {
|
||||||
|
val generatedIsolating: MutableSet<File> = HashSet(isolatingMapping.size)
|
||||||
|
|
||||||
|
isolatingMapping.keys.forEach {
|
||||||
|
if (it.isJavaFileOrClass()) {
|
||||||
|
generatedIsolating.add(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return typeInfoProvider(generatedIsolating)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -19,8 +19,18 @@ class JavaClassCacheManager(val file: File) : Closeable {
|
|||||||
private var closed = false
|
private var closed = false
|
||||||
|
|
||||||
fun updateCache(processors: List<IncrementalProcessor>, failedToAnalyzeSources: Boolean) {
|
fun updateCache(processors: List<IncrementalProcessor>, failedToAnalyzeSources: Boolean) {
|
||||||
if (failedToAnalyzeSources || !aptCache.updateCache(processors)) {
|
if (!aptCache.updateCache(processors, failedToAnalyzeSources)) {
|
||||||
javaCache.invalidateAll()
|
javaCache.invalidateAll()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Compilation is fully incremental, record types defined in generated .class files
|
||||||
|
processors.forEach { processor ->
|
||||||
|
processor.getGeneratedClassFilesToTypes().forEach { (classFile, type) ->
|
||||||
|
val typeInformation = SourceFileStructure(classFile.toURI()).also {
|
||||||
|
it.addDeclaredType(type)
|
||||||
|
}
|
||||||
|
javaCache.addSourceStructure(typeInformation)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,38 +49,93 @@ class JavaClassCacheManager(val file: File) : Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val changes = Changes(changedSources, dirtyClasspathFqNames.toSet())
|
val changes = Changes(changedSources, dirtyClasspathFqNames.toSet())
|
||||||
val filesToReprocess = javaCache.invalidateEntriesForChangedFiles(changes)
|
val aggregatingGeneratedTypes = aptCache.getAggregatingGeneratedTypes(javaCache::getTypesForFiles)
|
||||||
|
val impactedTypes = getAllImpactedTypes(changes, aggregatingGeneratedTypes)
|
||||||
|
val isolatingGeneratedTypes = aptCache.getIsolatingGeneratedTypes(javaCache::getTypesForFiles)
|
||||||
|
|
||||||
return when (filesToReprocess) {
|
val sourcesToReprocess = changedSources.toMutableSet()
|
||||||
is SourcesToReprocess.FullRebuild -> SourcesToReprocess.FullRebuild
|
val classNamesToReprocess = mutableListOf<String>()
|
||||||
is SourcesToReprocess.Incremental -> {
|
var shouldProcessAggregating = false
|
||||||
val toReprocess = filesToReprocess.toReprocess.toMutableSet()
|
|
||||||
|
|
||||||
val (invalidatedIsolatingGenerated, invalidatedIsolatingId) = aptCache.invalidateIsolatingGenerated(toReprocess)
|
for (impactedType in impactedTypes) {
|
||||||
val generatedDirtyTypes = javaCache.invalidateGeneratedTypes(invalidatedIsolatingGenerated).toMutableSet() /*+*/
|
if (impactedType !in isolatingGeneratedTypes && impactedType !in aggregatingGeneratedTypes) {
|
||||||
|
// Reprocess only if original source
|
||||||
val aggregatedTypes = mutableListOf<String>()
|
javaCache.getSourceForType(impactedType)?.let {
|
||||||
if (!toReprocess.isEmpty()) {
|
sourcesToReprocess.add(it)
|
||||||
// only if there are some files to reprocess we should invalidate the aggregating ones
|
|
||||||
val (aggregatingGenerated, aggregatedTypes1) = aptCache.invalidateAggregating()
|
|
||||||
aggregatedTypes.addAll(aggregatedTypes1)
|
|
||||||
generatedDirtyTypes.addAll(javaCache.invalidateGeneratedTypes(aggregatingGenerated))
|
|
||||||
|
|
||||||
toReprocess.addAll(
|
|
||||||
javaCache.invalidateEntriesAnnotatedWith(aptCache.getAggregatingClaimedAnnotations())
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
} else if (impactedType in isolatingGeneratedTypes) {
|
||||||
SourcesToReprocess.Incremental(
|
// this is a generated type by isolating AP
|
||||||
toReprocess.toList(),
|
val isolatingOrigin = aptCache.getOriginForGeneratedIsolatingType(impactedType, javaCache::getSourceForType)
|
||||||
generatedDirtyTypes,
|
if (isolatingOrigin in impactedTypes) {
|
||||||
aggregatedTypes.also {
|
// we'll process origin, no need to do it now
|
||||||
it.removeAll(filesToReprocess.dirtyTypes)
|
continue
|
||||||
it.removeAll(generatedDirtyTypes)
|
}
|
||||||
it.removeAll(invalidatedIsolatingId)
|
val originSource = javaCache.getSourceForType(isolatingOrigin)
|
||||||
})
|
if (originSource?.extension == "java") {
|
||||||
|
sourcesToReprocess.add(originSource)
|
||||||
|
} else if (originSource?.extension == "class") {
|
||||||
|
// This is a generated .class file that we need to reprocess.
|
||||||
|
classNamesToReprocess.add(isolatingOrigin)
|
||||||
|
} else {
|
||||||
|
// This is a type from classpath that was used as origin, just ignore it. It is used just to remove the generated file.
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// processed separately
|
||||||
|
shouldProcessAggregating = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (shouldProcessAggregating || sourcesToReprocess.isNotEmpty() || classNamesToReprocess.isNotEmpty()) {
|
||||||
|
for (aggregatingOrigin in aptCache.getAggregatingOrigins()) {
|
||||||
|
if (aggregatingOrigin in impactedTypes) continue
|
||||||
|
|
||||||
|
val originSource = javaCache.getSourceForType(aggregatingOrigin)
|
||||||
|
if (originSource?.extension == "java") {
|
||||||
|
sourcesToReprocess.add(originSource)
|
||||||
|
} else if (originSource?.extension == "class") {
|
||||||
|
// This is a generated .class file that we need to reprocess.
|
||||||
|
classNamesToReprocess.add(aggregatingOrigin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invalidate state only if there are some files that will be reprocessed
|
||||||
|
javaCache.invalidateDataForTypes(impactedTypes)
|
||||||
|
aptCache.invalidateAggregating()
|
||||||
|
// for isolating, invalidate both own types and classpath types
|
||||||
|
aptCache.invalidateIsolatingForOriginTypes(impactedTypes)
|
||||||
|
aptCache.invalidateIsolatingForOriginTypes(dirtyClasspathFqNames)
|
||||||
|
}
|
||||||
|
|
||||||
|
return SourcesToReprocess.Incremental(sourcesToReprocess.toList(), impactedTypes, classNamesToReprocess)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getAllImpactedTypes(changes: Changes, aggregatingGeneratedTypes: Set<String>): MutableSet<String> {
|
||||||
|
val impactedTypes = javaCache.getAllImpactedTypes(changes)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In order to find all impacted types we do the following:
|
||||||
|
* - impacted types is a set of types that have changed from the previous compilation
|
||||||
|
* - if there is a changed source or a source file that is impacted by type changes, we'll need to run aggregating APs, so we
|
||||||
|
* invalidate all aggregating generated types, and add them to set of impacted types.
|
||||||
|
* - using this new impacted types set, we find all types generated by isolating APs with origins in those types
|
||||||
|
* - if there are some generated types by isolating APs, we'll need to run aggregating APs, so we invalidate all aggregating types
|
||||||
|
* and add them to impacted types.
|
||||||
|
* - using the final value of impacted types we get all generated types by isolating APs and add them to impacted types
|
||||||
|
*/
|
||||||
|
if (changes.sourceChanges.isNotEmpty() || impactedTypes.isNotEmpty()) {
|
||||||
|
// Any source change or any source impacted by type change invalidates aggregating APs generated types
|
||||||
|
impactedTypes.addAll(aggregatingGeneratedTypes)
|
||||||
|
}
|
||||||
|
aptCache.getIsolatingGeneratedTypesForOrigins(changes.dirtyFqNamesFromClasspath, javaCache::getTypesForFiles).let {
|
||||||
|
if (it.isNotEmpty()) {
|
||||||
|
impactedTypes.addAll(it)
|
||||||
|
impactedTypes.addAll(aggregatingGeneratedTypes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
aptCache.getIsolatingGeneratedTypesForOrigins(impactedTypes, javaCache::getTypesForFiles).let {
|
||||||
|
impactedTypes.addAll(it)
|
||||||
|
}
|
||||||
|
return impactedTypes
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun maybeGetAptCacheFromFile(): IncrementalAptCache {
|
private fun maybeGetAptCacheFromFile(): IncrementalAptCache {
|
||||||
|
|||||||
+70
-84
@@ -11,17 +11,18 @@ import java.io.ObjectOutputStream
|
|||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
import java.lang.IllegalArgumentException
|
import java.lang.IllegalArgumentException
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.util.regex.Pattern
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores type information about processed and generated sources. For .java files a fine-grained type information
|
||||||
|
* exists i.e we know all referenced types. For .class files we only know which type is defined in the .class file.
|
||||||
|
*/
|
||||||
class JavaClassCache() : Serializable {
|
class JavaClassCache() : Serializable {
|
||||||
private var sourceCache = mutableMapOf<URI, SourceFileStructure>()
|
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. */
|
/** Map from types to files they are mentioned in. */
|
||||||
@Transient
|
@Transient
|
||||||
private var dependencyCache = mutableMapOf<String, MutableSet<URI>>()
|
private var dependencyCache = mutableMapOf<String, MutableSet<URI>>()
|
||||||
|
|
||||||
@Transient
|
@Transient
|
||||||
private var nonTransitiveCache = mutableMapOf<String, MutableSet<URI>>()
|
private var nonTransitiveCache = mutableMapOf<String, MutableSet<URI>>()
|
||||||
|
|
||||||
@@ -29,21 +30,31 @@ class JavaClassCache() : Serializable {
|
|||||||
sourceCache[sourceStructure.sourceFile] = sourceStructure
|
sourceCache[sourceStructure.sourceFile] = sourceStructure
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addGeneratedType(type: String, generatedFile: File) {
|
/** Invalidates types for these files, and return the list of invalidated types.*/
|
||||||
val typesInFile = generatedTypes[generatedFile] ?: ArrayList(1)
|
fun invalidateTypesForFiles(files: List<File>): Set<String> {
|
||||||
typesInFile.add(type)
|
val typesFromFiles = HashSet<String>()
|
||||||
generatedTypes[generatedFile] = typesInFile
|
for (file in files) {
|
||||||
|
sourceCache.remove(file.toURI())?.getDeclaredTypes()?.let {
|
||||||
|
typesFromFiles.addAll(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return typesFromFiles
|
||||||
}
|
}
|
||||||
|
|
||||||
fun invalidateGeneratedTypes(files: List<File>): Set<String> {
|
/** Returns all types defined in these files. */
|
||||||
return files.mapNotNull { generatedTypes.remove(it) }.flatten().toSet()
|
fun getTypesForFiles(files: Collection<File>): Set<String> {
|
||||||
|
val typesFromFiles = HashSet<String>(files.size)
|
||||||
|
for (file in files) {
|
||||||
|
sourceCache[file.toURI()]?.getDeclaredTypes()?.let {
|
||||||
|
typesFromFiles.addAll(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return typesFromFiles
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun readObject(input: ObjectInputStream) {
|
private fun readObject(input: ObjectInputStream) {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
sourceCache = input.readObject() as MutableMap<URI, SourceFileStructure>
|
sourceCache = input.readObject() as MutableMap<URI, SourceFileStructure>
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
generatedTypes = input.readObject() as MutableMap<File, MutableList<String>>
|
|
||||||
|
|
||||||
dependencyCache = HashMap(sourceCache.size * 4)
|
dependencyCache = HashMap(sourceCache.size * 4)
|
||||||
for (sourceInfo in sourceCache.values) {
|
for (sourceInfo in sourceCache.values) {
|
||||||
@@ -71,7 +82,6 @@ class JavaClassCache() : Serializable {
|
|||||||
|
|
||||||
private fun writeObject(output: ObjectOutputStream) {
|
private fun writeObject(output: ObjectOutputStream) {
|
||||||
output.writeObject(sourceCache)
|
output.writeObject(sourceCache)
|
||||||
output.writeObject(generatedTypes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isAlreadyProcessed(sourceFile: URI): Boolean {
|
fun isAlreadyProcessed(sourceFile: URI): Boolean {
|
||||||
@@ -84,8 +94,7 @@ class JavaClassCache() : Serializable {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return try {
|
return try {
|
||||||
val fileFromUri = File(sourceFile)
|
sourceCache.containsKey(sourceFile)
|
||||||
sourceCache.containsKey(sourceFile) || generatedTypes.containsKey(fileFromUri)
|
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
// unable to create File instance, avoid processing these files
|
// unable to create File instance, avoid processing these files
|
||||||
true
|
true
|
||||||
@@ -96,89 +105,66 @@ class JavaClassCache() : Serializable {
|
|||||||
internal fun getStructure(sourceFile: File) = sourceCache[sourceFile.toURI()]
|
internal fun getStructure(sourceFile: File) = sourceCache[sourceFile.toURI()]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invalidate cache entries for the specified files, and any files that depend on the changed ones. It returns the set of files that
|
* Compute the list of types that are impacted by source changes i.e [Changes.sourceChanges] and [Changes.dirtyFqNamesFromClasspath]
|
||||||
* should be re-processed.
|
* i.e classpath changes. The search is transitive, if a file is impacted, all files referencing types defined in that file are
|
||||||
* */
|
* also considered impacted. Only original sources and generated sources are reported as impacted (final result does not contain
|
||||||
fun invalidateEntriesForChangedFiles(changes: Changes): SourcesToReprocess {
|
* classpath types).
|
||||||
val allDirtyFiles = mutableSetOf<URI>()
|
*/
|
||||||
var currentDirtyFiles = changes.sourceChanges.map { it.toURI() }.toMutableSet()
|
fun getAllImpactedTypes(changes: Changes): MutableSet<String> {
|
||||||
|
fun findImpactedTypes(changedType: String, transitiveDeps: MutableSet<String>, nonTransitiveDeps: MutableSet<String>) {
|
||||||
for (classpathFqName in changes.dirtyFqNamesFromClasspath) {
|
dependencyCache[changedType]?.let { impactedSources ->
|
||||||
nonTransitiveCache[classpathFqName]?.let {
|
impactedSources.forEach {
|
||||||
allDirtyFiles.addAll(it)
|
transitiveDeps.addAll(sourceCache.getValue(it).getDeclaredTypes())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
nonTransitiveCache[changedType]?.let { impactedSources ->
|
||||||
dependencyCache[classpathFqName]?.let {
|
impactedSources.forEach {
|
||||||
currentDirtyFiles.addAll(it)
|
nonTransitiveDeps.addAll(sourceCache.getValue(it).getDeclaredTypes())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val allDirtyTypes = mutableSetOf<String>()
|
val allDirtyTypes = mutableSetOf<String>()
|
||||||
|
var currentDirtyTypes = getTypesForFiles(changes.sourceChanges).toMutableSet()
|
||||||
|
|
||||||
while (currentDirtyFiles.isNotEmpty()) {
|
changes.dirtyFqNamesFromClasspath.forEach { classpathChange ->
|
||||||
|
findImpactedTypes(classpathChange, currentDirtyTypes, allDirtyTypes)
|
||||||
|
}
|
||||||
|
|
||||||
val nextRound = mutableSetOf<URI>()
|
while (currentDirtyTypes.isNotEmpty()) {
|
||||||
for (dirtyFile in currentDirtyFiles) {
|
val nextRound = mutableSetOf<String>()
|
||||||
allDirtyFiles.add(dirtyFile)
|
for (dirtyType in currentDirtyTypes) {
|
||||||
|
allDirtyTypes.add(dirtyType)
|
||||||
val structure = sourceCache.remove(dirtyFile) ?: continue
|
findImpactedTypes(dirtyType, nextRound, allDirtyTypes)
|
||||||
val dirtyTypes = structure.getDeclaredTypes()
|
|
||||||
allDirtyTypes.addAll(dirtyTypes)
|
|
||||||
|
|
||||||
dirtyTypes.forEach { type ->
|
|
||||||
nonTransitiveCache[type]?.let {
|
|
||||||
allDirtyFiles.addAll(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencyCache[type]?.let {
|
|
||||||
nextRound.addAll(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
currentDirtyFiles = nextRound.filter { !allDirtyFiles.contains(it) }.toMutableSet()
|
currentDirtyTypes = nextRound.filterTo(HashSet()) { it !in allDirtyTypes }
|
||||||
}
|
}
|
||||||
|
return allDirtyTypes
|
||||||
return SourcesToReprocess.Incremental(allDirtyFiles.map { File(it) }, allDirtyTypes, emptyList())
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For aggregating annotation processors, we always need to reprocess all files annotated with an annotation claimed by the aggregating
|
|
||||||
* annotation processor. This search is not transitive.
|
|
||||||
*/
|
|
||||||
fun invalidateEntriesAnnotatedWith(annotations: Set<String>): Set<File> {
|
|
||||||
val patterns: List<Pattern> = if ("*" in annotations) {
|
|
||||||
// optimize this case - create only one pattern
|
|
||||||
listOf(Pattern.compile(".*"))
|
|
||||||
} else {
|
|
||||||
annotations.map {
|
|
||||||
Pattern.compile(
|
|
||||||
// These are already valid import statements, otherwise run fails when loading the annotation processor.
|
|
||||||
// Handles structure; TypeName [.*] e.g. org.jetbrains.annotations.NotNull and org.jetbrains.annotations.*
|
|
||||||
it.replace(".", "\\.").replace("*", ".+")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val matchesAnyPattern = { name: String -> patterns.any { it.matcher(name).matches() } }
|
|
||||||
|
|
||||||
val toReprocess = mutableSetOf<URI>()
|
|
||||||
|
|
||||||
for (cacheEntry in sourceCache) {
|
|
||||||
if (cacheEntry.value.getMentionedAnnotations().any(matchesAnyPattern)) {
|
|
||||||
toReprocess.add(cacheEntry.key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toReprocess.forEach {
|
|
||||||
sourceCache.remove(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
return toReprocess.map { File(it) }.toSet()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun invalidateAll() {
|
internal fun invalidateAll() {
|
||||||
sourceCache.clear()
|
sourceCache.clear()
|
||||||
generatedTypes.clear()
|
}
|
||||||
|
|
||||||
|
fun getSourceForType(type: String): File? {
|
||||||
|
sourceCache.forEach { (fileUri, typeInfo) ->
|
||||||
|
if (type in typeInfo.getDeclaredTypes()) {
|
||||||
|
return File(fileUri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun invalidateDataForTypes(impactedTypes: MutableSet<String>) {
|
||||||
|
val allSources = mutableSetOf<URI>()
|
||||||
|
sourceCache.forEach { (fileUri, typeInfo) ->
|
||||||
|
if (typeInfo.getDeclaredTypes().any { it in impactedTypes }) {
|
||||||
|
allSources.add(fileUri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allSources.forEach { sourceCache.remove(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+35
-30
@@ -70,11 +70,17 @@ class IncrementalProcessor(private val processor: Processor, private val kind: D
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun isUnableToRunIncrementally() = !kind.canRunIncrementally
|
fun isUnableToRunIncrementally() = !kind.canRunIncrementally
|
||||||
fun getGeneratedToSources() = dependencyCollector.value.getGeneratedToSources()
|
|
||||||
fun getGeneratedToSourcesAll() = dependencyCollector.value.getGeneratedToSourcesAll()
|
|
||||||
fun getAggregatedTypes() = dependencyCollector.value.getAggregatedTypes()
|
|
||||||
fun getRuntimeType(): RuntimeProcType = dependencyCollector.value.getRuntimeType()
|
|
||||||
|
|
||||||
|
/** Mapping fromm generated file to type that were used as originating elements. For aggregating APs types will be [null]. */
|
||||||
|
fun getGeneratedToSources(): Map<File, String?> = dependencyCollector.value.getGeneratedToSources()
|
||||||
|
|
||||||
|
/** All top-level types that were processed by aggregating APs. */
|
||||||
|
fun getAggregatedTypes() = dependencyCollector.value.getAggregatedTypes()
|
||||||
|
|
||||||
|
/** Mapping from generated class file to type defined in that file. */
|
||||||
|
fun getGeneratedClassFilesToTypes(): Map<File, String> = dependencyCollector.value.getGeneratedClassFilesToTypes()
|
||||||
|
|
||||||
|
fun getRuntimeType(): RuntimeProcType = dependencyCollector.value.getRuntimeType()
|
||||||
|
|
||||||
override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
|
override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
|
||||||
if (getRuntimeType() == RuntimeProcType.AGGREGATING) {
|
if (getRuntimeType() == RuntimeProcType.AGGREGATING) {
|
||||||
@@ -122,8 +128,9 @@ internal class AnnotationProcessorDependencyCollector(
|
|||||||
private val runtimeProcType: RuntimeProcType,
|
private val runtimeProcType: RuntimeProcType,
|
||||||
private val warningCollector: (String) -> Unit
|
private val warningCollector: (String) -> Unit
|
||||||
) {
|
) {
|
||||||
private val generatedToSource = mutableMapOf<File, Pair<String?, File?>?>()
|
private val generatedToSource = mutableMapOf<File, String?>()
|
||||||
private val aggregatedTypes = mutableSetOf<String>()
|
private val aggregatedTypes = mutableSetOf<String>()
|
||||||
|
private val generatedClassFilesToTypes = mutableMapOf<File, String>()
|
||||||
|
|
||||||
private var isFullRebuild = !runtimeProcType.isIncremental
|
private var isFullRebuild = !runtimeProcType.isIncremental
|
||||||
|
|
||||||
@@ -131,7 +138,7 @@ internal class AnnotationProcessorDependencyCollector(
|
|||||||
if (isFullRebuild) return
|
if (isFullRebuild) return
|
||||||
|
|
||||||
if (supportedAnnotationTypes.contains("*")) {
|
if (supportedAnnotationTypes.contains("*")) {
|
||||||
aggregatedTypes.addAll(getTopLevelClassNames(roundEnv.rootElements?.filterNotNull() ?: emptyList()))
|
aggregatedTypes.addAll(getTopLevelClassNames(roundEnv.rootElements?.filterNotNull() ?: emptySet()))
|
||||||
} else {
|
} else {
|
||||||
for (annotation in annotations) {
|
for (annotation in annotations) {
|
||||||
aggregatedTypes.addAll(
|
aggregatedTypes.addAll(
|
||||||
@@ -149,31 +156,40 @@ internal class AnnotationProcessorDependencyCollector(
|
|||||||
if (isFullRebuild) return
|
if (isFullRebuild) return
|
||||||
|
|
||||||
val generatedFile = File(createdFile)
|
val generatedFile = File(createdFile)
|
||||||
|
if (generatedFile.extension == "class") {
|
||||||
|
if (classId == null) {
|
||||||
|
isFullRebuild = true
|
||||||
|
warningCollector.invoke(
|
||||||
|
"Unable to determine type defined in $generatedFile."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
generatedClassFilesToTypes[generatedFile] = classId
|
||||||
|
}
|
||||||
|
|
||||||
if (runtimeProcType == RuntimeProcType.AGGREGATING) {
|
if (runtimeProcType == RuntimeProcType.AGGREGATING) {
|
||||||
generatedToSource[generatedFile] = classId to null
|
generatedToSource[generatedFile] = null
|
||||||
} else {
|
} else {
|
||||||
val srcFiles = getSrcFiles(originatingElements)
|
val srcClasses = getTopLevelClassNames(originatingElements.filterNotNull())
|
||||||
if (srcFiles.size != 1) {
|
if (srcClasses.size != 1) {
|
||||||
isFullRebuild = true
|
isFullRebuild = true
|
||||||
warningCollector.invoke(
|
warningCollector.invoke(
|
||||||
"Expected 1 originating source file when generating $generatedFile, " +
|
"Expected 1 originating source file when generating $generatedFile, " +
|
||||||
"but detected ${srcFiles.size}: [${srcFiles.joinToString()}]."
|
"but detected ${srcClasses.size}: [${srcClasses.joinToString()}]."
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
generatedToSource[generatedFile] = classId to srcFiles.single()
|
generatedToSource[generatedFile] = srcClasses.single()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun getGeneratedToSources(): Map<File, File?> = if (isFullRebuild) emptyMap() else generatedToSource.mapValues { (_, value) ->
|
/** Mapping from generated files to top level class names that cause that file generation. */
|
||||||
value?.second
|
internal fun getGeneratedToSources(): Map<File, String?> = if (isFullRebuild) emptyMap() else generatedToSource
|
||||||
}
|
|
||||||
|
|
||||||
internal fun getGeneratedToSourcesAll(): Map<File, Pair<String?, File?>?> =
|
|
||||||
if (isFullRebuild) emptyMap() else generatedToSource
|
|
||||||
|
|
||||||
internal fun getAggregatedTypes(): Set<String> = if (isFullRebuild) emptySet() else aggregatedTypes
|
internal fun getAggregatedTypes(): Set<String> = if (isFullRebuild) emptySet() else aggregatedTypes
|
||||||
|
|
||||||
|
internal fun getGeneratedClassFilesToTypes(): Map<File, String> = if (isFullRebuild) emptyMap() else generatedClassFilesToTypes
|
||||||
|
|
||||||
internal fun getRuntimeType(): RuntimeProcType {
|
internal fun getRuntimeType(): RuntimeProcType {
|
||||||
return if (isFullRebuild) {
|
return if (isFullRebuild) {
|
||||||
RuntimeProcType.NON_INCREMENTAL
|
RuntimeProcType.NON_INCREMENTAL
|
||||||
@@ -183,17 +199,6 @@ internal class AnnotationProcessorDependencyCollector(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSrcFiles(elements: Array<out Element?>): Set<File> {
|
|
||||||
return elements.filterNotNull().mapNotNull { elem ->
|
|
||||||
var origin = elem
|
|
||||||
while (origin.enclosingElement != null && origin.enclosingElement !is PackageElement) {
|
|
||||||
origin = origin.enclosingElement
|
|
||||||
}
|
|
||||||
val uri = (origin as? Symbol.ClassSymbol)?.sourcefile?.toUri()?.takeIf { it.isAbsolute }
|
|
||||||
uri?.let { File(it).canonicalFile }
|
|
||||||
}.toSet()
|
|
||||||
}
|
|
||||||
|
|
||||||
private const val PACKAGE_TYPE_NAME = "package-info"
|
private const val PACKAGE_TYPE_NAME = "package-info"
|
||||||
|
|
||||||
fun getElementName(current: Element?): String? {
|
fun getElementName(current: Element?): String? {
|
||||||
@@ -211,8 +216,8 @@ fun getElementName(current: Element?): String? {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getTopLevelClassNames(elements: Collection<Element>): Collection<String> {
|
private fun getTopLevelClassNames(elements: Collection<Element>): Set<String> {
|
||||||
return elements.mapNotNull { elem ->
|
return elements.mapNotNullTo(HashSet()) { elem ->
|
||||||
var origin = elem
|
var origin = elem
|
||||||
while (origin.enclosingElement != null && origin.enclosingElement !is PackageElement) {
|
while (origin.enclosingElement != null && origin.enclosingElement !is PackageElement) {
|
||||||
origin = origin.enclosingElement
|
origin = origin.enclosingElement
|
||||||
|
|||||||
-24
@@ -246,28 +246,4 @@ private class ConstantTreeVisitor(val sourceStructure: SourceFileStructure) : Tr
|
|||||||
|
|
||||||
sourceStructure.addMentionedConstant(containingClass.qualifiedName.toString(), name.toString())
|
sourceStructure.addMentionedConstant(containingClass.qualifiedName.toString(), name.toString())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
+2
-2
@@ -36,8 +36,8 @@ class DynamicIncrementalProcessorTest {
|
|||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
mapOf(
|
mapOf(
|
||||||
generatedSources.resolve("test/UserGenerated.java") to File("plugins/kapt3/kapt3-base/testData/runner/incremental/User.java").absoluteFile,
|
generatedSources.resolve("test/UserGenerated.java") to "test.User",
|
||||||
generatedSources.resolve("test/AddressGenerated.java") to File("plugins/kapt3/kapt3-base/testData/runner/incremental/Address.java").absoluteFile
|
generatedSources.resolve("test/AddressGenerated.java") to "test.Address"
|
||||||
),
|
),
|
||||||
dynamic.getGeneratedToSources()
|
dynamic.getGeneratedToSources()
|
||||||
)
|
)
|
||||||
|
|||||||
+13
-13
@@ -35,8 +35,8 @@ class IsolationgIncrementalProcessorTest {
|
|||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
mapOf(
|
mapOf(
|
||||||
generatedSources.resolve("test/UserGenerated.java") to File("plugins/kapt3/kapt3-base/testData/runner/incremental/User.java").absoluteFile,
|
generatedSources.resolve("test/UserGenerated.java") to "test.User",
|
||||||
generatedSources.resolve("test/AddressGenerated.java") to File("plugins/kapt3/kapt3-base/testData/runner/incremental/Address.java").absoluteFile
|
generatedSources.resolve("test/AddressGenerated.java") to "test.Address"
|
||||||
),
|
),
|
||||||
isolating.getGeneratedToSources()
|
isolating.getGeneratedToSources()
|
||||||
)
|
)
|
||||||
@@ -62,12 +62,12 @@ class IsolationgIncrementalProcessorTest {
|
|||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
mapOf(
|
mapOf(
|
||||||
generatedSources.resolve("test/UserGenerated.java") to TEST_DATA_DIR.resolve("User.java").absoluteFile,
|
generatedSources.resolve("test/UserGenerated.java") to "test.User",
|
||||||
generatedSources.resolve("test/UserGeneratedClass.class") to TEST_DATA_DIR.resolve("User.java").absoluteFile,
|
generatedSources.resolve("test/UserGeneratedClass.class") to "test.User",
|
||||||
generatedSources.resolve("test/UserGeneratedResource") to TEST_DATA_DIR.resolve("User.java").absoluteFile,
|
generatedSources.resolve("test/UserGeneratedResource") to "test.User",
|
||||||
generatedSources.resolve("test/AddressGenerated.java") to TEST_DATA_DIR.resolve("Address.java").absoluteFile,
|
generatedSources.resolve("test/AddressGenerated.java") to "test.Address",
|
||||||
generatedSources.resolve("test/AddressGeneratedClass.class") to TEST_DATA_DIR.resolve("Address.java").absoluteFile,
|
generatedSources.resolve("test/AddressGeneratedClass.class") to "test.Address",
|
||||||
generatedSources.resolve("test/AddressGeneratedResource") to TEST_DATA_DIR.resolve("Address.java").absoluteFile
|
generatedSources.resolve("test/AddressGeneratedResource") to "test.Address"
|
||||||
),
|
),
|
||||||
isolating.getGeneratedToSources()
|
isolating.getGeneratedToSources()
|
||||||
)
|
)
|
||||||
@@ -95,15 +95,15 @@ class IsolationgIncrementalProcessorTest {
|
|||||||
isolating.forEach { assertEquals(RuntimeProcType.ISOLATING, it.getRuntimeType()) }
|
isolating.forEach { assertEquals(RuntimeProcType.ISOLATING, it.getRuntimeType()) }
|
||||||
assertEquals(
|
assertEquals(
|
||||||
mapOf(
|
mapOf(
|
||||||
generatedSources.resolve("test/UserGenerated.java") to File("plugins/kapt3/kapt3-base/testData/runner/incremental/User.java").absoluteFile,
|
generatedSources.resolve("test/UserGenerated.java") to "test.User",
|
||||||
generatedSources.resolve("test/AddressGenerated.java") to File("plugins/kapt3/kapt3-base/testData/runner/incremental/Address.java").absoluteFile
|
generatedSources.resolve("test/AddressGenerated.java") to "test.Address"
|
||||||
), isolating[0].getGeneratedToSources()
|
), isolating[0].getGeneratedToSources()
|
||||||
)
|
)
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
mapOf(
|
mapOf(
|
||||||
generatedSources.resolve("test/UserGeneratedTwo.java") to File("plugins/kapt3/kapt3-base/testData/runner/incremental/User.java").absoluteFile,
|
generatedSources.resolve("test/UserGeneratedTwo.java") to "test.User",
|
||||||
generatedSources.resolve("test/AddressGeneratedTwo.java") to File("plugins/kapt3/kapt3-base/testData/runner/incremental/Address.java").absoluteFile
|
generatedSources.resolve("test/AddressGeneratedTwo.java") to "test.Address"
|
||||||
), isolating[1].getGeneratedToSources()
|
), isolating[1].getGeneratedToSources()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -116,7 +116,7 @@ class IsolationgIncrementalProcessorTest {
|
|||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
mapOf(
|
mapOf(
|
||||||
generatedSources.resolve("test/UserGenerated.java") to File("plugins/kapt3/kapt3-base/testData/runner/incremental/User.java").absoluteFile
|
generatedSources.resolve("test/UserGenerated.java") to "test.User"
|
||||||
), isolating[0].getGeneratedToSources()
|
), isolating[0].getGeneratedToSources()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-29
@@ -12,7 +12,6 @@ import org.junit.Rule
|
|||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.rules.TemporaryFolder
|
import org.junit.rules.TemporaryFolder
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.ObjectOutputStream
|
|
||||||
|
|
||||||
class JavaClassCacheManagerTest {
|
class JavaClassCacheManagerTest {
|
||||||
|
|
||||||
@@ -56,7 +55,8 @@ class JavaClassCacheManagerTest {
|
|||||||
}
|
}
|
||||||
prepareForIncremental()
|
prepareForIncremental()
|
||||||
|
|
||||||
val dirtyFiles = cache.invalidateAndGetDirtyFiles(listOf(File("Mentioned.java")), emptyList()) as SourcesToReprocess.Incremental
|
val dirtyFiles =
|
||||||
|
cache.invalidateAndGetDirtyFiles(listOf(File("Mentioned.java").absoluteFile), emptyList()) as SourcesToReprocess.Incremental
|
||||||
assertEquals(
|
assertEquals(
|
||||||
listOf(
|
listOf(
|
||||||
File("Mentioned.java").absoluteFile,
|
File("Mentioned.java").absoluteFile,
|
||||||
@@ -84,7 +84,8 @@ class JavaClassCacheManagerTest {
|
|||||||
}
|
}
|
||||||
prepareForIncremental()
|
prepareForIncremental()
|
||||||
|
|
||||||
val dirtyFiles = cache.invalidateAndGetDirtyFiles(listOf(File("Mentioned.java")), emptyList()) as SourcesToReprocess.Incremental
|
val dirtyFiles =
|
||||||
|
cache.invalidateAndGetDirtyFiles(listOf(File("Mentioned.java").absoluteFile), emptyList()) as SourcesToReprocess.Incremental
|
||||||
assertEquals(
|
assertEquals(
|
||||||
listOf(
|
listOf(
|
||||||
File("Mentioned.java").absoluteFile,
|
File("Mentioned.java").absoluteFile,
|
||||||
@@ -112,7 +113,8 @@ class JavaClassCacheManagerTest {
|
|||||||
}
|
}
|
||||||
prepareForIncremental()
|
prepareForIncremental()
|
||||||
|
|
||||||
val dirtyFiles = cache.invalidateAndGetDirtyFiles(listOf(File("TwoTypes.java")), emptyList()) as SourcesToReprocess.Incremental
|
val dirtyFiles =
|
||||||
|
cache.invalidateAndGetDirtyFiles(listOf(File("TwoTypes.java").absoluteFile), emptyList()) as SourcesToReprocess.Incremental
|
||||||
assertEquals(
|
assertEquals(
|
||||||
listOf(
|
listOf(
|
||||||
File("TwoTypes.java").absoluteFile,
|
File("TwoTypes.java").absoluteFile,
|
||||||
@@ -155,7 +157,7 @@ class JavaClassCacheManagerTest {
|
|||||||
|
|
||||||
val dirtyFiles =
|
val dirtyFiles =
|
||||||
cache.invalidateAndGetDirtyFiles(
|
cache.invalidateAndGetDirtyFiles(
|
||||||
listOf(File("Constants.java")), emptyList()
|
listOf(File("Constants.java").absoluteFile), emptyList()
|
||||||
) as SourcesToReprocess.Incremental
|
) as SourcesToReprocess.Incremental
|
||||||
assertEquals(
|
assertEquals(
|
||||||
listOf(File("Constants.java").absoluteFile, File("MentionsConst.java").absoluteFile),
|
listOf(File("Constants.java").absoluteFile, File("MentionsConst.java").absoluteFile),
|
||||||
@@ -163,30 +165,6 @@ class JavaClassCacheManagerTest {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testWithAnnotations() {
|
|
||||||
SourceFileStructure(File("Annotated1.java").toURI()).also {
|
|
||||||
it.addDeclaredType("test.Annotated1")
|
|
||||||
it.addMentionedAnnotations("test.Annotation")
|
|
||||||
cache.javaCache.addSourceStructure(it)
|
|
||||||
}
|
|
||||||
SourceFileStructure(File("Annotated2.java").toURI()).also {
|
|
||||||
it.addDeclaredType("test.Annotated2")
|
|
||||||
it.addMentionedAnnotations("com.test.MyAnnotation")
|
|
||||||
cache.javaCache.addSourceStructure(it)
|
|
||||||
}
|
|
||||||
SourceFileStructure(File("Annotated3.java").toURI()).also {
|
|
||||||
it.addDeclaredType("test.Annotated3")
|
|
||||||
it.addMentionedAnnotations("Runnable")
|
|
||||||
cache.javaCache.addSourceStructure(it)
|
|
||||||
}
|
|
||||||
prepareForIncremental()
|
|
||||||
|
|
||||||
assertEquals(setOf(File("Annotated1.java").absoluteFile), cache.javaCache.invalidateEntriesAnnotatedWith(setOf("test.Annotation")))
|
|
||||||
assertEquals(setOf(File("Annotated2.java").absoluteFile), cache.javaCache.invalidateEntriesAnnotatedWith(setOf("com.test.*")))
|
|
||||||
assertEquals(setOf(File("Annotated3.java").absoluteFile), cache.javaCache.invalidateEntriesAnnotatedWith(setOf("*")))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun prepareForIncremental() {
|
private fun prepareForIncremental() {
|
||||||
cache.close()
|
cache.close()
|
||||||
cache = JavaClassCacheManager(cacheDir)
|
cache = JavaClassCacheManager(cacheDir)
|
||||||
|
|||||||
+3
-3
@@ -36,7 +36,7 @@ class TestSimpleIncrementalAptCache {
|
|||||||
fun testAggregatingAnnotations() {
|
fun testAggregatingAnnotations() {
|
||||||
runProcessor(SimpleProcessor().toAggregating())
|
runProcessor(SimpleProcessor().toAggregating())
|
||||||
|
|
||||||
val dirtyFiles = cache.invalidateAndGetDirtyFiles(listOf(TEST_DATA_DIR.resolve("User.java")), emptyList()) as SourcesToReprocess.Incremental
|
val dirtyFiles = cache.invalidateAndGetDirtyFiles(listOf(TEST_DATA_DIR.resolve("User.java").absoluteFile), emptyList()) as SourcesToReprocess.Incremental
|
||||||
assertEquals(
|
assertEquals(
|
||||||
listOf(TEST_DATA_DIR.resolve("User.java").absoluteFile, TEST_DATA_DIR.resolve("Address.java").absoluteFile),
|
listOf(TEST_DATA_DIR.resolve("User.java").absoluteFile, TEST_DATA_DIR.resolve("Address.java").absoluteFile),
|
||||||
dirtyFiles.toReprocess
|
dirtyFiles.toReprocess
|
||||||
@@ -49,7 +49,7 @@ class TestSimpleIncrementalAptCache {
|
|||||||
fun testIsolatingAnnotations() {
|
fun testIsolatingAnnotations() {
|
||||||
runProcessor(SimpleProcessor().toIsolating())
|
runProcessor(SimpleProcessor().toIsolating())
|
||||||
|
|
||||||
val dirtyFiles = cache.invalidateAndGetDirtyFiles(listOf(TEST_DATA_DIR.resolve("User.java")), emptyList()) as SourcesToReprocess.Incremental
|
val dirtyFiles = cache.invalidateAndGetDirtyFiles(listOf(TEST_DATA_DIR.resolve("User.java").absoluteFile), emptyList()) as SourcesToReprocess.Incremental
|
||||||
assertFalse(generatedSources.resolve("test/UserGenerated.java").exists())
|
assertFalse(generatedSources.resolve("test/UserGenerated.java").exists())
|
||||||
assertEquals(
|
assertEquals(
|
||||||
listOf(TEST_DATA_DIR.resolve("User.java").absoluteFile),
|
listOf(TEST_DATA_DIR.resolve("User.java").absoluteFile),
|
||||||
@@ -61,7 +61,7 @@ class TestSimpleIncrementalAptCache {
|
|||||||
fun testNonIncremental() {
|
fun testNonIncremental() {
|
||||||
runProcessor(SimpleProcessor().toNonIncremental())
|
runProcessor(SimpleProcessor().toNonIncremental())
|
||||||
|
|
||||||
val dirtyFiles = cache.invalidateAndGetDirtyFiles(listOf(TEST_DATA_DIR.resolve("User.java")), emptyList())
|
val dirtyFiles = cache.invalidateAndGetDirtyFiles(listOf(TEST_DATA_DIR.resolve("User.java").absoluteFile), emptyList())
|
||||||
assertTrue(dirtyFiles is SourcesToReprocess.FullRebuild)
|
assertTrue(dirtyFiles is SourcesToReprocess.FullRebuild)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user