From 6b1fc6fc399b8a45936db47112abe6a379f0d946 Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Sat, 29 Oct 2016 05:02:31 +0300 Subject: [PATCH] Kapt3: Output stubs to .java files in verbose mode --- .../internal/Kapt3KotlinGradleSubplugin.kt | 71 ++++++++++++++----- .../kotlin/gradle/plugin/KotlinPlugin.kt | 10 +-- .../Kapt3AnalysisCompletedHandlerExtension.kt | 22 +++++- .../org/jetbrains/kotlin/kapt3/Kapt3Plugin.kt | 42 ++++++----- 4 files changed, 101 insertions(+), 44 deletions(-) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/Kapt3KotlinGradleSubplugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/Kapt3KotlinGradleSubplugin.kt index b6f2b39feb9..498bfb878ce 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/Kapt3KotlinGradleSubplugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/Kapt3KotlinGradleSubplugin.kt @@ -58,10 +58,26 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { return File(project.project.buildDir, "generated/source/kapt/$sourceSetName") } + fun getKaptStubsDir(project: Project, sourceSetName: String): File { + val dir = File(project.project.buildDir, "tmp/kapt3/stubs/$sourceSetName") + dir.mkdirs() + return dir + } + private fun Project.findKaptConfiguration(sourceSetName: String): Configuration? { return project.configurations.findByName(getKaptConfigurationName(sourceSetName)) } + private class Kapt3SubpluginContext( + val project: Project, + val kotlinCompile: KotlinCompile, + val javaCompile: AbstractCompile, + val variantData: Any?, + val javaSourceSet: SourceSet?, + val sourceSetName: String, + val kaptExtension: KaptExtension, + val kaptClasspath: MutableList) + override fun apply( project: Project, kotlinCompile: KotlinCompile, @@ -71,7 +87,6 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { ): List { assert((variantData != null) xor (javaSourceSet != null)) - val pluginOptions = mutableListOf() val kaptClasspath = arrayListOf() fun handleSourceSet(sourceSetName: String) { @@ -81,7 +96,7 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { kaptClasspath.addAll(kaptConfiguration.resolve()) } } - + val sourceSetName = if (variantData != null) { for (provider in (variantData as BaseVariantData<*>).sourceProviders) { handleSourceSet((provider as AndroidSourceSet).name) @@ -96,6 +111,18 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { javaSourceSet.name } + val kaptExtension = project.extensions.getByType(KaptExtension::class.java) + + val context = Kapt3SubpluginContext(project, kotlinCompile, javaCompile, + variantData, javaSourceSet, sourceSetName, kaptExtension, kaptClasspath) + + return context.buildOptions() + } + + // This method should be called no more than once for each Kapt3SubpluginContext + private fun Kapt3SubpluginContext.buildOptions(): List { + val pluginOptions = mutableListOf() + val generatedFilesDir = getKaptGeneratedDir(project, sourceSetName) if (variantData != null) { (variantData as BaseVariantData<*>).addJavaSourceFoldersToModel(generatedFilesDir) @@ -104,27 +131,13 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { // Skip annotation processing in kotlinc if no kapt dependencies were provided if (kaptClasspath.isEmpty()) return emptyList() kaptClasspath.forEach { pluginOptions += SubpluginOption("apclasspath", it.absolutePath) } - + javaCompile.source(generatedFilesDir) - - // Completely disable annotation processing in Java - (javaCompile as? JavaCompile)?.let { javaCompile -> - val options = javaCompile.options - options.compilerArgs = options.compilerArgs.filter { !it.startsWith("-proc:") } + "-proc:none" - } + disableAnnotationProcessingInJavaTask() - pluginOptions += SubpluginOption("generated", generatedFilesDir.canonicalPath) + pluginOptions += SubpluginOption("sources", generatedFilesDir.canonicalPath) pluginOptions += SubpluginOption("classes", kotlinCompile.destinationDir.canonicalPath) - val kaptExtension = project.extensions.getByType(KaptExtension::class.java) - if (kaptExtension.generateStubs) { - project.logger.warn("'kapt.generateStubs' is not used by the 'kotlin-kapt' plugin") - } - - if (project.hasProperty(VERBOSE_OPTION_NAME) && project.property(VERBOSE_OPTION_NAME) == "true") { - pluginOptions += SubpluginOption("verbose", "true") - } - val androidPlugin = variantData?.let { project.extensions.findByName("android") as? BaseExtension } @@ -136,9 +149,29 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { val annotationsFile = File(kotlinCompile.taskBuildDirectory, "source-annotations.txt") kotlinCompile.sourceAnnotationsRegistry = SourceAnnotationsRegistry(annotationsFile) + addMiscOptions(pluginOptions) + return pluginOptions } + private fun Kapt3SubpluginContext.addMiscOptions(pluginOptions: MutableList) { + if (kaptExtension.generateStubs) { + project.logger.warn("'kapt.generateStubs' is not used by the 'kotlin-kapt' plugin") + } + + if (project.hasProperty(VERBOSE_OPTION_NAME) && project.property(VERBOSE_OPTION_NAME) == "true") { + pluginOptions += SubpluginOption("verbose", "true") + pluginOptions += SubpluginOption("stubs", getKaptStubsDir(project, sourceSetName).canonicalPath) + } + } + + private fun Kapt3SubpluginContext.disableAnnotationProcessingInJavaTask() { + (javaCompile as? JavaCompile)?.let { javaCompile -> + val options = javaCompile.options + options.compilerArgs = options.compilerArgs.filter { !it.startsWith("-proc:") } + "-proc:none" + } + } + private val BaseVariantData<*>.sourceProviders: List get() = variantConfiguration.sortedSourceProviders diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt index 5359e50313d..e91a5a6a519 100755 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt @@ -21,8 +21,8 @@ import org.gradle.api.tasks.compile.AbstractCompile import org.gradle.api.tasks.compile.JavaCompile import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptionsImpl import org.jetbrains.kotlin.gradle.internal.AnnotationProcessingManager -import org.jetbrains.kotlin.gradle.internal.Kapt2GradleSubplugin -import org.jetbrains.kotlin.gradle.internal.Kapt2KotlinGradleSubplugin +import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin +import org.jetbrains.kotlin.gradle.internal.Kapt3KotlinGradleSubplugin import org.jetbrains.kotlin.gradle.internal.initKapt import org.jetbrains.kotlin.gradle.plugin.android.AndroidGradleWrapper import org.jetbrains.kotlin.gradle.tasks.* @@ -123,7 +123,7 @@ internal class Kotlin2JvmSourceSetProcessor( var kotlinAfterJavaTask: KotlinCompile? = null - if (aptConfiguration.dependencies.size > 1 && !Kapt2GradleSubplugin.isEnabled(project)) { + if (aptConfiguration.dependencies.size > 1 && !Kapt3GradleSubplugin.isEnabled(project)) { javaTask.dependsOn(aptConfiguration.buildDependencies) val (aptOutputDir, aptWorkingDir) = project.getAptDirsForSourceSet(sourceSetName) @@ -340,7 +340,7 @@ internal open class KotlinAndroidPlugin( kotlinTask.description = "Compiles the $variantDataName kotlin." kotlinTask.setDependsOn(javaTask.dependsOn) - val isKapt2Enabled = Kapt2GradleSubplugin.isEnabled(project) + val isKapt2Enabled = Kapt3GradleSubplugin.isEnabled(project) val aptFiles = arrayListOf() @@ -553,7 +553,7 @@ private fun Project.getAptDirsForSourceSet(sourceSetName: String): Pair, val javaSourceRoots: List, val sourcesOutputDir: File, - val classesOutputDir: File, + val classFilesOutputDir: File, + val stubsOutputDir: File?, val options: Map, //TODO val aptOnly: Boolean, val logger: Logger @@ -102,9 +105,13 @@ class Kapt3AnalysisCompletedHandlerExtension( val jcCompilationUnitsForKotlinClasses = treeConverter.convert() logger.info { "Stubs for Kotlin classes: " + jcCompilationUnitsForKotlinClasses.joinToString { it.sourcefile.name } } + if (stubsOutputDir != null) { + saveStubs(stubsOutputDir, jcCompilationUnitsForKotlinClasses) + } + kaptRunner.doAnnotationProcessing( javaFilesFromJavaSourceRoots, processors, - annotationProcessingClasspath, sourcesOutputDir, classesOutputDir, jcCompilationUnitsForKotlinClasses) + annotationProcessingClasspath, sourcesOutputDir, classFilesOutputDir, jcCompilationUnitsForKotlinClasses) } catch (thr: Throwable) { if (thr !is KaptError || thr.kind != KaptError.Kind.ERROR_RAISED) { logger.exception(thr) @@ -126,6 +133,17 @@ class Kapt3AnalysisCompletedHandlerExtension( } } + private fun saveStubs(outputDir: File, stubs: JavacList) { + for (stub in stubs) { + val className = (stub.defs.first { it is JCTree.JCClassDecl } as JCTree.JCClassDecl).simpleName.toString() + + val packageName = stub.packageName?.toString() ?: "" + val packageDir = if (packageName.isEmpty()) outputDir else File(outputDir, packageName.replace('.', '/')) + packageDir.mkdirs() + File(packageDir, className + ".java").writeText(stub.toString()) + } + } + private fun loadProcessors(classpath: List): List { val classLoader = URLClassLoader(classpath.map { it.toURI().toURL() }.toTypedArray()) return ServiceLoader.load(Processor::class.java, classLoader).toList() diff --git a/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/Kapt3Plugin.kt b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/Kapt3Plugin.kt index 67e55d6b48d..3e42f0f6008 100644 --- a/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/Kapt3Plugin.kt +++ b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/Kapt3Plugin.kt @@ -30,18 +30,20 @@ import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.config.CompilerConfigurationKey import org.jetbrains.kotlin.config.JVMConfigurationKeys import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages -import org.jetbrains.kotlin.kapt3.Kapt3ConfigurationKeys.APT_ONLY import org.jetbrains.kotlin.kapt3.diagnostic.DefaultErrorMessagesKapt3 import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisCompletedHandlerExtension import java.io.File object Kapt3ConfigurationKeys { - val GENERATED_OUTPUT_DIR: CompilerConfigurationKey = - CompilerConfigurationKey.create("generated files output directory") + val SOURCE_OUTPUT_DIR: CompilerConfigurationKey = + CompilerConfigurationKey.create("source files output directory") - val CLASS_FILES_OUTPUT_DIR: CompilerConfigurationKey = + val CLASS_OUTPUT_DIR: CompilerConfigurationKey = CompilerConfigurationKey.create("class files output directory") + val STUBS_OUTPUT_DIR: CompilerConfigurationKey = + CompilerConfigurationKey.create("stubs output directory") + val ANNOTATION_PROCESSOR_CLASSPATH: CompilerConfigurationKey> = CompilerConfigurationKey.create>("annotation processor classpath") @@ -59,12 +61,15 @@ class Kapt3CommandLineProcessor : CommandLineProcessor { companion object { val ANNOTATION_PROCESSING_COMPILER_PLUGIN_ID: String = "org.jetbrains.kotlin.kapt3" - val GENERATED_OUTPUT_DIR_OPTION: CliOption = - CliOption("generated", "", "Output path for the generated files", required = false) + val SOURCE_OUTPUT_DIR_OPTION: CliOption = + CliOption("sources", "", "Output path for the generated files", required = false) - val CLASS_FILES_OUTPUT_DIR_OPTION: CliOption = + val CLASS_OUTPUT_DIR_OPTION: CliOption = CliOption("classes", "", "Output path for the class files", required = false) + val STUBS_OUTPUT_DIR_OPTION: CliOption = + CliOption("stubs", "", "Output path for the Java stubs", required = false) + val ANNOTATION_PROCESSOR_CLASSPATH_OPTION: CliOption = CliOption("apclasspath", "", "Annotation processor classpath", required = false, allowMultipleOccurrences = true) @@ -83,8 +88,8 @@ class Kapt3CommandLineProcessor : CommandLineProcessor { override val pluginId: String = ANNOTATION_PROCESSING_COMPILER_PLUGIN_ID override val pluginOptions: Collection = - listOf(GENERATED_OUTPUT_DIR_OPTION, ANNOTATION_PROCESSOR_CLASSPATH_OPTION, APT_OPTIONS_OPTION, - CLASS_FILES_OUTPUT_DIR_OPTION, VERBOSE_MODE_OPTION) + listOf(SOURCE_OUTPUT_DIR_OPTION, ANNOTATION_PROCESSOR_CLASSPATH_OPTION, APT_OPTIONS_OPTION, + CLASS_OUTPUT_DIR_OPTION, VERBOSE_MODE_OPTION, STUBS_OUTPUT_DIR_OPTION) private fun CompilerConfiguration.appendList(option: CompilerConfigurationKey>, value: T) { val paths = getList(option).toMutableList() @@ -96,8 +101,9 @@ class Kapt3CommandLineProcessor : CommandLineProcessor { when (option) { ANNOTATION_PROCESSOR_CLASSPATH_OPTION -> configuration.appendList(ANNOTATION_PROCESSOR_CLASSPATH, value) APT_OPTIONS_OPTION -> configuration.appendList(APT_OPTIONS, value) - GENERATED_OUTPUT_DIR_OPTION -> configuration.put(Kapt3ConfigurationKeys.GENERATED_OUTPUT_DIR, value) - CLASS_FILES_OUTPUT_DIR_OPTION -> configuration.put(Kapt3ConfigurationKeys.CLASS_FILES_OUTPUT_DIR, value) + SOURCE_OUTPUT_DIR_OPTION -> configuration.put(Kapt3ConfigurationKeys.SOURCE_OUTPUT_DIR, value) + CLASS_OUTPUT_DIR_OPTION -> configuration.put(Kapt3ConfigurationKeys.CLASS_OUTPUT_DIR, value) + STUBS_OUTPUT_DIR_OPTION -> configuration.put(Kapt3ConfigurationKeys.STUBS_OUTPUT_DIR, value) VERBOSE_MODE_OPTION -> configuration.put(Kapt3ConfigurationKeys.VERBOSE_MODE, value) APT_ONLY_OPTION -> configuration.put(Kapt3ConfigurationKeys.APT_ONLY, value) else -> throw CliOptionProcessingException("Unknown option: ${option.name}") @@ -107,7 +113,10 @@ class Kapt3CommandLineProcessor : CommandLineProcessor { class Kapt3ComponentRegistrar : ComponentRegistrar { override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) { - val generatedOutputDir = configuration.get(Kapt3ConfigurationKeys.GENERATED_OUTPUT_DIR) ?: return + val sourcesOutputDir = configuration.get(Kapt3ConfigurationKeys.SOURCE_OUTPUT_DIR)?.let(::File) ?: return + val classFilesOutputDir = configuration.get(Kapt3ConfigurationKeys.CLASS_OUTPUT_DIR)?.let(::File) ?: return + val stubsOutputDir = configuration.get(Kapt3ConfigurationKeys.STUBS_OUTPUT_DIR)?.let(::File) + val apClasspath = configuration.get(ANNOTATION_PROCESSOR_CLASSPATH)?.map(::File) ?: return val apOptions = (configuration.get(APT_OPTIONS) ?: listOf()) @@ -116,7 +125,6 @@ class Kapt3ComponentRegistrar : ComponentRegistrar { .map { it[0] to it[1] } .toMap() - val sourcesOutputDir = File(generatedOutputDir) sourcesOutputDir.mkdirs() val contentRoots = configuration[JVMConfigurationKeys.CONTENT_ROOTS] ?: emptyList() @@ -126,9 +134,6 @@ class Kapt3ComponentRegistrar : ComponentRegistrar { val javaSourceRoots = contentRoots.filterIsInstance().map { it.file } - val classesOutputDir = File(configuration.get(Kapt3ConfigurationKeys.CLASS_FILES_OUTPUT_DIR) - ?: configuration[JVMConfigurationKeys.MODULES]!!.first().getOutputDirectory()) - val isVerbose = configuration.get(Kapt3ConfigurationKeys.VERBOSE_MODE) == "true" val isAptOnly = configuration.get(Kapt3ConfigurationKeys.APT_ONLY) == "true" @@ -139,14 +144,15 @@ class Kapt3ComponentRegistrar : ComponentRegistrar { logger.info("Kapt3 is enabled.") logger.info("Do annotation processing only: $isAptOnly") logger.info("Source output directory: $sourcesOutputDir") - logger.info("Classes output directory: $classesOutputDir") + logger.info("Classes output directory: $classFilesOutputDir") + logger.info("Stubs output directory: $stubsOutputDir") logger.info("Annotation processing classpath: " + classpath.joinToString()) logger.info("Java source roots: " + javaSourceRoots.joinToString()) logger.info("Options: $apOptions") } val kapt3AnalysisCompletedHandlerExtension = Kapt3AnalysisCompletedHandlerExtension( - classpath, javaSourceRoots, sourcesOutputDir, classesOutputDir, apOptions, isAptOnly, logger) + classpath, javaSourceRoots, sourcesOutputDir, classFilesOutputDir, stubsOutputDir, apOptions, isAptOnly, logger) AnalysisCompletedHandlerExtension.registerExtension(project, kapt3AnalysisCompletedHandlerExtension) } } \ No newline at end of file