From 5b8e6abdeb1ddb906c5693d8ca3ca2f57b20f740 Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Thu, 14 Jul 2016 23:39:31 +0300 Subject: [PATCH] Kapt: Support Java source root processing. Support resource file writing. (cherry picked from commit 7a06a91) --- .../annotation-processing.iml | 1 + .../AnnotationProcessingExtension.kt | 29 +++++++++++--- .../processing/AnnotationProcessingPlugin.kt | 19 ++++++++- .../annotation/processing/impl/KotlinFiler.kt | 22 ++++++++--- .../processing/impl/KotlinJavaFileObject.kt | 39 +++++++++++-------- 5 files changed, 81 insertions(+), 29 deletions(-) diff --git a/plugins/annotation-processing/annotation-processing.iml b/plugins/annotation-processing/annotation-processing.iml index 81a78835658..e6933c9078a 100644 --- a/plugins/annotation-processing/annotation-processing.iml +++ b/plugins/annotation-processing/annotation-processing.iml @@ -14,5 +14,6 @@ + \ No newline at end of file diff --git a/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/AnnotationProcessingExtension.kt b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/AnnotationProcessingExtension.kt index 000348d1212..f4c6f8aaa62 100644 --- a/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/AnnotationProcessingExtension.kt +++ b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/AnnotationProcessingExtension.kt @@ -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 + val generatedSourcesOutputDir: File, + val classesOutputDir: File, + val annotationProcessingClasspath: List, + val javaSourceRoots: List ) : 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() 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): List { @@ -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 diff --git a/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/AnnotationProcessingPlugin.kt b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/AnnotationProcessingPlugin.kt index 1e25d5486bc..753bffd7d0f 100644 --- a/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/AnnotationProcessingPlugin.kt +++ b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/AnnotationProcessingPlugin.kt @@ -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 = CompilerConfigurationKey.create("generated files output directory") + + val CLASS_FILES_OUTPUT_DIR: CompilerConfigurationKey = + CompilerConfigurationKey.create("class files output directory") val ANNOTATION_PROCESSOR_CLASSPATH: CompilerConfigurationKey> = CompilerConfigurationKey.create>("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", "", "Output path for generated files", required = false) + CliOption("generated", "", "Output path for the generated files", required = false) + + val CLASS_FILES_OUTPUT_DIR_OPTION: CliOption = + CliOption("classes", "", "Output path for the class files", required = false) val ANNOTATION_PROCESSOR_CLASSPATH_OPTION: CliOption = CliOption("apclasspath", "", "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()?.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) } } \ No newline at end of file diff --git a/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/impl/KotlinFiler.kt b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/impl/KotlinFiler.kt index 8398785203a..59392f1b1bd 100644 --- a/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/impl/KotlinFiler.kt +++ b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/impl/KotlinFiler.kt @@ -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 { diff --git a/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/impl/KotlinJavaFileObject.kt b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/impl/KotlinJavaFileObject.kt index 962522223d7..b8bcc67f68e 100644 --- a/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/impl/KotlinJavaFileObject.kt +++ b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/impl/KotlinJavaFileObject.kt @@ -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 } \ No newline at end of file