KAPT: add tests for processed types, remove dead code, simplify logic
Add integration test which checks if only types can be reprocessed in an incremental round. Also, remove unused `invalidateTypesForFiles` method. Furthermore, clarify that types that are reprocessed (i.e types from .class files) are not necessarily aggregating types, but simply types that should be reprocessed. Test: KaptIncrementalWithIsolatingApt.testClasspathChangesCauseTypesToBeReprocessed
This commit is contained in:
committed by
Mikhael Bogdanov
parent
08a2b47c77
commit
11673bd09c
+57
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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;
|
||||
import java.util.TreeSet;
|
||||
|
||||
public class IncrementalAggregatingReferencingClasspathProcessor extends AbstractProcessor {
|
||||
|
||||
// Type that the generated source will extend.
|
||||
public static final String CLASSPATH_TYPE = "com.example.FromClasspath";
|
||||
|
||||
private Set<String> values = new TreeSet<String>();
|
||||
|
||||
@Override
|
||||
public Set<String> getSupportedAnnotationTypes() {
|
||||
return Collections.singleton("example.KotlinFilerGenerated");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
|
||||
for (TypeElement annotation : annotations) {
|
||||
for (Element element : roundEnv.getElementsAnnotatedWith(annotation)) {
|
||||
if (element instanceof TypeElement || element instanceof ExecutableElement || element instanceof VariableElement) {
|
||||
values.add(element.getSimpleName().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (roundEnv.processingOver() && !values.isEmpty()) {
|
||||
|
||||
try (Writer writer = processingEnv.getFiler().createSourceFile("com.example.AggGenerated").openWriter()) {
|
||||
writer.append("package ").append("com.example").append(";");
|
||||
writer.append("\npublic class ").append("AggGenerated").append(" extends ").append(CLASSPATH_TYPE).append(" {}");
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
values.clear();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+97
@@ -5,11 +5,14 @@
|
||||
|
||||
package org.jetbrains.kotlin.gradle
|
||||
|
||||
import org.jetbrains.kotlin.gradle.incapt.IncrementalAggregatingReferencingClasspathProcessor
|
||||
import org.jetbrains.kotlin.gradle.incapt.IncrementalBinaryIsolatingProcessor
|
||||
import org.jetbrains.kotlin.gradle.incapt.IncrementalProcessor
|
||||
import org.jetbrains.kotlin.gradle.incapt.IncrementalProcessorReferencingClasspath
|
||||
import org.jetbrains.kotlin.gradle.util.AGPVersion
|
||||
import org.jetbrains.kotlin.gradle.util.modify
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Assume
|
||||
import org.junit.Test
|
||||
import test.kt33617.MyClass
|
||||
@@ -275,6 +278,92 @@ class KaptIncrementalWithIsolatingApt : KaptIncrementalIT() {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that changes to classpath can cause types to be reprocessed (i.e types in generated .class files that contain annotations
|
||||
* claimed by annotation processors).
|
||||
*/
|
||||
@Test
|
||||
fun testClasspathChangesCauseTypesToBeReprocessed() {
|
||||
val project = Project(
|
||||
"kaptIncrementalCompilationProject",
|
||||
GradleVersionRequired.None
|
||||
).apply {
|
||||
setupIncrementalAptProject(
|
||||
Pair("ISOLATING", IncrementalBinaryIsolatingProcessor::class.java),
|
||||
Pair("AGGREGATING", IncrementalAggregatingReferencingClasspathProcessor::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/" + IncrementalAggregatingReferencingClasspathProcessor.CLASSPATH_TYPE.replace(".", "/") + ".java")
|
||||
source.parentFile.mkdirs()
|
||||
|
||||
source.writeText(
|
||||
"""
|
||||
package ${IncrementalAggregatingReferencingClasspathProcessor.CLASSPATH_TYPE.substringBeforeLast(".")};
|
||||
public class ${IncrementalAggregatingReferencingClasspathProcessor.CLASSPATH_TYPE.substringAfterLast(".")} {}
|
||||
""".trimIndent()
|
||||
)
|
||||
return@run source
|
||||
}
|
||||
project.gradleBuildScript().appendText(
|
||||
"""
|
||||
|
||||
dependencies {
|
||||
implementation project(':lib')
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
// Remove all sources, and add only 1 source file
|
||||
project.projectDir.resolve("src").let {
|
||||
it.deleteRecursively()
|
||||
with(it.resolve("main/java/example/A.kt")) {
|
||||
parentFile.mkdirs()
|
||||
writeText(
|
||||
"""
|
||||
package example
|
||||
|
||||
annotation class ExampleAnnotation
|
||||
@ExampleAnnotation
|
||||
class A
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val allKotlinStubs = setOf(
|
||||
project.projectDir.resolve("build/tmp/kapt3/stubs/main/example/ExampleAnnotation.java").canonicalPath,
|
||||
project.projectDir.resolve("build/tmp/kapt3/stubs/main/example/A.java").canonicalPath,
|
||||
project.projectDir.resolve("build/tmp/kapt3/stubs/main/error/NonExistentClass.java").canonicalPath
|
||||
)
|
||||
|
||||
project.build("clean", "build") {
|
||||
assertSuccessful()
|
||||
assertEquals(allKotlinStubs, getProcessedSources(output))
|
||||
|
||||
assertTrue(
|
||||
"Aggregating sources exists",
|
||||
fileInWorkingDir("build/generated/source/kapt/main/com/example/AggGenerated.java").exists()
|
||||
)
|
||||
}
|
||||
|
||||
// change type that the aggregated generated source reference
|
||||
classpathTypeSource.writeText(classpathTypeSource.readText().replace("}", "int i = 10;\n}"))
|
||||
project.build("build") {
|
||||
assertSuccessful()
|
||||
assertEquals(emptySet<String>(), getProcessedSources(output))
|
||||
assertEquals(setOf("example.AGenerated"), getProcessedTypes(output))
|
||||
assertTrue(
|
||||
"Aggregating sources exists",
|
||||
fileInWorkingDir("build/generated/source/kapt/main/com/example/AggGenerated.java").exists()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val patternApt = "Processing java sources with annotation processors:"
|
||||
@@ -285,6 +374,14 @@ fun getProcessedSources(output: String): Set<String> {
|
||||
}
|
||||
}
|
||||
|
||||
private const val patternClassesApt = "Processing types with annotation processors: "
|
||||
fun getProcessedTypes(output: String): Set<String> {
|
||||
return output.lines().filter { it.contains(patternClassesApt) }.flatMapTo(HashSet()) { logging ->
|
||||
val indexOf = logging.indexOf(patternClassesApt) + patternClassesApt.length
|
||||
logging.drop(indexOf).split(",").map { it.trim() }.filter { !it.isEmpty() }.toSet()
|
||||
}
|
||||
}
|
||||
|
||||
fun BaseGradleIT.Project.setupIncrementalAptProject(
|
||||
procType: String,
|
||||
buildFile: File = projectDir.resolve("build.gradle"),
|
||||
|
||||
+4
@@ -15,11 +15,15 @@ apply plugin: "kotlin-kapt"
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenLocal()
|
||||
maven {
|
||||
url "https://jetbrains.bintray.com/intellij-third-party-dependencies/"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
implementation "org.jetbrains.kotlin:annotation-processor-example:$kotlin_version"
|
||||
kapt "org.jetbrains.kotlin:annotation-processor-example:$kotlin_version"
|
||||
kapt "org.jetbrains.intellij.deps:asm-all:9.0"
|
||||
testImplementation 'junit:junit:4.12'
|
||||
}
|
||||
@@ -45,7 +45,7 @@ object Kapt {
|
||||
kaptContext.doAnnotationProcessing(
|
||||
javaSourceFiles,
|
||||
processors.processors,
|
||||
aggregatedTypes = collectAggregatedTypes(kaptContext.sourcesToReprocess)
|
||||
binaryTypesToReprocess = collectAggregatedTypes(kaptContext.sourcesToReprocess)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -14,7 +14,6 @@ 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.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.KaptLogger
|
||||
import org.jetbrains.kotlin.kapt3.base.util.isJava9OrLater
|
||||
@@ -33,7 +32,7 @@ fun KaptContext.doAnnotationProcessing(
|
||||
javaSourceFiles: List<File>,
|
||||
processors: List<IncrementalProcessor>,
|
||||
additionalSources: JavacList<JCTree.JCCompilationUnit> = JavacList.nil(),
|
||||
aggregatedTypes: List<String> = emptyList()
|
||||
binaryTypesToReprocess: List<String> = emptyList()
|
||||
) {
|
||||
val processingEnvironment = JavacProcessingEnvironment.instance(context)
|
||||
|
||||
@@ -41,7 +40,7 @@ fun KaptContext.doAnnotationProcessing(
|
||||
|
||||
val compilerAfterAP: JavaCompiler
|
||||
try {
|
||||
if (javaSourceFiles.isEmpty() && aggregatedTypes.isEmpty() && additionalSources.isEmpty()) {
|
||||
if (javaSourceFiles.isEmpty() && binaryTypesToReprocess.isEmpty() && additionalSources.isEmpty()) {
|
||||
if (logger.isVerbose) {
|
||||
logger.info("Skipping annotation processing as all sources are up-to-date.")
|
||||
}
|
||||
@@ -57,6 +56,7 @@ fun KaptContext.doAnnotationProcessing(
|
||||
|
||||
if (logger.isVerbose) {
|
||||
logger.info("Processing java sources with annotation processors: ${javaSourceFiles.joinToString()}")
|
||||
logger.info("Processing types with annotation processors: ${binaryTypesToReprocess.joinToString()}")
|
||||
}
|
||||
val parsedJavaFiles = parseJavaFiles(javaSourceFiles)
|
||||
|
||||
@@ -74,7 +74,7 @@ fun KaptContext.doAnnotationProcessing(
|
||||
CompileState.PARSE, compiler.enterTrees(parsedJavaFiles + additionalSources)
|
||||
)
|
||||
|
||||
val additionalClassNames = JavacList.from(aggregatedTypes)
|
||||
val additionalClassNames = JavacList.from(binaryTypesToReprocess)
|
||||
if (isJava9OrLater()) {
|
||||
val processAnnotationsMethod =
|
||||
compiler.javaClass.getMethod("processAnnotations", JavacList::class.java, java.util.Collection::class.java)
|
||||
|
||||
@@ -93,8 +93,7 @@ class JavaClassCacheManager(val file: File) : Closeable {
|
||||
javaCache.invalidateDataForTypes(impactedTypes)
|
||||
aptCache.invalidateAggregating()
|
||||
// for isolating, invalidate both own types and classpath types
|
||||
aptCache.invalidateIsolatingForOriginTypes(impactedTypes)
|
||||
aptCache.invalidateIsolatingForOriginTypes(dirtyClasspathFqNames)
|
||||
aptCache.invalidateIsolatingForOriginTypes(impactedTypes + dirtyClasspathFqNames)
|
||||
}
|
||||
|
||||
return SourcesToReprocess.Incremental(sourcesToReprocess.toList(), impactedTypes, classNamesToReprocess)
|
||||
|
||||
-11
@@ -31,17 +31,6 @@ class JavaClassCache() : Serializable {
|
||||
sourceCache[sourceStructure.sourceFile] = sourceStructure
|
||||
}
|
||||
|
||||
/** Invalidates types for these files, and return the list of invalidated types.*/
|
||||
fun invalidateTypesForFiles(files: List<File>): Set<String> {
|
||||
val typesFromFiles = HashSet<String>()
|
||||
for (file in files) {
|
||||
sourceCache.remove(file.toURI())?.getDeclaredTypes()?.let {
|
||||
typesFromFiles.addAll(it)
|
||||
}
|
||||
}
|
||||
return typesFromFiles
|
||||
}
|
||||
|
||||
/** Returns all types defined in these files. */
|
||||
fun getTypesForFiles(files: Collection<File>): Set<String> {
|
||||
val typesFromFiles = HashSet<String>(files.size)
|
||||
|
||||
Reference in New Issue
Block a user