diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/ProcessedFilesCache.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/ProcessedFilesCache.kt index 53be8012d9d..2a75d55dc12 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/ProcessedFilesCache.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/ProcessedFilesCache.kt @@ -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() + val byHash = mutableMapOf() val byTarget = mutableMapOf() - 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 - 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 { - val result = mutableSetOf() - - 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) } } } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinCompilationNpmResolver.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinCompilationNpmResolver.kt index f2144ea57e9..f6e898902c7 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinCompilationNpmResolver.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinCompilationNpmResolver.kt @@ -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)