[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.testbase.*
import org.jetbrains.kotlin.gradle.util.jsCompilerType import org.jetbrains.kotlin.gradle.util.jsCompilerType
import org.jetbrains.kotlin.gradle.util.normalizePath import org.jetbrains.kotlin.gradle.util.normalizePath
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.condition.DisabledIf import org.junit.jupiter.api.condition.DisabledIf
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths import java.nio.file.Paths
import java.util.zip.ZipFile import java.util.zip.ZipFile
import kotlin.io.path.* import kotlin.io.path.*
@@ -110,7 +110,6 @@ class Kotlin2JsIrGradlePluginIT : AbstractKotlin2JsGradlePluginIT(true) {
} }
@GradleTest @GradleTest
@Disabled // Persistent IR is no longer supported
fun testJsIrIncrementalInParallel(gradleVersion: GradleVersion) { fun testJsIrIncrementalInParallel(gradleVersion: GradleVersion) {
project("kotlin-js-browser-project", gradleVersion) { project("kotlin-js-browser-project", gradleVersion) {
buildGradleKts.modify(::transformBuildScriptWithPluginsDsl) 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") @DisplayName("falsify kotlin js compiler args")
@GradleTest @GradleTest
fun testFalsifyKotlinJsCompilerArgs(gradleVersion: GradleVersion) { 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.NamedDomainObjectContainer
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.Task import org.gradle.api.Task
import org.gradle.api.tasks.Copy
import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.TaskProvider
import org.jetbrains.kotlin.gradle.dsl.KotlinJsOptions import org.jetbrains.kotlin.gradle.dsl.KotlinJsOptions
import org.jetbrains.kotlin.gradle.plugin.* 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.KotlinJsTarget
import org.jetbrains.kotlin.gradle.targets.js.binaryen.BinaryenExec import org.jetbrains.kotlin.gradle.targets.js.binaryen.BinaryenExec
import org.jetbrains.kotlin.gradle.targets.js.dsl.* 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.npm.npmProject
import org.jetbrains.kotlin.gradle.targets.js.typescript.TypeScriptValidationTask import org.jetbrains.kotlin.gradle.targets.js.typescript.TypeScriptValidationTask
import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask 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 compilation = binary.compilation
val npmProject = compilation.npmProject val npmProject = compilation.npmProject
return project.registerTask<Copy>(
return project.registerTask<SyncExecutableTask>(
binary.linkSyncTaskName binary.linkSyncTaskName
) { task -> ) { task ->
task.from( task.from(
@@ -145,18 +144,8 @@ constructor(
task.from(project.tasks.named(compilation.processResourcesTaskName)) task.from(project.tasks.named(compilation.processResourcesTaskName))
// Rewrite relative paths in sourcemaps in the target directory val hashDir = npmProject.dir.resolve("sync-hashes")
task.eachFile { task.hashDir.set(hashDir)
if (it.name.endsWith(".js.map")) {
it.filter(
mapOf(
"srcSourceRoot" to it.file.parentFile,
"targetSourceRoot" to npmProject.dist
),
RewriteSourceMapFilterReader::class.java
)
}
}
task.into(npmProject.dist) 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"
}
}