Incremental annotation processing with KAPT
Add support for incremental annotation processors in KAPT. These processors conform to https://docs.gradle.org/current/userguide/java_plugin.html#sec:incremental_annotation_processing specification. Support is provided by using javac compiler APIs and recording the source file structure. At runtime, processors are instrumented with custom Filer that is used to keep track of generated files. In order to support classpath changes, stub generation task is used to generated a list of changed FQCNs, and this is simply used by KAPT. Both worker and non-worker mode are supported. #KT-23880
This commit is contained in:
committed by
Alexey Tsvetkov
parent
600a955a51
commit
9f14daa682
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.gradle.incapt;
|
||||
|
||||
import javax.annotation.processing.AbstractProcessor;
|
||||
import javax.annotation.processing.RoundEnvironment;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
/** Simple processor that generates a class for every annotated element (class, field, method). */
|
||||
public class IncrementalProcessor extends AbstractProcessor {
|
||||
|
||||
@Override
|
||||
public Set<String> getSupportedAnnotationTypes() {
|
||||
return Collections.singleton("example.ExampleAnnotation");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
|
||||
if (annotations.isEmpty()) return true;
|
||||
|
||||
for (Element element : roundEnv.getElementsAnnotatedWith(annotations.iterator().next())) {
|
||||
if (element instanceof TypeElement || element instanceof ExecutableElement || element instanceof VariableElement) {
|
||||
String name = element.getSimpleName().toString();
|
||||
name = name.substring(0, 1).toUpperCase() + name.substring(1) + "Generated";
|
||||
|
||||
String packageName;
|
||||
if (element instanceof TypeElement) {
|
||||
packageName = element.getEnclosingElement().getSimpleName().toString();
|
||||
}
|
||||
else {
|
||||
packageName = element.getEnclosingElement().getEnclosingElement().getSimpleName().toString();
|
||||
}
|
||||
|
||||
try (Writer writer = processingEnv.getFiler().createSourceFile(packageName + "." + name, element).openWriter()) {
|
||||
writer.append("package ").append(packageName).append(";");
|
||||
writer.append("\npublic class ").append(name).append(" {}");
|
||||
}
|
||||
catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -197,7 +197,7 @@ abstract class BaseGradleIT {
|
||||
val parallelTasksInProject: Boolean? = null
|
||||
)
|
||||
|
||||
data class KaptOptions(val verbose: Boolean, val useWorkers: Boolean)
|
||||
data class KaptOptions(val verbose: Boolean, val useWorkers: Boolean, val incrementalKapt: Boolean = false, val includeCompileClasspath: Boolean = true)
|
||||
|
||||
open inner class Project(
|
||||
val projectName: String,
|
||||
@@ -619,6 +619,8 @@ abstract class BaseGradleIT {
|
||||
options.kaptOptions?.also { kaptOptions ->
|
||||
add("-Pkapt.verbose=${kaptOptions.verbose}")
|
||||
add("-Pkapt.use.worker.api=${kaptOptions.useWorkers}")
|
||||
add("-Pkapt.incremental.apt=${kaptOptions.incrementalKapt}")
|
||||
add("-Pkapt.include.compile.classpath=${kaptOptions.includeCompileClasspath}")
|
||||
}
|
||||
|
||||
options.parallelTasksInProject?.let {
|
||||
|
||||
+4
-4
@@ -6,12 +6,12 @@ import org.jetbrains.kotlin.gradle.util.getFileByName
|
||||
import org.jetbrains.kotlin.gradle.util.modify
|
||||
import org.junit.Test
|
||||
|
||||
class KaptIncrementalIT : BaseGradleIT() {
|
||||
open class KaptIncrementalIT : BaseGradleIT() {
|
||||
companion object {
|
||||
private val EXAMPLE_ANNOTATION_REGEX = "@(field:)?example.ExampleAnnotation".toRegex()
|
||||
}
|
||||
|
||||
private fun getProject() =
|
||||
open fun getProject() =
|
||||
Project(
|
||||
"kaptIncrementalCompilationProject",
|
||||
GradleVersionRequired.None
|
||||
@@ -25,13 +25,13 @@ class KaptIncrementalIT : BaseGradleIT() {
|
||||
|
||||
@Test
|
||||
fun testAddNewLine() {
|
||||
val project = Project("simple", directoryPrefix = "kapt2")
|
||||
val project = getProject()
|
||||
|
||||
project.build("clean", "build") {
|
||||
assertSuccessful()
|
||||
}
|
||||
|
||||
project.projectFile("test.kt").modify { "\n$it" }
|
||||
project.projectFile("useB.kt").modify { "\n$it" }
|
||||
project.build("build") {
|
||||
assertSuccessful()
|
||||
assertTasksExecuted(":kaptGenerateStubsKotlin", ":compileKotlin")
|
||||
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.gradle
|
||||
|
||||
import org.jetbrains.kotlin.gradle.util.modify
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
class KaptIncrementalWithAggregatingApt : KaptIncrementalIT() {
|
||||
|
||||
override fun getProject() =
|
||||
Project(
|
||||
"kaptIncrementalCompilationProject",
|
||||
GradleVersionRequired.None
|
||||
).apply {
|
||||
setupIncrementalAptProject("AGGREGATING")
|
||||
}
|
||||
|
||||
override fun defaultBuildOptions(): BuildOptions =
|
||||
super.defaultBuildOptions().copy(
|
||||
incremental = true,
|
||||
kaptOptions = KaptOptions(
|
||||
verbose = true,
|
||||
useWorkers = true,
|
||||
incrementalKapt = true,
|
||||
includeCompileClasspath = false
|
||||
)
|
||||
)
|
||||
|
||||
@Test
|
||||
fun testIncrementalChanges() {
|
||||
val project = getProject()
|
||||
|
||||
var aptTimestamp = 0L
|
||||
|
||||
project.build("clean", "build") {
|
||||
assertSuccessful()
|
||||
|
||||
val classpathHistory =
|
||||
fileInWorkingDir("build/kotlin/kaptGenerateStubsKotlin/classpath-fq-history").listFiles().asList().single()
|
||||
val stubsTimestamp = classpathHistory.name.toLong()
|
||||
|
||||
aptTimestamp = fileInWorkingDir("build/tmp/kapt3/incApCache/main/last-build-ts.bin").readText().toLong()
|
||||
assertTrue(stubsTimestamp < aptTimestamp)
|
||||
}
|
||||
|
||||
project.projectFile("useB.kt").modify { current -> "$current\nfun otherFunction() {}" }
|
||||
project.build("build") {
|
||||
assertSuccessful()
|
||||
|
||||
val newAptTimestamp = fileInWorkingDir("build/tmp/kapt3/incApCache/main/last-build-ts.bin").readText().toLong()
|
||||
assertTrue(aptTimestamp < newAptTimestamp)
|
||||
|
||||
assertEquals(
|
||||
setOf(
|
||||
fileInWorkingDir("build/tmp/kapt3/stubs/main/bar/UseBKt.java").absolutePath,
|
||||
fileInWorkingDir("build/tmp/kapt3/stubs/main/bar/B.java").absolutePath,
|
||||
fileInWorkingDir("build/tmp/kapt3/stubs/main/baz/UtilKt.java").absolutePath,
|
||||
fileInWorkingDir("build/tmp/kapt3/stubs/main/foo/A.java").absolutePath
|
||||
), getProcessedSources(output)
|
||||
)
|
||||
}
|
||||
|
||||
project.projectFile("JavaClass.java").modify { current ->
|
||||
val lastBrace = current.lastIndexOf("}")
|
||||
current.substring(0, lastBrace) + "private void anotherFun() {}\n }"
|
||||
}
|
||||
project.build("build") {
|
||||
assertSuccessful()
|
||||
assertEquals(
|
||||
setOf(
|
||||
project.projectFile("JavaClass.java").absolutePath,
|
||||
fileInWorkingDir("build/tmp/kapt3/stubs/main/bar/UseBKt.java").absolutePath,
|
||||
fileInWorkingDir("build/tmp/kapt3/stubs/main/bar/B.java").absolutePath,
|
||||
fileInWorkingDir("build/tmp/kapt3/stubs/main/baz/UtilKt.java").absolutePath,
|
||||
fileInWorkingDir("build/tmp/kapt3/stubs/main/foo/A.java").absolutePath
|
||||
),
|
||||
getProcessedSources(output)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.gradle
|
||||
|
||||
import org.jetbrains.kotlin.gradle.incapt.IncrementalProcessor
|
||||
import org.jetbrains.kotlin.gradle.util.modify
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipOutputStream
|
||||
|
||||
class KaptIncrementalWithIsolatingApt : KaptIncrementalIT() {
|
||||
|
||||
override fun getProject() =
|
||||
Project(
|
||||
"kaptIncrementalCompilationProject",
|
||||
GradleVersionRequired.None
|
||||
).apply {
|
||||
setupIncrementalAptProject("ISOLATING")
|
||||
}
|
||||
|
||||
override fun defaultBuildOptions(): BuildOptions =
|
||||
super.defaultBuildOptions().copy(
|
||||
incremental = true,
|
||||
kaptOptions = KaptOptions(
|
||||
verbose = true,
|
||||
useWorkers = true,
|
||||
incrementalKapt = true,
|
||||
includeCompileClasspath = false
|
||||
)
|
||||
)
|
||||
|
||||
@Test
|
||||
fun testIncrementalChanges() {
|
||||
val project = getProject()
|
||||
|
||||
var aptTimestamp = 0L
|
||||
|
||||
project.build("clean", "build") {
|
||||
assertSuccessful()
|
||||
|
||||
val classpathHistory =
|
||||
fileInWorkingDir("build/kotlin/kaptGenerateStubsKotlin/classpath-fq-history").listFiles().asList().single()
|
||||
val stubsTimestamp = classpathHistory.name.toLong()
|
||||
|
||||
aptTimestamp = fileInWorkingDir("build/tmp/kapt3/incApCache/main/last-build-ts.bin").readText().toLong()
|
||||
assertTrue(stubsTimestamp < aptTimestamp)
|
||||
}
|
||||
|
||||
project.projectFile("useB.kt").modify { current -> "$current\nfun otherFunction() {}" }
|
||||
project.build("build") {
|
||||
assertSuccessful()
|
||||
|
||||
val newAptTimestamp = fileInWorkingDir("build/tmp/kapt3/incApCache/main/last-build-ts.bin").readText().toLong()
|
||||
assertTrue(aptTimestamp < newAptTimestamp)
|
||||
|
||||
assertEquals(setOf(fileInWorkingDir("build/tmp/kapt3/stubs/main/bar/UseBKt.java").absolutePath), getProcessedSources(output))
|
||||
}
|
||||
|
||||
project.projectFile("B.kt").modify { current ->
|
||||
val lastBrace = current.lastIndexOf("}")
|
||||
current.substring(0, lastBrace) + "fun anotherFun() {}\n }"
|
||||
}
|
||||
project.build("build") {
|
||||
assertSuccessful()
|
||||
assertEquals(
|
||||
setOf(
|
||||
fileInWorkingDir("build/tmp/kapt3/stubs/main/bar/B.java").absolutePath,
|
||||
fileInWorkingDir("build/tmp/kapt3/stubs/main/bar/UseBKt.java").absolutePath
|
||||
),
|
||||
getProcessedSources(output)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val patternApt = "Processing java sources with annotation processors:"
|
||||
fun getProcessedSources(output: String): Set<String> {
|
||||
val logging = output.lines().single { it.contains(patternApt) }
|
||||
val indexOf = logging.indexOf(patternApt) + patternApt.length
|
||||
return logging.drop(indexOf).split(",").map { it.trim() }.toSet()
|
||||
}
|
||||
|
||||
fun BaseGradleIT.Project.setupIncrementalAptProject(procType: String) {
|
||||
setupWorkingDir()
|
||||
val buildFile = projectDir.resolve("build.gradle")
|
||||
val content = buildFile.readText()
|
||||
val processorPath = projectDir.resolve("incrementalProcessor.jar")
|
||||
|
||||
ZipOutputStream(processorPath.outputStream()).use {
|
||||
val path = IncrementalProcessor::class.java.name.replace(".", "/") + ".class"
|
||||
val inputStream = IncrementalProcessor::class.java.classLoader.getResourceAsStream(path)
|
||||
it.putNextEntry(ZipEntry(path))
|
||||
it.write(inputStream.readBytes())
|
||||
it.closeEntry()
|
||||
it.putNextEntry(ZipEntry("META-INF/gradle/incremental.annotation.processors"))
|
||||
it.write("${IncrementalProcessor::class.java.name},$procType".toByteArray())
|
||||
it.closeEntry()
|
||||
it.putNextEntry(ZipEntry("META-INF/services/javax.annotation.processing.Processor"))
|
||||
it.write(IncrementalProcessor::class.java.name.toByteArray())
|
||||
it.closeEntry()
|
||||
}
|
||||
|
||||
val updatedContent = content.replace(
|
||||
Regex("^\\s*kapt\\s\"org\\.jetbrain.*$", RegexOption.MULTILINE),
|
||||
" kapt files(\"$processorPath\")"
|
||||
)
|
||||
buildFile.writeText(updatedContent)
|
||||
}
|
||||
+2
-1
@@ -273,7 +273,8 @@ internal class GradleKotlinCompilerWork @Inject constructor(
|
||||
usePreciseJavaTracking = icEnv.usePreciseJavaTracking,
|
||||
outputFiles = outputFiles,
|
||||
multiModuleICSettings = icEnv.multiModuleICSettings,
|
||||
modulesInfo = incrementalModuleInfo!!
|
||||
modulesInfo = incrementalModuleInfo!!,
|
||||
classpathFqNamesHistory = icEnv.classpathFqNamesHistory
|
||||
)
|
||||
|
||||
log.info("Options for KOTLIN DAEMON: $compilationOptions")
|
||||
|
||||
+2
-1
@@ -15,7 +15,8 @@ internal class IncrementalCompilationEnvironment(
|
||||
val workingDir: File,
|
||||
val usePreciseJavaTracking: Boolean = false,
|
||||
val disableMultiModuleIC: Boolean = false,
|
||||
val multiModuleICSettings: MultiModuleICSettings
|
||||
val multiModuleICSettings: MultiModuleICSettings,
|
||||
val classpathFqNamesHistory: File? = null
|
||||
) : Serializable {
|
||||
companion object {
|
||||
const val serialVersionUID: Long = 0
|
||||
|
||||
+27
-2
@@ -90,6 +90,7 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
|
||||
private val USE_WORKER_API = "kapt.use.worker.api"
|
||||
private val INFO_AS_WARNINGS = "kapt.info.as.warnings"
|
||||
private val INCLUDE_COMPILE_CLASSPATH = "kapt.include.compile.classpath"
|
||||
private val INCREMENTAL_APT = "kapt.incremental.apt"
|
||||
|
||||
const val KAPT_WORKER_DEPENDENCIES_CONFIGURATION_NAME = "kotlinKaptWorkerDependencies"
|
||||
|
||||
@@ -119,6 +120,10 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
|
||||
return isWorkerAPISupported() && hasProperty(USE_WORKER_API) && property(USE_WORKER_API) == "true"
|
||||
}
|
||||
|
||||
fun Project.isIncrementalKapt(): Boolean {
|
||||
return hasProperty(INCREMENTAL_APT) && property(INCREMENTAL_APT) == "true"
|
||||
}
|
||||
|
||||
fun Project.isInfoAsWarnings(): Boolean {
|
||||
return hasProperty(INFO_AS_WARNINGS) && property(INFO_AS_WARNINGS) == "true"
|
||||
}
|
||||
@@ -157,6 +162,8 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
|
||||
|
||||
private fun Kapt3SubpluginContext.getKaptIncrementalDataDir() = createAndReturnTemporaryKaptDirectory("incrementalData")
|
||||
|
||||
private fun Kapt3SubpluginContext.getKaptIncrementalAnnotationProcessingCache() = createAndReturnTemporaryKaptDirectory("incApCache")
|
||||
|
||||
private fun Kapt3SubpluginContext.createAndReturnTemporaryKaptDirectory(name: String): File {
|
||||
val dir = File(project.buildDir, "tmp/kapt3/$name/$sourceSetName")
|
||||
dir.mkdirs()
|
||||
@@ -227,7 +234,9 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
|
||||
)
|
||||
|
||||
val kaptGenerateStubsTask = context.createKaptGenerateStubsTask()
|
||||
val kaptTask = context.createKaptKotlinTask(useWorkerApi = project.isUseWorkerApi())
|
||||
val kaptTask = context.createKaptKotlinTask(
|
||||
useWorkerApi = project.isUseWorkerApi(),
|
||||
classpathHistoryDir = kaptGenerateStubsTask.getClasspathFqNamesHistoryDir())
|
||||
|
||||
kaptGenerateStubsTask.source(*kaptConfigurations.toTypedArray())
|
||||
|
||||
@@ -365,7 +374,7 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
|
||||
}
|
||||
}
|
||||
|
||||
private fun Kapt3SubpluginContext.createKaptKotlinTask(useWorkerApi: Boolean): KaptTask {
|
||||
private fun Kapt3SubpluginContext.createKaptKotlinTask(useWorkerApi: Boolean, classpathHistoryDir: File? = null): KaptTask {
|
||||
val taskClass = if (useWorkerApi) KaptWithoutKotlincTask::class.java else KaptWithKotlincTask::class.java
|
||||
val kaptTask = project.tasks.create(getKaptTaskName("kapt"), taskClass)
|
||||
|
||||
@@ -379,6 +388,12 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
|
||||
kaptTask.classesDir = classesOutputDir
|
||||
kaptTask.includeCompileClasspath = includeCompileClasspath
|
||||
|
||||
kaptTask.isIncremental = project.isIncrementalKapt()
|
||||
if (kaptTask.isIncremental) {
|
||||
kaptTask.incAptCache = getKaptIncrementalAnnotationProcessingCache()
|
||||
kaptTask.classpathDirtyFqNamesHistoryDir = project.files(classpathHistoryDir)
|
||||
}
|
||||
|
||||
kotlinCompilation?.run {
|
||||
output.apply {
|
||||
addClassesDir { project.files(classesOutputDir).builtBy(kaptTask) }
|
||||
@@ -403,6 +418,16 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
|
||||
}
|
||||
|
||||
if (kaptTask is KaptWithKotlincTask) {
|
||||
if (kaptTask.isIncremental) {
|
||||
kaptTask.pluginOptions.addPluginArgument(
|
||||
getCompilerPluginId(),
|
||||
SubpluginOption("incrementalCache", kaptTask.incAptCache!!.absolutePath))
|
||||
|
||||
kaptTask.pluginOptions.addPluginArgument(
|
||||
getCompilerPluginId(),
|
||||
SubpluginOption("classpathFqNamesHistory", kaptTask.classpathDirtyFqNamesHistoryDir.singleFile!!.absolutePath))
|
||||
}
|
||||
|
||||
buildAndAddOptionsTo(kaptTask, kaptTask.pluginOptions, aptMode = "apt")
|
||||
}
|
||||
|
||||
|
||||
+5
@@ -74,6 +74,11 @@ open class KaptGenerateStubsTask : KotlinCompile() {
|
||||
super.setSource(sourceRootsContainer.set(sources))
|
||||
}
|
||||
|
||||
@Internal
|
||||
override fun getClasspathFqNamesHistoryDir(): File? {
|
||||
return taskBuildDirectory.resolve("classpath-fq-history")
|
||||
}
|
||||
|
||||
private fun isSourceRootAllowed(source: File): Boolean =
|
||||
!destinationDir.isParentOf(source) &&
|
||||
!stubsDir.isParentOf(source) &&
|
||||
|
||||
+30
@@ -5,9 +5,11 @@ import org.gradle.api.artifacts.Configuration
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.internal.ConventionTask
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.api.tasks.incremental.IncrementalTaskInputs
|
||||
import org.jetbrains.kotlin.gradle.internal.tasks.TaskWithLocalState
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import org.jetbrains.kotlin.gradle.tasks.cacheOnlyIfEnabledForKotlin
|
||||
import org.jetbrains.kotlin.gradle.tasks.clearLocalState
|
||||
import org.jetbrains.kotlin.gradle.tasks.isBuildCacheSupported
|
||||
import org.jetbrains.kotlin.gradle.utils.isJavaFile
|
||||
import java.io.File
|
||||
@@ -45,6 +47,11 @@ abstract class KaptTask : ConventionTask(), TaskWithLocalState {
|
||||
@get:Internal
|
||||
internal lateinit var kaptClasspathConfigurations: List<Configuration>
|
||||
|
||||
/** Output directory that contains caches necessary to support incremental annotation processing. */
|
||||
@get:OutputDirectory
|
||||
@get:Optional
|
||||
var incAptCache: File? = null
|
||||
|
||||
@get:OutputDirectory
|
||||
internal lateinit var classesDir: File
|
||||
|
||||
@@ -60,6 +67,12 @@ abstract class KaptTask : ConventionTask(), TaskWithLocalState {
|
||||
@get:Input
|
||||
internal var includeCompileClasspath: Boolean = true
|
||||
|
||||
@get:InputFiles
|
||||
internal var classpathDirtyFqNamesHistoryDir: FileCollection = project.files()
|
||||
|
||||
@get:Input
|
||||
internal var isIncremental = true
|
||||
|
||||
// @Internal because _abiClasspath and _nonAbiClasspath are used for actual checks
|
||||
@get:Internal
|
||||
val classpath: FileCollection
|
||||
@@ -142,6 +155,23 @@ abstract class KaptTask : ConventionTask(), TaskWithLocalState {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(gavra): Here we assume that kotlinc and javac output is available for incremental runs. We should insert some checks.
|
||||
@Internal
|
||||
protected fun getCompiledSources() = listOfNotNull(kotlinCompileTask.destinationDir, kotlinCompileTask.javaOutputDir)
|
||||
|
||||
protected fun getChangedFiles(inputs: IncrementalTaskInputs): List<File> {
|
||||
return if (!isIncremental || !inputs.isIncremental || !getCompiledSources().all { it.exists() }) {
|
||||
clearLocalState()
|
||||
emptyList()
|
||||
} else {
|
||||
with(mutableSetOf<File>()) {
|
||||
inputs.outOfDate { this.add(it.file) }
|
||||
inputs.removed { this.add(it.file) }
|
||||
return@with this.toList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun hasAnnotationProcessors(file: File): Boolean {
|
||||
val processorEntryPath = "META-INF/services/javax.annotation.processing.Processor"
|
||||
|
||||
|
||||
+19
-3
@@ -12,17 +12,22 @@ import org.gradle.api.tasks.Classpath
|
||||
import org.gradle.api.tasks.InputFiles
|
||||
import org.gradle.api.tasks.Internal
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.api.tasks.incremental.IncrementalTaskInputs
|
||||
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
||||
import org.jetbrains.kotlin.compilerRunner.GradleCompilerEnvironment
|
||||
import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner
|
||||
import org.jetbrains.kotlin.compilerRunner.IncrementalCompilationEnvironment
|
||||
import org.jetbrains.kotlin.gradle.logging.GradleKotlinLogger
|
||||
import org.jetbrains.kotlin.compilerRunner.OutputItemsCollectorImpl
|
||||
import org.jetbrains.kotlin.gradle.incremental.ChangedFiles
|
||||
import org.jetbrains.kotlin.gradle.internal.tasks.allOutputFiles
|
||||
import org.jetbrains.kotlin.gradle.plugin.PLUGIN_CLASSPATH_CONFIGURATION_NAME
|
||||
import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions
|
||||
import org.jetbrains.kotlin.gradle.logging.GradlePrintingMessageCollector
|
||||
import org.jetbrains.kotlin.gradle.tasks.clearLocalState
|
||||
import org.jetbrains.kotlin.gradle.utils.toSortedPathsArray
|
||||
import org.jetbrains.kotlin.incremental.ChangedFiles
|
||||
import java.io.File
|
||||
|
||||
open class KaptWithKotlincTask : KaptTask(), CompilerArgumentAwareWithInput<K2JVMCompilerArguments> {
|
||||
@get:Internal
|
||||
@@ -46,17 +51,28 @@ open class KaptWithKotlincTask : KaptTask(), CompilerArgumentAwareWithInput<K2JV
|
||||
|
||||
args.pluginClasspaths = pluginClasspath.toSortedPathsArray()
|
||||
|
||||
val pluginOptionsWithKapt: CompilerPluginOptions = pluginOptions.withWrappedKaptOptions(withApClasspath = kaptClasspath)
|
||||
val pluginOptionsWithKapt: CompilerPluginOptions = pluginOptions.withWrappedKaptOptions(
|
||||
withApClasspath = kaptClasspath,
|
||||
changedFiles = changedFiles,
|
||||
compiledSourcesDir = getCompiledSources())
|
||||
|
||||
args.pluginOptions = (pluginOptionsWithKapt.arguments + args.pluginOptions!!).toTypedArray()
|
||||
|
||||
args.verbose = project.hasProperty("kapt.verbose") && project.property("kapt.verbose").toString().toBoolean() == true
|
||||
}
|
||||
|
||||
/**
|
||||
* This will be part of the subplugin options that is not part of the input snapshotting, so just initialize it. Actual value is set
|
||||
* in the task action.
|
||||
*/
|
||||
private var changedFiles: List<File> = emptyList()
|
||||
|
||||
@TaskAction
|
||||
fun compile() {
|
||||
fun compile(inputs: IncrementalTaskInputs) {
|
||||
logger.debug("Running kapt annotation processing using the Kotlin compiler")
|
||||
checkAnnotationProcessorClasspath()
|
||||
clearLocalState()
|
||||
|
||||
changedFiles = getChangedFiles(inputs)
|
||||
|
||||
val args = prepareCompilerArguments()
|
||||
|
||||
|
||||
+18
-2
@@ -9,8 +9,10 @@ import org.gradle.api.tasks.Classpath
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputFiles
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.api.tasks.incremental.IncrementalTaskInputs
|
||||
import org.gradle.workers.IsolationMode
|
||||
import org.gradle.workers.WorkerExecutor
|
||||
import org.jetbrains.kotlin.gradle.incremental.ChangedFiles
|
||||
import org.jetbrains.kotlin.gradle.internal.Kapt3KotlinGradleSubplugin.Companion.KAPT_WORKER_DEPENDENCIES_CONFIGURATION_NAME
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinAndroidPluginWrapper
|
||||
import org.jetbrains.kotlin.gradle.tasks.clearLocalState
|
||||
@@ -45,10 +47,9 @@ open class KaptWithoutKotlincTask @Inject constructor(private val workerExecutor
|
||||
lateinit var javacOptions: Map<String, String>
|
||||
|
||||
@TaskAction
|
||||
fun compile() {
|
||||
fun compile(inputs: IncrementalTaskInputs) {
|
||||
logger.info("Running kapt annotation processing using the Gradle Worker API")
|
||||
checkAnnotationProcessorClasspath()
|
||||
clearLocalState()
|
||||
|
||||
val compileClasspath = classpath.files.toMutableList()
|
||||
if (project.plugins.none { it is KotlinAndroidPluginWrapper }) {
|
||||
@@ -66,6 +67,11 @@ open class KaptWithoutKotlincTask @Inject constructor(private val workerExecutor
|
||||
compileClasspath,
|
||||
javaSourceRoots.toList(),
|
||||
|
||||
getChangedFiles(inputs),
|
||||
getCompiledSources(),
|
||||
incAptCache,
|
||||
classpathDirtyFqNamesHistoryDir.singleOrNull(),
|
||||
|
||||
destinationDir,
|
||||
classesDir,
|
||||
stubsDir,
|
||||
@@ -155,6 +161,11 @@ private class KaptExecution @Inject constructor(
|
||||
compileClasspath,
|
||||
javaSourceRoots,
|
||||
|
||||
changedFiles,
|
||||
compiledSources,
|
||||
incAptCache,
|
||||
classpathFqNamesHistory,
|
||||
|
||||
sourcesOutputDir,
|
||||
classesOutputDir,
|
||||
stubsOutputDir,
|
||||
@@ -186,6 +197,11 @@ private data class KaptOptionsForWorker(
|
||||
val compileClasspath: List<File>,
|
||||
val javaSourceRoots: List<File>,
|
||||
|
||||
val changedFiles: List<File>,
|
||||
val compiledSources: List<File>,
|
||||
val incAptCache: File?,
|
||||
val classpathFqNamesHistory: File?,
|
||||
|
||||
val sourcesOutputDir: File,
|
||||
val classesOutputDir: File,
|
||||
val stubsOutputDir: File,
|
||||
|
||||
+13
-4
@@ -43,15 +43,24 @@ fun encodePluginOptions(options: Map<String, List<String>>): String {
|
||||
return Base64.getEncoder().encodeToString(os.toByteArray())
|
||||
}
|
||||
|
||||
internal fun CompilerPluginOptions.withWrappedKaptOptions(withApClasspath: Iterable<File>): CompilerPluginOptions {
|
||||
internal fun CompilerPluginOptions.withWrappedKaptOptions(
|
||||
withApClasspath: Iterable<File>, changedFiles: List<File> = emptyList(), compiledSourcesDir: List<File> = emptyList()
|
||||
): CompilerPluginOptions {
|
||||
val resultOptionsByPluginId: MutableMap<String, List<SubpluginOption>> =
|
||||
subpluginOptionsByPluginId.toMutableMap()
|
||||
|
||||
resultOptionsByPluginId.compute(Kapt3KotlinGradleSubplugin.KAPT_SUBPLUGIN_ID) { _, kaptOptions ->
|
||||
val kaptOptionsWithClasspath =
|
||||
kaptOptions.orEmpty() + withApClasspath.map { FilesSubpluginOption("apclasspath", listOf(it)) }
|
||||
val changedFilesOption = FilesSubpluginOption("changedFile", changedFiles).takeIf { changedFiles.isNotEmpty() }
|
||||
val compiledSourcesOption =
|
||||
FilesSubpluginOption("compiledSourcesDir", compiledSourcesDir).takeIf { compiledSourcesDir.isNotEmpty() }
|
||||
|
||||
wrapPluginOptions(kaptOptionsWithClasspath, "configuration")
|
||||
val kaptOptionsWithClasspath =
|
||||
kaptOptions.orEmpty() +
|
||||
withApClasspath.map { FilesSubpluginOption("apclasspath", listOf(it)) } +
|
||||
changedFilesOption +
|
||||
compiledSourcesOption
|
||||
|
||||
wrapPluginOptions(kaptOptionsWithClasspath.filterNotNull(), "configuration")
|
||||
}
|
||||
|
||||
val result = CompilerPluginOptions()
|
||||
|
||||
+1
@@ -79,6 +79,7 @@ class Android25ProjectHandler(
|
||||
.map { it.file.absolutePath }
|
||||
.toTypedArray()
|
||||
}
|
||||
kotlinTask.javaOutputDir = javaTask.destinationDir
|
||||
}
|
||||
|
||||
override fun getSourceProviders(variantData: BaseVariant): Iterable<SourceProvider> =
|
||||
|
||||
+6
-1
@@ -433,7 +433,8 @@ open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), Kotl
|
||||
taskBuildDirectory,
|
||||
usePreciseJavaTracking = usePreciseJavaTracking,
|
||||
disableMultiModuleIC = disableMultiModuleIC(),
|
||||
multiModuleICSettings = multiModuleICSettings
|
||||
multiModuleICSettings = multiModuleICSettings,
|
||||
classpathFqNamesHistory = getClasspathFqNamesHistoryDir()
|
||||
)
|
||||
} else null
|
||||
|
||||
@@ -475,6 +476,10 @@ open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), Kotl
|
||||
return false
|
||||
}
|
||||
|
||||
@Optional
|
||||
@Internal
|
||||
internal open fun getClasspathFqNamesHistoryDir(): File? = null
|
||||
|
||||
// override setSource to track source directory sets and files (for generated android folders)
|
||||
override fun setSource(sources: Any?) {
|
||||
sourceRootsContainer.set(sources)
|
||||
|
||||
Reference in New Issue
Block a user