Kapt3: Handle exceptions while annotation processing gracefully

This commit is contained in:
Yan Zhulanow
2016-10-21 03:30:10 +03:00
committed by Yan Zhulanow
parent aae80fa4ab
commit 65a9d9e8d2
3 changed files with 108 additions and 13 deletions
@@ -20,7 +20,9 @@ import com.sun.tools.javac.comp.CompileStates
import com.sun.tools.javac.file.JavacFileManager
import com.sun.tools.javac.main.JavaCompiler
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.tree.JCTree
import com.sun.tools.javac.util.Context
import com.sun.tools.javac.util.Log
import com.sun.tools.javac.util.Options
@@ -28,21 +30,65 @@ import java.io.File
import java.io.PrintWriter
import javax.annotation.processing.Processor
import javax.tools.JavaFileManager
import com.sun.tools.javac.util.List as JavacList
class KaptError : RuntimeException {
constructor(message: String) : super(message)
constructor(message: String, cause: Throwable) : super(message, cause)
val kind: Kind
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 {
fun doAnnotationProcessing(javaSourceFiles: List<File>, processors: List<Processor>, sourceOutputDir: File, classOutputDir: File) {
val context = Context()
private val context = Context()
private val options: Options
init {
JavacFileManager.preRegister(context)
KaptJavaCompiler.preRegister(context)
val options = Options.instance(context)
options = Options.instance(context)
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
classpath.forEach { options.put(Option.CLASSPATH, it.canonicalPath) }
options.put(Option.S, sourceOutputDir.canonicalPath)
options.put(Option.D, classOutputDir.canonicalPath)
@@ -56,13 +102,20 @@ class KaptRunner {
val javaFileObjects = fileManager.getJavaFileObjectsFromFiles(javaSourceFiles)
val parsedJavaFiles = compiler.parseFiles(javaFileObjects)
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))
compilerAfterAnnotationProcessing.close()
val compilerAfterAnnotationProcessing: JavaCompiler? = null
try {
compiler.processAnnotations(compiler.enterTrees(parsedJavaFiles))
} catch (e: AnnotationProcessingError) {
throw KaptError(KaptError.Kind.EXCEPTION, e.cause ?: e)
}
compilerAfterAnnotationProcessing?.close()
} finally {
processingEnvironment.close()
compiler.close()
fileManager.close()
}
}
+46 -5
View File
@@ -1,3 +1,4 @@
import org.jetbrains.kotlin.kapt3.KaptError
import org.jetbrains.kotlin.kapt3.KaptRunner
import org.junit.Assert
import org.junit.Assert.*
@@ -27,11 +28,8 @@ import javax.lang.model.element.TypeElement
class KaptRunnerTest {
private companion object {
val TEST_DATA_DIR = File("plugins/kapt3/testData/runner")
}
@Test
fun testSimple() {
val processor = object : AbstractProcessor() {
val SIMPLE_PROCESSOR = object : AbstractProcessor() {
override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
for (annotation in annotations) {
val annotationName = annotation.simpleName.toString()
@@ -54,12 +52,16 @@ class KaptRunnerTest {
override fun getSupportedAnnotationTypes() = setOf("test.MyAnnotation")
}
}
@Test
fun testSimple() {
val sourceOutputDir = Files.createTempDirectory("kaptRunner").toFile()
try {
KaptRunner().doAnnotationProcessing(
listOf(File(TEST_DATA_DIR, "Simple.java")),
listOf(processor),
listOf(SIMPLE_PROCESSOR),
emptyList(), // classpath
sourceOutputDir,
sourceOutputDir)
val myMethodFile = File(sourceOutputDir, "generated/MyMethodMyAnnotation.java")
@@ -68,4 +70,43 @@ class KaptRunnerTest {
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)
}
}
}
+1
View File
@@ -0,0 +1 @@
BLAHBLAH