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("stubs", getKaptStubsDir(project, sourceSetName).canonicalPath)
if (project.hasProperty(VERBOSE_OPTION_NAME) && project.property(VERBOSE_OPTION_NAME) == "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.sun.tools.javac.tree.JCTree
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
@@ -49,7 +48,7 @@ class ClasspathBasedKapt3Extension(
javaSourceRoots: List<File>,
sourcesOutputDir: File,
classFilesOutputDir: File,
stubsOutputDir: File?,
stubsOutputDir: File,
incrementalDataOutputDir: File?,
options: Map<String, String>,
aptOnly: Boolean,
@@ -98,7 +97,7 @@ abstract class AbstractKapt3Extension(
val javaSourceRoots: List<File>,
val sourcesOutputDir: File,
val classFilesOutputDir: File,
val stubsOutputDir: File?,
val stubsOutputDir: File,
val incrementalDataOutputDir: File?,
val options: Map<String, String>,
val aptOnly: Boolean,
@@ -141,13 +140,13 @@ abstract class AbstractKapt3Extension(
val (kaptContext, generationState) = compileStubs(project, module, bindingTrace.bindingContext, files.toList())
try {
generateKotlinSourceStubs(kaptContext, generationState)
val javaSourceFiles = collectJavaSourceFiles()
val kotlinSourceStubs = generateKotlinSourceStubs(kaptContext, generationState)
val (annotationProcessingTime) = measureTimeMillis {
kaptContext.doAnnotationProcessing(
javaSourceFiles, processors, compileClasspath, annotationProcessingClasspath,
sourcesOutputDir, classFilesOutputDir, kotlinSourceStubs)
sourcesOutputDir, classFilesOutputDir)
}
logger.info { "Annotation processing took $annotationProcessingTime ms" }
@@ -202,7 +201,7 @@ abstract class AbstractKapt3Extension(
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 (stubGenerationTime, kotlinSourceStubs) = measureTimeMillis {
@@ -214,11 +213,10 @@ abstract class AbstractKapt3Extension(
saveStubs(kotlinSourceStubs)
saveIncrementalData(generationState, logger.messageCollector, converter)
return kotlinSourceStubs
}
private fun collectJavaSourceFiles(): List<File> {
val javaFilesFromJavaSourceRoots = javaSourceRoots.flatMap {
val javaFilesFromJavaSourceRoots = (javaSourceRoots + stubsOutputDir).flatMap {
root -> root.walk().filter { it.isFile && it.extension == "java" }.toList()
}
logger.info { "Java source files: " + javaFilesFromJavaSourceRoots.joinToString { it.canonicalPath } }
@@ -227,12 +225,11 @@ abstract class AbstractKapt3Extension(
}
protected open fun saveStubs(stubs: JavacList<JCTree.JCCompilationUnit>) {
val outputDir = stubsOutputDir ?: return
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('.', '/'))
val packageDir = if (packageName.isEmpty()) stubsOutputDir else File(stubsOutputDir, packageName.replace('.', '/'))
packageDir.mkdirs()
File(packageDir, className + ".java").writeText(stub.toString())
}
@@ -246,7 +243,7 @@ abstract class AbstractKapt3Extension(
generationState.factory.writeAll(incrementalDataOutputDir) { file, sources, output ->
val stubFileObject = converter.bindings[file.relativePath.substringBeforeLast(".class", missingDelimiterValue = "")]
if (stubFileObject != null && stubsOutputDir != null) {
if (stubFileObject != null) {
val stubFile = File(stubsOutputDir, stubFileObject.name)
if (stubFile.exists()) {
messageCollector.report(CompilerMessageSeverity.OUTPUT,
@@ -153,10 +153,16 @@ class Kapt3ComponentRegistrar : ComponentRegistrar {
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) {
val subject = if (apClasspath == null) "Annotation processing classpath" else "Generated files output directories"
logger.warn("$subject is not specified, skipping annotation processing")
val nonExistentOptionName = when {
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())
}
return
@@ -71,7 +71,7 @@ class KotlinKapt3IntegrationTests : AbstractKotlinKapt3IntegrationTest() {
val bindings = kaptExtension.savedBindings!!
test(stubsOutputDir!!, incrementalDataOutputDir!!, bindings)
test(stubsOutputDir, incrementalDataOutputDir!!, bindings)
}
}