From 63e27a971302c1afb36f768864f2fad8e4f6e1d4 Mon Sep 17 00:00:00 2001 From: Ilya Goncharov Date: Tue, 14 Mar 2023 11:16:20 +0000 Subject: [PATCH] [Gradle, JS] Implement own IncrementalSyncTask --- .../api/kotlin-gradle-plugin-api.api | 10 ++ .../gradle/tasks/IncrementalSyncTask.kt | 24 +++++ .../kotlin/gradle/Kotlin2JsGradlePluginIT.kt | 1 - .../js/ir/DefaultIncrementalSyncTask.kt | 95 +++++++++++++++++++ .../kotlin/gradle/targets/js/ir/JsBinaries.kt | 7 +- .../gradle/targets/js/ir/KotlinBrowserJsIr.kt | 4 +- .../kotlin/gradle/targets/js/ir/KotlinD8Ir.kt | 4 +- .../targets/js/ir/KotlinJsIrSubTarget.kt | 4 +- .../gradle/targets/js/ir/KotlinJsIrTarget.kt | 13 +-- .../gradle/targets/js/ir/KotlinNodeJsIr.kt | 4 +- .../targets/js/ir/SyncExecutableTask.kt | 92 ------------------ 11 files changed, 145 insertions(+), 113 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin-api/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/IncrementalSyncTask.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/DefaultIncrementalSyncTask.kt delete mode 100644 libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/SyncExecutableTask.kt diff --git a/libraries/tools/kotlin-gradle-plugin-api/api/kotlin-gradle-plugin-api.api b/libraries/tools/kotlin-gradle-plugin-api/api/kotlin-gradle-plugin-api.api index 194fdc35c8b..6ed57648845 100644 --- a/libraries/tools/kotlin-gradle-plugin-api/api/kotlin-gradle-plugin-api.api +++ b/libraries/tools/kotlin-gradle-plugin-api/api/kotlin-gradle-plugin-api.api @@ -1484,6 +1484,16 @@ public abstract interface class org/jetbrains/kotlin/gradle/tasks/CompileUsingKo public abstract fun getUseDaemonFallbackStrategy ()Lorg/gradle/api/provider/Property; } +public abstract interface class org/jetbrains/kotlin/gradle/tasks/IncrementalSyncTask : org/gradle/api/Task { + public abstract fun getDestinationDir ()Ljava/io/File; + public abstract fun getDestinationDirectory ()Lorg/gradle/api/provider/Property; + public abstract fun getFrom ()Lorg/gradle/api/file/ConfigurableFileCollection; +} + +public final class org/jetbrains/kotlin/gradle/tasks/IncrementalSyncTask$DefaultImpls { + public static fun getDestinationDir (Lorg/jetbrains/kotlin/gradle/tasks/IncrementalSyncTask;)Ljava/io/File; +} + public abstract interface class org/jetbrains/kotlin/gradle/tasks/Kapt : org/jetbrains/kotlin/gradle/tasks/BaseKapt { public abstract fun getAddJdkClassesToClasspath ()Lorg/gradle/api/provider/Property; public abstract fun getKaptJars ()Lorg/gradle/api/file/ConfigurableFileCollection; diff --git a/libraries/tools/kotlin-gradle-plugin-api/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/IncrementalSyncTask.kt b/libraries/tools/kotlin-gradle-plugin-api/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/IncrementalSyncTask.kt new file mode 100644 index 00000000000..734294703d7 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-api/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/IncrementalSyncTask.kt @@ -0,0 +1,24 @@ +package org.jetbrains.kotlin.gradle.tasks + +import org.gradle.api.Task +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.provider.Property +import org.gradle.api.tasks.* +import org.gradle.work.NormalizeLineEndings +import java.io.File + +interface IncrementalSyncTask : Task { + @get:InputFiles + @get:NormalizeLineEndings + @get:IgnoreEmptyDirectories + @get:SkipWhenEmpty + val from: ConfigurableFileCollection + + @get:OutputDirectory + val destinationDirectory: Property + + @get:Internal + @Deprecated("Use destinationDirProperty with Provider API", ReplaceWith("destinationDirProperty.get()")) + val destinationDir: File + get() = destinationDirectory.get() +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt index 7e0855d412b..572039028cd 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt @@ -209,7 +209,6 @@ class Kotlin2JsIrGradlePluginIT : AbstractKotlin2JsGradlePluginIT(true) { 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") } } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/DefaultIncrementalSyncTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/DefaultIncrementalSyncTask.kt new file mode 100644 index 00000000000..53873dee6ec --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/DefaultIncrementalSyncTask.kt @@ -0,0 +1,95 @@ +package org.jetbrains.kotlin.gradle.targets.js.ir + +import org.gradle.api.DefaultTask +import org.gradle.api.file.CopySpec +import org.gradle.api.file.FileSystemOperations +import org.gradle.api.model.ObjectFactory +import org.gradle.api.tasks.* +import org.gradle.work.ChangeType +import org.gradle.work.DisableCachingByDefault +import org.gradle.work.InputChanges +import org.jetbrains.kotlin.gradle.targets.js.internal.RewriteSourceMapFilterReader +import org.jetbrains.kotlin.gradle.tasks.IncrementalSyncTask +import java.io.File +import javax.inject.Inject + +@DisableCachingByDefault +abstract class DefaultIncrementalSyncTask : DefaultTask(), IncrementalSyncTask { + + @get:Inject + abstract val fs: FileSystemOperations + + @get:Inject + abstract val objectFactory: ObjectFactory + + @TaskAction + fun doCopy(inputChanges: InputChanges) { + val destinationDir = destinationDirectory.get() + val commonAction: CopySpec.() -> Unit = { + into(destinationDir) + // 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 + ) + } + } + } + + val work = if (!inputChanges.isIncremental) { + fs.copy { + it.from(from) + it.commonAction() + }.didWork + } else { + val changedFiles = inputChanges.getFileChanges(from) + + val modified = changedFiles + .filter { + it.changeType == ChangeType.ADDED || it.changeType == ChangeType.MODIFIED + } + .map { it.file } + .toSet() + + val forCopy = from.asFileTree + .matching { patternFilterable -> + patternFilterable.exclude { + it.file.isFile && it.file !in modified + } + } + + val nonRemovingFiles = mutableSetOf() + + from.asFileTree + .visit { + nonRemovingFiles.add(it.relativePath.getFile(destinationDir)) + } + + val removingFiles = objectFactory.fileTree() + .from(destinationDir) + .also { fileTree -> + fileTree.exclude { + it.file.isFile && it.file in nonRemovingFiles + } + } + + val deleteWork = fs.delete { + it.delete(removingFiles) + } + + val copyWork = fs.copy { + it.from(forCopy) + it.commonAction() + } + + deleteWork.didWork || copyWork.didWork + } + + didWork = work + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/JsBinaries.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/JsBinaries.kt index d9c6995af64..52cac6abe3f 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/JsBinaries.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/JsBinaries.kt @@ -6,7 +6,6 @@ package org.jetbrains.kotlin.gradle.targets.js.ir import org.gradle.api.Project -import org.gradle.api.tasks.Copy import org.gradle.api.tasks.TaskProvider import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation import org.jetbrains.kotlin.gradle.plugin.KotlinTarget @@ -14,9 +13,9 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation import org.jetbrains.kotlin.gradle.targets.js.dsl.Distribution import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsBinaryMode import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsBinaryContainer.Companion.generateBinaryName -import org.jetbrains.kotlin.gradle.targets.js.subtargets.DefaultDistribution import org.jetbrains.kotlin.gradle.targets.js.subtargets.createDefaultDistribution import org.jetbrains.kotlin.gradle.targets.js.typescript.TypeScriptValidationTask +import org.jetbrains.kotlin.gradle.tasks.IncrementalSyncTask import org.jetbrains.kotlin.gradle.tasks.withType import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName @@ -56,9 +55,9 @@ sealed class JsIrBinary( val validateGeneratedTsTaskName: String = validateTypeScriptTaskName() - val linkSyncTask: TaskProvider + val linkSyncTask: TaskProvider get() = target.project.tasks - .withType() + .withType() .named(linkSyncTaskName) private fun linkSyncTaskName(): String = diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinBrowserJsIr.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinBrowserJsIr.kt index 664e02446e2..c1a6dfdfa9e 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinBrowserJsIr.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinBrowserJsIr.kt @@ -164,7 +164,7 @@ abstract class KotlinBrowserJsIr @Inject constructor(target: KotlinJsIrTarget) : task.commonConfigure( compilation = compilation, mode = mode, - inputFilesDirectory = binary.linkSyncTask.map { it.destinationDir }, + inputFilesDirectory = binary.linkSyncTask.flatMap { it.destinationDirectory }, entryModuleName = binary.linkTask.flatMap { it.compilerOptions.moduleName }, configurationActions = runTaskConfigurations, nodeJs = nodeJs, @@ -229,7 +229,7 @@ abstract class KotlinBrowserJsIr @Inject constructor(target: KotlinJsIrTarget) : task.commonConfigure( compilation = compilation, mode = mode, - inputFilesDirectory = binary.linkSyncTask.map { it.destinationDir }, + inputFilesDirectory = binary.linkSyncTask.flatMap { it.destinationDirectory }, entryModuleName = binary.linkTask.flatMap { it.compilerOptions.moduleName }, configurationActions = webpackTaskConfigurations, nodeJs = nodeJs, diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinD8Ir.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinD8Ir.kt index 7e0b2df8698..bd0ed97f11d 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinD8Ir.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinD8Ir.kt @@ -37,8 +37,8 @@ abstract class KotlinD8Ir @Inject constructor(target: KotlinJsIrTarget) : inputFileProperty.fileProvider( binary.linkSyncTask.flatMap { linkSyncTask -> binary.linkTask.flatMap { linkTask -> - linkTask.outputFileProperty.map { - linkSyncTask.destinationDir.resolve(it.name) + linkTask.outputFileProperty.map { file -> + linkSyncTask.destinationDirectory.get().resolve(file.name) } } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrSubTarget.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrSubTarget.kt index 65d348b0464..644a0235459 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrSubTarget.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrSubTarget.kt @@ -125,8 +125,8 @@ abstract class KotlinJsIrSubTarget( testJs.inputFileProperty.fileProvider( binary.linkSyncTask.flatMap { linkSyncTask -> binary.linkTask.flatMap { linkTask -> - linkTask.outputFileProperty.map { - linkSyncTask.destinationDir.resolve(it.name) + linkTask.outputFileProperty.map { file -> + linkSyncTask.destinationDirectory.get().resolve(file.name) } } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrTarget.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrTarget.kt index 435baff6ad0..3d90deea5c0 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrTarget.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrTarget.kt @@ -131,25 +131,22 @@ constructor( } } - private fun registerCompileSync(binary: JsIrBinary): TaskProvider { + private fun registerCompileSync(binary: JsIrBinary): TaskProvider { val compilation = binary.compilation val npmProject = compilation.npmProject - return project.registerTask( + return project.registerTask( binary.linkSyncTaskName ) { task -> - task.from( + task.from.from( binary.linkTask.flatMap { linkTask -> linkTask.destinationDirectory.map { it.asFile } } ) - task.from(project.tasks.named(compilation.processResourcesTaskName)) + task.from.from(project.tasks.named(compilation.processResourcesTaskName)) - val hashDir = npmProject.dir.resolve("sync-hashes") - task.hashDir.set(hashDir) - - task.into(npmProject.dist) + task.destinationDirectory.set(npmProject.dist) } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinNodeJsIr.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinNodeJsIr.kt index cef08c5035b..44b4d30539f 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinNodeJsIr.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinNodeJsIr.kt @@ -40,8 +40,8 @@ abstract class KotlinNodeJsIr @Inject constructor(target: KotlinJsIrTarget) : inputFileProperty.fileProvider( binary.linkSyncTask.flatMap { linkSyncTask -> binary.linkTask.flatMap { linkTask -> - linkTask.outputFileProperty.map { - linkSyncTask.destinationDir.resolve(it.name) + linkTask.outputFileProperty.map { file -> + linkSyncTask.destinationDirectory.get().resolve(file.name) } } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/SyncExecutableTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/SyncExecutableTask.kt deleted file mode 100644 index 10c02f95bbb..00000000000 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/SyncExecutableTask.kt +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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 - - override fun copy() { - val actualFiles = mutableSetOf() - - 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" - } -} \ No newline at end of file