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.
|
* Cache for preventing processing some files twice.
|
||||||
* Files are precessed to some subdirectories of [targetDir] called `target`.
|
* Files are precessed to some subdirectories of [targetDir] called `target`.
|
||||||
* [getOrCompute] returns path to directory with files produced for given input file.
|
* [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.
|
* `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.
|
* See [org.jetbrains.kotlin.gradle.targets.js.npm.GradleNodeModuleBuilder] for example.
|
||||||
@@ -74,7 +73,7 @@ internal open class ProcessedFilesCache(
|
|||||||
json.name("items")
|
json.name("items")
|
||||||
json.obj {
|
json.obj {
|
||||||
byHash.forEach {
|
byHash.forEach {
|
||||||
json.name(it.key.toHex())
|
json.name(it.key.contents.toHex())
|
||||||
json.obj {
|
json.obj {
|
||||||
json.name("src").value(it.value.src)
|
json.name("src").value(it.value.src)
|
||||||
json.name("target")
|
json.name("target")
|
||||||
@@ -124,15 +123,32 @@ internal open class ProcessedFilesCache(
|
|||||||
|
|
||||||
private fun Char.toDigit(): Int = Character.digit(this, 16).also { check(it != -1) }
|
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 {
|
private class State {
|
||||||
val byHash = mutableMapOf<ByteArray, Element>()
|
val byHash = mutableMapOf<ByteArrayWrapper, Element>()
|
||||||
val byTarget = mutableMapOf<String, 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) {
|
operator fun set(elementHash: ByteArray, element: Element) {
|
||||||
elementHash.toHexString()
|
elementHash.toHexString()
|
||||||
byHash[elementHash] = element
|
byHash[ByteArrayWrapper(elementHash)] = element
|
||||||
val target = element.target
|
val target = element.target
|
||||||
if (target != null) {
|
if (target != null) {
|
||||||
byTarget[target] = element
|
byTarget[target] = element
|
||||||
@@ -146,87 +162,65 @@ internal open class ProcessedFilesCache(
|
|||||||
)
|
)
|
||||||
|
|
||||||
private val stateFile = targetDir.resolve(stateFileName)
|
private val stateFile = targetDir.resolve(stateFileName)
|
||||||
private val old: State
|
private val state: State
|
||||||
|
|
||||||
init {
|
init {
|
||||||
targetDir.mkdirs()
|
targetDir.mkdirs()
|
||||||
|
|
||||||
val state: State? = if (stateFile.exists()) try {
|
state = (if (stateFile.exists()) {
|
||||||
gson.newJsonReader(stateFile.reader()).use {
|
try {
|
||||||
readFrom(it)
|
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) {
|
} else null) ?: State()
|
||||||
project.logger.warn("Cannot read $stateFile", e)
|
|
||||||
if (targetDir.exists()) {
|
|
||||||
targetDir.deleteRecursively()
|
|
||||||
}
|
|
||||||
null
|
|
||||||
} else null
|
|
||||||
|
|
||||||
old = state ?: State()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val new = State()
|
|
||||||
|
|
||||||
internal fun getOrCompute(
|
internal fun getOrCompute(
|
||||||
file: File,
|
file: File,
|
||||||
compute: () -> String?
|
compute: () -> File?
|
||||||
|
): File? = getOrComputeKey(file, compute)?.let { File(targetDir, it) }
|
||||||
|
|
||||||
|
private fun getOrComputeKey(
|
||||||
|
file: File,
|
||||||
|
compute: () -> File?
|
||||||
): String? {
|
): String? {
|
||||||
val hash = hasher.hash(file).toByteArray()
|
val hash = hasher.hash(file).toByteArray()
|
||||||
val old = old[hash]
|
val old = state[hash]
|
||||||
|
|
||||||
if (old != null) {
|
if (old != null) {
|
||||||
new[hash] = old
|
if (checkTarget(old.target)) return old.target
|
||||||
return old.target
|
else project.logger.warn("Cannot find ${File(targetDir.relativeTo(project.projectDir), old.target!!)}, rebuilding")
|
||||||
} 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
fun checkTarget(target: String?): Boolean {
|
||||||
targetDir.resolve(key).deleteRecursively()
|
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 =
|
protected open fun reportTargetClash(target: String, existedSrc: File, newSrc: File): Nothing =
|
||||||
error("Both `$existedSrc` and `$newSrc` produces `$target`")
|
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() {
|
override fun close() {
|
||||||
getDeletedTargets().forEach {
|
|
||||||
deleteTarget(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
stateFile.parentFile.mkdirs()
|
stateFile.parentFile.mkdirs()
|
||||||
gson.newJsonWriter(stateFile.writer()).use {
|
gson.newJsonWriter(stateFile.writer()).use {
|
||||||
new.writeTo(it)
|
state.writeTo(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+2
-1
@@ -190,7 +190,8 @@ internal class KotlinCompilationNpmResolver(
|
|||||||
|
|
||||||
fun createPackageJson(): KotlinCompilationNpmResolution {
|
fun createPackageJson(): KotlinCompilationNpmResolution {
|
||||||
val resolvedInternalDependencies = internalDependencies.map {
|
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 {
|
val importedExternalGradleDependencies = externalGradleDependencies.mapNotNull {
|
||||||
resolver.gradleNodeModules.get(it.dependency, it.artifact)
|
resolver.gradleNodeModules.get(it.dependency, it.artifact)
|
||||||
|
|||||||
Reference in New Issue
Block a user