Build non-incrementally when input class-file is changed

This commit is contained in:
Alexey Tsvetkov
2016-03-30 18:59:15 +03:00
parent 1511a03027
commit d6cd46e079
8 changed files with 124 additions and 18 deletions
@@ -307,7 +307,8 @@ open class KotlinCompile() : AbstractKotlinCompile<K2JVMCompilerArguments>() {
// 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<K2JVMCompilerArguments>() {
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> =
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"
@@ -86,6 +86,9 @@ abstract class BaseGradleIT {
copyRecursively(this.resourcesRoot, workingDir)
copyDirRecursively(File(resourcesRootFile, "GradleWrapper-$wrapperVersion"), projectDir)
}
fun relativePaths(files: Iterable<File>): List<String> =
files.map { it.relativeTo(projectDir).path }
}
class CompiledProject(val project: Project, val output: String, val resultCode: Int) {
@@ -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> =
File(projectDir, "src/test").allKotlinFiles()
}
@@ -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<File> =
walk().filter { it.isFile && it.extension.toLowerCase().equals("kt") }.toList()
fun File.modify(transform: (String)->String) {
writeText(transform(readText()))
}
@@ -0,0 +1,3 @@
package demo
open class Greeter(val greeting: String)
@@ -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<String?>()
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)
@@ -0,0 +1,10 @@
package demo
import org.testng.Assert.assertEquals
class TestGreeter {
fun test() {
val greeter = Greeter("Hi!")
assertEquals("Hi!", greeter.greeting)
}
}
@@ -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")