diff --git a/.idea/modules.xml b/.idea/modules.xml index 021661f45d5..7be217f18b4 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -68,6 +68,7 @@ + diff --git a/plugins/kapt3/kapt3.iml b/plugins/kapt3/kapt3.iml new file mode 100644 index 00000000000..b87c6efed97 --- /dev/null +++ b/plugins/kapt3/kapt3.iml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/kapt3/src/KaptJavaCompiler.kt b/plugins/kapt3/src/KaptJavaCompiler.kt new file mode 100644 index 00000000000..4adc7cb5deb --- /dev/null +++ b/plugins/kapt3/src/KaptJavaCompiler.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.kapt3 + +import com.sun.tools.javac.comp.CompileStates +import com.sun.tools.javac.main.JavaCompiler +import com.sun.tools.javac.util.Context + +class KaptJavaCompiler(context: Context) : JavaCompiler(context) { + public override fun shouldStop(cs: CompileStates.CompileState) = super.shouldStop(cs) + + companion object { + fun preRegister(context: Context) { + context.put(compilerKey, Context.Factory(::KaptJavaCompiler)) + } + } +} \ No newline at end of file diff --git a/plugins/kapt3/src/KaptRunner.kt b/plugins/kapt3/src/KaptRunner.kt new file mode 100644 index 00000000000..a3c93ba6ef0 --- /dev/null +++ b/plugins/kapt3/src/KaptRunner.kt @@ -0,0 +1,69 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.kapt3 + +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.JavacProcessingEnvironment +import com.sun.tools.javac.util.Context +import com.sun.tools.javac.util.Log +import com.sun.tools.javac.util.Options +import java.io.File +import java.io.PrintWriter +import javax.annotation.processing.Processor +import javax.tools.JavaFileManager + +class KaptError : RuntimeException { + constructor(message: String) : super(message) + constructor(message: String, cause: Throwable) : super(message, cause) +} + +class KaptRunner { + fun doAnnotationProcessing(javaSourceFiles: List, processors: List, sourceOutputDir: File, classOutputDir: File) { + val context = Context() + JavacFileManager.preRegister(context) + KaptJavaCompiler.preRegister(context) + + val options = Options.instance(context) + context.put(Log.outKey, PrintWriter(System.err, true)) + options.put(Option.PROC, "only") // Only process annotations + options.put(Option.S, sourceOutputDir.canonicalPath) + options.put(Option.D, classOutputDir.canonicalPath) + + val fileManager = context.get(JavaFileManager::class.java) as JavacFileManager + val compiler = JavaCompiler.instance(context) as KaptJavaCompiler + val processingEnvironment = JavacProcessingEnvironment.instance(context) + + try { + compiler.initProcessAnnotations(processors) + + val javaFileObjects = fileManager.getJavaFileObjectsFromFiles(javaSourceFiles) + val parsedJavaFiles = compiler.parseFiles(javaFileObjects) + if (compiler.shouldStop(CompileStates.CompileState.PARSE)) { + throw KaptError("Error while parsing Java files.") + } + + val compilerAfterAnnotationProcessing = compiler.processAnnotations(compiler.enterTrees(parsedJavaFiles)) + compilerAfterAnnotationProcessing.close() + } finally { + processingEnvironment.close() + fileManager.close() + } + } +} \ No newline at end of file diff --git a/plugins/kapt3/test/KaptRunnerTest.kt b/plugins/kapt3/test/KaptRunnerTest.kt new file mode 100644 index 00000000000..959fa9f33f9 --- /dev/null +++ b/plugins/kapt3/test/KaptRunnerTest.kt @@ -0,0 +1,71 @@ +import org.jetbrains.kotlin.kapt3.KaptRunner +import org.junit.Assert +import org.junit.Assert.* +import org.junit.Test +import java.io.File +import java.nio.file.Files +import javax.annotation.processing.AbstractProcessor +import javax.annotation.processing.RoundEnvironment +import javax.lang.model.element.TypeElement + +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class KaptRunnerTest { + private companion object { + val TEST_DATA_DIR = File("plugins/kapt3/testData/runner") + } + + @Test + fun testSimple() { + val processor = object : AbstractProcessor() { + override fun process(annotations: Set, 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(""" + package generated; + class $generatedClassName {} + """.trimIndent()) + } + } + } + + return true; + } + + override fun getSupportedAnnotationTypes() = setOf("test.MyAnnotation") + } + + val sourceOutputDir = Files.createTempDirectory("kaptRunner").toFile() + try { + KaptRunner().doAnnotationProcessing( + listOf(File(TEST_DATA_DIR, "Simple.java")), + listOf(processor), + sourceOutputDir, + sourceOutputDir) + val myMethodFile = File(sourceOutputDir, "generated/MyMethodMyAnnotation.java") + assertTrue(myMethodFile.exists()) + } finally { + sourceOutputDir.deleteRecursively() + } + } +} \ No newline at end of file diff --git a/plugins/kapt3/testData/runner/Simple.java b/plugins/kapt3/testData/runner/Simple.java new file mode 100644 index 00000000000..f2807ab9651 --- /dev/null +++ b/plugins/kapt3/testData/runner/Simple.java @@ -0,0 +1,12 @@ +package test; + +class Simple { + @MyAnnotation + void myMethod() { + // do nothing + } +} + +@interface MyAnnotation { + +} \ No newline at end of file