Kapt: Support Java 9 in compiler plugin

This commit is contained in:
Yan Zhulanow
2017-10-17 20:54:09 +03:00
parent e97b3ed324
commit c6eab6f72e
5 changed files with 107 additions and 22 deletions
@@ -36,6 +36,7 @@ import org.jetbrains.kotlin.kapt3.AptMode.*
import org.jetbrains.kotlin.kapt3.diagnostic.KaptError
import org.jetbrains.kotlin.kapt3.stubs.ClassFileToSourceStubConverter
import org.jetbrains.kotlin.kapt3.util.KaptLogger
import org.jetbrains.kotlin.kapt3.util.getPackageNameJava9Aware
import org.jetbrains.kotlin.modules.TargetId
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingContext
@@ -271,7 +272,7 @@ abstract class AbstractKapt3Extension(
for (stub in stubs) {
val className = (stub.defs.first { it is JCTree.JCClassDecl } as JCTree.JCClassDecl).simpleName.toString()
val packageName = stub.packageName?.toString() ?: ""
val packageName = stub.getPackageNameJava9Aware()?.toString() ?: ""
val packageDir = if (packageName.isEmpty()) stubsOutputDir else File(stubsOutputDir, packageName.replace('.', '/'))
packageDir.mkdirs()
File(packageDir, className + ".java").writeText(stub.toString())
@@ -25,9 +25,12 @@ import com.sun.tools.javac.processing.JavacFiler
import com.sun.tools.javac.processing.JavacProcessingEnvironment
import com.sun.tools.javac.tree.JCTree
import org.jetbrains.kotlin.kapt3.diagnostic.KaptError
import org.jetbrains.kotlin.kapt3.util.isJava9OrLater
import org.jetbrains.kotlin.kapt3.util.putJavacOption
import java.io.File
import javax.annotation.processing.Processor
import javax.tools.JavaFileManager
import javax.tools.JavaFileObject
import com.sun.tools.javac.util.List as JavacList
fun KaptContext<*>.doAnnotationProcessing(
@@ -45,32 +48,55 @@ fun KaptContext<*>.doAnnotationProcessing(
put(Option.PROC, "only") // Only process annotations
if (!withJdk) {
put(Option.BOOTCLASSPATH, "") // No boot classpath
putJavacOption("BOOTCLASSPATH", "BOOT_CLASS_PATH", "") // No boot classpath
}
put(Option.CLASSPATH, compileClasspath.joinToString(File.pathSeparator) { it.canonicalPath })
put(Option.PROCESSORPATH, annotationProcessingClasspath.joinToString(File.pathSeparator) { it.canonicalPath })
putJavacOption("CLASSPATH", "CLASS_PATH", compileClasspath.joinToString(File.pathSeparator) { it.canonicalPath })
putJavacOption("PROCESSORPATH", "PROCESSOR_PATH", annotationProcessingClasspath.joinToString(File.pathSeparator) { it.canonicalPath })
put(Option.PROCESSOR, annotationProcessors)
put(Option.S, sourcesOutputDir.canonicalPath)
put(Option.D, classesOutputDir.canonicalPath)
put(Option.ENCODING, "UTF-8")
}
val fileManager = context.get(JavaFileManager::class.java) as JavacFileManager
val processingEnvironment = JavacProcessingEnvironment.instance(context)
val compilerAfterAP: JavaCompiler
try {
compiler.initProcessAnnotations(processors)
if (isJava9OrLater) {
val initProcessAnnotationsMethod = JavaCompiler::class.java.declaredMethods.single { it.name == "initProcessAnnotations" }
initProcessAnnotationsMethod.invoke(compiler, processors, emptyList<JavaFileObject>(), emptyList<String>())
}
else {
compiler.initProcessAnnotations(processors)
}
val javaFileObjects = fileManager.getJavaFileObjectsFromFiles(javaSourceFiles)
val parsedJavaFiles = compiler.parseFiles(javaFileObjects)
var parsedJavaFiles = compiler.stopIfErrorOccurred(
CompileStates.CompileState.PARSE, compiler.parseFiles(javaFileObjects))
if (isJava9OrLater) {
val initModulesMethod = compiler.javaClass.getMethod("initModules", JavacList::class.java)
@Suppress("UNCHECKED_CAST")
parsedJavaFiles = compiler.stopIfErrorOccurred(
CompileStates.CompileState.PARSE,
initModulesMethod.invoke(compiler, parsedJavaFiles) as JavacList<JCTree.JCCompilationUnit>)
}
compilerAfterAP = try {
javaLog.interceptorData.files = parsedJavaFiles.map { it.sourceFile to it }.toMap()
val analyzedFiles = compiler.stopIfErrorOccurred(
CompileStates.CompileState.PARSE, compiler.enterTrees(parsedJavaFiles + additionalSources))
compiler.processAnnotations(analyzedFiles)
if (isJava9OrLater) {
val processAnnotationsMethod = compiler.javaClass.getMethod("processAnnotations", JavacList::class.java)
processAnnotationsMethod.invoke(compiler, analyzedFiles)
compiler
}
else {
compiler.processAnnotations(analyzedFiles)
}
} catch (e: AnnotationProcessingError) {
throw KaptError(KaptError.Kind.EXCEPTION, e.cause ?: e)
}
@@ -16,10 +16,8 @@
package org.jetbrains.kotlin.kapt3.javac
import com.sun.tools.javac.file.BaseFileObject
import com.sun.tools.javac.file.JavacFileManager
import com.sun.tools.javac.tree.JCTree
import java.io.*
import org.jetbrains.kotlin.kapt3.util.getPackageNameJava9Aware
import java.net.URI
import java.net.URL
import javax.lang.model.element.Modifier
@@ -29,12 +27,9 @@ import javax.tools.JavaFileObject
class KaptJavaFileObject(
val compilationUnit: JCTree.JCCompilationUnit,
val clazz: JCTree.JCClassDecl,
fileManager: JavacFileManager,
val timestamp: Long = System.currentTimeMillis()
) : BaseFileObject(fileManager) {
override fun getShortName() = clazz.simpleName.toString()
override fun inferBinaryName(path: MutableIterable<File>?) = throw UnsupportedOperationException()
) : JavaFileObject {
override fun toString() = "${javaClass.simpleName}[$name]"
override fun isNameCompatible(simpleName: String?, kind: JavaFileObject.Kind?): Boolean {
if (simpleName == null || kind == null) return false
@@ -44,7 +39,7 @@ class KaptJavaFileObject(
override fun getKind() = JavaFileObject.Kind.SOURCE
override fun getName(): String {
val packageName = compilationUnit.packageName
val packageName = compilationUnit.getPackageNameJava9Aware()
if (packageName == null || packageName.toString() == "") {
return clazz.name.toString() + ".java"
}
@@ -120,8 +120,9 @@ class ClassFileToSourceStubConverter(
JavacList.nil(),
JavacList.nil())
val topLevel = treeMaker.TopLevel(JavacList.nil(), treeMaker.SimpleName("error"), JavacList.of(nonExistentClass))
topLevel.sourcefile = KaptJavaFileObject(topLevel, nonExistentClass, fileManager)
val topLevel = treeMaker.TopLevelJava9Aware(treeMaker.SimpleName("error"), JavacList.of(nonExistentClass))
topLevel.sourcefile = KaptJavaFileObject(topLevel, nonExistentClass)
// We basically don't need to add binding for NonExistentClass
return topLevel
@@ -135,7 +136,6 @@ class ClassFileToSourceStubConverter(
// Nested classes will be processed during the outer classes conversion
if ((descriptor as? ClassDescriptor)?.isNested ?: false) return null
val packageAnnotations = JavacList.nil<JCAnnotation>()
val packageName = ktFile.packageFqName.asString()
val packageClause = if (packageName.isEmpty()) null else treeMaker.FqName(packageName)
@@ -144,9 +144,9 @@ class ClassFileToSourceStubConverter(
val imports = if (correctErrorTypes) convertImports(ktFile, classDeclaration) else JavacList.nil()
val classes = JavacList.of<JCTree>(classDeclaration)
val topLevel = treeMaker.TopLevel(packageAnnotations, packageClause, imports + classes)
val topLevel = treeMaker.TopLevelJava9Aware(packageClause, imports + classes)
KaptJavaFileObject(topLevel, classDeclaration, fileManager).apply {
KaptJavaFileObject(topLevel, classDeclaration).apply {
topLevel.sourcefile = this
_bindings[clazz.name] = this
}
@@ -0,0 +1,63 @@
/*
* Copyright 2010-2017 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.util
import com.intellij.openapi.util.SystemInfo
import com.sun.tools.javac.main.Option
import com.sun.tools.javac.tree.JCTree
import com.sun.tools.javac.tree.TreeMaker
import com.sun.tools.javac.util.Options
import com.sun.tools.javac.util.List as JavacList
import org.jetbrains.kotlin.kapt3.plus
internal val isJava9OrLater: Boolean
get() = SystemInfo.isJavaVersionAtLeast("9")
internal fun Options.putJavacOption(jdk8Name: String, jdk9Name: String, value: String) {
val option = if (isJava9OrLater) {
Option.valueOf(jdk9Name)
}
else {
Option.valueOf(jdk8Name)
}
put(option, value)
}
internal fun TreeMaker.TopLevelJava9Aware(packageClause: JCTree.JCExpression?, declarations: JavacList<JCTree>): JCTree.JCCompilationUnit {
return if (isJava9OrLater) {
val topLevelMethod = TreeMaker::class.java.declaredMethods.single { it.name == "TopLevel" }
val packageDecl: JCTree? = packageClause?.let {
val packageDeclMethod = TreeMaker::class.java.methods.single { it.name == "PackageDecl" }
packageDeclMethod.invoke(this, JavacList.nil<JCTree>(), packageClause) as JCTree
}
val allDeclarations = if (packageDecl != null) JavacList.of(packageDecl) + declarations else declarations
topLevelMethod.invoke(this, allDeclarations) as JCTree.JCCompilationUnit
}
else {
TopLevel(JavacList.nil(), packageClause, declarations)
}
}
internal fun JCTree.JCCompilationUnit.getPackageNameJava9Aware(): JCTree? {
return if (isJava9OrLater) {
JCTree.JCCompilationUnit::class.java.getDeclaredMethod("getPackageName").invoke(this) as JCTree?
}
else {
this.packageName
}
}