Support fetching build history files from directories for Android

Android Gradle plugin will start publishing clases in directories.
This commit adds support to find build history files when changed
files come from directories. It is similar to existing implementation
for jars i.e. it looks for .kotlin_module file under META-INF.

Test: ModulesApiHistoryAndroidTest
This commit is contained in:
Ivan Gavrilovic
2018-06-05 15:44:33 +01:00
committed by Alexey Tsvetkov
parent b2d9015723
commit 6bb54fd9a3
2 changed files with 218 additions and 19 deletions
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.incremental.multiproject
import org.jetbrains.kotlin.daemon.common.IncrementalModuleEntry
import org.jetbrains.kotlin.daemon.common.IncrementalModuleInfo
import org.jetbrains.kotlin.incremental.util.Either
import java.io.File
@@ -23,7 +24,7 @@ object EmptyModulesApiHistory : ModulesApiHistory {
open class ModulesApiHistoryJvm(protected val modulesInfo: IncrementalModuleInfo) : ModulesApiHistory {
protected val projectRootPath: Path = Paths.get(modulesInfo.projectRoot.absolutePath)
private val dirToHistoryFileCache = HashMap<File, File?>()
protected val dirToHistoryFileCache = HashMap<File, Set<File>>()
override fun historyFilesForChangedFiles(changedFiles: Set<File>): Either<Set<File>> {
val result = HashSet<File>()
@@ -53,28 +54,37 @@ open class ModulesApiHistoryJvm(protected val modulesInfo: IncrementalModuleInfo
val classFileDirs = classFiles.groupBy { it.parentFile }
for ((dir, files) in classFileDirs) {
val buildHistory = getBuildHistoryForDir(dir)
?: return Either.Error("Could not get build history for class files: ${files.joinToString()}")
result.add(buildHistory)
val historyEither = getBuildHistoryForDir(dir)
when (historyEither) {
is Either.Success<Set<File>> -> result.addAll(historyEither.value)
is Either.Error -> return historyEither
}
}
return Either.Success(result)
}
private fun getBuildHistoryForDir(file: File): File? =
dirToHistoryFileCache.getOrPut(file) {
protected open fun getBuildHistoryForDir(file: File): Either<Set<File>> {
val history = dirToHistoryFileCache.getOrPut(file) {
val module = modulesInfo.dirToModule[file]
val parent = file.parentFile
when {
module != null ->
module.buildHistoryFile
parent != null && projectRootPath.isParentOf(parent) ->
getBuildHistoryForDir(parent)
setOf(module.buildHistoryFile)
parent != null && projectRootPath.isParentOf(parent) -> {
val parentHistory = getBuildHistoryForDir(parent)
when (parentHistory) {
is Either.Success<Set<File>> -> parentHistory.value
is Either.Error -> return parentHistory
}
}
else ->
null
setOf()
}
}
return Either.Success(history)
}
protected open fun getBuildHistoryFilesForJar(jar: File): Either<Set<File>> {
val classListFile = modulesInfo.jarToClassListFile[jar] ?: return Either.Error("Unknown jar: $jar")
@@ -89,9 +99,11 @@ open class ModulesApiHistoryJvm(protected val modulesInfo: IncrementalModuleInfo
val classFileDirs = classFiles.groupBy { it.parentFile }
val result = HashSet<File>()
for ((dir, files) in classFileDirs) {
val buildHistory = getBuildHistoryForDir(dir)
?: return Either.Error("Could not get build history for class files: ${files.joinToString()}")
result.add(buildHistory)
val historyEither = getBuildHistoryForDir(dir)
when (historyEither) {
is Either.Success<Set<File>> -> result.addAll(historyEither.value)
is Either.Error -> return historyEither
}
}
return Either.Success(result)
}
@@ -103,14 +115,24 @@ class ModulesApiHistoryAndroid(modulesInfo: IncrementalModuleInfo) : ModulesApiH
if (!projectRootPath.isParentOf(jar)) return Either.Error("Non-project jar is modified $jar")
val jarPath = Paths.get(jar.absolutePath)
val possibleModules = getPossibleModuleNamesFromJar(jarPath)
.flatMapTo(HashSet()) { modulesInfo.nameToModules[it] ?: emptySet() }
return getHistoryForModuleNames(jarPath, getPossibleModuleNamesFromJar(jarPath))
}
val modules = possibleModules.filter { Paths.get(it.buildDir.absolutePath).isParentOf(jarPath) }
if (modules.isEmpty()) return Either.Error("Unknown module for $jar (candidates: ${possibleModules.joinToString()})")
override fun getBuildHistoryForDir(file: File): Either<Set<File>> {
if (!projectRootPath.isParentOf(file)) return Either.Error("Non-project file while looking for history $file")
val result = modules.mapTo(HashSet()) { it.buildHistoryFile }
return Either.Success(result)
// check both meta-inf and META-INF directories
val moduleNames =
getPossibleModuleNamesForDir(file.resolve("meta-inf")) + getPossibleModuleNamesForDir(file.resolve("META-INF"))
if (moduleNames.isEmpty()) {
return if (file.parentFile == null) {
Either.Error("Unable to find history for $file")
} else {
getBuildHistoryForDir(file.parentFile)
}
}
return getHistoryForModuleNames(file.toPath(), moduleNames)
}
private fun getPossibleModuleNamesFromJar(path: Path): Collection<String> {
@@ -133,6 +155,22 @@ class ModulesApiHistoryAndroid(modulesInfo: IncrementalModuleInfo) : ModulesApiH
return result
}
private fun getPossibleModuleNamesForDir(path: File): List<String> {
if (!path.isDirectory) return listOf()
return path.listFiles().filter { it.name.endsWith(".kotlin_module", ignoreCase = true) }.map { it.nameWithoutExtension }
}
private fun getHistoryForModuleNames(path: Path, moduleNames: Iterable<String>): Either<Set<File>> {
val possibleModules =
moduleNames.flatMapTo(HashSet<IncrementalModuleEntry>()) { modulesInfo.nameToModules[it] ?: emptySet() }
val modules = possibleModules.filter { Paths.get(it.buildDir.absolutePath).isParentOf(path) }
if (modules.isEmpty()) return Either.Error("Unknown module for $path (candidates: ${possibleModules.joinToString()})")
val result = modules.mapTo(HashSet()) { it.buildHistoryFile }
return Either.Success(result)
}
}
private fun Path.isParentOf(path: Path) = path.startsWith(this)