[Gradle, JS] Not sync files which are not changed

[Gradle, JS] Try to fix test in windows

[Gradle, JS] Support hierarchical sync compile

[Gradle, JS] Add test on syncing only changed files

[Gradle, JS] Use relative path instead of just name

^KT-56719 fixed
This commit is contained in:
Ilya Goncharov
2023-02-17 14:36:45 +00:00
committed by Space Team
parent fbcaf3efeb
commit a4ce2ced6d
3 changed files with 187 additions and 18 deletions
@@ -25,10 +25,10 @@ import org.jetbrains.kotlin.gradle.tasks.USING_JS_IR_BACKEND_MESSAGE
import org.jetbrains.kotlin.gradle.testbase.*
import org.jetbrains.kotlin.gradle.util.jsCompilerType
import org.jetbrains.kotlin.gradle.util.normalizePath
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.condition.DisabledIf
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import java.util.zip.ZipFile
import kotlin.io.path.*
@@ -110,7 +110,6 @@ class Kotlin2JsIrGradlePluginIT : AbstractKotlin2JsGradlePluginIT(true) {
}
@GradleTest
@Disabled // Persistent IR is no longer supported
fun testJsIrIncrementalInParallel(gradleVersion: GradleVersion) {
project("kotlin-js-browser-project", gradleVersion) {
buildGradleKts.modify(::transformBuildScriptWithPluginsDsl)
@@ -126,6 +125,95 @@ class Kotlin2JsIrGradlePluginIT : AbstractKotlin2JsGradlePluginIT(true) {
}
}
@DisplayName("Only changed files synced during JS IR build")
@GradleTest
fun testJsIrOnlyChangedFilesSynced(gradleVersion: GradleVersion) {
project("kotlin-js-browser-project", gradleVersion) {
buildGradleKts.modify(::transformBuildScriptWithPluginsDsl)
val filesModified: MutableMap<String, Long> = mutableMapOf()
build("compileDevelopmentExecutableKotlinJs") {
assertTasksExecuted(":app:developmentExecutableCompileSync")
projectPath.resolve("build/js/packages/kotlin-js-browser-app")
.resolve("kotlin")
.toFile()
.walkTopDown()
.associateByTo(filesModified, { it.path }) {
it.lastModified()
}
}
projectPath.resolve("base/src/main/kotlin/Base.kt").modify {
it.replace("73", "37")
}
val fooTxt = projectPath.resolve("app/src/main/resources/foo/foo.txt")
fooTxt.parent.toFile().mkdirs()
fooTxt.createFile().writeText("foo")
build("compileDevelopmentExecutableKotlinJs") {
assertTasksExecuted(":app:developmentExecutableCompileSync")
val modified = projectPath.resolve("build/js/packages/kotlin-js-browser-app")
.resolve("kotlin")
.toFile()
.walkTopDown()
.filter { it.isFile }
.filterNot { filesModified[it.path] == it.lastModified() }
.toSet()
assertEquals(
setOf(
projectPath.resolve("build/js/packages/kotlin-js-browser-app/kotlin/kotlin-js-browser-base-js-ir.js").toFile(),
projectPath.resolve("build/js/packages/kotlin-js-browser-app/kotlin/foo/foo.txt").toFile(),
),
modified.toSet()
)
}
projectPath.resolve("build/js/packages/kotlin-js-browser-app")
.resolve("kotlin")
.toFile()
.walkTopDown()
.associateByTo(filesModified, { it.path }) {
it.lastModified()
}
fooTxt.writeText("bar")
build("compileDevelopmentExecutableKotlinJs") {
assertTasksExecuted(":app:developmentExecutableCompileSync")
val modified = projectPath.resolve("build/js/packages/kotlin-js-browser-app")
.resolve("kotlin")
.toFile()
.walkTopDown()
.filter { it.isFile }
.filterNot { filesModified[it.path] == it.lastModified() }
.toSet()
assertEquals(
setOf(
projectPath.resolve("build/js/packages/kotlin-js-browser-app/kotlin/foo/foo.txt").toFile(),
),
modified.toSet()
)
}
fooTxt.deleteExisting()
build("compileDevelopmentExecutableKotlinJs") {
assertTasksExecuted(":app:developmentExecutableCompileSync")
assertFileInProjectNotExists("build/js/packages/kotlin-js-browser-app/kotlin/foo/foo.txt")
assertFileInProjectNotExists("build/js/packages/kotlin-js-browser-app/sync-hashes/foo/foo.txt.hash")
}
}
}
@DisplayName("falsify kotlin js compiler args")
@GradleTest
fun testFalsifyKotlinJsCompilerArgs(gradleVersion: GradleVersion) {
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.gradle.targets.js.ir
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.tasks.Copy
import org.gradle.api.tasks.TaskProvider
import org.jetbrains.kotlin.gradle.dsl.KotlinJsOptions
import org.jetbrains.kotlin.gradle.plugin.*
@@ -22,7 +21,6 @@ import org.jetbrains.kotlin.gradle.targets.js.KotlinJsReportAggregatingTestRun
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget
import org.jetbrains.kotlin.gradle.targets.js.binaryen.BinaryenExec
import org.jetbrains.kotlin.gradle.targets.js.dsl.*
import org.jetbrains.kotlin.gradle.targets.js.internal.RewriteSourceMapFilterReader
import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject
import org.jetbrains.kotlin.gradle.targets.js.typescript.TypeScriptValidationTask
import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask
@@ -131,10 +129,11 @@ constructor(
}
}
private fun registerCompileSync(binary: JsIrBinary): TaskProvider<Copy> {
private fun registerCompileSync(binary: JsIrBinary): TaskProvider<SyncExecutableTask> {
val compilation = binary.compilation
val npmProject = compilation.npmProject
return project.registerTask<Copy>(
return project.registerTask<SyncExecutableTask>(
binary.linkSyncTaskName
) { task ->
task.from(
@@ -145,18 +144,8 @@ constructor(
task.from(project.tasks.named(compilation.processResourcesTaskName))
// Rewrite relative paths in sourcemaps in the target directory
task.eachFile {
if (it.name.endsWith(".js.map")) {
it.filter(
mapOf(
"srcSourceRoot" to it.file.parentFile,
"targetSourceRoot" to npmProject.dist
),
RewriteSourceMapFilterReader::class.java
)
}
}
val hashDir = npmProject.dir.resolve("sync-hashes")
task.hashDir.set(hashDir)
task.into(npmProject.dist)
}
@@ -0,0 +1,92 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.gradle.targets.js.ir
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Copy
import org.gradle.api.tasks.OutputDirectory
import org.gradle.internal.hash.FileHasher
import org.jetbrains.kotlin.gradle.targets.js.internal.RewriteSourceMapFilterReader
import org.jetbrains.kotlin.gradle.targets.js.toHex
import java.io.File
import javax.inject.Inject
abstract class SyncExecutableTask : Copy() {
@get:Inject
abstract val fileHasher: FileHasher
@get:Inject
abstract val objects: ObjectFactory
@get:OutputDirectory
abstract val hashDir: Property<File>
override fun copy() {
val actualFiles = mutableSetOf<File>()
val hashDirFile = hashDir.get()
eachFile {
actualFiles.add(it.relativeSourcePath.getFile(destinationDir))
val hashFile = hashDirFile.resolve(it.relativeSourcePath.pathString + ".$HASH_EXTENSION")
val currentHash = fileHasher.hash(it.file)
val currentHashHex = currentHash.toByteArray().toHex()
if (hashFile.exists()) {
val previousHash = hashFile.readText()
if (previousHash == currentHashHex) {
it.exclude()
return@eachFile
}
} else {
hashFile.parentFile.mkdirs()
}
hashFile.writeText(currentHashHex)
}
// Rewrite relative paths in sourcemaps in the target directory
eachFile {
if (it.name.endsWith(".js.map")) {
it.filter(
mapOf(
RewriteSourceMapFilterReader::srcSourceRoot.name to it.file.parentFile,
RewriteSourceMapFilterReader::targetSourceRoot.name to destinationDir
),
RewriteSourceMapFilterReader::class.java
)
}
}
super.copy()
objects.fileTree()
.from(hashDirFile)
.files
.forEach {
if (destinationDir.resolve(it.relativeTo(hashDirFile).path.removeSuffix(".$HASH_EXTENSION")) !in actualFiles) {
it.delete()
}
}
objects.fileTree()
.from(destinationDir)
.files
.forEach {
if (it !in actualFiles) {
it.delete()
}
}
}
companion object {
const val HASH_EXTENSION = "hash"
}
}