Gradle, ProcessedFilesCache: use file as target type (not string)
recompute removed target directories
This commit is contained in:
+57
-63
@@ -19,7 +19,6 @@ import java.io.File
|
||||
* Cache for preventing processing some files twice.
|
||||
* Files are precessed to some subdirectories of [targetDir] called `target`.
|
||||
* [getOrCompute] returns path to directory with files produced for given input file.
|
||||
* Absent target directories will be deleted on [close].
|
||||
* `compute` will be called only for source files that was changed since last processing.
|
||||
*
|
||||
* See [org.jetbrains.kotlin.gradle.targets.js.npm.GradleNodeModuleBuilder] for example.
|
||||
@@ -74,7 +73,7 @@ internal open class ProcessedFilesCache(
|
||||
json.name("items")
|
||||
json.obj {
|
||||
byHash.forEach {
|
||||
json.name(it.key.toHex())
|
||||
json.name(it.key.contents.toHex())
|
||||
json.obj {
|
||||
json.name("src").value(it.value.src)
|
||||
json.name("target")
|
||||
@@ -124,15 +123,32 @@ internal open class ProcessedFilesCache(
|
||||
|
||||
private fun Char.toDigit(): Int = Character.digit(this, 16).also { check(it != -1) }
|
||||
|
||||
class ByteArrayWrapper(val contents: ByteArray) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as ByteArrayWrapper
|
||||
|
||||
if (!contents.contentEquals(other.contents)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return contents.contentHashCode()
|
||||
}
|
||||
}
|
||||
|
||||
private class State {
|
||||
val byHash = mutableMapOf<ByteArray, Element>()
|
||||
val byHash = mutableMapOf<ByteArrayWrapper, Element>()
|
||||
val byTarget = mutableMapOf<String, Element>()
|
||||
|
||||
operator fun get(elementHash: ByteArray) = byHash[elementHash]
|
||||
operator fun get(elementHash: ByteArray) = byHash[ByteArrayWrapper(elementHash)]
|
||||
|
||||
operator fun set(elementHash: ByteArray, element: Element) {
|
||||
elementHash.toHexString()
|
||||
byHash[elementHash] = element
|
||||
byHash[ByteArrayWrapper(elementHash)] = element
|
||||
val target = element.target
|
||||
if (target != null) {
|
||||
byTarget[target] = element
|
||||
@@ -146,87 +162,65 @@ internal open class ProcessedFilesCache(
|
||||
)
|
||||
|
||||
private val stateFile = targetDir.resolve(stateFileName)
|
||||
private val old: State
|
||||
private val state: State
|
||||
|
||||
init {
|
||||
targetDir.mkdirs()
|
||||
|
||||
val state: State? = if (stateFile.exists()) try {
|
||||
gson.newJsonReader(stateFile.reader()).use {
|
||||
readFrom(it)
|
||||
state = (if (stateFile.exists()) {
|
||||
try {
|
||||
gson.newJsonReader(stateFile.reader()).use { readFrom(it) }
|
||||
} catch (e: Throwable) {
|
||||
project.logger.warn("Cannot read $stateFile", e)
|
||||
if (targetDir.exists()) {
|
||||
targetDir.deleteRecursively()
|
||||
}
|
||||
null
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
project.logger.warn("Cannot read $stateFile", e)
|
||||
if (targetDir.exists()) {
|
||||
targetDir.deleteRecursively()
|
||||
}
|
||||
null
|
||||
} else null
|
||||
|
||||
old = state ?: State()
|
||||
} else null) ?: State()
|
||||
}
|
||||
|
||||
private val new = State()
|
||||
|
||||
internal fun getOrCompute(
|
||||
file: File,
|
||||
compute: () -> String?
|
||||
compute: () -> File?
|
||||
): File? = getOrComputeKey(file, compute)?.let { File(targetDir, it) }
|
||||
|
||||
private fun getOrComputeKey(
|
||||
file: File,
|
||||
compute: () -> File?
|
||||
): String? {
|
||||
val hash = hasher.hash(file).toByteArray()
|
||||
val old = old[hash]
|
||||
val old = state[hash]
|
||||
|
||||
if (old != null) {
|
||||
new[hash] = old
|
||||
return old.target
|
||||
} else {
|
||||
val key = compute()
|
||||
val existedTarget = new.byTarget[key]
|
||||
if (key != null && existedTarget != null) {
|
||||
if (existedTarget.src != file.canonicalPath) {
|
||||
reportTargetClash(key, file, File(existedTarget.src))
|
||||
}
|
||||
}
|
||||
new[hash] = Element(file.canonicalPath, key)
|
||||
return key
|
||||
if (checkTarget(old.target)) return old.target
|
||||
else project.logger.warn("Cannot find ${File(targetDir.relativeTo(project.projectDir), old.target!!)}, rebuilding")
|
||||
}
|
||||
|
||||
val key = compute()?.relativeTo(targetDir)?.toString()
|
||||
val existedTarget = state.byTarget[key]
|
||||
if (key != null && existedTarget != null) {
|
||||
if (existedTarget.src != file.canonicalPath) {
|
||||
reportTargetClash(key, file, File(existedTarget.src))
|
||||
}
|
||||
}
|
||||
state[hash] = Element(file.canonicalPath, key)
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
fun deleteTarget(key: String) {
|
||||
targetDir.resolve(key).deleteRecursively()
|
||||
fun checkTarget(target: String?): Boolean {
|
||||
if (target == null) return true
|
||||
return targetDir.resolve(target).exists()
|
||||
}
|
||||
|
||||
val targets: Collection<String>
|
||||
get() = new.byHash.mapNotNullTo(mutableSetOf()) { it.value.target }
|
||||
|
||||
protected open fun reportTargetClash(target: String, existedSrc: File, newSrc: File): Nothing =
|
||||
error("Both `$existedSrc` and `$newSrc` produces `$target`")
|
||||
|
||||
private fun getDeletedTargets(): MutableSet<String> {
|
||||
val result = mutableSetOf<String>()
|
||||
|
||||
old.byHash.forEach {
|
||||
val target = it.value.target
|
||||
if (target != null) {
|
||||
result.add(target)
|
||||
}
|
||||
}
|
||||
|
||||
new.byHash.forEach {
|
||||
val target = it.value.target
|
||||
if (target != null) {
|
||||
result.remove(target)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
getDeletedTargets().forEach {
|
||||
deleteTarget(it)
|
||||
}
|
||||
|
||||
stateFile.parentFile.mkdirs()
|
||||
gson.newJsonWriter(stateFile.writer()).use {
|
||||
new.writeTo(it)
|
||||
state.writeTo(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -190,7 +190,8 @@ internal class KotlinCompilationNpmResolver(
|
||||
|
||||
fun createPackageJson(): KotlinCompilationNpmResolution {
|
||||
val resolvedInternalDependencies = internalDependencies.map {
|
||||
it.resolution ?: error("Unresolved dependent npm package: ${this@KotlinCompilationNpmResolver} -> $it")
|
||||
it.resolution
|
||||
?: error("Unresolved dependent npm package: ${this@KotlinCompilationNpmResolver} -> $it")
|
||||
}
|
||||
val importedExternalGradleDependencies = externalGradleDependencies.mapNotNull {
|
||||
resolver.gradleNodeModules.get(it.dependency, it.artifact)
|
||||
|
||||
Reference in New Issue
Block a user