Add gradle IC version file

This commit is contained in:
Alexey Tsvetkov
2016-03-28 07:45:54 +03:00
parent ede900ca87
commit c8b7764576
6 changed files with 125 additions and 59 deletions
@@ -14,7 +14,6 @@ import org.apache.commons.lang.StringUtils
import org.codehaus.groovy.runtime.MethodClosure
import org.gradle.api.GradleException
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.internal.AbstractTask
import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging
import org.gradle.api.plugins.ExtraPropertiesExtension
@@ -51,12 +50,10 @@ import java.util.*
const val DEFAULT_ANNOTATIONS = "org.jebrains.kotlin.gradle.defaultAnnotations"
const val ANNOTATIONS_PLUGIN_NAME = "org.jetbrains.kotlin.kapt"
const val KOTLIN_CACHES_DIR_NAME = "kotlin-caches"
const val KOTLIN_BUILD_DIR_NAME = "kotlin"
const val CACHES_DIR_NAME = "caches"
const val DIRTY_SOURCES_FILE_NAME = "dirty-sources.txt"
val AbstractTask.kotlinCachesDir: File
get() = File(File(project.buildDir, KOTLIN_CACHES_DIR_NAME), name)
abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractCompile() {
abstract protected val compiler: CLICompiler<T>
abstract protected fun createBlankArgs(): T
@@ -82,7 +79,6 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractCo
@TaskAction
fun execute(inputs: IncrementalTaskInputs): Unit {
logger.debug("Starting ${javaClass} task")
logger.kotlinDebug("all sources ${getSource().joinToString { it.path }}")
logger.kotlinDebug("is incremental == ${inputs.isIncremental}")
val modified = arrayListOf<File>()
@@ -107,7 +103,7 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractCo
beforeCompileHook(args)
try {
callCompiler(args, sources, inputs.isIncremental, modified, removed, kotlinCachesDir)
callCompiler(args, sources, inputs.isIncremental, modified, removed)
}
finally {
afterCompileHook(args)
@@ -130,8 +126,7 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractCo
args.noInline = kotlinOptions.noInline
}
protected open fun callCompiler(args: T, sources: List<File>, isIncremental: Boolean, modified: List<File>, removed: List<File>, cachesBaseDir: File) {
protected open fun callCompiler(args: T, sources: List<File>, isIncremental: Boolean, modified: List<File>, removed: List<File>) {
populateSources(args, sources)
val messageCollector = GradleMessageCollector(logger)
@@ -164,6 +159,17 @@ open class KotlinCompile() : AbstractKotlinCompile<K2JVMCompilerArguments>() {
// TODO: find out whether we really need to be able to override destination dir here, and how it should work with destinationDir property
private val compilerDestinationDir: String get() = if (StringUtils.isEmpty(kotlinOptions.destination)) { kotlinDestinationDir?.path.orEmpty() } else { kotlinOptions.destination }
// lazy because name is probably not available when constructor is called
private val taskBuildDirectory: File by lazy { File(File(project.buildDir, KOTLIN_BUILD_DIR_NAME), name) }
private val cacheDirectory: File by lazy { File(taskBuildDirectory, CACHES_DIR_NAME) }
private val dirtySourcesSinceLastTimeFile: File by lazy { File(taskBuildDirectory, DIRTY_SOURCES_FILE_NAME) }
private val cacheVersions by lazy {
listOf(normalCacheVersion(taskBuildDirectory),
experimentalCacheVersion(taskBuildDirectory),
dataContainerCacheVersion(taskBuildDirectory),
gradleCacheVersion(taskBuildDirectory))
}
override fun populateTargetSpecificArgs(args: K2JVMCompilerArguments) {
// show kotlin compiler where to look for java source files
// args.freeArgs = (args.freeArgs + getJavaSourceRoots().map { it.getAbsolutePath() }).toSet().toList()
@@ -216,7 +222,7 @@ open class KotlinCompile() : AbstractKotlinCompile<K2JVMCompilerArguments>() {
logger.kotlinDebug("args.moduleName = ${args.moduleName}")
}
override fun callCompiler(args: K2JVMCompilerArguments, sources: List<File>, isIncrementalRequested: Boolean, modified: List<File>, removed: List<File>, cachesBaseDir: File) {
override fun callCompiler(args: K2JVMCompilerArguments, sources: List<File>, isIncrementalRequested: Boolean, modified: List<File>, removed: List<File>) {
fun projectRelativePath(f: File) = f.toRelativeString(project.projectDir)
@@ -232,15 +238,14 @@ open class KotlinCompile() : AbstractKotlinCompile<K2JVMCompilerArguments>() {
val targets = listOf(TargetId(moduleName, targetType))
val outputDir = File(compilerDestinationDir)
val caches = hashMapOf<TargetId, GradleIncrementalCacheImpl>()
val lookupStorage = LookupStorage(File(cachesBaseDir, "lookups"))
val lookupStorage = LookupStorage(File(cacheDirectory, "lookups"))
val lookupTracker = LookupTrackerImpl(LookupTracker.DO_NOTHING)
var currentRemoved = removed.filter { it.isKotlinFile() }
val allGeneratedFiles = hashSetOf<GeneratedFile<TargetId>>()
val logAction = { logStr: String -> logger.kotlinInfo(logStr) }
val dirtySourcesSinceLastTimeFile = File(cachesBaseDir, DIRTY_SOURCES_FILE_NAME)
fun getOrCreateIncrementalCache(target: TargetId): GradleIncrementalCacheImpl {
val cacheDir = File(cachesBaseDir, "increCache.${target.name}")
val cacheDir = File(cacheDirectory, "increCache.${target.name}")
cacheDir.mkdirs()
return GradleIncrementalCacheImpl(targetDataRoot = cacheDir, targetOutputDir = outputDir, target = target)
}
@@ -294,58 +299,21 @@ open class KotlinCompile() : AbstractKotlinCompile<K2JVMCompilerArguments>() {
return changedClasspath.any()
}
fun allCachesVersions() = allCachesVersions(cachesBaseDir, listOf(cachesBaseDir))
fun calculateSourcesToCompile(): Pair<Set<File>, Boolean> {
if (!experimentalIncremental ||
!isIncrementalRequested ||
// 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() }
if (!experimentalIncremental
|| !isIncrementalRequested
// 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() }
|| cacheVersions.any { it.checkVersion() != CacheVersion.Action.DO_NOTHING }
) {
logger.kotlinInfo(if (!isIncrementalRequested) "clean caches on rebuild" else "classpath changed, rebuilding all kotlin files")
targets.forEach { getIncrementalCache(it).clean() }
lookupStorage.clean()
dirtySourcesSinceLastTimeFile.delete()
return Pair(sources.toSet(), false)
}
val actions = if (isIncrementalRequested) allCachesVersions().map { it.checkVersion() }
else listOf(CacheVersion.Action.REBUILD_ALL_KOTLIN)
// TODO: find out whether these flags should be emulated too
// val hasKotlin = HasKotlinMarker(dataManager)
// val rebuildAfterCacheVersionChanged = RebuildAfterCacheVersionChangeMarker(dataManager)
for (status in actions.distinct().sorted()) {
when (status) {
CacheVersion.Action.REBUILD_ALL_KOTLIN -> {
logger.kotlinInfo("Kotlin global lookup map format changed, rebuilding all kotlin files")
targets.forEach { getIncrementalCache(it).clean() }
lookupStorage.clean()
return Pair(sources.toSet(), false)
}
CacheVersion.Action.REBUILD_CHUNK -> {
logger.kotlinInfo("Clearing caches for " + targets.joinToString { it.name })
targets.forEach { getIncrementalCache(it).clean() }
}
CacheVersion.Action.CLEAN_NORMAL_CACHES -> {
logger.kotlinInfo("Clearing caches for all targets")
targets.forEach { getIncrementalCache(it).clean() }
}
CacheVersion.Action.CLEAN_EXPERIMENTAL_CACHES -> {
logger.kotlinInfo("Clearing experimental caches for all targets")
targets.forEach { getIncrementalCache(it).cleanExperimental() }
}
CacheVersion.Action.CLEAN_DATA_CONTAINER -> {
logger.kotlinInfo("Clearing lookup cache")
lookupStorage.clean()
dataContainerCacheVersion(cachesBaseDir).clean()
}
else -> {
assert(status == CacheVersion.Action.DO_NOTHING) { "Unknown version status $status" }
}
}
}
val dirtyFiles = dirtyKotlinSourcesFromGradle()
if (dirtySourcesSinceLastTimeFile.exists()) {
@@ -426,7 +394,7 @@ open class KotlinCompile() : AbstractKotlinCompile<K2JVMCompilerArguments>() {
getIncrementalCache = { caches[it]!! })
lookupStorage.update(lookupTracker, sourcesToCompile, currentRemoved)
allCachesVersions().forEach { it.saveIfNeeded() }
cacheVersions.forEach { it.saveIfNeeded() }
processCompilerExitCode(exitCode)
if (!isIncrementalDecided) break;
@@ -0,0 +1,16 @@
package org.jetbrains.kotlin.gradle.tasks
import org.jetbrains.kotlin.config.IncrementalCompilation
import org.jetbrains.kotlin.incremental.CacheVersion
import java.io.File
const val GRADLE_CACHE_VERSION = 0
const val GRADLE_CACHE_VERSION_FILE_NAME = "gradle-format-version.txt"
fun gradleCacheVersion(dataRoot: File): CacheVersion =
CacheVersion(ownVersion = GRADLE_CACHE_VERSION,
versionFile = File(dataRoot, GRADLE_CACHE_VERSION_FILE_NAME),
whenVersionChanged = CacheVersion.Action.REBUILD_ALL_KOTLIN,
whenTurnedOn = CacheVersion.Action.REBUILD_ALL_KOTLIN,
whenTurnedOff = CacheVersion.Action.REBUILD_ALL_KOTLIN,
isEnabled = { IncrementalCompilation.isExperimental() })
@@ -0,0 +1,67 @@
package org.jetbrains.kotlin.gradle
import org.jetbrains.kotlin.gradle.util.findFileByName
import org.jetbrains.kotlin.gradle.util.modify
import org.junit.Test
import java.io.File
import kotlin.test.assertNotEquals
import kotlin.test.assertTrue
class CacheVersionChangedIT : BaseGradleIT() {
@Test
fun testGradleCacheVersionChanged() {
compileIncrementallyWithChangedVersion("gradle-format-version.txt")
}
@Test
fun testNormalCacheVersionChanged() {
compileIncrementallyWithChangedVersion("format-version.txt")
}
@Test
fun testExperimentalCacheVersionChanged() {
compileIncrementallyWithChangedVersion("experimental-format-version.txt")
}
@Test
fun testDataContainerCacheVersionChanged() {
compileIncrementallyWithChangedVersion("data-container-format-version.txt")
}
private fun compileIncrementallyWithChangedVersion(versionFileName: String) {
val project = Project("kotlinProject", "2.10")
val options = defaultBuildOptions().copy(incremental = true)
fun File.projectPath() = relativeTo(project.projectDir).path
project.build("build", options = options) {
assertSuccessful()
}
val versionFile = File(project.projectDir, "build/kotlin/compileKotlin/$versionFileName")
assertTrue(versionFile.exists(), "${versionFile.projectPath()} does not exist!")
val modifiedVersion = "777"
versionFile.writeText(modifiedVersion)
// gradle won't call kotlin task if no file is changed
val dummyKtFile = project.projectDir.findFileByName("Dummy.kt")
dummyKtFile.modify { it + " " }
project.build("build", options = options) {
assertSuccessful()
assertNotEquals(modifiedVersion, versionFile.readText(), "${versionFile.projectPath()} was not rewritten by build")
val mainDir = File(project.projectDir, "src/main")
val mainKotlinFiles = mainDir.walk().filter { it.isFile && it.extension.equals("kt", ignoreCase = true) }
val mainKotlinRelativePaths = mainKotlinFiles.map { it.projectPath() }.toList()
assertCompiledKotlinSources(mainKotlinRelativePaths)
}
dummyKtFile.modify { it + " " }
project.build("build", options = options) {
assertSuccessful()
assertCompiledKotlinSources(listOf(dummyKtFile.projectPath()))
}
}
}
@@ -0,0 +1,11 @@
package org.jetbrains.kotlin.gradle.util
import java.io.File
fun File.findFileByName(name: String): File =
walk().filter { it.isFile && it.name.equals(name, ignoreCase = true) }.firstOrNull()
?: throw AssertionError("Could not find file with name '$name' in $this")
fun File.modify(transform: (String)->String) {
writeText(transform(readText()))
}
@@ -0,0 +1 @@
org.gradle.jvmargs=-Xmx1024m -XX:MaxPermSize=512m