Prevent compile tasks from running when nothing changes
#KT-10932 fixed
This commit is contained in:
+18
-23
@@ -46,13 +46,13 @@ fun Project.initKapt(
|
||||
|
||||
val stubsDir = File(buildDir, "tmp/kapt/$variantName/classFileStubs")
|
||||
kotlinTask.extensions.extraProperties.set("kaptStubsDir", stubsDir)
|
||||
javaTask.appendClasspathDynamically(stubsDir)
|
||||
|
||||
javaTask.doFirst {
|
||||
javaTask.classpath += files(stubsDir)
|
||||
}
|
||||
|
||||
kotlinTask.doFirst {
|
||||
javaTask.doLast {
|
||||
kotlinAfterJavaTask.source(kotlinTask.source)
|
||||
kotlinAfterJavaTask.source(kaptManager.javaAptSourceDir)
|
||||
// we don't want kotlinAfterJavaTask to track modifications in generated class
|
||||
kotlinAfterJavaTask.classpath -= project.files(javaTask.destinationDir)
|
||||
}
|
||||
|
||||
subpluginEnvironment.addSubpluginArguments(this, kotlinAfterJavaTask)
|
||||
@@ -61,6 +61,9 @@ fun Project.initKapt(
|
||||
kotlinTask.logger.kotlinDebug("kapt: Class file stubs are not used")
|
||||
}
|
||||
|
||||
javaTask.appendClasspathDynamically(kaptManager.wrappersDirectory)
|
||||
javaTask.source(kaptManager.javaAptSourceDir)
|
||||
|
||||
if (kaptExtension.inheritedAnnotations) {
|
||||
kotlinTask.extensions.extraProperties.set("kaptInheritedAnnotations", true)
|
||||
}
|
||||
@@ -70,13 +73,16 @@ fun Project.initKapt(
|
||||
kotlinAfterJavaTask?.source(kaptManager.getGeneratedKotlinSourceDir())
|
||||
}
|
||||
|
||||
var originalJavaCompilerArgs: List<String>? = null
|
||||
javaTask.doFirst {
|
||||
originalJavaCompilerArgs = (javaTask as JavaCompile).options.compilerArgs
|
||||
kaptManager.setupKapt()
|
||||
kaptManager.generateJavaHackFile()
|
||||
kotlinAfterJavaTask?.source(kaptManager.getGeneratedKotlinSourceDir())
|
||||
}
|
||||
|
||||
javaTask.doLast {
|
||||
(javaTask as JavaCompile).options.compilerArgs = originalJavaCompilerArgs
|
||||
kaptManager.afterJavaCompile()
|
||||
}
|
||||
|
||||
@@ -92,8 +98,8 @@ private fun Project.createKotlinAfterJavaTask(
|
||||
): AbstractCompile {
|
||||
val kotlinAfterJavaTask = with (taskFactory(KOTLIN_AFTER_JAVA_TASK_SUFFIX)) {
|
||||
kotlinDestinationDir = kotlinTask.kotlinDestinationDir
|
||||
destinationDir = javaTask.destinationDir
|
||||
classpath = javaTask.classpath
|
||||
destinationDir = kotlinTask.destinationDir
|
||||
classpath = kotlinTask.classpath - project.files(javaTask.destinationDir)
|
||||
this
|
||||
}
|
||||
|
||||
@@ -124,10 +130,11 @@ public class AnnotationProcessingManager(
|
||||
|
||||
private val project = task.project
|
||||
private val random = Random()
|
||||
val wrappersDirectory = File(aptWorkingDir, "wrappers")
|
||||
val javaAptSourceDir = File(aptWorkingDir, "java_src")
|
||||
|
||||
private companion object {
|
||||
val JAVA_FQNAME_PATTERN = "^([\\p{L}_$][\\p{L}\\p{N}_$]*\\.)*[\\p{L}_$][\\p{L}\\p{N}_$]*$".toRegex()
|
||||
val WRAPPERS_DIRECTORY = "wrappers"
|
||||
val GEN_ANNOTATION = "__gen/annotation"
|
||||
|
||||
private val ANDROID_APT_PLUGIN_ID = "com.neenbedankt.android-apt"
|
||||
@@ -135,7 +142,7 @@ public class AnnotationProcessingManager(
|
||||
|
||||
fun getAnnotationFile(): File {
|
||||
if (!aptWorkingDir.exists()) aptWorkingDir.mkdirs()
|
||||
return File(aptWorkingDir, "$WRAPPERS_DIRECTORY/annotations.$taskQualifier.txt")
|
||||
return File(wrappersDirectory, "annotations.$taskQualifier.txt")
|
||||
}
|
||||
|
||||
fun getGeneratedKotlinSourceDir(): File {
|
||||
@@ -153,16 +160,13 @@ public class AnnotationProcessingManager(
|
||||
|
||||
val annotationProcessorFqNames = lookupAnnotationProcessors(aptFiles)
|
||||
|
||||
val stubOutputDir = File(aptWorkingDir, WRAPPERS_DIRECTORY)
|
||||
generateAnnotationProcessorStubs(javaTask, annotationProcessorFqNames, stubOutputDir)
|
||||
generateAnnotationProcessorStubs(javaTask, annotationProcessorFqNames, wrappersDirectory)
|
||||
|
||||
val processorPath = setOf(stubOutputDir) + aptFiles
|
||||
val processorPath = setOf(wrappersDirectory) + aptFiles
|
||||
setProcessorPath(javaTask, (processorPath + javaTask.classpath).joinToString(File.pathSeparator))
|
||||
javaTask.appendClasspath(stubOutputDir)
|
||||
|
||||
addGeneratedSourcesOutputToCompilerArgs(javaTask, aptOutputDir)
|
||||
|
||||
|
||||
appendAnnotationsArguments()
|
||||
appendAdditionalComplerArgs()
|
||||
}
|
||||
@@ -177,7 +181,6 @@ public class AnnotationProcessingManager(
|
||||
}
|
||||
|
||||
fun generateJavaHackFile() {
|
||||
val javaAptSourceDir = File(aptWorkingDir, "java_src")
|
||||
val javaHackPackageDir = File(javaAptSourceDir, GEN_ANNOTATION)
|
||||
|
||||
if (!javaHackPackageDir.exists()) javaHackPackageDir.mkdirs()
|
||||
@@ -193,10 +196,6 @@ public class AnnotationProcessingManager(
|
||||
|
||||
project.logger.kotlinDebug("kapt: Java file stub generated: $javaHackClFile " +
|
||||
"(previously existed: $previouslyExisted)")
|
||||
|
||||
if (!javaTask.source.contains(javaHackClFile)) {
|
||||
javaTask.source(javaAptSourceDir)
|
||||
}
|
||||
}
|
||||
|
||||
private fun appendAnnotationsArguments() {
|
||||
@@ -241,10 +240,6 @@ public class AnnotationProcessingManager(
|
||||
addWrappersToCompilerArgs(javaTask, annotationProcessorWrapperFqNames)
|
||||
}
|
||||
|
||||
private fun JavaCompile.appendClasspath(file: File) {
|
||||
classpath += project.files(file)
|
||||
}
|
||||
|
||||
private fun addWrappersToCompilerArgs(javaTask: JavaCompile, wrapperFqNames: String) {
|
||||
javaTask.addCompilerArgument("-processor") { prevValue ->
|
||||
if (prevValue != null) "$prevValue,$wrapperFqNames" else wrapperFqNames
|
||||
|
||||
+21
-23
@@ -134,7 +134,7 @@ class Kotlin2JvmSourceSetProcessor(
|
||||
|
||||
val javaTask = project.tasks.findByName(sourceSet.compileJavaTaskName) as AbstractCompile?
|
||||
if (javaTask != null) {
|
||||
setUpKotlinToJavaDependency(project, kotlinTask, javaTask, logger)
|
||||
setUpKotlinToJavaDependency(kotlinTask, javaTask, logger)
|
||||
}
|
||||
|
||||
val kotlinAnnotationProcessingDep = cachedKotlinAnnotationProcessingDep ?: run {
|
||||
@@ -367,7 +367,6 @@ open class KotlinAndroidPlugin @Inject constructor(val scriptHandler: ScriptHand
|
||||
kotlinTask.kotlinDestinationDir = File(project.buildDir, "tmp/kotlin-classes/$variantDataName")
|
||||
kotlinTask.destinationDir = javaTask.destinationDir
|
||||
kotlinTask.description = "Compiles the ${variantDataName} kotlin."
|
||||
kotlinTask.classpath = javaTask.classpath
|
||||
kotlinTask.setDependsOn(javaTask.dependsOn)
|
||||
|
||||
fun SourceDirectorySet.addSourceDirectories(additionalSourceFiles: Collection<File>) {
|
||||
@@ -408,13 +407,17 @@ open class KotlinAndroidPlugin @Inject constructor(val scriptHandler: ScriptHand
|
||||
|
||||
subpluginEnvironment.addSubpluginArguments(project, kotlinTask)
|
||||
|
||||
kotlinTask.doFirst {
|
||||
// should not be evaluated until right before compileKotlin evaluation since android can change
|
||||
// java classpath during project evaluation (see prepareComAndroidSupportSupportV42311Library)
|
||||
val fullClasspath = lazy {
|
||||
val androidRT = project.files(AndroidGradleWrapper.getRuntimeJars(androidPlugin, androidExt))
|
||||
val fullClasspath = (javaTask.classpath + androidRT) - project.files(kotlinTask.kotlinDestinationDir)
|
||||
(it as AbstractCompile).classpath = fullClasspath
|
||||
|
||||
(javaTask.classpath + androidRT) - project.files(kotlinTask.kotlinDestinationDir)
|
||||
}
|
||||
kotlinTask.classpath = javaTask.classpath
|
||||
kotlinTask.updateClasspathBeforeTask { fullClasspath.value }
|
||||
kotlinTask.doFirst {
|
||||
for (task in project.getTasksByName(kotlinTaskName + KOTLIN_AFTER_JAVA_TASK_SUFFIX, false)) {
|
||||
(task as AbstractCompile).classpath = project.files(fullClasspath, javaTask.destinationDir)
|
||||
(task as AbstractCompile).classpath = project.files(fullClasspath.value, javaTask.destinationDir)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -432,7 +435,7 @@ open class KotlinAndroidPlugin @Inject constructor(val scriptHandler: ScriptHand
|
||||
}
|
||||
}
|
||||
|
||||
setUpKotlinToJavaDependency(project, kotlinTask, javaTask, logger)
|
||||
setUpKotlinToJavaDependency(kotlinTask, javaTask, logger)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -448,7 +451,7 @@ open class KotlinAndroidPlugin @Inject constructor(val scriptHandler: ScriptHand
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpKotlinToJavaDependency(project: Project, kotlinTask: AbstractCompile, javaTask: AbstractCompile, logger: Logger) {
|
||||
private fun setUpKotlinToJavaDependency(kotlinTask: AbstractCompile, javaTask: AbstractCompile, logger: Logger) {
|
||||
// Since we cannot update classpath statically, java not able to detect changes in the classpath after kotlin compiler.
|
||||
// Therefore this (probably inefficient since java cannot decide "uptodateness" by the list of changed class files, but told
|
||||
// explicitly being out of date whenever any kotlin files are compiled
|
||||
@@ -465,20 +468,15 @@ private fun setUpKotlinToJavaDependency(project: Project, kotlinTask: AbstractCo
|
||||
}
|
||||
|
||||
javaTask.dependsOn(kotlinTask.name)
|
||||
javaTask.doFirst {
|
||||
/*
|
||||
* It's important to modify javaTask.classpath only in doFirst,
|
||||
* because Android plugin uses ConventionMapping to modify it too (see JavaCompileConfigAction.execute),
|
||||
* and setting classpath explicitly prevents usage of Android mappings.
|
||||
* Also classpath setted by Android can be modified after excecution of some tasks (see VarianConfiguration.getCompileClasspath)
|
||||
* ex. it adds some support libraries jars after execution of prepareComAndroidSupportSupportV42311Library task,
|
||||
* so it's only safe to modify javaTask.classpath right before its usage
|
||||
*/
|
||||
javaTask.classpath += project.files(kotlinTask.property("kotlinDestinationDir"))
|
||||
}
|
||||
javaTask.doLast {
|
||||
javaTask.classpath -= project.files(kotlinTask.property("kotlinDestinationDir"))
|
||||
}
|
||||
/*
|
||||
* It's important to modify javaTask.classpath only in doFirst,
|
||||
* because Android plugin uses ConventionMapping to modify it too (see JavaCompileConfigAction.execute),
|
||||
* and setting classpath explicitly prevents usage of Android mappings.
|
||||
* Also classpath setted by Android can be modified after excecution of some tasks (see VarianConfiguration.getCompileClasspath)
|
||||
* ex. it adds some support libraries jars after execution of prepareComAndroidSupportSupportV42311Library task,
|
||||
* so it's only safe to modify javaTask.classpath right before its usage
|
||||
*/
|
||||
javaTask.appendClasspathDynamically(kotlinTask.kotlinDestinationDir!!)
|
||||
}
|
||||
|
||||
private fun loadSubplugins(project: Project): SubpluginEnvironment {
|
||||
|
||||
+46
@@ -1,10 +1,56 @@
|
||||
package org.jetbrains.kotlin.gradle.plugin
|
||||
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.internal.AbstractTask
|
||||
import org.gradle.api.tasks.compile.AbstractCompile
|
||||
import java.io.File
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
/*
|
||||
* Adding fake up-to-date check to do something before execution, and before input snapshots are taken.
|
||||
* Note, this hack is sensitive to up-to-date checks order, so it should be added before any custom up-to-date checks
|
||||
* that could return false.
|
||||
*
|
||||
* Should be used only for compileKotlinTask.
|
||||
*
|
||||
* // todo: could add configureKotlinTask that always executes before compileKotlin instead
|
||||
*/
|
||||
internal fun AbstractCompile.updateClasspathBeforeTask(newClassPath: (oldClassPath: FileCollection) -> FileCollection) {
|
||||
// Gradle can take inputs snapshots before task run (normally) or after task run (--rerun-tasks or some up-to-date when returned false).
|
||||
// Fake up-to-date check to update classpath dynamically before inputs snapshot is taken.
|
||||
// Won't be called in case of some other up-to-date check returned false before this one evaluation
|
||||
// Won't be called in case of --rerun-tasks
|
||||
var classpathIsUpdated = false
|
||||
outputs.upToDateWhen {
|
||||
classpath = newClassPath(classpath)
|
||||
classpathIsUpdated = true
|
||||
true
|
||||
}
|
||||
|
||||
// in case fake up-to-date check was not called (see comment above)
|
||||
doFirst {
|
||||
if (!classpathIsUpdated) {
|
||||
classpath = newClassPath(classpath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun AbstractCompile.appendClasspathDynamically(file: File) {
|
||||
var added = false
|
||||
|
||||
doFirst {
|
||||
if (file !in classpath) {
|
||||
classpath += project.files(file)
|
||||
added = true
|
||||
}
|
||||
}
|
||||
doLast {
|
||||
if (added) {
|
||||
classpath -= project.files(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal var AbstractTask.anyClassesCompiled: Boolean? by TaskPropertyDelegate("anyClassesCompiled")
|
||||
internal var AbstractTask.kotlinDestinationDir: File? by TaskPropertyDelegate("kotlinDestinationDir")
|
||||
|
||||
|
||||
+21
@@ -86,6 +86,27 @@ fun getSomething() = 10
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIncrementalBuildWithNoChanges() {
|
||||
val project = Project("AndroidIncrementalSingleModuleProject", gradleVersion)
|
||||
val tasksToExecute = arrayOf(
|
||||
":app:prepareComAndroidSupportAppcompatV72311Library",
|
||||
":app:prepareComAndroidSupportSupportV42311Library",
|
||||
":app:compileDebugKotlin",
|
||||
":app:compileDebugJavaWithJavac"
|
||||
)
|
||||
|
||||
project.build("assembleDebug") {
|
||||
assertSuccessful()
|
||||
assertContains(*tasksToExecute)
|
||||
}
|
||||
|
||||
project.build("assembleDebug") {
|
||||
assertSuccessful()
|
||||
assertContains(*tasksToExecute.map { it + " UP-TO-DATE" }.toTypedArray())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testModuleNameAndroid() {
|
||||
val project = Project("AndroidProject", gradleVersion)
|
||||
|
||||
+35
-8
@@ -3,6 +3,7 @@ package org.jetbrains.kotlin.gradle
|
||||
import org.gradle.api.logging.LogLevel
|
||||
import org.jetbrains.kotlin.gradle.plugin.CleanUpBuildListener
|
||||
import org.jetbrains.kotlin.gradle.tasks.USING_EXPERIMENTAL_INCREMENTAL_MESSAGE
|
||||
import org.jetbrains.kotlin.gradle.util.getFileByName
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
import kotlin.test.assertTrue
|
||||
@@ -221,23 +222,49 @@ class KotlinGradleIT: BaseGradleIT() {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testKaptSimpleIncrementalBuild() {
|
||||
doTestKaptIncrementalBuild("kaptSimple", arrayOf(":compileKotlin", ":compileJava"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testKaptStubsIncrementalBuild() {
|
||||
val project = Project("kaptStubs", GRADLE_VERSION)
|
||||
doTestKaptIncrementalBuild("kaptStubs", arrayOf(":compileKotlin", ":compileJava", ":compileKotlinAfterJava"))
|
||||
}
|
||||
|
||||
private fun doTestKaptIncrementalBuild(projectName: String, compileTasks: Array<String>) {
|
||||
val compileTasksUpToDate = compileTasks.map { it + " UP-TO-DATE" }.toTypedArray()
|
||||
val project = Project(projectName, GRADLE_VERSION)
|
||||
|
||||
project.build("build") {
|
||||
assertSuccessful()
|
||||
|
||||
// Modify the Kotlin source file somehow
|
||||
val someJavaFile = fileInWorkingDir("src/main/java/test.kt")
|
||||
someJavaFile.appendText(" ")
|
||||
}
|
||||
|
||||
project.projectDir.getFileByName("test.kt").appendText(" ")
|
||||
project.build("build") {
|
||||
assertSuccessful()
|
||||
assertContains(":compileKotlin")
|
||||
assertContains(":compileJava")
|
||||
assertNotContains(":compileJava UP-TO-DATE")
|
||||
assertContains(*compileTasks)
|
||||
assertNotContains(*compileTasksUpToDate)
|
||||
}
|
||||
|
||||
repeat(2) {
|
||||
project.build("build") {
|
||||
assertSuccessful()
|
||||
assertContains(*compileTasksUpToDate)
|
||||
}
|
||||
}
|
||||
|
||||
project.build("clean", "build") {
|
||||
assertSuccessful()
|
||||
assertContains(*compileTasks)
|
||||
assertNotContains(*compileTasksUpToDate)
|
||||
}
|
||||
|
||||
repeat(2) {
|
||||
project.build("build") {
|
||||
assertSuccessful()
|
||||
assertContains(*compileTasksUpToDate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -50,5 +50,6 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile 'com.android.support:appcompat-v7:23.1.1'
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib:1.1-SNAPSHOT"
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
org.gradle.jvmargs=-XX:MaxPermSize=512M
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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 javaPackage;
|
||||
|
||||
public class JavaClass {
|
||||
|
||||
public void test() {
|
||||
System.out.println(example.TestClassGenerated.class.getName());
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user