Kapt3: Avoid annotation processing execution when possible (KT-17620)

Do not execute annotation processing if no stubs (and Java files) were changed since the previous execution.
This commit is contained in:
Yan Zhulanow
2017-04-17 20:07:39 +03:00
parent c8d7c339cf
commit 19c00abc62
7 changed files with 63 additions and 37 deletions
@@ -74,6 +74,35 @@ abstract class KaptIncrementalBaseIT(val shouldUseStubs: Boolean, val useKapt3:
}
}
@Test
fun testChangeFunctionBodyWithoutChangingSignature() {
val project = getProject()
project.build("build") {
assertSuccessful()
checkStubUsage()
checkGenerated(*annotatedElements)
checkNotGenerated("notAnnotatedFun")
assertContains("foo.ATest PASSED")
}
val utilKt = project.projectDir.getFileByName("util.kt")
utilKt.modify { oldContent ->
assert(oldContent.contains("2 * 2 == 4"))
oldContent.replace("2 * 2 == 4", "2 * 2 == 5")
}
project.build("build") {
assertSuccessful()
checkStubUsage()
if (useKapt3) {
assertNotContains(":kaptGenerateStubsKotlin UP-TO-DATE")
assertContains(":kaptKotlin UP-TO-DATE")
}
}
}
@Test
fun testAddAnnotatedElement() {
val project = getProject()
@@ -6,4 +6,10 @@ val valUtil = 0
@example.ExampleAnnotation
fun funUtil() {}
fun notAnnotatedFun() {}
fun notAnnotatedFun() {}
fun functionWithBody() {
if (2 * 2 == 4) {
// All's right
}
}
@@ -68,7 +68,6 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
File(project.project.buildDir, "tmp/kapt3/classes/$sourceSetName")
}
private val kotlinToKaptTasksMap = mutableMapOf<KotlinCompile, KaptTask>()
private val kotlinToKaptGenerateStubsTasksMap = mutableMapOf<KotlinCompile, KaptGenerateStubsTask>()
override fun isApplicable(project: Project, task: KotlinCompile) = Kapt3GradleSubplugin.isEnabled(project)
@@ -158,9 +157,8 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
}
override fun getSubpluginKotlinTasks(project: Project, kotlinCompile: KotlinCompile): List<AbstractCompile> {
val kaptTask = kotlinToKaptTasksMap[kotlinCompile]
val kaptGenerateStubsTask = kotlinToKaptGenerateStubsTasksMap[kotlinCompile]
return listOf(kaptTask, kaptGenerateStubsTask).filterNotNull()
return if (kaptGenerateStubsTask == null) emptyList() else listOf(kaptGenerateStubsTask)
}
// This method should be called no more than once for each Kapt3SubpluginContext
@@ -254,7 +252,6 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
private fun Kapt3SubpluginContext.createKaptKotlinTask(kaptGenerateStubsTask: KaptGenerateStubsTask) {
val kaptTask = project.tasks.create(getKaptTaskName("kapt"), KaptTask::class.java)
kaptTask.kotlinCompileTask = kotlinCompile
kotlinToKaptTasksMap[kotlinCompile] = kaptTask
kaptClasspathArtifacts.forEach { kaptTask.pluginOptions.addClasspathEntry(it) }
@@ -263,6 +260,16 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
kaptTask.mapClasspath { kotlinCompile.classpath }
kaptTask.classesDir = classesOutputDir
kaptTask.mapSource {
val sourcesFromKotlinTask = kotlinCompile.source
.filter { it.extension == "java" && !kaptTask.isInsideDestinationDirs(it) }
.asFileTree
val stubSources = project.fileTree(kaptTask.stubsDir)
sourcesFromKotlinTask + stubSources
}
kaptTask.dependsOn(kaptGenerateStubsTask)
kotlinCompile.dependsOn(kaptTask)
@@ -18,19 +18,16 @@ package org.jetbrains.kotlin.gradle.internal
import org.gradle.api.tasks.SourceTask
import org.gradle.api.tasks.incremental.IncrementalTaskInputs
import org.jetbrains.kotlin.cli.common.ExitCode
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.GradleICReporter
import org.jetbrains.kotlin.incremental.ICReporter
import org.jetbrains.kotlin.incremental.pathsAsStringRelativeTo
import java.io.File
open class KaptGenerateStubsTask : KotlinCompile() {
override val sourceRootsContainer = FilteringSourceRootsContainer({ isSourceRootAllowed(it) })
override val sourceRootsContainer = FilteringSourceRootsContainer(emptyList(), { isSourceRootAllowed(it) })
internal lateinit var kotlinCompileTask: KotlinCompile
@@ -56,8 +53,6 @@ open class KaptGenerateStubsTask : KotlinCompile() {
val sourceRoots = kotlinCompileTask.getSourceRoots()
val allKotlinSources = sourceRoots.kotlinSourceFiles
generatedSourcesDir.deleteRecursively()
logger.kotlinDebug { "All kotlin sources: ${allKotlinSources.pathsAsStringRelativeTo(project.rootProject.projectDir)}" }
if (allKotlinSources.isEmpty()) {
@@ -1,7 +1,6 @@
package org.jetbrains.kotlin.gradle.internal
import org.gradle.api.GradleException
import org.gradle.api.tasks.SourceTask
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.compile.AbstractCompile
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
@@ -9,33 +8,16 @@ import org.jetbrains.kotlin.com.intellij.openapi.util.io.FileUtil
import org.jetbrains.kotlin.compilerRunner.GradleCompilerEnvironment
import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner
import org.jetbrains.kotlin.compilerRunner.OutputItemsCollectorImpl
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptionsImpl
import org.jetbrains.kotlin.gradle.dsl.fillDefaultValues
import org.jetbrains.kotlin.gradle.tasks.*
import org.jetbrains.kotlin.incremental.classpathAsList
import org.jetbrains.kotlin.incremental.destinationAsFile
import java.io.File
import java.net.URLDecoder
import java.nio.charset.Charset
open class KaptTask : AbstractCompile() {
private val rawSourceRoots = FilteringSourceRootsContainer({ !it.isInsideDestinationDir() })
internal val pluginOptions = CompilerPluginOptions()
internal lateinit var kotlinCompileTask: KotlinCompile
override fun setSource(sources: Any?) {
val filteredSources = rawSourceRoots.set(sources)
super.setSource(filteredSources)
}
override fun source(vararg sources: Any?): SourceTask? {
val filteredSources = rawSourceRoots.add(*sources)
return super.source(filteredSources)
}
private fun File.isInsideDestinationDir(): Boolean {
return FileUtil.isAncestor(destinationDir, this, /* strict = */ false)
fun isInsideDestinationDirs(file: File): Boolean {
return FileUtil.isAncestor(destinationDir, file, /* strict = */ false)
|| FileUtil.isAncestor(classesDir, file, /* strict = */ false)
}
lateinit var classesDir: File
@@ -43,12 +25,14 @@ open class KaptTask : AbstractCompile() {
@TaskAction
override fun compile() {
/** Delete everything inside the [destinationDir] (sources output dir) */
/** Delete everything inside generated sources and classes output directory
* (annotation processing is not incremental) */
destinationDir.clearDirectory()
classesDir.clearDirectory()
val sourceRoots = SourceRoots.ForJvm.create(getSource(), rawSourceRoots)
val sourceRootsFromKotlin = kotlinCompileTask.sourceRootsContainer.sourceRoots
val rawSourceRoots = FilteringSourceRootsContainer(sourceRootsFromKotlin, { !isInsideDestinationDirs(it) })
val sourceRoots = SourceRoots.ForJvm.create(kotlinCompileTask.source, rawSourceRoots)
val args = K2JVMCompilerArguments()
kotlinCompileTask.setupCompilerArgs(args)
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.gradle.plugin
import org.gradle.api.Task
import org.gradle.api.file.FileCollection
import org.gradle.api.file.FileTree
import org.gradle.api.internal.HasConvention
import org.gradle.api.logging.Logger
import org.gradle.api.plugins.ExtensionAware
@@ -50,6 +51,10 @@ internal fun AbstractCompile.mapClasspath(fn: () -> FileCollection) {
conventionMapping.map("classpath", fn)
}
internal fun AbstractCompile.mapSource(fn: () -> FileTree) {
conventionMapping.map("source", fn)
}
internal inline fun <reified T : Any> Any.addConvention(name: String, plugin: T) {
(this as HasConvention).convention.plugins[name] = plugin
}
@@ -58,8 +58,8 @@ internal sealed class SourceRoots(val kotlinSourceFiles: List<File>) {
}
}
internal class FilteringSourceRootsContainer(val filter: (File) -> Boolean = { true }) {
private val mutableSourceRoots = mutableListOf<File>()
internal class FilteringSourceRootsContainer(roots: List<File> = emptyList(), val filter: (File) -> Boolean = { true }) {
private val mutableSourceRoots = roots.filterTo(mutableListOf(), filter)
val sourceRoots: List<File>
get() = mutableSourceRoots