Kapt3: Handle exceptions while annotation processing gracefully
This commit is contained in:
committed by
Yan Zhulanow
parent
aae80fa4ab
commit
65a9d9e8d2
+61
-8
@@ -20,7 +20,9 @@ import com.sun.tools.javac.comp.CompileStates
|
|||||||
import com.sun.tools.javac.file.JavacFileManager
|
import com.sun.tools.javac.file.JavacFileManager
|
||||||
import com.sun.tools.javac.main.JavaCompiler
|
import com.sun.tools.javac.main.JavaCompiler
|
||||||
import com.sun.tools.javac.main.Option
|
import com.sun.tools.javac.main.Option
|
||||||
|
import com.sun.tools.javac.processing.AnnotationProcessingError
|
||||||
import com.sun.tools.javac.processing.JavacProcessingEnvironment
|
import com.sun.tools.javac.processing.JavacProcessingEnvironment
|
||||||
|
import com.sun.tools.javac.tree.JCTree
|
||||||
import com.sun.tools.javac.util.Context
|
import com.sun.tools.javac.util.Context
|
||||||
import com.sun.tools.javac.util.Log
|
import com.sun.tools.javac.util.Log
|
||||||
import com.sun.tools.javac.util.Options
|
import com.sun.tools.javac.util.Options
|
||||||
@@ -28,21 +30,65 @@ import java.io.File
|
|||||||
import java.io.PrintWriter
|
import java.io.PrintWriter
|
||||||
import javax.annotation.processing.Processor
|
import javax.annotation.processing.Processor
|
||||||
import javax.tools.JavaFileManager
|
import javax.tools.JavaFileManager
|
||||||
|
import com.sun.tools.javac.util.List as JavacList
|
||||||
|
|
||||||
class KaptError : RuntimeException {
|
class KaptError : RuntimeException {
|
||||||
constructor(message: String) : super(message)
|
val kind: Kind
|
||||||
constructor(message: String, cause: Throwable) : super(message, cause)
|
|
||||||
|
enum class Kind(val message: String) {
|
||||||
|
JAVA_FILE_PARSING_ERROR("Java file parsing error"),
|
||||||
|
EXCEPTION("Exception while annotation processing"),
|
||||||
|
UNKNOWN("Unknown error while annotation processing")
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(kind: Kind) : super(kind.message) {
|
||||||
|
this.kind = kind
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(kind: Kind, cause: Throwable) : super(kind.message, cause) {
|
||||||
|
this.kind = kind
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class KaptRunner {
|
class KaptRunner {
|
||||||
fun doAnnotationProcessing(javaSourceFiles: List<File>, processors: List<Processor>, sourceOutputDir: File, classOutputDir: File) {
|
private val context = Context()
|
||||||
val context = Context()
|
private val options: Options
|
||||||
|
|
||||||
|
init {
|
||||||
JavacFileManager.preRegister(context)
|
JavacFileManager.preRegister(context)
|
||||||
KaptJavaCompiler.preRegister(context)
|
KaptJavaCompiler.preRegister(context)
|
||||||
|
|
||||||
val options = Options.instance(context)
|
options = Options.instance(context)
|
||||||
context.put(Log.outKey, PrintWriter(System.err, true))
|
context.put(Log.outKey, PrintWriter(System.err, true))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun parseJavaFiles(
|
||||||
|
javaSourceFiles: List<File>,
|
||||||
|
classpath: List<File>
|
||||||
|
): JavacList<JCTree.JCCompilationUnit> {
|
||||||
|
classpath.forEach { options.put(Option.CLASSPATH, it.canonicalPath) }
|
||||||
|
|
||||||
|
val fileManager = context.get(JavaFileManager::class.java) as JavacFileManager
|
||||||
|
val compiler = JavaCompiler.instance(context) as KaptJavaCompiler
|
||||||
|
|
||||||
|
try {
|
||||||
|
val javaFileObjects = fileManager.getJavaFileObjectsFromFiles(javaSourceFiles)
|
||||||
|
return compiler.parseFiles(javaFileObjects)
|
||||||
|
} finally {
|
||||||
|
fileManager.close()
|
||||||
|
compiler.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun doAnnotationProcessing(
|
||||||
|
javaSourceFiles: List<File>,
|
||||||
|
processors: List<Processor>,
|
||||||
|
classpath: List<File>,
|
||||||
|
sourceOutputDir: File,
|
||||||
|
classOutputDir: File
|
||||||
|
) {
|
||||||
options.put(Option.PROC, "only") // Only process annotations
|
options.put(Option.PROC, "only") // Only process annotations
|
||||||
|
classpath.forEach { options.put(Option.CLASSPATH, it.canonicalPath) }
|
||||||
options.put(Option.S, sourceOutputDir.canonicalPath)
|
options.put(Option.S, sourceOutputDir.canonicalPath)
|
||||||
options.put(Option.D, classOutputDir.canonicalPath)
|
options.put(Option.D, classOutputDir.canonicalPath)
|
||||||
|
|
||||||
@@ -56,13 +102,20 @@ class KaptRunner {
|
|||||||
val javaFileObjects = fileManager.getJavaFileObjectsFromFiles(javaSourceFiles)
|
val javaFileObjects = fileManager.getJavaFileObjectsFromFiles(javaSourceFiles)
|
||||||
val parsedJavaFiles = compiler.parseFiles(javaFileObjects)
|
val parsedJavaFiles = compiler.parseFiles(javaFileObjects)
|
||||||
if (compiler.shouldStop(CompileStates.CompileState.PARSE)) {
|
if (compiler.shouldStop(CompileStates.CompileState.PARSE)) {
|
||||||
throw KaptError("Error while parsing Java files.")
|
throw KaptError(KaptError.Kind.JAVA_FILE_PARSING_ERROR)
|
||||||
}
|
}
|
||||||
|
|
||||||
val compilerAfterAnnotationProcessing = compiler.processAnnotations(compiler.enterTrees(parsedJavaFiles))
|
val compilerAfterAnnotationProcessing: JavaCompiler? = null
|
||||||
compilerAfterAnnotationProcessing.close()
|
try {
|
||||||
|
compiler.processAnnotations(compiler.enterTrees(parsedJavaFiles))
|
||||||
|
} catch (e: AnnotationProcessingError) {
|
||||||
|
throw KaptError(KaptError.Kind.EXCEPTION, e.cause ?: e)
|
||||||
|
}
|
||||||
|
|
||||||
|
compilerAfterAnnotationProcessing?.close()
|
||||||
} finally {
|
} finally {
|
||||||
processingEnvironment.close()
|
processingEnvironment.close()
|
||||||
|
compiler.close()
|
||||||
fileManager.close()
|
fileManager.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import org.jetbrains.kotlin.kapt3.KaptError
|
||||||
import org.jetbrains.kotlin.kapt3.KaptRunner
|
import org.jetbrains.kotlin.kapt3.KaptRunner
|
||||||
import org.junit.Assert
|
import org.junit.Assert
|
||||||
import org.junit.Assert.*
|
import org.junit.Assert.*
|
||||||
@@ -27,11 +28,8 @@ import javax.lang.model.element.TypeElement
|
|||||||
class KaptRunnerTest {
|
class KaptRunnerTest {
|
||||||
private companion object {
|
private companion object {
|
||||||
val TEST_DATA_DIR = File("plugins/kapt3/testData/runner")
|
val TEST_DATA_DIR = File("plugins/kapt3/testData/runner")
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
val SIMPLE_PROCESSOR = object : AbstractProcessor() {
|
||||||
fun testSimple() {
|
|
||||||
val processor = object : AbstractProcessor() {
|
|
||||||
override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
|
override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
|
||||||
for (annotation in annotations) {
|
for (annotation in annotations) {
|
||||||
val annotationName = annotation.simpleName.toString()
|
val annotationName = annotation.simpleName.toString()
|
||||||
@@ -54,12 +52,16 @@ class KaptRunnerTest {
|
|||||||
|
|
||||||
override fun getSupportedAnnotationTypes() = setOf("test.MyAnnotation")
|
override fun getSupportedAnnotationTypes() = setOf("test.MyAnnotation")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSimple() {
|
||||||
val sourceOutputDir = Files.createTempDirectory("kaptRunner").toFile()
|
val sourceOutputDir = Files.createTempDirectory("kaptRunner").toFile()
|
||||||
try {
|
try {
|
||||||
KaptRunner().doAnnotationProcessing(
|
KaptRunner().doAnnotationProcessing(
|
||||||
listOf(File(TEST_DATA_DIR, "Simple.java")),
|
listOf(File(TEST_DATA_DIR, "Simple.java")),
|
||||||
listOf(processor),
|
listOf(SIMPLE_PROCESSOR),
|
||||||
|
emptyList(), // classpath
|
||||||
sourceOutputDir,
|
sourceOutputDir,
|
||||||
sourceOutputDir)
|
sourceOutputDir)
|
||||||
val myMethodFile = File(sourceOutputDir, "generated/MyMethodMyAnnotation.java")
|
val myMethodFile = File(sourceOutputDir, "generated/MyMethodMyAnnotation.java")
|
||||||
@@ -68,4 +70,43 @@ class KaptRunnerTest {
|
|||||||
sourceOutputDir.deleteRecursively()
|
sourceOutputDir.deleteRecursively()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testException() {
|
||||||
|
val exceptionMessage = "Here we are!"
|
||||||
|
|
||||||
|
val processor = object : AbstractProcessor() {
|
||||||
|
override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
|
||||||
|
throw RuntimeException(exceptionMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSupportedAnnotationTypes() = setOf("test.MyAnnotation")
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
KaptRunner().doAnnotationProcessing(
|
||||||
|
listOf(File(TEST_DATA_DIR, "Simple.java")),
|
||||||
|
listOf(processor),
|
||||||
|
emptyList(),
|
||||||
|
TEST_DATA_DIR,
|
||||||
|
TEST_DATA_DIR)
|
||||||
|
} catch (e: KaptError) {
|
||||||
|
assertEquals(KaptError.Kind.EXCEPTION, e.kind)
|
||||||
|
assertEquals("Here we are!", e.cause!!.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testParsingError() {
|
||||||
|
try {
|
||||||
|
KaptRunner().doAnnotationProcessing(
|
||||||
|
listOf(File(TEST_DATA_DIR, "ParseError.java")),
|
||||||
|
listOf(SIMPLE_PROCESSOR),
|
||||||
|
emptyList(),
|
||||||
|
TEST_DATA_DIR,
|
||||||
|
TEST_DATA_DIR)
|
||||||
|
} catch (e: KaptError) {
|
||||||
|
assertEquals(KaptError.Kind.JAVA_FILE_PARSING_ERROR, e.kind)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
BLAHBLAH
|
||||||
Reference in New Issue
Block a user