Kapt3: Support incremental compilation of Java stubs (KT-15151)
This commit is contained in:
+64
-25
@@ -28,6 +28,7 @@ import org.gradle.api.tasks.compile.AbstractCompile
|
||||
import org.gradle.api.tasks.compile.JavaCompile
|
||||
import org.jetbrains.kotlin.gradle.plugin.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.android.AndroidGradleWrapper
|
||||
import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
@@ -68,6 +69,7 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
|
||||
}
|
||||
|
||||
private val kotlinToKaptTasksMap = mutableMapOf<KotlinCompile, KaptTask>()
|
||||
private val kotlinToKaptGenerateStubsTasksMap = mutableMapOf<KotlinCompile, KaptGenerateStubsTask>()
|
||||
|
||||
override fun isApplicable(project: Project, task: KotlinCompile) = Kapt3GradleSubplugin.isEnabled(project)
|
||||
|
||||
@@ -79,8 +81,12 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
|
||||
return File(project.project.buildDir, "generated/source/kaptKotlin/$sourceSetName")
|
||||
}
|
||||
|
||||
fun getKaptStubsDir(project: Project, sourceSetName: String): File {
|
||||
val dir = File(project.project.buildDir, "tmp/kapt3/stubs/$sourceSetName")
|
||||
private fun Kapt3SubpluginContext.getKaptStubsDir() = createAndReturnTemporaryKaptDirectory("stubs")
|
||||
|
||||
private fun Kapt3SubpluginContext.getKaptIncrementalDataDir() = createAndReturnTemporaryKaptDirectory("incrementalData")
|
||||
|
||||
private fun Kapt3SubpluginContext.createAndReturnTemporaryKaptDirectory(name: String): File {
|
||||
val dir = File(project.buildDir, "tmp/kapt3/$name/$sourceSetName")
|
||||
dir.mkdirs()
|
||||
return dir
|
||||
}
|
||||
@@ -96,6 +102,10 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
|
||||
val sourcesOutputDir = getKaptGeneratedDir(project, sourceSetName)
|
||||
val kotlinSourcesOutputDir = getKaptGeneratedDirForKotlin(project, sourceSetName)
|
||||
val classesOutputDir = getKaptClasssesDir(project, sourceSetName)
|
||||
|
||||
val kaptClasspathArtifacts = project
|
||||
.resolveSubpluginArtifacts(listOf(this@Kapt3KotlinGradleSubplugin))
|
||||
.flatMap { it.value }
|
||||
}
|
||||
|
||||
override fun apply(
|
||||
@@ -140,18 +150,21 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
|
||||
val context = Kapt3SubpluginContext(project, kotlinCompile, javaCompile,
|
||||
variantData, sourceSetName, kaptExtension, kaptClasspath)
|
||||
|
||||
context.createKaptKotlinTask()
|
||||
val kaptGenerateStubsTask = context.createKaptGenerateStubsTask()
|
||||
context.createKaptKotlinTask(kaptGenerateStubsTask)
|
||||
|
||||
/** Plugin options are applied to kapt*Compile inside [createKaptKotlinTask] */
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override fun getSubpluginKotlinTasks(project: Project, kotlinCompile: KotlinCompile): List<KaptTask> {
|
||||
return kotlinToKaptTasksMap[kotlinCompile]?.let { listOf(it) } ?: emptyList()
|
||||
override fun getSubpluginKotlinTasks(project: Project, kotlinCompile: KotlinCompile): List<AbstractCompile> {
|
||||
val kaptTask = kotlinToKaptTasksMap[kotlinCompile]
|
||||
val kaptGenerateStubsTask = kotlinToKaptGenerateStubsTasksMap[kotlinCompile]
|
||||
return listOf(kaptTask, kaptGenerateStubsTask).filterNotNull()
|
||||
}
|
||||
|
||||
// This method should be called no more than once for each Kapt3SubpluginContext
|
||||
private fun Kapt3SubpluginContext.buildOptions(): List<SubpluginOption> {
|
||||
private fun Kapt3SubpluginContext.buildOptions(aptMode: String): List<SubpluginOption> {
|
||||
val pluginOptions = mutableListOf<SubpluginOption>()
|
||||
|
||||
val generatedFilesDir = getKaptGeneratedDir(project, sourceSetName)
|
||||
@@ -159,7 +172,7 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
|
||||
(variantData as BaseVariantData<*>).addJavaSourceFoldersToModel(generatedFilesDir)
|
||||
}
|
||||
|
||||
pluginOptions += SubpluginOption("aptOnly", "true")
|
||||
pluginOptions += SubpluginOption("aptMode", aptMode)
|
||||
disableAnnotationProcessingInJavaTask()
|
||||
|
||||
// Skip annotation processing in kotlinc if no kapt dependencies were provided
|
||||
@@ -172,6 +185,8 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
|
||||
pluginOptions += SubpluginOption("sources", generatedFilesDir.canonicalPath)
|
||||
pluginOptions += SubpluginOption("classes", getKaptClasssesDir(project, sourceSetName).canonicalPath)
|
||||
|
||||
pluginOptions += SubpluginOption("incrementalData", getKaptIncrementalDataDir().canonicalPath)
|
||||
|
||||
val annotationProcessors = kaptExtension.processors
|
||||
if (annotationProcessors.isNotEmpty()) {
|
||||
pluginOptions += SubpluginOption("processors", annotationProcessors)
|
||||
@@ -201,6 +216,13 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
|
||||
return pluginOptions
|
||||
}
|
||||
|
||||
private fun Kapt3SubpluginContext.buildAndAddOptionsTo(container: CompilerPluginOptions, aptMode: String) {
|
||||
val compilerPluginId = getCompilerPluginId()
|
||||
for (option in buildOptions(aptMode)) {
|
||||
container.addPluginArgument(compilerPluginId, option.key, option.value)
|
||||
}
|
||||
}
|
||||
|
||||
fun encodeOptions(options: Map<String, String>): String {
|
||||
val os = ByteArrayOutputStream()
|
||||
val oos = ObjectOutputStream(os)
|
||||
@@ -222,42 +244,59 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
|
||||
|
||||
pluginOptions += SubpluginOption("useLightAnalysis", "${kaptExtension.useLightAnalysis}")
|
||||
pluginOptions += SubpluginOption("correctErrorTypes", "${kaptExtension.correctErrorTypes}")
|
||||
pluginOptions += SubpluginOption("stubs", getKaptStubsDir(project, sourceSetName).canonicalPath)
|
||||
pluginOptions += SubpluginOption("stubs", getKaptStubsDir().canonicalPath)
|
||||
|
||||
if (project.hasProperty(VERBOSE_OPTION_NAME) && project.property(VERBOSE_OPTION_NAME) == "true") {
|
||||
pluginOptions += SubpluginOption("verbose", "true")
|
||||
}
|
||||
}
|
||||
|
||||
private fun Kapt3SubpluginContext.createKaptKotlinTask() {
|
||||
// Replace compile*Kotlin to kapt*Kotlin
|
||||
assert(kotlinCompile.name.startsWith("compile"))
|
||||
val kaptTaskName = kotlinCompile.name.replaceFirst("compile", "kapt")
|
||||
val kaptTask = project.tasks.create(kaptTaskName, KaptTask::class.java)
|
||||
private fun Kapt3SubpluginContext.createKaptKotlinTask(kaptGenerateStubsTask: KaptGenerateStubsTask) {
|
||||
val kaptTask = project.tasks.create(getKaptTaskName("kapt"), KaptTask::class.java)
|
||||
kaptTask.kotlinCompileTask = kotlinCompile
|
||||
kotlinToKaptTasksMap[kotlinCompile] = kaptTask
|
||||
|
||||
project.resolveSubpluginArtifacts(listOf(this@Kapt3KotlinGradleSubplugin)).flatMap { it.value }.forEach {
|
||||
kaptTask.pluginOptions.addClasspathEntry(it)
|
||||
}
|
||||
kaptClasspathArtifacts.forEach { kaptTask.pluginOptions.addClasspathEntry(it) }
|
||||
|
||||
kaptTask.stubsDir = getKaptStubsDir(project, sourceSetName)
|
||||
|
||||
kaptTask.mapClasspath { kotlinCompile.classpath }
|
||||
kaptTask.stubsDir = getKaptStubsDir()
|
||||
kaptTask.destinationDir = sourcesOutputDir
|
||||
kaptTask.mapClasspath { kotlinCompile.classpath }
|
||||
kaptTask.classesDir = classesOutputDir
|
||||
kaptTask.dependsOn(*(javaCompile.dependsOn.filter { it !== kotlinCompile }.toTypedArray()))
|
||||
|
||||
kaptTask.dependsOn(kaptGenerateStubsTask)
|
||||
kotlinCompile.dependsOn(kaptTask)
|
||||
|
||||
// Add generated source dir as a source root for kotlinCompile and javaCompile
|
||||
kotlinCompile.source(sourcesOutputDir, kotlinSourcesOutputDir)
|
||||
javaCompile.source(sourcesOutputDir)
|
||||
|
||||
val pluginOptions = kaptTask.pluginOptions
|
||||
val compilerPluginId = getCompilerPluginId()
|
||||
for (option in buildOptions()) {
|
||||
pluginOptions.addPluginArgument(compilerPluginId, option.key, option.value)
|
||||
}
|
||||
buildAndAddOptionsTo(kaptTask.pluginOptions, aptMode = "apt")
|
||||
}
|
||||
|
||||
private fun Kapt3SubpluginContext.createKaptGenerateStubsTask(): KaptGenerateStubsTask {
|
||||
val kaptTask = project.tasks.create(getKaptTaskName("kaptGenerateStubs"), KaptGenerateStubsTask::class.java)
|
||||
kaptTask.kotlinCompileTask = kotlinCompile
|
||||
kotlinToKaptGenerateStubsTasksMap[kotlinCompile] = kaptTask
|
||||
|
||||
kaptClasspathArtifacts.forEach { kaptTask.pluginOptions.addClasspathEntry(it) }
|
||||
|
||||
kaptTask.stubsDir = getKaptStubsDir()
|
||||
kaptTask.destinationDir = getKaptIncrementalDataDir()
|
||||
kaptTask.mapClasspath { kotlinCompile.classpath }
|
||||
kaptTask.generatedSourcesDir = sourcesOutputDir
|
||||
|
||||
kaptTask.dependsOn(*(javaCompile.dependsOn.filter { it !== kotlinCompile }.toTypedArray()))
|
||||
|
||||
buildAndAddOptionsTo(kaptTask.pluginOptions, aptMode = "stubs")
|
||||
|
||||
return kaptTask
|
||||
}
|
||||
|
||||
private fun Kapt3SubpluginContext.getKaptTaskName(prefix: String): String {
|
||||
// Replace compile*Kotlin to kapt*Kotlin
|
||||
val baseName = kotlinCompile.name
|
||||
assert(baseName.startsWith("compile"))
|
||||
return baseName.replaceFirst("compile", prefix)
|
||||
}
|
||||
|
||||
private fun Kapt3SubpluginContext.disableAnnotationProcessingInJavaTask() {
|
||||
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.gradle.internal
|
||||
|
||||
import org.gradle.api.tasks.SourceTask
|
||||
import org.gradle.api.tasks.incremental.IncrementalTaskInputs
|
||||
import org.jetbrains.kotlin.com.intellij.openapi.util.io.FileUtil
|
||||
import org.jetbrains.kotlin.gradle.plugin.kotlinDebug
|
||||
import org.jetbrains.kotlin.gradle.tasks.FilteringSourceRootsContainer
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import org.jetbrains.kotlin.incremental.ChangedFiles
|
||||
import org.jetbrains.kotlin.incremental.pathsAsStringRelativeTo
|
||||
import java.io.File
|
||||
|
||||
open class KaptGenerateStubsTask : KotlinCompile() {
|
||||
override val sourceRootsContainer = FilteringSourceRootsContainer({ isSourceRootAllowed(it) })
|
||||
|
||||
internal lateinit var kotlinCompileTask: KotlinCompile
|
||||
|
||||
lateinit var stubsDir: File
|
||||
lateinit var generatedSourcesDir: File
|
||||
|
||||
override fun source(vararg sources: Any?): SourceTask? {
|
||||
return super.source(sourceRootsContainer.add(sources))
|
||||
}
|
||||
override fun setSource(sources: Any?) {
|
||||
super.setSource(sourceRootsContainer.set(sources))
|
||||
}
|
||||
|
||||
private fun isSourceRootAllowed(source: File): Boolean {
|
||||
fun File.isInside(parent: File) = FileUtil.isAncestor(parent, this, /* strict = */ false)
|
||||
|
||||
return !source.isInside(destinationDir) &&
|
||||
!source.isInside(stubsDir) &&
|
||||
!source.isInside(generatedSourcesDir)
|
||||
}
|
||||
|
||||
override fun execute(inputs: IncrementalTaskInputs) {
|
||||
val sourceRoots = kotlinCompileTask.getSourceRoots()
|
||||
val allKotlinSources = sourceRoots.kotlinSourceFiles
|
||||
|
||||
generatedSourcesDir.deleteRecursively()
|
||||
|
||||
logger.kotlinDebug { "All kotlin sources: ${allKotlinSources.pathsAsStringRelativeTo(project.rootProject.projectDir)}" }
|
||||
|
||||
if (allKotlinSources.isEmpty()) {
|
||||
logger.kotlinDebug { "No Kotlin files found, skipping KaptGenerateStubs task" }
|
||||
return
|
||||
}
|
||||
|
||||
sourceRoots.log(this.name, logger)
|
||||
val args = createCompilerArgs()
|
||||
|
||||
kotlinCompileTask.setupCompilerArgs(args)
|
||||
args.pluginClasspaths = (pluginOptions.classpath + args.pluginClasspaths).toSet().toTypedArray()
|
||||
args.pluginOptions = (pluginOptions.arguments + args.pluginOptions).toTypedArray()
|
||||
args.verbose = project.hasProperty("kapt.verbose") && project.property("kapt.verbose").toString().toBoolean() == true
|
||||
|
||||
compilerCalled = true
|
||||
callCompiler(args, sourceRoots, ChangedFiles(inputs))
|
||||
}
|
||||
}
|
||||
+1
-4
@@ -43,14 +43,11 @@ open class KaptTask : AbstractCompile() {
|
||||
|
||||
@TaskAction
|
||||
override fun compile() {
|
||||
/** Delete everything inside the [destinationDir] */
|
||||
/** Delete everything inside the [destinationDir] (sources output dir) */
|
||||
destinationDir.clearDirectory()
|
||||
|
||||
classesDir.clearDirectory()
|
||||
|
||||
// Kapt3 doesn't support incremental compilation so we should delete the existing stubs
|
||||
stubsDir.clearDirectory()
|
||||
|
||||
val sourceRoots = SourceRoots.ForJvm.create(getSource(), rawSourceRoots)
|
||||
|
||||
val args = K2JVMCompilerArguments()
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ internal fun Task.finalizedByIfNotFailed(finalizer: Task) {
|
||||
this.finalizedBy(finalizer)
|
||||
}
|
||||
|
||||
internal fun AbstractCompile.mapClasspath(fn: ()->FileCollection) {
|
||||
internal fun AbstractCompile.mapClasspath(fn: () -> FileCollection) {
|
||||
conventionMapping.map("classpath", fn)
|
||||
}
|
||||
|
||||
|
||||
+2
@@ -79,6 +79,8 @@ internal class FilteringSourceRootsContainer(val filter: (File) -> Boolean = { t
|
||||
when (source) {
|
||||
is SourceDirectorySet -> filteredDirs += source.srcDirs.filter { filter(it) }
|
||||
is File -> if (filter(source)) filteredDirs.add(source)
|
||||
is Collection<*> -> source.forEach { filteredDirs += add(it) }
|
||||
is Array<*> -> source.forEach { filteredDirs += add(it) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -122,7 +122,7 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractCo
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun execute(inputs: IncrementalTaskInputs): Unit {
|
||||
open fun execute(inputs: IncrementalTaskInputs): Unit {
|
||||
val sourceRoots = getSourceRoots()
|
||||
val allKotlinSources = sourceRoots.kotlinSourceFiles
|
||||
|
||||
@@ -162,7 +162,7 @@ open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), Kotl
|
||||
private val kotlinOptionsImpl = KotlinJvmOptionsImpl()
|
||||
override val kotlinOptions: KotlinJvmOptions
|
||||
get() = kotlinOptionsImpl
|
||||
internal val sourceRootsContainer = FilteringSourceRootsContainer()
|
||||
internal open val sourceRootsContainer = FilteringSourceRootsContainer()
|
||||
|
||||
internal val taskBuildDirectory: File
|
||||
get() = File(File(project.buildDir, KOTLIN_BUILD_DIR_NAME), name).apply { mkdirs() }
|
||||
|
||||
Reference in New Issue
Block a user