Kapt: Support Java source root processing.

Support resource file writing.
(cherry picked from commit 7a06a91)
This commit is contained in:
Yan Zhulanow
2016-07-14 23:39:31 +03:00
committed by Yan Zhulanow
parent 9d2144f221
commit 5b8e6abdeb
5 changed files with 81 additions and 29 deletions
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.annotation
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.StandardFileSystems
import com.intellij.psi.*
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analyzer.AnalysisResult
@@ -35,8 +36,10 @@ import java.util.*
import javax.annotation.processing.Processor
class AnnotationProcessingExtension(
val generatedOutputDir: File,
val annotationProcessingClasspath: List<String>
val generatedSourcesOutputDir: File,
val classesOutputDir: File,
val annotationProcessingClasspath: List<String>,
val javaSourceRoots: List<File>
) : AnalysisCompletedHandlerExtension {
private var annotationProcessingComplete = false
@@ -56,12 +59,25 @@ class AnnotationProcessingExtension(
val analysisContext = AnalysisContext(hashMapOf())
analysisContext.analyzeFiles(files)
val psiManager = PsiManager.getInstance(project)
for (javaSourceRoot in javaSourceRoots) {
javaSourceRoot.walk().filter { it.isFile && it.extension == "java" }.forEach {
val vFile = StandardFileSystems.local().findFileByPath(it.absolutePath)
if (vFile != null) {
val javaFile = psiManager.findFile(vFile) as? PsiJavaFile
if (javaFile != null) {
analysisContext.analyzeFile(javaFile)
}
}
}
}
val options = emptyMap<String, String>()
val javaPsiFacade = JavaPsiFacade.getInstance(project)
val projectScope = GlobalSearchScope.projectScope(project)
val filer = KotlinFiler(generatedOutputDir)
val filer = KotlinFiler(generatedSourcesOutputDir, classesOutputDir)
val messages = KotlinMessager()
val types = KotlinTypes(javaPsiFacade, PsiManager.getInstance(project), projectScope)
val elements = KotlinElements(javaPsiFacade, projectScope)
@@ -69,7 +85,6 @@ class AnnotationProcessingExtension(
val processingEnvironment = KotlinProcessingEnvironment(elements, types, messages, options, filer)
processors.forEach { it.init(processingEnvironment) }
// Round 1
val round1Environment = KotlinRoundEnvironment(analysisContext)
for (processor in processors) {
@@ -97,7 +112,7 @@ class AnnotationProcessingExtension(
}
annotationProcessingComplete = true
return AnalysisResult.RetryWithAdditionalJavaRoots(bindingContext, module, listOf(generatedOutputDir))
return AnalysisResult.RetryWithAdditionalJavaRoots(bindingContext, module, listOf(generatedSourcesOutputDir))
}
private fun loadAnnotationProcessors(classpath: List<String>): List<Processor> {
@@ -128,6 +143,10 @@ private fun AnalysisContext.analyzeFile(file: KtFile) {
}
}
private fun AnalysisContext.analyzeFile(file: PsiJavaFile) {
file.classes.forEach { analyzeDeclaration(it) }
}
private fun AnalysisContext.analyzeDeclaration(declaration: PsiElement) {
if (declaration !is PsiModifierListOwner) return
@@ -18,18 +18,23 @@ package org.jetbrains.kotlin.annotation.processing
import com.intellij.mock.MockProject
import org.jetbrains.kotlin.annotation.AnnotationProcessingExtension
import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot
import org.jetbrains.kotlin.compiler.plugin.CliOption
import org.jetbrains.kotlin.compiler.plugin.CliOptionProcessingException
import org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.CompilerConfigurationKey
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisCompletedHandlerExtension
import java.io.File
object AnnotationProcessingConfigurationKeys {
val GENERATED_OUTPUT_DIR: CompilerConfigurationKey<String> =
CompilerConfigurationKey.create<String>("generated files output directory")
val CLASS_FILES_OUTPUT_DIR: CompilerConfigurationKey<String> =
CompilerConfigurationKey.create<String>("class files output directory")
val ANNOTATION_PROCESSOR_CLASSPATH: CompilerConfigurationKey<List<String>> =
CompilerConfigurationKey.create<List<String>>("annotation processor classpath")
@@ -40,7 +45,10 @@ class AnnotationProcessingCommandLineProcessor : CommandLineProcessor {
val ANNOTATION_PROCESSING_COMPILER_PLUGIN_ID: String = "org.jetbrains.kotlin.kapt2"
val GENERATED_OUTPUT_DIR_OPTION: CliOption =
CliOption("generated", "<path>", "Output path for generated files", required = false)
CliOption("generated", "<path>", "Output path for the generated files", required = false)
val CLASS_FILES_OUTPUT_DIR_OPTION: CliOption =
CliOption("classes", "<path>", "Output path for the class files", required = false)
val ANNOTATION_PROCESSOR_CLASSPATH_OPTION: CliOption =
CliOption("apclasspath", "<classpath>", "Annotation processor classpath",
@@ -60,6 +68,7 @@ class AnnotationProcessingCommandLineProcessor : CommandLineProcessor {
configuration.put(AnnotationProcessingConfigurationKeys.ANNOTATION_PROCESSOR_CLASSPATH, paths)
}
GENERATED_OUTPUT_DIR_OPTION -> configuration.put(AnnotationProcessingConfigurationKeys.GENERATED_OUTPUT_DIR, value)
CLASS_FILES_OUTPUT_DIR_OPTION -> configuration.put(AnnotationProcessingConfigurationKeys.CLASS_FILES_OUTPUT_DIR, value)
else -> throw CliOptionProcessingException("Unknown option: ${option.name}")
}
}
@@ -73,7 +82,13 @@ class AnnotationProcessingComponentRegistrar : ComponentRegistrar {
val generatedOutputDirFile = File(generatedOutputDir)
generatedOutputDirFile.mkdirs()
val annotationProcessingExtension = AnnotationProcessingExtension(generatedOutputDirFile, classpath)
val javaRoots = configuration[JVMConfigurationKeys.CONTENT_ROOTS]
?.filterIsInstance<JavaSourceRoot>()?.map { it.file } ?: emptyList()
val classesOutputDir = File(configuration.get(AnnotationProcessingConfigurationKeys.CLASS_FILES_OUTPUT_DIR)
?: configuration[JVMConfigurationKeys.MODULES]!!.first().getOutputDirectory())
val annotationProcessingExtension = AnnotationProcessingExtension(generatedOutputDirFile, classesOutputDir, classpath, javaRoots)
AnalysisCompletedHandlerExtension.registerExtension(project, annotationProcessingExtension)
}
}
@@ -22,8 +22,9 @@ import javax.lang.model.element.Element
import javax.tools.FileObject
import javax.tools.JavaFileManager
import javax.tools.JavaFileObject
import javax.tools.StandardLocation
class KotlinFiler(val generatedDir: File) : Filer {
class KotlinFiler(val generatedSourceDir: File, val classesOutputDir: File) : Filer {
private companion object {
val PACKAGE_INFO_SUFFIX = ".packageInfo"
}
@@ -33,7 +34,7 @@ class KotlinFiler(val generatedDir: File) : Filer {
private fun createDotGeneratedIfNeeded() {
if (!dotGeneratedGenerated) {
dotGeneratedGenerated = true
File(generatedDir, ".generated").writeText("Generated by kapt2")
File(generatedSourceDir, ".generated").writeText("Generated by kapt2")
}
}
@@ -46,7 +47,7 @@ class KotlinFiler(val generatedDir: File) : Filer {
val packageName = fqName.substringBeforeLast('.')
val packageDir = File(generatedDir, packageName.replace('.', '/'))
val packageDir = File(generatedSourceDir, packageName.replace('.', '/'))
packageDir.mkdirs()
val fileName = fqName.substringAfterLast('.') + (if (isPackageInfo) PACKAGE_INFO_SUFFIX else extension)
@@ -62,12 +63,23 @@ class KotlinFiler(val generatedDir: File) : Filer {
}
override fun createResource(
location: JavaFileManager.Location?,
location: JavaFileManager.Location,
pkg: CharSequence,
relativeName: CharSequence,
vararg originatingElements: Element?
): FileObject? {
throw UnsupportedOperationException()
val baseDir = when (location) {
StandardLocation.CLASS_OUTPUT -> classesOutputDir
StandardLocation.SOURCE_OUTPUT -> generatedSourceDir
else -> throw IllegalArgumentException("Location is not supported: $location (${location.name})")
}
val targetDir = File(baseDir, pkg.toString().replace('.', '/'))
targetDir.mkdirs()
val targetFile = File(targetDir, relativeName.toString())
return KotlinFileObject(targetFile)
}
override fun createClassFile(name: CharSequence, vararg originatingElements: Element?): JavaFileObject {
@@ -17,9 +17,30 @@
package org.jetbrains.kotlin.annotation.processing.impl
import java.io.File
import javax.tools.FileObject
import javax.tools.JavaFileObject
class KotlinJavaFileObject(val file: File) : JavaFileObject {
class KotlinJavaFileObject(file: File) : KotlinAbstractFileObject(file), JavaFileObject {
//TODO
override fun isNameCompatible(simpleName: String, kind: JavaFileObject.Kind) = true
override fun getKind() = when (file.extension) {
"class" -> JavaFileObject.Kind.CLASS
"java" -> JavaFileObject.Kind.SOURCE
"html" -> JavaFileObject.Kind.HTML
else -> JavaFileObject.Kind.OTHER
}
//TODO
override fun getAccessLevel() = null
//TODO
override fun getNestingKind() = null
}
class KotlinFileObject(file: File) : KotlinAbstractFileObject(file)
abstract class KotlinAbstractFileObject(val file: File) : FileObject {
override fun openOutputStream() = file.outputStream()
override fun getName() = file.name
@@ -39,20 +60,4 @@ class KotlinJavaFileObject(val file: File) : JavaFileObject {
override fun openReader(ignoreEncodingErrors: Boolean) = file.reader()
override fun delete() = file.delete()
//TODO
override fun isNameCompatible(simpleName: String, kind: JavaFileObject.Kind) = true
override fun getKind() = when (file.extension) {
"class" -> JavaFileObject.Kind.CLASS
"java" -> JavaFileObject.Kind.SOURCE
"html" -> JavaFileObject.Kind.HTML
else -> JavaFileObject.Kind.OTHER
}
//TODO
override fun getAccessLevel() = null
//TODO
override fun getNestingKind() = null
}