Incremental annotation processing with KAPT
Add support for incremental annotation processors in KAPT. These processors conform to https://docs.gradle.org/current/userguide/java_plugin.html#sec:incremental_annotation_processing specification. Support is provided by using javac compiler APIs and recording the source file structure. At runtime, processors are instrumented with custom Filer that is used to keep track of generated files. In order to support classpath changes, stub generation task is used to generated a list of changed FQCNs, and this is simply used by KAPT. Both worker and non-worker mode are supported. #KT-23880
This commit is contained in:
committed by
Alexey Tsvetkov
parent
600a955a51
commit
9f14daa682
@@ -11,6 +11,8 @@ import org.jetbrains.kotlin.base.kapt3.KaptFlag
|
||||
import org.jetbrains.kotlin.base.kapt3.KaptOptions
|
||||
import org.jetbrains.kotlin.kapt3.base.KaptContext
|
||||
import org.jetbrains.kotlin.kapt3.base.doAnnotationProcessing
|
||||
import org.jetbrains.kotlin.kapt3.base.incremental.DeclaredProcType
|
||||
import org.jetbrains.kotlin.kapt3.base.incremental.IncrementalProcessor
|
||||
import org.jetbrains.kotlin.kapt3.base.util.KaptBaseError
|
||||
import org.jetbrains.kotlin.kapt3.base.util.WriterBackedKaptLogger
|
||||
import org.junit.Test
|
||||
@@ -25,32 +27,36 @@ class JavaKaptContextTest : TestCase() {
|
||||
companion object {
|
||||
private val TEST_DATA_DIR = File("plugins/kapt3/kapt3-base/testData/runner")
|
||||
|
||||
fun simpleProcessor() = object : AbstractProcessor() {
|
||||
override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
|
||||
for (annotation in annotations) {
|
||||
val annotationName = annotation.simpleName.toString()
|
||||
val annotatedElements = roundEnv.getElementsAnnotatedWith(annotation)
|
||||
fun simpleProcessor() = IncrementalProcessor(
|
||||
object : AbstractProcessor() {
|
||||
override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
|
||||
for (annotation in annotations) {
|
||||
val annotationName = annotation.simpleName.toString()
|
||||
val annotatedElements = roundEnv.getElementsAnnotatedWith(annotation)
|
||||
|
||||
for (annotatedElement in annotatedElements) {
|
||||
val generatedClassName = annotatedElement.simpleName.toString().capitalize() + annotationName.capitalize()
|
||||
val file = processingEnv.filer.createSourceFile("generated." + generatedClassName)
|
||||
file.openWriter().use {
|
||||
it.write("""
|
||||
for (annotatedElement in annotatedElements) {
|
||||
val generatedClassName = annotatedElement.simpleName.toString().capitalize() + annotationName.capitalize()
|
||||
val file = processingEnv.filer.createSourceFile("generated." + generatedClassName)
|
||||
file.openWriter().use {
|
||||
it.write(
|
||||
"""
|
||||
package generated;
|
||||
class $generatedClassName {}
|
||||
""".trimIndent())
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getSupportedAnnotationTypes() = setOf("test.MyAnnotation")
|
||||
}
|
||||
override fun getSupportedAnnotationTypes() = setOf("test.MyAnnotation")
|
||||
}, DeclaredProcType.NON_INCREMENTAL
|
||||
)
|
||||
}
|
||||
|
||||
private fun doAnnotationProcessing(javaSourceFile: File, processor: Processor, outputDir: File) {
|
||||
private fun doAnnotationProcessing(javaSourceFile: File, processor: IncrementalProcessor, outputDir: File) {
|
||||
val options = KaptOptions.Builder().apply {
|
||||
projectBaseDir = javaSourceFile.parentFile
|
||||
|
||||
@@ -93,7 +99,7 @@ class JavaKaptContextTest : TestCase() {
|
||||
}
|
||||
|
||||
try {
|
||||
doAnnotationProcessing(File(TEST_DATA_DIR, "Simple.java"), processor, TEST_DATA_DIR)
|
||||
doAnnotationProcessing(File(TEST_DATA_DIR, "Simple.java"), IncrementalProcessor(processor, DeclaredProcType.NON_INCREMENTAL), TEST_DATA_DIR)
|
||||
} catch (e: KaptBaseError) {
|
||||
assertEquals(KaptBaseError.Kind.EXCEPTION, e.kind)
|
||||
assertEquals("Here we are!", e.cause!!.message)
|
||||
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. 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.kapt.base.test.org.jetbrains.kotlin.kapt3.base.incremental
|
||||
|
||||
import org.jetbrains.kotlin.kapt3.base.incremental.RuntimeProcType
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import java.io.File
|
||||
|
||||
class AggregatingIncrementalProcessorTest {
|
||||
@JvmField
|
||||
@Rule
|
||||
val tmp: TemporaryFolder = TemporaryFolder()
|
||||
|
||||
private lateinit var generatedSources: File
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
generatedSources = tmp.newFolder()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDependenciesRecorded() {
|
||||
val srcFiles = listOf("User.java", "Address.java", "Observable.java").map { File(TEST_DATA_DIR, it) }
|
||||
val aggregating = SimpleProcessor().toAggregating()
|
||||
runAnnotationProcessing(srcFiles, listOf(aggregating), generatedSources)
|
||||
|
||||
assertEquals(RuntimeProcType.AGGREGATING, aggregating.getRuntimeType())
|
||||
|
||||
assertEquals(
|
||||
mapOf(
|
||||
generatedSources.resolve("test/UserGenerated.java") to null,
|
||||
generatedSources.resolve("test/AddressGenerated.java") to null
|
||||
),
|
||||
aggregating.getGeneratedToSources()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNoSourcesToProcess() {
|
||||
val srcFiles = listOf("Observable.java").map { File(TEST_DATA_DIR, it) }
|
||||
val aggregating = SimpleProcessor().toAggregating()
|
||||
runAnnotationProcessing(srcFiles, listOf(aggregating), generatedSources)
|
||||
|
||||
assertEquals(RuntimeProcType.AGGREGATING, aggregating.getRuntimeType())
|
||||
assertEquals(emptyMap<File, File>(), aggregating.getGeneratedToSources())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGeneratingSourcesClassesResources() {
|
||||
val srcFiles = listOf("User.java", "Address.java", "Observable.java").map { File(TEST_DATA_DIR, it) }
|
||||
val aggregating = SimpleCreatingClassFilesAndResources().toAggregating()
|
||||
runAnnotationProcessing(srcFiles, listOf(aggregating), generatedSources)
|
||||
|
||||
assertEquals(RuntimeProcType.AGGREGATING, aggregating.getRuntimeType())
|
||||
|
||||
assertEquals(
|
||||
mapOf(
|
||||
generatedSources.resolve("test/UserGenerated.java") to null,
|
||||
generatedSources.resolve("test/UserGeneratedClass.class") to null,
|
||||
generatedSources.resolve("test/UserGeneratedResource") to null,
|
||||
generatedSources.resolve("test/AddressGenerated.java") to null,
|
||||
generatedSources.resolve("test/AddressGeneratedClass.class") to null,
|
||||
generatedSources.resolve("test/AddressGeneratedResource") to null
|
||||
),
|
||||
aggregating.getGeneratedToSources()
|
||||
)
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. 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.kapt.base.test.org.jetbrains.kotlin.kapt3.base.incremental
|
||||
|
||||
import org.jetbrains.kotlin.kapt3.base.incremental.AnnotationProcessorDependencyCollector
|
||||
import org.jetbrains.kotlin.kapt3.base.incremental.RuntimeProcType
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
|
||||
class AnnotationProcessorDependencyCollectorTest {
|
||||
@Test
|
||||
fun testAggregating() {
|
||||
val aggregating = AnnotationProcessorDependencyCollector(RuntimeProcType.AGGREGATING)
|
||||
val generated = listOf("GeneratedA.java", "GeneratedB.java", "GeneratedC.java").map { File(it).toURI() }
|
||||
generated.forEach { aggregating.add(it, emptyArray()) }
|
||||
|
||||
assertEquals(aggregating.getGeneratedToSources(), generated.map { File(it) to null }.toMap())
|
||||
assertEquals(aggregating.getRuntimeType(), RuntimeProcType.AGGREGATING)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIsolatingWithoutOrigin() {
|
||||
val isolating = AnnotationProcessorDependencyCollector(RuntimeProcType.ISOLATING)
|
||||
isolating.add(File("GeneratedA.java").toURI(), emptyArray())
|
||||
|
||||
assertEquals(isolating.getRuntimeType(), RuntimeProcType.NON_INCREMENTAL)
|
||||
assertEquals(isolating.getGeneratedToSources(), emptyMap<File, File?>())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNonIncremental() {
|
||||
val nonIncremental = AnnotationProcessorDependencyCollector(RuntimeProcType.NON_INCREMENTAL)
|
||||
nonIncremental.add(File("GeneratedA.java").toURI(), emptyArray())
|
||||
nonIncremental.add(File("GeneratedB.java").toURI(), emptyArray())
|
||||
|
||||
assertEquals(nonIncremental.getRuntimeType(), RuntimeProcType.NON_INCREMENTAL)
|
||||
assertEquals(nonIncremental.getGeneratedToSources(), emptyMap<File, File?>())
|
||||
}
|
||||
}
|
||||
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. 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.kapt.base.test.org.jetbrains.kotlin.kapt3.base.incremental
|
||||
|
||||
import org.jetbrains.kotlin.kapt3.base.incremental.RuntimeProcType
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import java.io.File
|
||||
|
||||
|
||||
class DynamicIncrementalProcessorTest {
|
||||
@JvmField
|
||||
@Rule
|
||||
val tmp: TemporaryFolder = TemporaryFolder()
|
||||
|
||||
private lateinit var generatedSources: File
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
generatedSources = tmp.newFolder()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIfIsolating() {
|
||||
val srcFiles = listOf("User.java", "Address.java", "Observable.java").map { File(TEST_DATA_DIR, it) }
|
||||
val dynamic = DynamicProcessor(kind = RuntimeProcType.ISOLATING).toDynamic()
|
||||
runAnnotationProcessing(srcFiles, listOf(dynamic), generatedSources)
|
||||
|
||||
assertEquals(RuntimeProcType.ISOLATING, dynamic.getRuntimeType())
|
||||
|
||||
assertEquals(
|
||||
mapOf(
|
||||
generatedSources.resolve("test/UserGenerated.java") to File("plugins/kapt3/kapt3-base/testData/runner/incremental/User.java").absoluteFile,
|
||||
generatedSources.resolve("test/AddressGenerated.java") to File("plugins/kapt3/kapt3-base/testData/runner/incremental/Address.java").absoluteFile
|
||||
),
|
||||
dynamic.getGeneratedToSources()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIfAggregating() {
|
||||
val srcFiles = listOf("User.java", "Address.java", "Observable.java").map { File(TEST_DATA_DIR, it) }
|
||||
val dynamic = DynamicProcessor(kind = RuntimeProcType.AGGREGATING).toDynamic()
|
||||
runAnnotationProcessing(srcFiles, listOf(dynamic), generatedSources)
|
||||
|
||||
assertEquals(RuntimeProcType.AGGREGATING, dynamic.getRuntimeType())
|
||||
|
||||
assertEquals(
|
||||
mapOf(
|
||||
generatedSources.resolve("test/UserGenerated.java") to null,
|
||||
generatedSources.resolve("test/AddressGenerated.java") to null
|
||||
),
|
||||
dynamic.getGeneratedToSources()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIfNonIncremental() {
|
||||
val srcFiles = listOf("User.java", "Address.java", "Observable.java").map { File(TEST_DATA_DIR, it) }
|
||||
val dynamic = DynamicProcessor(kind = RuntimeProcType.NON_INCREMENTAL).toDynamic()
|
||||
runAnnotationProcessing(srcFiles, listOf(dynamic), generatedSources)
|
||||
|
||||
assertEquals(RuntimeProcType.NON_INCREMENTAL, dynamic.getRuntimeType())
|
||||
assertEquals(
|
||||
emptyMap<File, File>(),
|
||||
dynamic.getGeneratedToSources()
|
||||
)
|
||||
}
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. 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.kapt.base.test.org.jetbrains.kotlin.kapt3.base.incremental
|
||||
|
||||
import org.jetbrains.kotlin.base.kapt3.KaptOptions
|
||||
import org.jetbrains.kotlin.base.kapt3.collectJavaSourceFiles
|
||||
import org.jetbrains.kotlin.kapt3.base.KaptContext
|
||||
import org.jetbrains.kotlin.kapt3.base.doAnnotationProcessing
|
||||
import org.jetbrains.kotlin.kapt3.base.util.WriterBackedKaptLogger
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
|
||||
class IncrementalKaptTest {
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
var tmp = TemporaryFolder()
|
||||
|
||||
@Test
|
||||
fun testIncrementalRun() {
|
||||
val sourcesDir = tmp.newFolder().resolve("test").also { base ->
|
||||
base.mkdir()
|
||||
listOf("User.java", "Address.java", "Observable.java").map {
|
||||
TEST_DATA_DIR.resolve(it).copyTo(base.resolve(it))
|
||||
}
|
||||
}
|
||||
|
||||
val outputDir = tmp.newFolder()
|
||||
val incrementalCacheDir = tmp.newFolder()
|
||||
val classpathHistory = tmp.newFolder().also {
|
||||
it.resolve("0").createNewFile()
|
||||
}
|
||||
val options = KaptOptions.Builder().apply {
|
||||
projectBaseDir = tmp.newFolder()
|
||||
javaSourceRoots.add(sourcesDir)
|
||||
|
||||
sourcesOutputDir = outputDir
|
||||
classesOutputDir = outputDir
|
||||
stubsOutputDir = outputDir
|
||||
incrementalDataOutputDir = outputDir
|
||||
|
||||
incrementalCache = incrementalCacheDir
|
||||
classpathFqNamesHistory = classpathHistory
|
||||
}.build()
|
||||
|
||||
val logger = WriterBackedKaptLogger(isVerbose = true)
|
||||
KaptContext(options, true, logger).use {
|
||||
it.doAnnotationProcessing(
|
||||
options.collectJavaSourceFiles(it.cacheManager), listOf(SimpleProcessor().toIsolating())
|
||||
)
|
||||
}
|
||||
|
||||
val classesOutput = tmp.newFolder()
|
||||
compileSources(sourcesDir.listFiles().asIterable(), classesOutput)
|
||||
|
||||
val addressTimestamp = outputDir.resolve("test/AddressGenerated.java").lastModified()
|
||||
assertTrue(outputDir.resolve("test/UserGenerated.java").exists())
|
||||
assertTrue(outputDir.resolve("test/AddressGenerated.java").exists())
|
||||
|
||||
val optionsForSecondRun = KaptOptions.Builder().apply {
|
||||
projectBaseDir = tmp.newFolder()
|
||||
|
||||
sourcesOutputDir = outputDir
|
||||
classesOutputDir = outputDir
|
||||
stubsOutputDir = outputDir
|
||||
incrementalDataOutputDir = outputDir
|
||||
|
||||
incrementalCache = incrementalCacheDir
|
||||
classpathFqNamesHistory = classpathHistory
|
||||
compiledSources.add(classesOutput)
|
||||
changedFiles.add(sourcesDir.resolve("User.java"))
|
||||
}.build()
|
||||
|
||||
KaptContext(optionsForSecondRun, true, logger).use {
|
||||
val sourcesToReprocess = optionsForSecondRun.collectJavaSourceFiles(it.cacheManager)
|
||||
assertFalse(outputDir.resolve("test/UserGenerated.java").exists())
|
||||
|
||||
it.doAnnotationProcessing(
|
||||
sourcesToReprocess, listOf(SimpleProcessor().toIsolating())
|
||||
)
|
||||
}
|
||||
|
||||
assertEquals(addressTimestamp, outputDir.resolve("test/AddressGenerated.java").lastModified())
|
||||
assertTrue(outputDir.resolve("test/UserGenerated.java").exists())
|
||||
|
||||
sourcesDir.resolve("User.java").delete()
|
||||
KaptContext(optionsForSecondRun, true, logger).use {
|
||||
it.doAnnotationProcessing(
|
||||
optionsForSecondRun.collectJavaSourceFiles(it.cacheManager), listOf(SimpleProcessor().toIsolating())
|
||||
)
|
||||
}
|
||||
|
||||
assertEquals(addressTimestamp, outputDir.resolve("test/AddressGenerated.java").lastModified())
|
||||
assertFalse(outputDir.resolve("test/UserGenerated.java").exists())
|
||||
}
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. 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.kapt.base.test.org.jetbrains.kotlin.kapt3.base.incremental
|
||||
|
||||
import org.jetbrains.kotlin.kapt3.base.incremental.DeclaredProcType
|
||||
import org.jetbrains.kotlin.kapt3.base.incremental.getIncrementalProcessorsFromClasspath
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipOutputStream
|
||||
|
||||
class IncrementalProcessorDiscoveryTest {
|
||||
|
||||
private val markerFileContent = """
|
||||
Input1Processor1,AGGREGATING
|
||||
Input1Processor2,ISOLATING
|
||||
Input1Processor3,DYNAMIC
|
||||
Input1Processor4,UNKNOWN
|
||||
Input1Processor5 this is malformed input
|
||||
""".trimIndent();
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
var tmp = TemporaryFolder()
|
||||
|
||||
@Test
|
||||
fun locateInJars() {
|
||||
val inputJar = tmp.root.resolve("inputJar.jar")
|
||||
ZipOutputStream(inputJar.outputStream()).use {
|
||||
it.putNextEntry(ZipEntry("META-INF/gradle/incremental.annotation.processors"))
|
||||
it.write(markerFileContent.toByteArray())
|
||||
it.closeEntry()
|
||||
}
|
||||
|
||||
val info = getIncrementalProcessorsFromClasspath(
|
||||
setOf("Input1Processor4", "Input1Processor3", "Input1Processor2", "Input1Processor1"),
|
||||
listOf(inputJar)
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
mapOf(
|
||||
"Input1Processor1" to DeclaredProcType.AGGREGATING,
|
||||
"Input1Processor2" to DeclaredProcType.ISOLATING,
|
||||
"Input1Processor3" to DeclaredProcType.DYNAMIC
|
||||
),
|
||||
info
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun locateInDir() {
|
||||
val inputDir = tmp.root.resolve("inputDir")
|
||||
inputDir.resolve("META-INF/gradle/incremental.annotation.processors").let {
|
||||
it.parentFile.mkdirs()
|
||||
it.writeText(markerFileContent)
|
||||
}
|
||||
|
||||
val info = getIncrementalProcessorsFromClasspath(
|
||||
setOf("Input1Processor4", "Input1Processor3", "Input1Processor2", "Input1Processor1"),
|
||||
listOf(inputDir)
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
mapOf(
|
||||
"Input1Processor1" to DeclaredProcType.AGGREGATING,
|
||||
"Input1Processor2" to DeclaredProcType.ISOLATING,
|
||||
"Input1Processor3" to DeclaredProcType.DYNAMIC
|
||||
),
|
||||
info
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun locateInJarsAndDirs() {
|
||||
val inputJar = tmp.root.resolve("inputJar.jar")
|
||||
ZipOutputStream(inputJar.outputStream()).use {
|
||||
it.putNextEntry(ZipEntry("META-INF/gradle/incremental.annotation.processors"))
|
||||
it.write("InputJarProcessor,ISOLATING".toByteArray())
|
||||
it.closeEntry()
|
||||
}
|
||||
|
||||
val inputDir = tmp.root.resolve("inputDir")
|
||||
inputDir.resolve("META-INF/gradle/incremental.annotation.processors").let {
|
||||
it.parentFile.mkdirs()
|
||||
it.writeText("InputDirProcessor,DYNAMIC")
|
||||
}
|
||||
|
||||
val info = getIncrementalProcessorsFromClasspath(
|
||||
setOf("InputJarNonIncrementalProcessor", "InputJarProcessor", "InputDirNonIncrementalProcessor", "InputDirProcessor"),
|
||||
listOf(inputJar, inputDir)
|
||||
)
|
||||
assertEquals(
|
||||
mapOf(
|
||||
"InputJarProcessor" to DeclaredProcType.ISOLATING,
|
||||
"InputDirProcessor" to DeclaredProcType.DYNAMIC
|
||||
),
|
||||
info
|
||||
)
|
||||
}
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. 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.kapt.base.test.org.jetbrains.kotlin.kapt3.base.incremental
|
||||
|
||||
import org.jetbrains.kotlin.kapt3.base.incremental.RuntimeProcType
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import java.io.File
|
||||
|
||||
class IsolationgIncrementalProcessorTest {
|
||||
@JvmField
|
||||
@Rule
|
||||
val tmp: TemporaryFolder = TemporaryFolder()
|
||||
|
||||
private lateinit var generatedSources: File
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
generatedSources = tmp.newFolder()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDependenciesRecorded() {
|
||||
val srcFiles = listOf("User.java", "Address.java", "Observable.java").map { File(TEST_DATA_DIR, it) }
|
||||
val isolating = SimpleProcessor().toIsolating()
|
||||
runAnnotationProcessing(srcFiles, listOf(isolating), generatedSources)
|
||||
|
||||
assertEquals(RuntimeProcType.ISOLATING, isolating.getRuntimeType())
|
||||
|
||||
assertEquals(
|
||||
mapOf(
|
||||
generatedSources.resolve("test/UserGenerated.java") to File("plugins/kapt3/kapt3-base/testData/runner/incremental/User.java").absoluteFile,
|
||||
generatedSources.resolve("test/AddressGenerated.java") to File("plugins/kapt3/kapt3-base/testData/runner/incremental/Address.java").absoluteFile
|
||||
),
|
||||
isolating.getGeneratedToSources()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNoSourcesToProcess() {
|
||||
val srcFiles = listOf("Observable.java").map { File(TEST_DATA_DIR, it) }
|
||||
val isolating = SimpleProcessor().toIsolating()
|
||||
runAnnotationProcessing(srcFiles, listOf(isolating), generatedSources)
|
||||
|
||||
assertEquals(RuntimeProcType.ISOLATING, isolating.getRuntimeType())
|
||||
assertEquals(emptyMap<File, File>(), isolating.getGeneratedToSources())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGeneratingSourcesClassesResources() {
|
||||
val srcFiles = listOf("User.java", "Address.java", "Observable.java").map { File(TEST_DATA_DIR, it) }
|
||||
val isolating = SimpleCreatingClassFilesAndResources().toIsolating()
|
||||
runAnnotationProcessing(srcFiles, listOf(isolating), generatedSources)
|
||||
|
||||
assertEquals(RuntimeProcType.ISOLATING, isolating.getRuntimeType())
|
||||
|
||||
assertEquals(
|
||||
mapOf(
|
||||
generatedSources.resolve("test/UserGenerated.java") to TEST_DATA_DIR.resolve("User.java").absoluteFile,
|
||||
generatedSources.resolve("test/UserGeneratedClass.class") to TEST_DATA_DIR.resolve("User.java").absoluteFile,
|
||||
generatedSources.resolve("test/UserGeneratedResource") to TEST_DATA_DIR.resolve("User.java").absoluteFile,
|
||||
generatedSources.resolve("test/AddressGenerated.java") to TEST_DATA_DIR.resolve("Address.java").absoluteFile,
|
||||
generatedSources.resolve("test/AddressGeneratedClass.class") to TEST_DATA_DIR.resolve("Address.java").absoluteFile,
|
||||
generatedSources.resolve("test/AddressGeneratedResource") to TEST_DATA_DIR.resolve("Address.java").absoluteFile
|
||||
),
|
||||
isolating.getGeneratedToSources()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testWrongOriginElement() {
|
||||
val srcFiles = listOf("User.java", "Address.java", "Observable.java").map { File(TEST_DATA_DIR, it) }
|
||||
val isolating = SimpleProcessor(wrongOrigin = true).toIsolating()
|
||||
runAnnotationProcessing(srcFiles, listOf(isolating), generatedSources)
|
||||
|
||||
assertEquals(RuntimeProcType.NON_INCREMENTAL, isolating.getRuntimeType())
|
||||
assertEquals(emptyMap<File, File>(), isolating.getGeneratedToSources())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testTwoIsolating() {
|
||||
val srcFiles = listOf("User.java", "Address.java", "Observable.java").map { File(TEST_DATA_DIR, it) }
|
||||
val isolating = listOf(
|
||||
SimpleProcessor().toIsolating(),
|
||||
SimpleProcessor(generatedSuffix = "Two").toIsolating()
|
||||
)
|
||||
runAnnotationProcessing(srcFiles, isolating, generatedSources)
|
||||
|
||||
isolating.forEach { assertEquals(RuntimeProcType.ISOLATING, it.getRuntimeType()) }
|
||||
assertEquals(
|
||||
mapOf(
|
||||
generatedSources.resolve("test/UserGenerated.java") to File("plugins/kapt3/kapt3-base/testData/runner/incremental/User.java").absoluteFile,
|
||||
generatedSources.resolve("test/AddressGenerated.java") to File("plugins/kapt3/kapt3-base/testData/runner/incremental/Address.java").absoluteFile
|
||||
), isolating[0].getGeneratedToSources()
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
mapOf(
|
||||
generatedSources.resolve("test/UserGeneratedTwo.java") to File("plugins/kapt3/kapt3-base/testData/runner/incremental/User.java").absoluteFile,
|
||||
generatedSources.resolve("test/AddressGeneratedTwo.java") to File("plugins/kapt3/kapt3-base/testData/runner/incremental/Address.java").absoluteFile
|
||||
), isolating[1].getGeneratedToSources()
|
||||
)
|
||||
}
|
||||
}
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. 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.kapt3.base.incremental;
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import java.io.File
|
||||
import java.io.ObjectOutputStream
|
||||
|
||||
class JavaClassCacheManagerTest {
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
var tmp = TemporaryFolder()
|
||||
|
||||
private lateinit var cache: JavaClassCacheManager
|
||||
private lateinit var cacheDir: File
|
||||
private lateinit var classpathHistory: File
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
cacheDir = tmp.newFolder()
|
||||
classpathHistory = tmp.newFolder()
|
||||
cache = JavaClassCacheManager(cacheDir, classpathHistory)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testClosingCache() {
|
||||
cache.close()
|
||||
|
||||
assertTrue(cacheDir.resolve("java-cache.bin").exists())
|
||||
assertTrue(cacheDir.resolve("apt-cache.bin").exists())
|
||||
assertTrue(cacheDir.resolve("last-build-ts.bin").exists())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMentionedTypes() {
|
||||
SourceFileStructure(File("Mentioned.java").toURI()).also {
|
||||
it.addDeclaredType("test.Mentioned")
|
||||
cache.javaCache.addSourceStructure(it)
|
||||
}
|
||||
SourceFileStructure(File("Src.java").toURI()).also {
|
||||
it.addDeclaredType("test.Src")
|
||||
it.addMentionedType("test.Mentioned")
|
||||
cache.javaCache.addSourceStructure(it)
|
||||
}
|
||||
SourceFileStructure(File("ReferencesSrc.java").toURI()).also {
|
||||
it.addDeclaredType("test.ReferencesSrc")
|
||||
it.addPrivateType("test.Src")
|
||||
cache.javaCache.addSourceStructure(it)
|
||||
}
|
||||
prepareForIncremental()
|
||||
|
||||
val dirtyFiles = cache.invalidateAndGetDirtyFiles(listOf(File("Mentioned.java"))) as SourcesToReprocess.Incremental
|
||||
assertEquals(
|
||||
listOf(
|
||||
File("Mentioned.java").absoluteFile,
|
||||
File("Src.java").absoluteFile,
|
||||
File("ReferencesSrc.java").absoluteFile
|
||||
), dirtyFiles.toReprocess
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPrivateTypes() {
|
||||
SourceFileStructure(File("Mentioned.java").toURI()).also {
|
||||
it.addDeclaredType("test.Mentioned")
|
||||
cache.javaCache.addSourceStructure(it)
|
||||
}
|
||||
SourceFileStructure(File("Src.java").toURI()).also {
|
||||
it.addDeclaredType("test.Src")
|
||||
it.addPrivateType("test.Mentioned")
|
||||
cache.javaCache.addSourceStructure(it)
|
||||
}
|
||||
SourceFileStructure(File("ReferencesSrc.java").toURI()).also {
|
||||
it.addDeclaredType("test.ReferencesSrc")
|
||||
it.addPrivateType("test.Src")
|
||||
cache.javaCache.addSourceStructure(it)
|
||||
}
|
||||
prepareForIncremental()
|
||||
|
||||
val dirtyFiles = cache.invalidateAndGetDirtyFiles(listOf(File("Mentioned.java"))) as SourcesToReprocess.Incremental
|
||||
assertEquals(
|
||||
listOf(
|
||||
File("Mentioned.java").absoluteFile,
|
||||
File("Src.java").absoluteFile
|
||||
), dirtyFiles.toReprocess
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMultipleDeclared() {
|
||||
SourceFileStructure(File("TwoTypes.java").toURI()).also {
|
||||
it.addDeclaredType("test.TwoTypes")
|
||||
it.addDeclaredType("test.AnotherType")
|
||||
cache.javaCache.addSourceStructure(it)
|
||||
}
|
||||
SourceFileStructure(File("ReferencesTwoTypes.java").toURI()).also {
|
||||
it.addDeclaredType("test.ReferencesTwoTypes")
|
||||
it.addPrivateType("test.TwoTypes")
|
||||
cache.javaCache.addSourceStructure(it)
|
||||
}
|
||||
SourceFileStructure(File("ReferencesAnotherType.java").toURI()).also {
|
||||
it.addDeclaredType("test.ReferencesAnotherType")
|
||||
it.addPrivateType("test.AnotherType")
|
||||
cache.javaCache.addSourceStructure(it)
|
||||
}
|
||||
prepareForIncremental()
|
||||
|
||||
val dirtyFiles = cache.invalidateAndGetDirtyFiles(listOf(File("TwoTypes.java"))) as SourcesToReprocess.Incremental
|
||||
assertEquals(
|
||||
listOf(
|
||||
File("TwoTypes.java").absoluteFile,
|
||||
File("ReferencesTwoTypes.java").absoluteFile,
|
||||
File("ReferencesAnotherType.java").absoluteFile
|
||||
), dirtyFiles.toReprocess
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNoClasspathHistory() {
|
||||
SourceFileStructure(File("Src.java").toURI()).also {
|
||||
it.addDeclaredType("test.Src")
|
||||
cache.javaCache.addSourceStructure(it)
|
||||
}
|
||||
cache.close()
|
||||
classpathHistory.resolve(Long.MAX_VALUE.toString()).createNewFile()
|
||||
|
||||
val dirtyFiles = cache.invalidateAndGetDirtyFiles(listOf())
|
||||
assertEquals(SourcesToReprocess.FullRebuild, dirtyFiles)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testWithClasspathHistoryButNoNewChanges() {
|
||||
SourceFileStructure(File("Src.java").toURI()).also {
|
||||
it.addDeclaredType("test.Src")
|
||||
it.addMentionedType("test.Mentioned")
|
||||
cache.javaCache.addSourceStructure(it)
|
||||
}
|
||||
prepareForIncremental()
|
||||
ObjectOutputStream(classpathHistory.resolve("0").outputStream()).use {
|
||||
it.writeObject(listOf("test.Mentioned"))
|
||||
}
|
||||
|
||||
val dirtyFiles = cache.invalidateAndGetDirtyFiles(listOf()) as SourcesToReprocess.Incremental
|
||||
assertEquals(emptyList<File>(), dirtyFiles.toReprocess)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testWithClasspathHistoryWithChanges() {
|
||||
SourceFileStructure(File("Src.java").toURI()).also {
|
||||
it.addDeclaredType("test.Src")
|
||||
it.addMentionedType("test.Mentioned")
|
||||
cache.javaCache.addSourceStructure(it)
|
||||
}
|
||||
prepareForIncremental()
|
||||
ObjectOutputStream(classpathHistory.resolve(Long.MAX_VALUE.toString()).outputStream()).use {
|
||||
it.writeObject(listOf("test.Mentioned"))
|
||||
}
|
||||
|
||||
val dirtyFiles = cache.invalidateAndGetDirtyFiles(listOf())
|
||||
assertEquals(SourcesToReprocess.FullRebuild, dirtyFiles)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDefinesConstant() {
|
||||
SourceFileStructure(File("Constants.java").toURI()).also {
|
||||
it.addDeclaredType("test.Constants")
|
||||
it.addDefinedConstant("CONST", 123)
|
||||
cache.javaCache.addSourceStructure(it)
|
||||
}
|
||||
SourceFileStructure(File("Unrelated1.java").toURI()).also {
|
||||
it.addDeclaredType("test.Unrelated1")
|
||||
cache.javaCache.addSourceStructure(it)
|
||||
}
|
||||
SourceFileStructure(File("Unrelated2.java").toURI()).also {
|
||||
it.addDeclaredType("test.Unrelated2")
|
||||
cache.javaCache.addSourceStructure(it)
|
||||
}
|
||||
prepareForIncremental()
|
||||
|
||||
val dirtyFiles = cache.invalidateAndGetDirtyFiles(listOf(File("Constants.java")))
|
||||
assertEquals(SourcesToReprocess.FullRebuild, dirtyFiles)
|
||||
}
|
||||
|
||||
@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() {
|
||||
cache.close()
|
||||
classpathHistory.resolve("0").createNewFile()
|
||||
cache = JavaClassCacheManager(cacheDir, classpathHistory)
|
||||
}
|
||||
}
|
||||
+210
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. 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.kapt.base.test.org.jetbrains.kotlin.kapt3.base.incremental
|
||||
|
||||
import org.jetbrains.kotlin.kapt3.base.incremental.JavaClassCacheManager
|
||||
import org.jetbrains.kotlin.kapt3.base.incremental.MentionedTypesTaskListener
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.BeforeClass
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import java.io.File
|
||||
|
||||
private val MY_TEST_DIR = File("plugins/kapt3/kapt3-base/testData/runner/incremental/complex")
|
||||
|
||||
class TestComplexIncrementalAptCache {
|
||||
|
||||
companion object {
|
||||
@ClassRule
|
||||
@JvmField
|
||||
var tmp = TemporaryFolder()
|
||||
|
||||
private lateinit var cache: JavaClassCacheManager
|
||||
private lateinit var generatedSources: File
|
||||
|
||||
@JvmStatic
|
||||
@BeforeClass
|
||||
fun setUp() {
|
||||
val classpathHistory = tmp.newFolder()
|
||||
cache = JavaClassCacheManager(tmp.newFolder(), classpathHistory)
|
||||
generatedSources = tmp.newFolder()
|
||||
cache.close()
|
||||
classpathHistory.resolve("0").createNewFile()
|
||||
val processor = SimpleProcessor().toAggregating()
|
||||
val srcFiles = listOf(
|
||||
"MyEnum.java",
|
||||
"MyNumber.java",
|
||||
"NumberAnnotation.java",
|
||||
"NumberException.java",
|
||||
"NumberHolder.java",
|
||||
"NumberManager.java",
|
||||
"GenericNumber.java"
|
||||
).map { File(MY_TEST_DIR, it) }
|
||||
runAnnotationProcessing(
|
||||
srcFiles,
|
||||
listOf(processor),
|
||||
generatedSources
|
||||
) { trees -> MentionedTypesTaskListener(cache.javaCache, trees) }
|
||||
cache.updateCache(listOf(processor))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEnum() {
|
||||
val myEnum = cache.javaCache.getStructure(MY_TEST_DIR.resolve("MyEnum.java"))!!
|
||||
|
||||
assertEquals(setOf("test.MyEnum"), myEnum.getDeclaredTypes())
|
||||
assertEquals(emptySet<String>(), myEnum.getMentionedAnnotations())
|
||||
assertEquals(emptySet<String>(), myEnum.getPrivateTypes())
|
||||
assertEquals(setOf("test.MyEnum", "test.TypeGeneratedByApt"), myEnum.getMentionedTypes())
|
||||
assertEquals(emptyMap<String, Any>(), myEnum.getDefinedConstants())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMyNumber() {
|
||||
val myNumber = cache.javaCache.getStructure(MY_TEST_DIR.resolve("MyNumber.java"))!!
|
||||
|
||||
assertEquals(
|
||||
setOf(
|
||||
"test.MyNumber",
|
||||
"test.FieldAnnotation",
|
||||
"test.MethodAnnotation",
|
||||
"test.ParameterAnnotation",
|
||||
"test.TypeUseAnnotation",
|
||||
"test.AnotherTypeUseAnnotation",
|
||||
"test.ThrowTypeUseAnnotation"
|
||||
), myNumber.getDeclaredTypes()
|
||||
)
|
||||
assertEquals(
|
||||
setOf(
|
||||
"java.lang.annotation.Target",
|
||||
"test.FieldAnnotation",
|
||||
"test.ParameterAnnotation",
|
||||
"test.MethodAnnotation",
|
||||
"test.TypeUseAnnotation",
|
||||
"test.AnotherTypeUseAnnotation",
|
||||
"test.ThrowTypeUseAnnotation"
|
||||
), myNumber.getMentionedAnnotations()
|
||||
)
|
||||
assertEquals(
|
||||
setOf(
|
||||
"test.FieldAnnotation",
|
||||
"java.lang.String",
|
||||
"test.ParameterAnnotation",
|
||||
"test.MethodAnnotation",
|
||||
"test.AnotherTypeUseAnnotation",
|
||||
"test.ThrowTypeUseAnnotation",
|
||||
"java.lang.Number",
|
||||
"java.lang.RuntimeException"
|
||||
), myNumber.getPrivateTypes()
|
||||
)
|
||||
assertEquals(
|
||||
setOf(
|
||||
"test.MyNumber",
|
||||
"java.lang.annotation.Target",
|
||||
"test.FieldAnnotation",
|
||||
"test.ParameterAnnotation",
|
||||
"test.MethodAnnotation",
|
||||
"test.TypeUseAnnotation",
|
||||
"test.AnotherTypeUseAnnotation",
|
||||
"test.ThrowTypeUseAnnotation",
|
||||
"java.util.HashSet"
|
||||
), myNumber.getMentionedTypes()
|
||||
)
|
||||
assertEquals(emptyMap<String, String>(), myNumber.getDefinedConstants())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAnnotation() {
|
||||
val numberAnnotation = cache.javaCache.getStructure(MY_TEST_DIR.resolve("NumberAnnotation.java"))!!
|
||||
|
||||
assertEquals(setOf("test.NumberAnnotation", "test.BaseAnnotation"), numberAnnotation.getDeclaredTypes())
|
||||
assertEquals(setOf("test.BaseAnnotation"), numberAnnotation.getMentionedAnnotations())
|
||||
assertEquals(emptySet<String>(), numberAnnotation.getPrivateTypes())
|
||||
assertEquals(
|
||||
setOf(
|
||||
"test.BaseAnnotation",
|
||||
"test.NumberAnnotation",
|
||||
"java.lang.Class",
|
||||
"test.MyEnum",
|
||||
"test.NumberManager"
|
||||
), numberAnnotation.getMentionedTypes()
|
||||
)
|
||||
assertEquals(emptyMap<String, String>(), numberAnnotation.getDefinedConstants())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNumberException() {
|
||||
val numberException = cache.javaCache.getStructure(MY_TEST_DIR.resolve("NumberException.java"))!!
|
||||
|
||||
assertEquals(setOf("test.NumberException"), numberException.getDeclaredTypes())
|
||||
assertEquals(emptySet<String>(), numberException.getMentionedAnnotations())
|
||||
assertEquals(emptySet<String>(), numberException.getPrivateTypes())
|
||||
assertEquals(setOf("test.NumberException", "java.lang.RuntimeException"), numberException.getMentionedTypes())
|
||||
assertEquals(emptyMap<String, String>(), numberException.getDefinedConstants())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNumberHolder() {
|
||||
val numberHolder = cache.javaCache.getStructure(MY_TEST_DIR.resolve("NumberHolder.java"))!!
|
||||
|
||||
assertEquals(setOf("test.NumberHolder", "test.NumberHolder.MyInnerClass"), numberHolder.getDeclaredTypes())
|
||||
assertEquals(setOf("test.NumberAnnotation"), numberHolder.getMentionedAnnotations())
|
||||
assertEquals(setOf("test.NumberManager"), numberHolder.getPrivateTypes())
|
||||
assertEquals(
|
||||
setOf(
|
||||
"test.NumberHolder",
|
||||
"test.NumberHolder.MyInnerClass",
|
||||
"test.NumberAnnotation",
|
||||
"test.NumberManager",
|
||||
"test.MyNumber",
|
||||
"java.util.HashSet",
|
||||
"java.lang.Runnable",
|
||||
"java.lang.String",
|
||||
"test.NumberException"
|
||||
), numberHolder.getMentionedTypes()
|
||||
)
|
||||
assertEquals(emptyMap<String, String>(), numberHolder.getDefinedConstants())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNumberManager() {
|
||||
val numberManager = cache.javaCache.getStructure(MY_TEST_DIR.resolve("NumberManager.java"))!!
|
||||
|
||||
assertEquals(setOf("test.NumberManager"), numberManager.getDeclaredTypes())
|
||||
assertEquals(emptySet<String>(), numberManager.getMentionedAnnotations())
|
||||
assertEquals(setOf("test.MyEnum"), numberManager.getPrivateTypes())
|
||||
assertEquals(
|
||||
setOf(
|
||||
"test.NumberManager",
|
||||
"java.lang.String",
|
||||
"test.NumberHolder"
|
||||
), numberManager.getMentionedTypes()
|
||||
)
|
||||
assertEquals(mapOf("CONST" to "STRING_CONST", "INT_CONST" to 246), numberManager.getDefinedConstants())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGenericNumber() {
|
||||
val genericNumber = cache.javaCache.getStructure(MY_TEST_DIR.resolve("GenericNumber.java"))!!
|
||||
|
||||
assertEquals(setOf("test.GenericNumber"), genericNumber.getDeclaredTypes())
|
||||
assertEquals(emptySet<String>(), genericNumber.getMentionedAnnotations())
|
||||
assertEquals(emptySet<String>(), genericNumber.getPrivateTypes())
|
||||
assertEquals(
|
||||
setOf(
|
||||
"test.GenericNumber",
|
||||
"java.util.HashSet",
|
||||
"java.lang.Runnable",
|
||||
"java.lang.Cloneable",
|
||||
"java.lang.CharSequence",
|
||||
"java.lang.Number"
|
||||
), genericNumber.getMentionedTypes()
|
||||
)
|
||||
assertEquals(emptyMap<String, String>(), genericNumber.getDefinedConstants())
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. 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.kapt.base.test.org.jetbrains.kotlin.kapt3.base.incremental
|
||||
|
||||
import org.jetbrains.kotlin.kapt3.base.incremental.IncrementalProcessor
|
||||
import org.jetbrains.kotlin.kapt3.base.incremental.JavaClassCacheManager
|
||||
import org.jetbrains.kotlin.kapt3.base.incremental.MentionedTypesTaskListener
|
||||
import org.jetbrains.kotlin.kapt3.base.incremental.SourcesToReprocess
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import java.io.File
|
||||
|
||||
class TestSimpleIncrementalAptCache {
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
var tmp = TemporaryFolder()
|
||||
|
||||
private lateinit var cache: JavaClassCacheManager
|
||||
private lateinit var generatedSources: File
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
val classpathHistory = tmp.newFolder()
|
||||
cache = JavaClassCacheManager(tmp.newFolder(), classpathHistory)
|
||||
generatedSources = tmp.newFolder()
|
||||
cache.close()
|
||||
classpathHistory.resolve("0").createNewFile()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAggregatingAnnotations() {
|
||||
runProcessor(SimpleProcessor().toAggregating())
|
||||
|
||||
val dirtyFiles = cache.invalidateAndGetDirtyFiles(listOf(TEST_DATA_DIR.resolve("User.java"))) as SourcesToReprocess.Incremental
|
||||
assertEquals(
|
||||
listOf(TEST_DATA_DIR.resolve("User.java").absoluteFile, TEST_DATA_DIR.resolve("Address.java").absoluteFile),
|
||||
dirtyFiles.toReprocess
|
||||
)
|
||||
assertFalse(generatedSources.resolve("test/UserGenerated.java").exists())
|
||||
assertFalse(generatedSources.resolve("test/AddressGenerated.java").exists())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIsolatingAnnotations() {
|
||||
runProcessor(SimpleProcessor().toIsolating())
|
||||
|
||||
val dirtyFiles = cache.invalidateAndGetDirtyFiles(listOf(TEST_DATA_DIR.resolve("User.java"))) as SourcesToReprocess.Incremental
|
||||
assertFalse(generatedSources.resolve("test/UserGenerated.java").exists())
|
||||
assertEquals(
|
||||
listOf(TEST_DATA_DIR.resolve("User.java").absoluteFile),
|
||||
dirtyFiles.toReprocess
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNonIncremental() {
|
||||
runProcessor(SimpleProcessor().toNonIncremental())
|
||||
|
||||
val dirtyFiles = cache.invalidateAndGetDirtyFiles(listOf(TEST_DATA_DIR.resolve("User.java")))
|
||||
assertTrue(dirtyFiles is SourcesToReprocess.FullRebuild)
|
||||
}
|
||||
|
||||
private fun runProcessor(processor: IncrementalProcessor) {
|
||||
val srcFiles = listOf("User.java", "Address.java", "Observable.java").map { File(TEST_DATA_DIR, it) }
|
||||
runAnnotationProcessing(
|
||||
srcFiles,
|
||||
listOf(processor),
|
||||
generatedSources
|
||||
) { trees -> MentionedTypesTaskListener(cache.javaCache, trees) }
|
||||
cache.updateCache(listOf(processor))
|
||||
}
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. 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.kapt.base.test.org.jetbrains.kotlin.kapt3.base.incremental
|
||||
|
||||
import com.sun.source.util.TaskListener
|
||||
import com.sun.source.util.Trees
|
||||
import com.sun.tools.javac.api.JavacTaskImpl
|
||||
import org.jetbrains.kotlin.kapt3.base.incremental.DeclaredProcType
|
||||
import org.jetbrains.kotlin.kapt3.base.incremental.IncrementalProcessor
|
||||
import org.jetbrains.kotlin.kapt3.base.incremental.RuntimeProcType
|
||||
import java.io.File
|
||||
import javax.annotation.processing.AbstractProcessor
|
||||
import javax.annotation.processing.Filer
|
||||
import javax.annotation.processing.ProcessingEnvironment
|
||||
import javax.annotation.processing.RoundEnvironment
|
||||
import javax.lang.model.SourceVersion
|
||||
import javax.lang.model.element.TypeElement
|
||||
import javax.tools.StandardLocation
|
||||
import javax.tools.ToolProvider
|
||||
|
||||
val TEST_DATA_DIR = File("plugins/kapt3/kapt3-base/testData/runner/incremental")
|
||||
|
||||
fun runAnnotationProcessing(
|
||||
srcFiles: List<File>,
|
||||
processor: List<IncrementalProcessor>,
|
||||
generatedSources: File,
|
||||
listener: (Trees) -> TaskListener? = { null }
|
||||
) {
|
||||
val compiler = ToolProvider.getSystemJavaCompiler()
|
||||
compiler.getStandardFileManager(null, null, null).use { fileManager ->
|
||||
val javaSrcs = fileManager.getJavaFileObjectsFromFiles(srcFiles)
|
||||
val compilationTask =
|
||||
compiler.getTask(
|
||||
null,
|
||||
fileManager,
|
||||
null,
|
||||
listOf("-proc:only", "-s", generatedSources.absolutePath, "-d", generatedSources.absolutePath),
|
||||
null,
|
||||
javaSrcs
|
||||
) as JavacTaskImpl
|
||||
|
||||
val taskListener = listener(Trees.instance(compilationTask))
|
||||
taskListener?.let { compilationTask.addTaskListener(it) }
|
||||
|
||||
compilationTask.setProcessors(processor)
|
||||
compilationTask.call()
|
||||
}
|
||||
}
|
||||
|
||||
fun compileSources(srcFiles: Iterable<File>, outputDir: File) {
|
||||
val compiler = ToolProvider.getSystemJavaCompiler()
|
||||
compiler.getStandardFileManager(null, null, null).use { fileManager ->
|
||||
val compilationTask =
|
||||
compiler.getTask(
|
||||
null,
|
||||
fileManager,
|
||||
null,
|
||||
listOf("-d", outputDir.absolutePath),
|
||||
null,
|
||||
fileManager.getJavaFileObjectsFromFiles(srcFiles)
|
||||
) as JavacTaskImpl
|
||||
|
||||
compilationTask.call()
|
||||
}
|
||||
}
|
||||
|
||||
fun SimpleProcessor.toAggregating() = IncrementalProcessor(this, DeclaredProcType.AGGREGATING)
|
||||
fun SimpleProcessor.toIsolating() = IncrementalProcessor(this, DeclaredProcType.ISOLATING)
|
||||
fun SimpleProcessor.toNonIncremental() = IncrementalProcessor(this, DeclaredProcType.NON_INCREMENTAL)
|
||||
fun DynamicProcessor.toDynamic() = IncrementalProcessor(this, DeclaredProcType.DYNAMIC)
|
||||
|
||||
open class SimpleProcessor(private val wrongOrigin: Boolean = false, private val generatedSuffix: String = "") : AbstractProcessor() {
|
||||
lateinit var filer: Filer
|
||||
|
||||
override fun init(processingEnv: ProcessingEnvironment?) {
|
||||
super.init(processingEnv)
|
||||
filer = processingEnv!!.filer
|
||||
}
|
||||
|
||||
override fun getSupportedAnnotationTypes(): MutableSet<String> = mutableSetOf("test.Observable")
|
||||
|
||||
override fun process(annotations: MutableSet<out TypeElement>, roundEnv: RoundEnvironment): Boolean {
|
||||
if (annotations.isEmpty()) return false
|
||||
|
||||
roundEnv.getElementsAnnotatedWith(annotations.single()).forEach {
|
||||
it as TypeElement
|
||||
|
||||
val generatedName = "${it.qualifiedName}Generated$generatedSuffix"
|
||||
filer.createSourceFile(generatedName, it.takeUnless { wrongOrigin }).openWriter().use { it.write("") }
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getSupportedSourceVersion(): SourceVersion {
|
||||
return SourceVersion.latest()
|
||||
}
|
||||
}
|
||||
|
||||
class DynamicProcessor(private val kind: RuntimeProcType) : SimpleProcessor() {
|
||||
override fun getSupportedOptions(): MutableSet<String> {
|
||||
return mutableSetOf("org.gradle.annotation.processing.${kind.name}")
|
||||
}
|
||||
}
|
||||
|
||||
class SimpleCreatingClassFilesAndResources : SimpleProcessor() {
|
||||
override fun process(annotations: MutableSet<out TypeElement>, roundEnv: RoundEnvironment): Boolean {
|
||||
super.process(annotations, roundEnv)
|
||||
|
||||
if (annotations.isEmpty()) return false
|
||||
roundEnv.getElementsAnnotatedWith(annotations.single()).forEach {
|
||||
it as TypeElement
|
||||
|
||||
val generatedName = "${it.qualifiedName}Generated"
|
||||
filer.createClassFile("${generatedName}Class", it).openWriter().use { it.write("") }
|
||||
filer.createResource(StandardLocation.SOURCE_OUTPUT, "test", "${it.simpleName}GeneratedResource", it).openWriter().use { it.write("") }
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user