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
|
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.IncrementalProcessor
|
||||||
import org.jetbrains.kotlin.gradle.incapt.IncrementalProcessorReferencingClasspath
|
import org.jetbrains.kotlin.gradle.incapt.IncrementalProcessorReferencingClasspath
|
||||||
import org.jetbrains.kotlin.gradle.util.AGPVersion
|
import org.jetbrains.kotlin.gradle.util.AGPVersion
|
||||||
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.Assert.assertTrue
|
||||||
import org.junit.Assume
|
import org.junit.Assume
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import test.kt33617.MyClass
|
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:"
|
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(
|
fun BaseGradleIT.Project.setupIncrementalAptProject(
|
||||||
procType: String,
|
procType: String,
|
||||||
buildFile: File = projectDir.resolve("build.gradle"),
|
buildFile: File = projectDir.resolve("build.gradle"),
|
||||||
|
|||||||
+4
@@ -15,11 +15,15 @@ apply plugin: "kotlin-kapt"
|
|||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
jcenter()
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
|
maven {
|
||||||
|
url "https://jetbrains.bintray.com/intellij-third-party-dependencies/"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
implementation "org.jetbrains.kotlin:annotation-processor-example:$kotlin_version"
|
implementation "org.jetbrains.kotlin:annotation-processor-example:$kotlin_version"
|
||||||
kapt "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'
|
testImplementation 'junit:junit:4.12'
|
||||||
}
|
}
|
||||||
@@ -45,7 +45,7 @@ object Kapt {
|
|||||||
kaptContext.doAnnotationProcessing(
|
kaptContext.doAnnotationProcessing(
|
||||||
javaSourceFiles,
|
javaSourceFiles,
|
||||||
processors.processors,
|
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 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
|
||||||
@@ -33,7 +32,7 @@ fun KaptContext.doAnnotationProcessing(
|
|||||||
javaSourceFiles: List<File>,
|
javaSourceFiles: List<File>,
|
||||||
processors: List<IncrementalProcessor>,
|
processors: List<IncrementalProcessor>,
|
||||||
additionalSources: JavacList<JCTree.JCCompilationUnit> = JavacList.nil(),
|
additionalSources: JavacList<JCTree.JCCompilationUnit> = JavacList.nil(),
|
||||||
aggregatedTypes: List<String> = emptyList()
|
binaryTypesToReprocess: List<String> = emptyList()
|
||||||
) {
|
) {
|
||||||
val processingEnvironment = JavacProcessingEnvironment.instance(context)
|
val processingEnvironment = JavacProcessingEnvironment.instance(context)
|
||||||
|
|
||||||
@@ -41,7 +40,7 @@ fun KaptContext.doAnnotationProcessing(
|
|||||||
|
|
||||||
val compilerAfterAP: JavaCompiler
|
val compilerAfterAP: JavaCompiler
|
||||||
try {
|
try {
|
||||||
if (javaSourceFiles.isEmpty() && aggregatedTypes.isEmpty() && additionalSources.isEmpty()) {
|
if (javaSourceFiles.isEmpty() && binaryTypesToReprocess.isEmpty() && additionalSources.isEmpty()) {
|
||||||
if (logger.isVerbose) {
|
if (logger.isVerbose) {
|
||||||
logger.info("Skipping annotation processing as all sources are up-to-date.")
|
logger.info("Skipping annotation processing as all sources are up-to-date.")
|
||||||
}
|
}
|
||||||
@@ -57,6 +56,7 @@ fun KaptContext.doAnnotationProcessing(
|
|||||||
|
|
||||||
if (logger.isVerbose) {
|
if (logger.isVerbose) {
|
||||||
logger.info("Processing java sources with annotation processors: ${javaSourceFiles.joinToString()}")
|
logger.info("Processing java sources with annotation processors: ${javaSourceFiles.joinToString()}")
|
||||||
|
logger.info("Processing types with annotation processors: ${binaryTypesToReprocess.joinToString()}")
|
||||||
}
|
}
|
||||||
val parsedJavaFiles = parseJavaFiles(javaSourceFiles)
|
val parsedJavaFiles = parseJavaFiles(javaSourceFiles)
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ fun KaptContext.doAnnotationProcessing(
|
|||||||
CompileState.PARSE, compiler.enterTrees(parsedJavaFiles + additionalSources)
|
CompileState.PARSE, compiler.enterTrees(parsedJavaFiles + additionalSources)
|
||||||
)
|
)
|
||||||
|
|
||||||
val additionalClassNames = JavacList.from(aggregatedTypes)
|
val additionalClassNames = JavacList.from(binaryTypesToReprocess)
|
||||||
if (isJava9OrLater()) {
|
if (isJava9OrLater()) {
|
||||||
val processAnnotationsMethod =
|
val processAnnotationsMethod =
|
||||||
compiler.javaClass.getMethod("processAnnotations", JavacList::class.java, java.util.Collection::class.java)
|
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)
|
javaCache.invalidateDataForTypes(impactedTypes)
|
||||||
aptCache.invalidateAggregating()
|
aptCache.invalidateAggregating()
|
||||||
// for isolating, invalidate both own types and classpath types
|
// for isolating, invalidate both own types and classpath types
|
||||||
aptCache.invalidateIsolatingForOriginTypes(impactedTypes)
|
aptCache.invalidateIsolatingForOriginTypes(impactedTypes + dirtyClasspathFqNames)
|
||||||
aptCache.invalidateIsolatingForOriginTypes(dirtyClasspathFqNames)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return SourcesToReprocess.Incremental(sourcesToReprocess.toList(), impactedTypes, classNamesToReprocess)
|
return SourcesToReprocess.Incremental(sourcesToReprocess.toList(), impactedTypes, classNamesToReprocess)
|
||||||
|
|||||||
-11
@@ -31,17 +31,6 @@ class JavaClassCache() : Serializable {
|
|||||||
sourceCache[sourceStructure.sourceFile] = sourceStructure
|
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. */
|
/** Returns all types defined in these files. */
|
||||||
fun getTypesForFiles(files: Collection<File>): Set<String> {
|
fun getTypesForFiles(files: Collection<File>): Set<String> {
|
||||||
val typesFromFiles = HashSet<String>(files.size)
|
val typesFromFiles = HashSet<String>(files.size)
|
||||||
|
|||||||
Reference in New Issue
Block a user