Proper support of aggregated processors
This commit is contained in:
committed by
max-kammerer
parent
f382a55b17
commit
6748560184
+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 javax.tools.*;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* Simple processor that generates resource file that contains names of annotated elements.
|
||||
*/
|
||||
public class IncrementalAggregatingProcessor extends AbstractProcessor {
|
||||
|
||||
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().createResource(StandardLocation.CLASS_OUTPUT, "", "generated.txt").openWriter()) {
|
||||
for (String value : values) {
|
||||
writer.append(value).append("\n");
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
values.clear();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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 org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
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.OutputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
|
||||
import static org.objectweb.asm.Opcodes.ACC_SUPER;
|
||||
|
||||
/** Simple processor that generates a class for every annotated element (class, field, method). */
|
||||
public class IncrementalBinaryIsolatingProcessor extends AbstractProcessor {
|
||||
|
||||
@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";
|
||||
System.out.println("kapt: IncrementalBinaryIsolatingProcessor " + name);
|
||||
String packageName;
|
||||
if (element instanceof TypeElement) {
|
||||
packageName = element.getEnclosingElement().getSimpleName().toString();
|
||||
}
|
||||
else {
|
||||
packageName = element.getEnclosingElement().getEnclosingElement().getSimpleName().toString();
|
||||
}
|
||||
|
||||
String generatedClassName = packageName + "." + name;
|
||||
try (OutputStream stream = processingEnv.getFiler().createClassFile(generatedClassName, element).openOutputStream()) {
|
||||
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||
writer.visit(Opcodes.V1_8,
|
||||
ACC_PUBLIC | ACC_SUPER,
|
||||
generatedClassName.replaceAll("\\.", "/"),
|
||||
null,
|
||||
"java/lang/Object",
|
||||
null);
|
||||
|
||||
writer.visitAnnotation(Type.getObjectType("example/KotlinFilerGenerated").getDescriptor(), true);
|
||||
writer.visitEnd();
|
||||
stream.write(writer.toByteArray());
|
||||
}
|
||||
catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/** Simple processor that generates a class for every annotated element (class, field, method). */
|
||||
public class IncrementalIsolatingProcessor extends AbstractProcessor {
|
||||
|
||||
@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("\n@example.KotlinFilerGenerated").append("\n");
|
||||
writer.append("\npublic class ").append(name).append(" {}");
|
||||
}
|
||||
catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+164
-3
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.gradle
|
||||
|
||||
import org.jetbrains.kotlin.gradle.incapt.*
|
||||
import org.jetbrains.kotlin.gradle.util.modify
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
@@ -34,6 +35,11 @@ class KaptIncrementalWithAggregatingApt : KaptIncrementalIT() {
|
||||
)
|
||||
)
|
||||
|
||||
private fun jdk9Options(javaHome: File): BuildOptions {
|
||||
Assume.assumeTrue("JDK 9 isn't available", javaHome.isDirectory)
|
||||
return defaultBuildOptions().copy(javaHome = javaHome)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIncrementalChanges() {
|
||||
val project = getProject()
|
||||
@@ -80,8 +86,7 @@ class KaptIncrementalWithAggregatingApt : KaptIncrementalIT() {
|
||||
@Test
|
||||
fun testIncrementalChangesWithJdk9() {
|
||||
val javaHome = File(System.getProperty("jdk9Home")!!)
|
||||
Assume.assumeTrue("JDK 9 isn't available", javaHome.isDirectory)
|
||||
val options = defaultBuildOptions().copy(javaHome = javaHome)
|
||||
val options = jdk9Options(javaHome)
|
||||
val project = getProject()
|
||||
|
||||
project.build("clean", "build", options = options) {
|
||||
@@ -111,7 +116,7 @@ class KaptIncrementalWithAggregatingApt : KaptIncrementalIT() {
|
||||
GradleVersionRequired.None
|
||||
).apply {
|
||||
setupWorkingDir()
|
||||
val processorPath = generateProcessor("AGGREGATING")
|
||||
val processorPath = generateProcessor("AGGREGATING" to IncrementalProcessor::class.java)
|
||||
|
||||
projectDir.resolve("app/build.gradle").appendText(
|
||||
"""
|
||||
@@ -202,4 +207,160 @@ class KaptIncrementalWithAggregatingApt : KaptIncrementalIT() {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIncrementalAggregatingChanges() {
|
||||
doIncrementalAggregatingChanges()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIncrementalAggregatingChangesWithJdk9() {
|
||||
val javaHome = File(System.getProperty("jdk9Home")!!)
|
||||
doIncrementalAggregatingChanges(buildOptions = jdk9Options(javaHome))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIncrementalBinaryAggregatingChanges() {
|
||||
doIncrementalAggregatingChanges(true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIncrementalBinaryAggregatingChangesWithJdk9() {
|
||||
val javaHome = File(System.getProperty("jdk9Home")!!)
|
||||
val options = jdk9Options(javaHome)
|
||||
doIncrementalAggregatingChanges(true, options)
|
||||
}
|
||||
|
||||
private fun CompiledProject.checkAggregatingResource(check: (List<String>) -> Unit) {
|
||||
val aggregatingResource = "build/tmp/kapt3/classes/main/generated.txt"
|
||||
assertFileExists(aggregatingResource)
|
||||
val lines = fileInWorkingDir(aggregatingResource).readLines()
|
||||
check(lines)
|
||||
}
|
||||
|
||||
fun doIncrementalAggregatingChanges(isBinary: Boolean = false, buildOptions: BuildOptions = defaultBuildOptions()) {
|
||||
val project = Project(
|
||||
"kaptIncrementalAggregatingProcessorProject",
|
||||
GradleVersionRequired.None
|
||||
).apply {
|
||||
setupIncrementalAptProject(
|
||||
"ISOLATING" to if (isBinary) IncrementalBinaryIsolatingProcessor::class.java else IncrementalIsolatingProcessor::class.java,
|
||||
"AGGREGATING" to IncrementalAggregatingProcessor::class.java
|
||||
)
|
||||
}
|
||||
|
||||
project.build("clean", "build", options = buildOptions) {
|
||||
assertSuccessful()
|
||||
|
||||
assertEquals(
|
||||
setOf(
|
||||
fileInWorkingDir("build/tmp/kapt3/stubs/main/bar/WithAnnotation.java").canonicalPath,
|
||||
fileInWorkingDir("build/tmp/kapt3/stubs/main/bar/noAnnotations.java").canonicalPath,
|
||||
fileInWorkingDir("build/tmp/kapt3/stubs/main/error/NonExistentClass.java").canonicalPath
|
||||
), getProcessedSources(output)
|
||||
)
|
||||
|
||||
checkAggregatingResource { lines ->
|
||||
assertEquals(1, lines.size)
|
||||
assertTrue(lines.contains("WithAnnotationGenerated"))
|
||||
}
|
||||
}
|
||||
|
||||
//change file without annotations
|
||||
project.projectFile("noAnnotations.kt").modify { current -> "$current\nfun otherFunction() {}" }
|
||||
|
||||
project.build("build", options = buildOptions) {
|
||||
assertSuccessful()
|
||||
|
||||
assertEquals(
|
||||
setOf(
|
||||
fileInWorkingDir("build/tmp/kapt3/stubs/main/bar/NoAnnotationsKt.java").canonicalPath,
|
||||
fileInWorkingDir("build/tmp/kapt3/stubs/main/error/NonExistentClass.java").canonicalPath
|
||||
), getProcessedSources(output)
|
||||
)
|
||||
|
||||
checkAggregatingResource { lines ->
|
||||
assertEquals(1, lines.size)
|
||||
assertTrue(lines.contains("WithAnnotationGenerated"))
|
||||
}
|
||||
}
|
||||
|
||||
//add new file with annotations
|
||||
val newFile = File(project.projectDir, "src/main/java/baz/BazClass.kt")
|
||||
newFile.parentFile.mkdirs()
|
||||
newFile.createNewFile()
|
||||
project.projectFile("BazClass.kt").modify {
|
||||
"""
|
||||
package baz
|
||||
|
||||
@example.ExampleAnnotation
|
||||
class BazClass() {}
|
||||
""".trimIndent()
|
||||
}
|
||||
project.build("build", options = buildOptions) {
|
||||
assertSuccessful()
|
||||
assertEquals(
|
||||
setOf(
|
||||
fileInWorkingDir("build/tmp/kapt3/stubs/main/baz/BazClass.java").canonicalPath,
|
||||
fileInWorkingDir("build/tmp/kapt3/stubs/main/error/NonExistentClass.java").canonicalPath
|
||||
),
|
||||
getProcessedSources(output)
|
||||
)
|
||||
|
||||
checkAggregatingResource { lines ->
|
||||
assertEquals(2, lines.size)
|
||||
assertTrue(lines.contains("WithAnnotationGenerated"))
|
||||
assertTrue(lines.contains("BazClassGenerated"))
|
||||
}
|
||||
}
|
||||
|
||||
//move annotation to nested class
|
||||
project.projectFile("BazClass.kt").modify {
|
||||
"""
|
||||
package baz
|
||||
|
||||
class BazClass() {
|
||||
@example.ExampleAnnotation
|
||||
class BazNested {}
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
||||
project.build("build", options = buildOptions) {
|
||||
assertSuccessful()
|
||||
assertEquals(
|
||||
setOf(
|
||||
fileInWorkingDir("build/tmp/kapt3/stubs/main/baz/BazClass.java").canonicalPath,
|
||||
fileInWorkingDir("build/tmp/kapt3/stubs/main/error/NonExistentClass.java").canonicalPath
|
||||
),
|
||||
getProcessedSources(output)
|
||||
)
|
||||
|
||||
checkAggregatingResource { lines ->
|
||||
assertEquals(2, lines.size)
|
||||
assertTrue(lines.contains("WithAnnotationGenerated"))
|
||||
assertTrue(lines.contains("BazNestedGenerated"))
|
||||
}
|
||||
}
|
||||
|
||||
//change file without annotations to check that nested class is aggregated
|
||||
project.projectFile("noAnnotations.kt").modify { current -> "$current\nfun otherFunction2() {}" }
|
||||
|
||||
project.build("build", options = buildOptions) {
|
||||
assertSuccessful()
|
||||
|
||||
assertEquals(
|
||||
setOf(
|
||||
fileInWorkingDir("build/tmp/kapt3/stubs/main/bar/NoAnnotationsKt.java").canonicalPath,
|
||||
fileInWorkingDir("build/tmp/kapt3/stubs/main/error/NonExistentClass.java").canonicalPath
|
||||
), getProcessedSources(output)
|
||||
)
|
||||
|
||||
checkAggregatingResource { lines ->
|
||||
assertEquals(2, lines.size)
|
||||
assertTrue(lines.contains("WithAnnotationGenerated"))
|
||||
assertTrue(lines.contains("BazNestedGenerated"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+28
-10
@@ -201,10 +201,21 @@ fun getProcessedSources(output: String): Set<String> {
|
||||
return logging.drop(indexOf).split(",").map { it.trim() }.filter { !it.isEmpty() }.toSet()
|
||||
}
|
||||
|
||||
fun BaseGradleIT.Project.setupIncrementalAptProject(procType: String, buildFile: File = projectDir.resolve("build.gradle")) {
|
||||
fun BaseGradleIT.Project.setupIncrementalAptProject(
|
||||
procType: String,
|
||||
buildFile: File = projectDir.resolve("build.gradle"),
|
||||
procClass: Class<*> = IncrementalProcessor::class.java
|
||||
) {
|
||||
setupIncrementalAptProject(procType to procClass, buildFile = buildFile)
|
||||
}
|
||||
|
||||
fun BaseGradleIT.Project.setupIncrementalAptProject(
|
||||
vararg processors: Pair<String, Class<*>>,
|
||||
buildFile: File = projectDir.resolve("build.gradle")
|
||||
) {
|
||||
setupWorkingDir()
|
||||
val content = buildFile.readText()
|
||||
val processorPath = generateProcessor(procType)
|
||||
val processorPath = generateProcessor(*processors)
|
||||
|
||||
val updatedContent = content.replace(
|
||||
Regex("^\\s*kapt\\s\"org\\.jetbrain.*$", RegexOption.MULTILINE),
|
||||
@@ -213,20 +224,27 @@ fun BaseGradleIT.Project.setupIncrementalAptProject(procType: String, buildFile:
|
||||
buildFile.writeText(updatedContent)
|
||||
}
|
||||
|
||||
fun BaseGradleIT.Project.generateProcessor(procType: String): File {
|
||||
fun BaseGradleIT.Project.generateProcessor(vararg processors: Pair<String, Class<*>>): File {
|
||||
val processorPath = projectDir.resolve("incrementalProcessor.jar")
|
||||
|
||||
ZipOutputStream(processorPath.outputStream()).use {
|
||||
val path = IncrementalProcessor::class.java.name.replace(".", "/") + ".class"
|
||||
val inputStream = IncrementalProcessor::class.java.classLoader.getResourceAsStream(path)
|
||||
it.putNextEntry(ZipEntry(path))
|
||||
it.write(inputStream.readBytes())
|
||||
it.closeEntry()
|
||||
for ((_, procClass) in processors) {
|
||||
val path = procClass.name.replace(".", "/") + ".class"
|
||||
procClass.classLoader.getResourceAsStream(path).use { inputStream ->
|
||||
it.putNextEntry(ZipEntry(path))
|
||||
it.write(inputStream.readBytes())
|
||||
it.closeEntry()
|
||||
}
|
||||
}
|
||||
it.putNextEntry(ZipEntry("META-INF/gradle/incremental.annotation.processors"))
|
||||
it.write("${IncrementalProcessor::class.java.name},$procType".toByteArray())
|
||||
it.write(processors.joinToString("\n") { (procType, procClass) ->
|
||||
"${procClass.name},$procType"
|
||||
}.toByteArray())
|
||||
it.closeEntry()
|
||||
it.putNextEntry(ZipEntry("META-INF/services/javax.annotation.processing.Processor"))
|
||||
it.write(IncrementalProcessor::class.java.name.toByteArray())
|
||||
it.write(processors.joinToString("\n") { (_, procClass) ->
|
||||
procClass.name
|
||||
}.toByteArray())
|
||||
it.closeEntry()
|
||||
}
|
||||
return processorPath
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: "java"
|
||||
apply plugin: "kotlin"
|
||||
apply plugin: "kotlin-kapt"
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenLocal()
|
||||
}
|
||||
|
||||
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.ow2.asm:asm:9.0"
|
||||
testImplementation 'junit:junit:4.12'
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package bar
|
||||
|
||||
class noAnnotations {
|
||||
val valB = "text"
|
||||
|
||||
fun funB() {}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package bar
|
||||
|
||||
@example.ExampleAnnotation
|
||||
class WithAnnotation() {}
|
||||
@@ -42,7 +42,11 @@ object Kapt {
|
||||
val processors = processorLoader.loadProcessors(findClassLoaderWithJavac())
|
||||
|
||||
val annotationProcessingTime = measureTimeMillis {
|
||||
kaptContext.doAnnotationProcessing(javaSourceFiles, processors.processors)
|
||||
kaptContext.doAnnotationProcessing(
|
||||
javaSourceFiles,
|
||||
processors.processors,
|
||||
aggregatedTypes = collectAggregatedTypes(kaptContext.sourcesToReprocess)
|
||||
)
|
||||
}
|
||||
|
||||
logger.info { "Annotation processing took $annotationProcessingTime ms" }
|
||||
|
||||
@@ -111,7 +111,7 @@ open class KaptContext(val options: KaptOptions, val withJdk: Boolean, val logge
|
||||
val compileClasspath = if (sourcesToReprocess is SourcesToReprocess.FullRebuild) {
|
||||
options.compileClasspath
|
||||
} else {
|
||||
options.compileClasspath + options.compiledSources
|
||||
options.compileClasspath + options.compiledSources + options.classesOutputDir
|
||||
}
|
||||
|
||||
putJavacOption("CLASSPATH", "CLASS_PATH",
|
||||
|
||||
@@ -168,6 +168,15 @@ fun KaptOptions.collectJavaSourceFiles(sourcesToReprocess: SourcesToReprocess =
|
||||
}
|
||||
}
|
||||
|
||||
fun collectAggregatedTypes(sourcesToReprocess: SourcesToReprocess = SourcesToReprocess.FullRebuild): List<String> {
|
||||
return when (sourcesToReprocess) {
|
||||
is SourcesToReprocess.FullRebuild -> emptyList()
|
||||
is SourcesToReprocess.Incremental -> {
|
||||
sourcesToReprocess.unchangedAggregatedTypes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun KaptOptions.logString(additionalInfo: String = "") = buildString {
|
||||
val additionalInfoRendered = if (additionalInfo.isEmpty()) "" else " ($additionalInfo)"
|
||||
appendLine("Kapt3 is enabled$additionalInfoRendered.")
|
||||
|
||||
+8
-5
@@ -31,7 +31,8 @@ import com.sun.tools.javac.util.List as JavacList
|
||||
fun KaptContext.doAnnotationProcessing(
|
||||
javaSourceFiles: List<File>,
|
||||
processors: List<IncrementalProcessor>,
|
||||
additionalSources: JavacList<JCTree.JCCompilationUnit> = JavacList.nil()
|
||||
additionalSources: JavacList<JCTree.JCCompilationUnit> = JavacList.nil(),
|
||||
aggregatedTypes: List<String> = emptyList()
|
||||
) {
|
||||
val processingEnvironment = JavacProcessingEnvironment.instance(context)
|
||||
|
||||
@@ -70,12 +71,14 @@ fun KaptContext.doAnnotationProcessing(
|
||||
GeneratedTypesTaskListener(cacheManager!!.javaCache)
|
||||
}?.also { compiler.getTaskListeners().add(it) }
|
||||
|
||||
val additionalClassNames = JavacList.from(aggregatedTypes)
|
||||
if (isJava9OrLater()) {
|
||||
val processAnnotationsMethod = compiler.javaClass.getMethod("processAnnotations", JavacList::class.java)
|
||||
processAnnotationsMethod.invoke(compiler, analyzedFiles)
|
||||
val processAnnotationsMethod =
|
||||
compiler.javaClass.getMethod("processAnnotations", JavacList::class.java, java.util.Collection::class.java)
|
||||
processAnnotationsMethod.invoke(compiler, analyzedFiles, additionalClassNames)
|
||||
compiler
|
||||
} else {
|
||||
compiler.processAnnotations(analyzedFiles).also {
|
||||
compiler.processAnnotations(analyzedFiles, additionalClassNames).also {
|
||||
generatedSourcesListener?.let { compiler.getTaskListeners().remove(it) }
|
||||
}
|
||||
}
|
||||
@@ -156,7 +159,7 @@ private class ProcessorWrapper(private val delegate: IncrementalProcessor) : Pro
|
||||
private var initTime: Long = 0
|
||||
private val roundTime = mutableListOf<Long>()
|
||||
|
||||
override fun process(annotations: MutableSet<out TypeElement>?, roundEnv: RoundEnvironment?): Boolean {
|
||||
override fun process(annotations: MutableSet<out TypeElement>, roundEnv: RoundEnvironment): Boolean {
|
||||
val (time, result) = measureTimeMillisWithResult {
|
||||
delegate.process(annotations, roundEnv)
|
||||
}
|
||||
|
||||
+9
-2
@@ -11,6 +11,7 @@ import java.io.Serializable
|
||||
class IncrementalAptCache : Serializable {
|
||||
|
||||
private val aggregatingGenerated: MutableSet<File> = mutableSetOf()
|
||||
private val aggregatedTypes: MutableSet<String> = linkedSetOf()
|
||||
private val isolatingMapping: MutableMap<File, File> = mutableMapOf()
|
||||
// Annotations claimed by aggregating annotation processors
|
||||
private val aggregatingClaimedAnnotations: MutableSet<String> = mutableSetOf()
|
||||
@@ -41,6 +42,9 @@ class IncrementalAptCache : Serializable {
|
||||
aggregatingClaimedAnnotations.clear()
|
||||
aggregatingClaimedAnnotations.addAll(aggregating.flatMap { it.supportedAnnotationTypes })
|
||||
|
||||
aggregatedTypes.clear()
|
||||
aggregatedTypes.addAll(aggregating.flatMap { it.getAggregatedTypes() })
|
||||
|
||||
for (isolatingProcessor in isolating) {
|
||||
isolatingProcessor.getGeneratedToSources().forEach {
|
||||
isolatingMapping[it.key] = it.value!!
|
||||
@@ -52,12 +56,15 @@ class IncrementalAptCache : Serializable {
|
||||
fun getAggregatingClaimedAnnotations(): Set<String> = aggregatingClaimedAnnotations
|
||||
|
||||
/** Returns generated Java sources originating from aggregating APs. */
|
||||
fun invalidateAggregating(): List<File> {
|
||||
fun invalidateAggregating(): Pair<List<File>, List<String>> {
|
||||
val dirtyAggregating = aggregatingGenerated.filter { it.extension == "java" }
|
||||
aggregatingGenerated.forEach { it.delete() }
|
||||
aggregatingGenerated.clear()
|
||||
|
||||
return dirtyAggregating
|
||||
val dirtyAggregated = ArrayList(aggregatedTypes)
|
||||
aggregatedTypes.clear()
|
||||
|
||||
return dirtyAggregating to dirtyAggregated
|
||||
}
|
||||
|
||||
/** Returns generated Java sources originating from the specified sources, and generated by isloating APs. */
|
||||
|
||||
@@ -48,10 +48,11 @@ class JavaClassCacheManager(val file: File) : Closeable {
|
||||
|
||||
val isolatingGenerated = aptCache.invalidateIsolatingGenerated(toReprocess)
|
||||
val generatedDirtyTypes = javaCache.invalidateGeneratedTypes(isolatingGenerated).toMutableSet()
|
||||
|
||||
val aggregatedTypes = mutableListOf<String>()
|
||||
if (!toReprocess.isEmpty()) {
|
||||
// only if there are some files to reprocess we should invalidate the aggregating ones
|
||||
val aggregatingGenerated = aptCache.invalidateAggregating()
|
||||
val (aggregatingGenerated, aggregatedTypes1) = aptCache.invalidateAggregating()
|
||||
aggregatedTypes.addAll(aggregatedTypes1)
|
||||
generatedDirtyTypes.addAll(javaCache.invalidateGeneratedTypes(aggregatingGenerated))
|
||||
|
||||
toReprocess.addAll(
|
||||
@@ -59,7 +60,13 @@ class JavaClassCacheManager(val file: File) : Closeable {
|
||||
)
|
||||
}
|
||||
|
||||
SourcesToReprocess.Incremental(toReprocess.toList(), generatedDirtyTypes)
|
||||
SourcesToReprocess.Incremental(
|
||||
toReprocess.toList(),
|
||||
generatedDirtyTypes,
|
||||
aggregatedTypes.also {
|
||||
it.removeAll(filesToReprocess.dirtyTypes)
|
||||
it.removeAll(generatedDirtyTypes)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,6 +125,11 @@ class JavaClassCacheManager(val file: File) : Closeable {
|
||||
}
|
||||
|
||||
sealed class SourcesToReprocess {
|
||||
class Incremental(val toReprocess: List<File>, val dirtyTypes: Set<String>) : SourcesToReprocess()
|
||||
class Incremental(
|
||||
val toReprocess: List<File>,
|
||||
val dirtyTypes: Set<String>,
|
||||
val unchangedAggregatedTypes: List<String>
|
||||
) : SourcesToReprocess()
|
||||
|
||||
object FullRebuild : SourcesToReprocess()
|
||||
}
|
||||
+1
-1
@@ -139,7 +139,7 @@ class JavaClassCache() : Serializable {
|
||||
currentDirtyFiles = nextRound.filter { !allDirtyFiles.contains(it) }.toMutableSet()
|
||||
}
|
||||
|
||||
return SourcesToReprocess.Incremental(allDirtyFiles.map { File(it) }, allDirtyTypes)
|
||||
return SourcesToReprocess.Incremental(allDirtyFiles.map { File(it) }, allDirtyTypes, emptyList())
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+62
-1
@@ -12,8 +12,10 @@ import java.net.URI
|
||||
import javax.annotation.processing.Filer
|
||||
import javax.annotation.processing.ProcessingEnvironment
|
||||
import javax.annotation.processing.Processor
|
||||
import javax.annotation.processing.RoundEnvironment
|
||||
import javax.lang.model.element.Element
|
||||
import javax.lang.model.element.PackageElement
|
||||
import javax.lang.model.element.TypeElement
|
||||
import javax.tools.FileObject
|
||||
import javax.tools.JavaFileManager
|
||||
import javax.tools.JavaFileObject
|
||||
@@ -69,7 +71,16 @@ class IncrementalProcessor(private val processor: Processor, private val kind: D
|
||||
|
||||
fun isUnableToRunIncrementally() = !kind.canRunIncrementally
|
||||
fun getGeneratedToSources() = dependencyCollector.value.getGeneratedToSources()
|
||||
fun getAggregatedTypes() = dependencyCollector.value.getAggregatedTypes()
|
||||
fun getRuntimeType(): RuntimeProcType = dependencyCollector.value.getRuntimeType()
|
||||
|
||||
|
||||
override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
|
||||
if (getRuntimeType() == RuntimeProcType.AGGREGATING) {
|
||||
dependencyCollector.value.recordProcessingInputs(processor.supportedAnnotationTypes, annotations, roundEnv)
|
||||
}
|
||||
return processor.process(annotations, roundEnv)
|
||||
}
|
||||
}
|
||||
|
||||
internal class IncrementalProcessingEnvironment(private val processingEnv: ProcessingEnvironment, private val incFiler: IncrementalFiler) :
|
||||
@@ -111,9 +122,29 @@ internal class AnnotationProcessorDependencyCollector(
|
||||
private val warningCollector: (String) -> Unit
|
||||
) {
|
||||
private val generatedToSource = mutableMapOf<File, File?>()
|
||||
private val aggregatedTypes = mutableSetOf<String>()
|
||||
|
||||
private var isFullRebuild = !runtimeProcType.isIncremental
|
||||
|
||||
internal fun add(createdFile: URI, originatingElements: Array<out Element?>) {
|
||||
internal fun recordProcessingInputs(supportedAnnotationTypes: Set<String>, annotations: Set<TypeElement>, roundEnv: RoundEnvironment) {
|
||||
if (isFullRebuild) return
|
||||
|
||||
if (supportedAnnotationTypes.contains("*")) {
|
||||
aggregatedTypes.addAll(getTopLevelClassNames(roundEnv.rootElements?.filterNotNull() ?: emptyList()))
|
||||
} else {
|
||||
for (annotation in annotations) {
|
||||
aggregatedTypes.addAll(
|
||||
getTopLevelClassNames(
|
||||
roundEnv.getElementsAnnotatedWith(
|
||||
annotation
|
||||
)?.filterNotNull() ?: emptyList()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun add(createdFile: URI, originatingElements: Array<out Element?>, classId: String?) {
|
||||
if (isFullRebuild) return
|
||||
|
||||
val generatedFile = File(createdFile)
|
||||
@@ -134,6 +165,9 @@ internal class AnnotationProcessorDependencyCollector(
|
||||
}
|
||||
|
||||
internal fun getGeneratedToSources(): Map<File, File?> = if (isFullRebuild) emptyMap() else generatedToSource
|
||||
|
||||
internal fun getAggregatedTypes(): Set<String> = if (isFullRebuild) emptySet() else aggregatedTypes
|
||||
|
||||
internal fun getRuntimeType(): RuntimeProcType {
|
||||
return if (isFullRebuild) {
|
||||
RuntimeProcType.NON_INCREMENTAL
|
||||
@@ -154,6 +188,33 @@ private fun getSrcFiles(elements: Array<out Element?>): Set<File> {
|
||||
}.toSet()
|
||||
}
|
||||
|
||||
private const val PACKAGE_TYPE_NAME = "package-info"
|
||||
|
||||
fun getElementName(current: Element?): String? {
|
||||
if (current is PackageElement) {
|
||||
val packageName = current.qualifiedName.toString()
|
||||
return if (packageName.isEmpty()) {
|
||||
PACKAGE_TYPE_NAME
|
||||
} else {
|
||||
"$packageName.$PACKAGE_TYPE_NAME"
|
||||
}
|
||||
}
|
||||
if (current is TypeElement) {
|
||||
return current.qualifiedName.toString()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun getTopLevelClassNames(elements: Collection<Element>): Collection<String> {
|
||||
return elements.mapNotNull { elem ->
|
||||
var origin = elem
|
||||
while (origin.enclosingElement != null && origin.enclosingElement !is PackageElement) {
|
||||
origin = origin.enclosingElement
|
||||
}
|
||||
getElementName(origin)
|
||||
}
|
||||
}
|
||||
|
||||
enum class DeclaredProcType(val canRunIncrementally: Boolean) {
|
||||
AGGREGATING(true) {
|
||||
override fun toRuntimeType() = RuntimeProcType.AGGREGATING
|
||||
|
||||
Reference in New Issue
Block a user