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.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()
} }
} }
+46 -5
View File
@@ -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)
}
}
} }
+1
View File
@@ -0,0 +1 @@
BLAHBLAH