Kapt3: Output stubs to .java files in verbose mode

This commit is contained in:
Yan Zhulanow
2016-10-29 05:02:31 +03:00
committed by Yan Zhulanow
parent aa15e0ad67
commit 6b1fc6fc39
4 changed files with 101 additions and 44 deletions
@@ -58,10 +58,26 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
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<File>)
override fun apply(
project: Project,
kotlinCompile: KotlinCompile,
@@ -71,7 +87,6 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
): List<SubpluginOption> {
assert((variantData != null) xor (javaSourceSet != null))
val pluginOptions = mutableListOf<SubpluginOption>()
val kaptClasspath = arrayListOf<File>()
fun handleSourceSet(sourceSetName: String) {
@@ -81,7 +96,7 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
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<KotlinCompile> {
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<SubpluginOption> {
val pluginOptions = mutableListOf<SubpluginOption>()
val generatedFilesDir = getKaptGeneratedDir(project, sourceSetName)
if (variantData != null) {
(variantData as BaseVariantData<*>).addJavaSourceFoldersToModel(generatedFilesDir)
@@ -104,27 +131,13 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
// 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<KotlinCompile> {
val annotationsFile = File(kotlinCompile.taskBuildDirectory, "source-annotations.txt")
kotlinCompile.sourceAnnotationsRegistry = SourceAnnotationsRegistry(annotationsFile)
addMiscOptions(pluginOptions)
return pluginOptions
}
private fun Kapt3SubpluginContext.addMiscOptions(pluginOptions: MutableList<SubpluginOption>) {
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<SourceProvider>
get() = variantConfiguration.sortedSourceProviders
@@ -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<File>()
@@ -553,7 +553,7 @@ private fun Project.getAptDirsForSourceSet(sourceSetName: String): Pair<File, Fi
}
private fun Project.createAptConfiguration(sourceSetName: String, kotlinPluginVersion: String): Configuration {
val aptConfigurationName = Kapt2KotlinGradleSubplugin.getKaptConfigurationName(sourceSetName)
val aptConfigurationName = Kapt3KotlinGradleSubplugin.getKaptConfigurationName(sourceSetName)
val aptConfiguration = configurations.create(aptConfigurationName)
val kotlinAnnotationProcessingDep = "org.jetbrains.kotlin:kotlin-annotation-processing:$kotlinPluginVersion"
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.kapt3
import com.intellij.openapi.project.Project
import com.sun.tools.javac.tree.JCTree
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.state.GenerationState
@@ -36,12 +37,14 @@ import java.io.File
import java.net.URLClassLoader
import java.util.*
import javax.annotation.processing.Processor
import com.sun.tools.javac.util.List as JavacList
class Kapt3AnalysisCompletedHandlerExtension(
val annotationProcessingClasspath: List<File>,
val javaSourceRoots: List<File>,
val sourcesOutputDir: File,
val classesOutputDir: File,
val classFilesOutputDir: File,
val stubsOutputDir: File?,
val options: Map<String, String>, //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<JCTree.JCCompilationUnit>) {
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<File>): List<Processor> {
val classLoader = URLClassLoader(classpath.map { it.toURI().toURL() }.toTypedArray())
return ServiceLoader.load(Processor::class.java, classLoader).toList()
@@ -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<String> =
CompilerConfigurationKey.create<String>("generated files output directory")
val SOURCE_OUTPUT_DIR: CompilerConfigurationKey<String> =
CompilerConfigurationKey.create<String>("source files output directory")
val CLASS_FILES_OUTPUT_DIR: CompilerConfigurationKey<String> =
val CLASS_OUTPUT_DIR: CompilerConfigurationKey<String> =
CompilerConfigurationKey.create<String>("class files output directory")
val STUBS_OUTPUT_DIR: CompilerConfigurationKey<String> =
CompilerConfigurationKey.create<String>("stubs output directory")
val ANNOTATION_PROCESSOR_CLASSPATH: CompilerConfigurationKey<List<String>> =
CompilerConfigurationKey.create<List<String>>("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", "<path>", "Output path for the generated files", required = false)
val SOURCE_OUTPUT_DIR_OPTION: CliOption =
CliOption("sources", "<path>", "Output path for the generated files", required = false)
val CLASS_FILES_OUTPUT_DIR_OPTION: CliOption =
val CLASS_OUTPUT_DIR_OPTION: CliOption =
CliOption("classes", "<path>", "Output path for the class files", required = false)
val STUBS_OUTPUT_DIR_OPTION: CliOption =
CliOption("stubs", "<path>", "Output path for the Java stubs", required = false)
val ANNOTATION_PROCESSOR_CLASSPATH_OPTION: CliOption =
CliOption("apclasspath", "<classpath>", "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<CliOption> =
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 <T> CompilerConfiguration.appendList(option: CompilerConfigurationKey<List<T>>, 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<JavaSourceRoot>().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)
}
}