Kapt3: Always write Kotlin stubs to .java files, and make javac always parse them from source files, not from JCTree (in order to support incremental compilation).

This commit is contained in:
Yan Zhulanow
2016-12-28 18:22:44 +03:00
committed by Yan Zhulanow
parent 9b9b36e332
commit 78a3dae314
4 changed files with 19 additions and 16 deletions
@@ -180,10 +180,10 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
} }
pluginOptions += SubpluginOption("useLightAnalysis", "${kaptExtension.useLightAnalysis}") pluginOptions += SubpluginOption("useLightAnalysis", "${kaptExtension.useLightAnalysis}")
pluginOptions += SubpluginOption("stubs", getKaptStubsDir(project, sourceSetName).canonicalPath)
if (project.hasProperty(VERBOSE_OPTION_NAME) && project.property(VERBOSE_OPTION_NAME) == "true") { if (project.hasProperty(VERBOSE_OPTION_NAME) && project.property(VERBOSE_OPTION_NAME) == "true") {
pluginOptions += SubpluginOption("verbose", "true") pluginOptions += SubpluginOption("verbose", "true")
pluginOptions += SubpluginOption("stubs", getKaptStubsDir(project, sourceSetName).canonicalPath)
} }
} }
@@ -18,7 +18,6 @@ package org.jetbrains.kotlin.kapt3
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.sun.tools.javac.tree.JCTree import com.sun.tools.javac.tree.JCTree
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit
import org.jetbrains.kotlin.analyzer.AnalysisResult import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
@@ -49,7 +48,7 @@ class ClasspathBasedKapt3Extension(
javaSourceRoots: List<File>, javaSourceRoots: List<File>,
sourcesOutputDir: File, sourcesOutputDir: File,
classFilesOutputDir: File, classFilesOutputDir: File,
stubsOutputDir: File?, stubsOutputDir: File,
incrementalDataOutputDir: File?, incrementalDataOutputDir: File?,
options: Map<String, String>, options: Map<String, String>,
aptOnly: Boolean, aptOnly: Boolean,
@@ -98,7 +97,7 @@ abstract class AbstractKapt3Extension(
val javaSourceRoots: List<File>, val javaSourceRoots: List<File>,
val sourcesOutputDir: File, val sourcesOutputDir: File,
val classFilesOutputDir: File, val classFilesOutputDir: File,
val stubsOutputDir: File?, val stubsOutputDir: File,
val incrementalDataOutputDir: File?, val incrementalDataOutputDir: File?,
val options: Map<String, String>, val options: Map<String, String>,
val aptOnly: Boolean, val aptOnly: Boolean,
@@ -141,13 +140,13 @@ abstract class AbstractKapt3Extension(
val (kaptContext, generationState) = compileStubs(project, module, bindingTrace.bindingContext, files.toList()) val (kaptContext, generationState) = compileStubs(project, module, bindingTrace.bindingContext, files.toList())
try { try {
generateKotlinSourceStubs(kaptContext, generationState)
val javaSourceFiles = collectJavaSourceFiles() val javaSourceFiles = collectJavaSourceFiles()
val kotlinSourceStubs = generateKotlinSourceStubs(kaptContext, generationState)
val (annotationProcessingTime) = measureTimeMillis { val (annotationProcessingTime) = measureTimeMillis {
kaptContext.doAnnotationProcessing( kaptContext.doAnnotationProcessing(
javaSourceFiles, processors, compileClasspath, annotationProcessingClasspath, javaSourceFiles, processors, compileClasspath, annotationProcessingClasspath,
sourcesOutputDir, classFilesOutputDir, kotlinSourceStubs) sourcesOutputDir, classFilesOutputDir)
} }
logger.info { "Annotation processing took $annotationProcessingTime ms" } logger.info { "Annotation processing took $annotationProcessingTime ms" }
@@ -202,7 +201,7 @@ abstract class AbstractKapt3Extension(
return Pair(KaptContext(logger, compiledClasses, origins, options), generationState) return Pair(KaptContext(logger, compiledClasses, origins, options), generationState)
} }
private fun generateKotlinSourceStubs(kaptContext: KaptContext, generationState: GenerationState): JavacList<JCCompilationUnit> { private fun generateKotlinSourceStubs(kaptContext: KaptContext, generationState: GenerationState) {
val converter = ClassFileToSourceStubConverter(kaptContext, generationState.typeMapper, generateNonExistentClass = true) val converter = ClassFileToSourceStubConverter(kaptContext, generationState.typeMapper, generateNonExistentClass = true)
val (stubGenerationTime, kotlinSourceStubs) = measureTimeMillis { val (stubGenerationTime, kotlinSourceStubs) = measureTimeMillis {
@@ -214,11 +213,10 @@ abstract class AbstractKapt3Extension(
saveStubs(kotlinSourceStubs) saveStubs(kotlinSourceStubs)
saveIncrementalData(generationState, logger.messageCollector, converter) saveIncrementalData(generationState, logger.messageCollector, converter)
return kotlinSourceStubs
} }
private fun collectJavaSourceFiles(): List<File> { private fun collectJavaSourceFiles(): List<File> {
val javaFilesFromJavaSourceRoots = javaSourceRoots.flatMap { val javaFilesFromJavaSourceRoots = (javaSourceRoots + stubsOutputDir).flatMap {
root -> root.walk().filter { it.isFile && it.extension == "java" }.toList() root -> root.walk().filter { it.isFile && it.extension == "java" }.toList()
} }
logger.info { "Java source files: " + javaFilesFromJavaSourceRoots.joinToString { it.canonicalPath } } logger.info { "Java source files: " + javaFilesFromJavaSourceRoots.joinToString { it.canonicalPath } }
@@ -227,12 +225,11 @@ abstract class AbstractKapt3Extension(
} }
protected open fun saveStubs(stubs: JavacList<JCTree.JCCompilationUnit>) { protected open fun saveStubs(stubs: JavacList<JCTree.JCCompilationUnit>) {
val outputDir = stubsOutputDir ?: return
for (stub in stubs) { for (stub in stubs) {
val className = (stub.defs.first { it is JCTree.JCClassDecl } as JCTree.JCClassDecl).simpleName.toString() val className = (stub.defs.first { it is JCTree.JCClassDecl } as JCTree.JCClassDecl).simpleName.toString()
val packageName = stub.packageName?.toString() ?: "" val packageName = stub.packageName?.toString() ?: ""
val packageDir = if (packageName.isEmpty()) outputDir else File(outputDir, packageName.replace('.', '/')) val packageDir = if (packageName.isEmpty()) stubsOutputDir else File(stubsOutputDir, packageName.replace('.', '/'))
packageDir.mkdirs() packageDir.mkdirs()
File(packageDir, className + ".java").writeText(stub.toString()) File(packageDir, className + ".java").writeText(stub.toString())
} }
@@ -246,7 +243,7 @@ abstract class AbstractKapt3Extension(
generationState.factory.writeAll(incrementalDataOutputDir) { file, sources, output -> generationState.factory.writeAll(incrementalDataOutputDir) { file, sources, output ->
val stubFileObject = converter.bindings[file.relativePath.substringBeforeLast(".class", missingDelimiterValue = "")] val stubFileObject = converter.bindings[file.relativePath.substringBeforeLast(".class", missingDelimiterValue = "")]
if (stubFileObject != null && stubsOutputDir != null) { if (stubFileObject != null) {
val stubFile = File(stubsOutputDir, stubFileObject.name) val stubFile = File(stubsOutputDir, stubFileObject.name)
if (stubFile.exists()) { if (stubFile.exists()) {
messageCollector.report(CompilerMessageSeverity.OUTPUT, messageCollector.report(CompilerMessageSeverity.OUTPUT,
@@ -153,10 +153,16 @@ class Kapt3ComponentRegistrar : ComponentRegistrar {
val apClasspath = configuration.get(ANNOTATION_PROCESSOR_CLASSPATH)?.map(::File) val apClasspath = configuration.get(ANNOTATION_PROCESSOR_CLASSPATH)?.map(::File)
if (sourcesOutputDir == null || classFilesOutputDir == null || apClasspath == null) { if (sourcesOutputDir == null || classFilesOutputDir == null || apClasspath == null || stubsOutputDir == null) {
if (isAptOnly) { if (isAptOnly) {
val subject = if (apClasspath == null) "Annotation processing classpath" else "Generated files output directories" val nonExistentOptionName = when {
logger.warn("$subject is not specified, skipping annotation processing") sourcesOutputDir == null -> "Sources output directory"
classFilesOutputDir == null -> "Classes output directory"
apClasspath == null -> "Annotation processing classpath"
stubsOutputDir == null -> "Stubs output directory"
else -> throw IllegalStateException()
}
logger.warn("$nonExistentOptionName is not specified, skipping annotation processing")
AnalysisHandlerExtension.registerExtension(project, AbortAnalysisHandlerExtension()) AnalysisHandlerExtension.registerExtension(project, AbortAnalysisHandlerExtension())
} }
return return
@@ -71,7 +71,7 @@ class KotlinKapt3IntegrationTests : AbstractKotlinKapt3IntegrationTest() {
val bindings = kaptExtension.savedBindings!! val bindings = kaptExtension.savedBindings!!
test(stubsOutputDir!!, incrementalDataOutputDir!!, bindings) test(stubsOutputDir, incrementalDataOutputDir!!, bindings)
} }
} }