From fa4083a5cb665942f71e8a55a5daf0b5f437e1ce Mon Sep 17 00:00:00 2001 From: Adam Semenenko <152864218+adam-enko@users.noreply.github.com> Date: Thu, 3 Jul 2025 10:12:57 +0200 Subject: [PATCH] Fix deleting stale files in Gradle transform task When source files are deleted Atomicfu would not delete the transformed files. Previously transformed files should be deleted whenever the transformer runs, so stale files do not linger. #547 --- .../plugin/gradle/AtomicFUGradlePlugin.kt | 5 ++- .../cases/JvmProjectTest.kt | 42 ++++++++++++++++++- .../framework/runner/BuildRunner.kt | 3 ++ .../framework/runner/Commands.kt | 9 +++- 4 files changed, 54 insertions(+), 5 deletions(-) diff --git a/atomicfu-gradle-plugin/src/main/kotlin/kotlinx/atomicfu/plugin/gradle/AtomicFUGradlePlugin.kt b/atomicfu-gradle-plugin/src/main/kotlin/kotlinx/atomicfu/plugin/gradle/AtomicFUGradlePlugin.kt index bb9a521d..212f0a6e 100644 --- a/atomicfu-gradle-plugin/src/main/kotlin/kotlinx/atomicfu/plugin/gradle/AtomicFUGradlePlugin.kt +++ b/atomicfu-gradle-plugin/src/main/kotlin/kotlinx/atomicfu/plugin/gradle/AtomicFUGradlePlugin.kt @@ -74,8 +74,8 @@ private fun Project.checkCompatibility(afuPluginVersion: String) { ) } if (!kotlinVersion.atLeast(1, 9, 0)) { - // Since Kotlin 1.9.0 the logic of the Gradle plugin from the Kotlin repo (AtomicfuKotlinGradleSubplugin) - // may be moved to the Gradle plugin in the library. The sources of the compiler plugin + // Since Kotlin 1.9.0 the logic of the Gradle plugin from the Kotlin repo (AtomicfuKotlinGradleSubplugin) + // may be moved to the Gradle plugin in the library. The sources of the compiler plugin // are published as `kotlin-atomicfu-compiler-plugin-embeddable` since Kotlin 1.9.0 and may be accessed out of the Kotlin repo. error( "You are applying `kotlinx-atomicfu` plugin of version $afuPluginVersion. " + @@ -487,6 +487,7 @@ abstract class AtomicFUTransformTask : ConventionTask() { @TaskAction fun transform() { + destinationDirectory.get().asFile.deleteRecursively() val cp = classPath.files.map { it.absolutePath } inputFiles.files.forEach { inputDir -> AtomicFUTransformer(cp, inputDir, destinationDirectory.get().asFile).let { t -> diff --git a/integration-testing/src/functionalTest/kotlin/kotlinx.atomicfu.gradle.plugin.test/cases/JvmProjectTest.kt b/integration-testing/src/functionalTest/kotlin/kotlinx.atomicfu.gradle.plugin.test/cases/JvmProjectTest.kt index de673131..14256794 100644 --- a/integration-testing/src/functionalTest/kotlin/kotlinx.atomicfu.gradle.plugin.test/cases/JvmProjectTest.kt +++ b/integration-testing/src/functionalTest/kotlin/kotlinx.atomicfu.gradle.plugin.test/cases/JvmProjectTest.kt @@ -33,10 +33,50 @@ class JvmProjectTest { jvmSample.checkConsumableDependencies(false) jvmSample.buildAndCheckBytecode() } - + // This test checks that jar is packed without duplicates, see #303 @Test fun testJar() { assertTrue(jvmSample.cleanAndJar().isSuccessful) } + + @Test + fun testFilesDeleted() { + + val buildClassesAtomicfuDir = jvmSample.targetDir.resolve("build/classes/atomicfu") + + /** Get all that Atomicfu generates in `build/classes/atomicfu/` */ + fun buildClassesAtomicFuDirFiles(): String = + buildClassesAtomicfuDir.walk() + .filter { it.isFile } + .map { it.relativeTo(jvmSample.targetDir).invariantSeparatorsPath } + .sorted() + .joinToString("\n") + + jvmSample.build().apply { + assertTrue { buildClassesAtomicfuDir.exists() } + assertEquals( + """ + build/classes/atomicfu/main/IntArithmetic.class + build/classes/atomicfu/main/META-INF/jvm-sample.kotlin_module + build/classes/atomicfu/main/MainKt.class + build/classes/atomicfu/test/ArithmeticTest.class + build/classes/atomicfu/test/META-INF/jvm-sample_test.kotlin_module + """.trimIndent(), + buildClassesAtomicFuDirFiles() + ) + } + + val projectSrcDir = jvmSample.targetDir.resolve("src") + projectSrcDir.deleteRecursively() + assertFalse { projectSrcDir.exists() } + + jvmSample.build().apply { + assertTrue { buildClassesAtomicfuDir.exists() } + assertEquals( + "", + buildClassesAtomicFuDirFiles(), + ) + } + } } diff --git a/integration-testing/src/functionalTest/kotlin/kotlinx.atomicfu.gradle.plugin.test/framework/runner/BuildRunner.kt b/integration-testing/src/functionalTest/kotlin/kotlinx.atomicfu.gradle.plugin.test/framework/runner/BuildRunner.kt index 867c0d39..8add7254 100644 --- a/integration-testing/src/functionalTest/kotlin/kotlinx.atomicfu.gradle.plugin.test/framework/runner/BuildRunner.kt +++ b/integration-testing/src/functionalTest/kotlin/kotlinx.atomicfu.gradle.plugin.test/framework/runner/BuildRunner.kt @@ -7,6 +7,9 @@ package kotlinx.atomicfu.gradle.plugin.test.framework.runner import java.io.File import java.nio.file.Files +/** + * @param[targetDir] The root Gradle project directory. + */ internal class GradleBuild(val projectName: String, val targetDir: File) { var enableJvmIrTransformation = false var enableJsIrTransformation = false diff --git a/integration-testing/src/functionalTest/kotlin/kotlinx.atomicfu.gradle.plugin.test/framework/runner/Commands.kt b/integration-testing/src/functionalTest/kotlin/kotlinx.atomicfu.gradle.plugin.test/framework/runner/Commands.kt index 13d1fec4..a895cab3 100644 --- a/integration-testing/src/functionalTest/kotlin/kotlinx.atomicfu.gradle.plugin.test/framework/runner/Commands.kt +++ b/integration-testing/src/functionalTest/kotlin/kotlinx.atomicfu.gradle.plugin.test/framework/runner/Commands.kt @@ -6,6 +6,11 @@ package kotlinx.atomicfu.gradle.plugin.test.framework.runner import kotlinx.atomicfu.gradle.plugin.test.framework.checker.getProjectClasspath +internal fun GradleBuild.build(): BuildResult = + runGradle(listOf("build")).also { + require(it.isSuccessful) { "${this.projectName}:build task FAILED: ${it.output} " } + } + internal fun GradleBuild.cleanAndBuild(): BuildResult = runGradle(listOf("clean", "build")).also { require(it.isSuccessful) { "${this.projectName}:build task FAILED: ${it.output} " } @@ -13,8 +18,8 @@ internal fun GradleBuild.cleanAndBuild(): BuildResult = internal fun GradleBuild.cleanAndJar(): BuildResult = runGradle(listOf("clean", "jar")) -internal fun GradleBuild.dependencies(): BuildResult = - runGradle(listOf("dependencies")).also { +internal fun GradleBuild.dependencies(): BuildResult = + runGradle(listOf("dependencies")).also { require(it.isSuccessful) { "${this.projectName}:dependencies task FAILED: ${it.output} " } }