From d6cd46e0794c920cef702f033e1d1bb1aa692133 Mon Sep 17 00:00:00 2001 From: Alexey Tsvetkov Date: Wed, 30 Mar 2016 18:59:15 +0300 Subject: [PATCH] Build non-incrementally when input class-file is changed --- .../jetbrains/kotlin/gradle/tasks/Tasks.kt | 13 ++- .../jetbrains/kotlin/gradle/BaseGradleIT.kt | 3 + .../kotlin/gradle/TestRootAffectedIT.kt | 88 +++++++++++++++++++ .../jetbrains/kotlin/gradle/util/fileUtil.kt | 3 + .../kotlinProject/src/main/kotlin/Greeter.kt | 3 + ...{helloWorld.kt => KotlinGreetingJoiner.kt} | 10 +-- .../src/test/kotlin/TestGreeter.kt | 10 +++ .../{tests.kt => TestKotlinGreetingJoiner.kt} | 12 +-- 8 files changed, 124 insertions(+), 18 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/TestRootAffectedIT.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/test/resources/testProject/kotlinProject/src/main/kotlin/Greeter.kt rename libraries/tools/kotlin-gradle-plugin/src/test/resources/testProject/kotlinProject/src/main/kotlin/{helloWorld.kt => KotlinGreetingJoiner.kt} (54%) create mode 100644 libraries/tools/kotlin-gradle-plugin/src/test/resources/testProject/kotlinProject/src/test/kotlin/TestGreeter.kt rename libraries/tools/kotlin-gradle-plugin/src/test/resources/testProject/kotlinProject/src/test/kotlin/{tests.kt => TestKotlinGreetingJoiner.kt} (56%) diff --git a/libraries/tools/kotlin-gradle-plugin-core/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt b/libraries/tools/kotlin-gradle-plugin-core/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt index e48089d83c2..40a5504bf4f 100644 --- a/libraries/tools/kotlin-gradle-plugin-core/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt +++ b/libraries/tools/kotlin-gradle-plugin-core/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt @@ -307,7 +307,8 @@ open class KotlinCompile() : AbstractKotlinCompile() { // TODO: more precise will be not to rebuild unconditionally on classpath changes, but retrieve lookup info and try to find out which sources are affected by cp changes || isClassPathChanged() // so far considering it not incremental TODO: store java files in the cache and extract removed symbols from it here - || removed.any { it.isJavaFile() } + || removed.any { it.isJavaFile() || it.hasClassFileExtension() } + || modified.any { it.hasClassFileExtension() } || cacheVersions.any { it.checkVersion() != CacheVersion.Action.DO_NOTHING } ) { logger.kotlinInfo(if (!isIncrementalRequested) "clean caches on rebuild" else "classpath changed, rebuilding all kotlin files") @@ -376,7 +377,10 @@ open class KotlinCompile() : AbstractKotlinCompile() { it.removeClassfilesBySources(removedAndModified) }} - logger.kotlinInfo("compile iteration: ${sourcesToCompile.joinToString{ projectRelativePath(it) }}") + // can be empty if only removed sources are present + if (sourcesToCompile.isNotEmpty()) { + logger.kotlinInfo("compile iteration: ${sourcesToCompile.joinToString { projectRelativePath(it) }}") + } val (existingSource, nonExistingSource) = sourcesToCompile.partition { it.isFile } assert(nonExistingSource.isEmpty()) { "Trying to compile removed files: ${nonExistingSource.map(::projectRelativePath)}" } @@ -710,4 +714,7 @@ internal fun Logger.kotlinDebug(message: ()->String) { } internal fun listClassFiles(path: String): Sequence = - File(path).walk().filter { it.isFile && it.extension.toLowerCase() == "class" } + File(path).walk().filter { it.isFile && it.hasClassFileExtension() } + +private fun File.hasClassFileExtension(): Boolean = + extension.toLowerCase() == "class" diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/BaseGradleIT.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/BaseGradleIT.kt index 017e716d721..c136284f625 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/BaseGradleIT.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/BaseGradleIT.kt @@ -86,6 +86,9 @@ abstract class BaseGradleIT { copyRecursively(this.resourcesRoot, workingDir) copyDirRecursively(File(resourcesRootFile, "GradleWrapper-$wrapperVersion"), projectDir) } + + fun relativePaths(files: Iterable): List = + files.map { it.relativeTo(projectDir).path } } class CompiledProject(val project: Project, val output: String, val resultCode: Int) { diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/TestRootAffectedIT.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/TestRootAffectedIT.kt new file mode 100644 index 00000000000..607530ff4e9 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/TestRootAffectedIT.kt @@ -0,0 +1,88 @@ +package org.jetbrains.kotlin.gradle + +import org.jetbrains.kotlin.gradle.util.allKotlinFiles +import org.jetbrains.kotlin.gradle.util.getFileByName +import org.jetbrains.kotlin.gradle.util.modify +import org.junit.Test +import java.io.File + +class TestRootAffectedIT : BaseGradleIT() { + @Test + fun testSourceRootClassIsModifiedIC() { + val project = Project("kotlinProject", "2.10") + val buildOptions = defaultBuildOptions().copy(incremental = true) + + project.build("build", options = buildOptions) { + assertSuccessful() + } + + val kotlinGreetingJoinerFile = project.projectDir.getFileByName("KotlinGreetingJoiner.kt") + kotlinGreetingJoinerFile.modify { + val replacing = "fun addName(name: String?): Unit" + val replacement = "fun addName(name: String): Unit" + assert(it.contains(replacing)) { "API has changed!" } + it.replace(replacing, replacement) + } + + project.build("build", options = buildOptions) { + assertSuccessful() + val expectedToCompile = project.relativePaths(listOf(kotlinGreetingJoinerFile) + project.allTestKotlinFiles()) + assertCompiledKotlinSources(expectedToCompile) + } + + project.build("build", options = buildOptions) { + assertSuccessful() + assertCompiledKotlinSources(emptyList()) + } + } + + @Test + fun testSourceRootClassIsRemovedIC() { + val project = Project("kotlinProject", "2.10") + val buildOptions = defaultBuildOptions().copy(incremental = true) + + project.build("build", options = buildOptions) { + assertSuccessful() + } + + val dummyFile = project.projectDir.getFileByName("Dummy.kt") + dummyFile.delete() + + project.build("build", options = buildOptions) { + assertSuccessful() + val expectedToCompile = project.relativePaths(project.allTestKotlinFiles()) + assertCompiledKotlinSources(expectedToCompile) + } + + project.build("build", options = buildOptions) { + assertSuccessful() + assertCompiledKotlinSources(emptyList()) + } + } + + @Test + fun testTestRootClassIsRemovedIC() { + val project = Project("kotlinProject", "2.10") + val buildOptions = defaultBuildOptions().copy(incremental = true) + + project.build("build", options = buildOptions) { + assertSuccessful() + } + + val greeterTestFile = project.projectDir.getFileByName("TestGreeter.kt") + greeterTestFile.delete() + + project.build("build", options = buildOptions) { + assertSuccessful() + assertCompiledKotlinSources(emptyList()) + } + + project.build("build", options = buildOptions) { + assertSuccessful() + assertCompiledKotlinSources(emptyList()) + } + } + + private fun Project.allTestKotlinFiles(): Iterable = + File(projectDir, "src/test").allKotlinFiles() +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/util/fileUtil.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/util/fileUtil.kt index ba96c1012cd..21ab43ea99c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/util/fileUtil.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/util/fileUtil.kt @@ -8,6 +8,9 @@ fun File.getFileByName(name: String): File = fun File.findFileByName(name: String): File? = walk().filter { it.isFile && it.name.equals(name, ignoreCase = true) }.firstOrNull() +fun File.allKotlinFiles(): Iterable = + walk().filter { it.isFile && it.extension.toLowerCase().equals("kt") }.toList() + fun File.modify(transform: (String)->String) { writeText(transform(readText())) } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/resources/testProject/kotlinProject/src/main/kotlin/Greeter.kt b/libraries/tools/kotlin-gradle-plugin/src/test/resources/testProject/kotlinProject/src/main/kotlin/Greeter.kt new file mode 100644 index 00000000000..accb6e430f8 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/test/resources/testProject/kotlinProject/src/main/kotlin/Greeter.kt @@ -0,0 +1,3 @@ +package demo + +open class Greeter(val greeting: String) diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/resources/testProject/kotlinProject/src/main/kotlin/helloWorld.kt b/libraries/tools/kotlin-gradle-plugin/src/test/resources/testProject/kotlinProject/src/main/kotlin/KotlinGreetingJoiner.kt similarity index 54% rename from libraries/tools/kotlin-gradle-plugin/src/test/resources/testProject/kotlinProject/src/main/kotlin/helloWorld.kt rename to libraries/tools/kotlin-gradle-plugin/src/test/resources/testProject/kotlinProject/src/main/kotlin/KotlinGreetingJoiner.kt index 5db792c9294..e75fca8cbc6 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/test/resources/testProject/kotlinProject/src/main/kotlin/helloWorld.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/test/resources/testProject/kotlinProject/src/main/kotlin/KotlinGreetingJoiner.kt @@ -1,22 +1,18 @@ package demo -import com.google.common.primitives.Ints import com.google.common.base.Joiner import java.util.ArrayList -class KotlinGreetingJoiner(val greeter : Greeter) { - +class KotlinGreetingJoiner(val greeter: Greeter) { val names = ArrayList() - fun addName(name : String?): Unit{ + fun addName(name: String?): Unit { names.add(name) } - fun getJoinedGreeting() : String? { + fun getJoinedGreeting(): String? { val joiner = Joiner.on(" and ").skipNulls(); return "${greeter.greeting} ${joiner.join(names)}" } } -public open class Greeter(val greeting : String) - diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/resources/testProject/kotlinProject/src/test/kotlin/TestGreeter.kt b/libraries/tools/kotlin-gradle-plugin/src/test/resources/testProject/kotlinProject/src/test/kotlin/TestGreeter.kt new file mode 100644 index 00000000000..978621e4ef5 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/test/resources/testProject/kotlinProject/src/test/kotlin/TestGreeter.kt @@ -0,0 +1,10 @@ +package demo + +import org.testng.Assert.assertEquals + +class TestGreeter { + fun test() { + val greeter = Greeter("Hi!") + assertEquals("Hi!", greeter.greeting) + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/resources/testProject/kotlinProject/src/test/kotlin/tests.kt b/libraries/tools/kotlin-gradle-plugin/src/test/resources/testProject/kotlinProject/src/test/kotlin/TestKotlinGreetingJoiner.kt similarity index 56% rename from libraries/tools/kotlin-gradle-plugin/src/test/resources/testProject/kotlinProject/src/test/kotlin/tests.kt rename to libraries/tools/kotlin-gradle-plugin/src/test/resources/testProject/kotlinProject/src/test/kotlin/TestKotlinGreetingJoiner.kt index 1a7a3a23b80..2fec341a8e4 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/test/resources/testProject/kotlinProject/src/test/kotlin/tests.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/test/resources/testProject/kotlinProject/src/test/kotlin/TestKotlinGreetingJoiner.kt @@ -1,18 +1,14 @@ package demo -import com.google.common.primitives.Ints -import com.google.common.base.Joiner -import org.testng.Assert.* -import org.testng.annotations.AfterMethod -import org.testng.annotations.BeforeMethod +import org.testng.Assert.assertEquals import org.testng.annotations.Test as test -class TestSource() { - @test fun f() { +class TestKotlinGreetingJoiner() { + @test + fun test() { val example : KotlinGreetingJoiner = KotlinGreetingJoiner(Greeter("Hi")) example.addName("Harry") example.addName("Ron") - example.addName(null) example.addName("Hermione") assertEquals(example.getJoinedGreeting(), "Hi Harry and Ron and Hermione")