From 2eb5dacffe2efe7a98e9f13c8f825c12071d8545 Mon Sep 17 00:00:00 2001 From: Ting-Yuan Huang Date: Fri, 21 Jul 2023 12:51:11 -0700 Subject: [PATCH] Keep timestamps when copying from java shadows so that it won't break build system's incremental mechanism. (cherry picked from commit b36093101d55551b5fd1f651df805d153c952748) --- .../google/devtools/ksp/IncrementalUtil.kt | 21 ++++++++++++++++ .../com/google/devtools/ksp/Incremental.kt | 25 ++----------------- .../ksp/KotlinSymbolProcessingExtension.kt | 2 +- .../google/devtools/ksp/test/OutputDepsIt.kt | 24 ++++++++++++++++++ .../src/main/kotlin/TestProcessor.kt | 6 +++++ 5 files changed, 54 insertions(+), 24 deletions(-) diff --git a/common-util/src/main/kotlin/com/google/devtools/ksp/IncrementalUtil.kt b/common-util/src/main/kotlin/com/google/devtools/ksp/IncrementalUtil.kt index f0c94dc705..72e45161c1 100644 --- a/common-util/src/main/kotlin/com/google/devtools/ksp/IncrementalUtil.kt +++ b/common-util/src/main/kotlin/com/google/devtools/ksp/IncrementalUtil.kt @@ -19,6 +19,8 @@ package com.google.devtools.ksp import com.google.devtools.ksp.symbol.* import java.io.File +import java.nio.file.Files +import java.nio.file.StandardCopyOption abstract class KSVirtualFile(val baseDir: File, val name: String) : KSFile { override val annotations: Sequence @@ -62,3 +64,22 @@ class NoSourceFile(baseDir: File, val fqn: String) : KSVirtualFile(baseDir, "NoS override val fileName: String get() = "" } + +// Copy recursively, including last-modified-time of file and its parent dirs. +// +// `java.nio.file.Files.copy(path1, path2, options...)` keeps last-modified-time (if supported) according to +// https://docs.oracle.com/javase/7/docs/api/java/nio/file/Files.html +fun copyWithTimestamp(src: File, dst: File, overwrite: Boolean) { + if (!dst.parentFile.exists()) + copyWithTimestamp(src.parentFile, dst.parentFile, false) + if (overwrite) { + Files.copy( + src.toPath(), + dst.toPath(), + StandardCopyOption.COPY_ATTRIBUTES, + StandardCopyOption.REPLACE_EXISTING + ) + } else { + Files.copy(src.toPath(), dst.toPath(), StandardCopyOption.COPY_ATTRIBUTES) + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/Incremental.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/Incremental.kt index 586536d112..fc184e199c 100644 --- a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/Incremental.kt +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/Incremental.kt @@ -46,8 +46,6 @@ import org.jetbrains.kotlin.types.typeUtil.supertypes import java.io.DataInput import java.io.DataOutput import java.io.File -import java.nio.file.Files -import java.nio.file.StandardCopyOption import java.util.* abstract class PersistentMap, V>( @@ -456,25 +454,6 @@ class IncrementalContext( fun File.abs() = File(baseDir, path) fun File.bak() = File(bakRoot, abs().toRelativeString(outRoot)) - // Copy recursively, including last-modified-time of file and its parent dirs. - // - // `java.nio.file.Files.copy(path1, path2, options...)` keeps last-modified-time (if supported) according to - // https://docs.oracle.com/javase/7/docs/api/java/nio/file/Files.html - fun copy(src: File, dst: File, overwrite: Boolean) { - if (!dst.parentFile.exists()) - copy(src.parentFile, dst.parentFile, false) - if (overwrite) { - Files.copy( - src.toPath(), - dst.toPath(), - StandardCopyOption.COPY_ATTRIBUTES, - StandardCopyOption.REPLACE_EXISTING - ) - } else { - Files.copy(src.toPath(), dst.toPath(), StandardCopyOption.COPY_ATTRIBUTES) - } - } - // Backing up outputs is necessary for two reasons: // // 1. Currently, outputs are always cleaned up in gradle plugin before compiler is called. @@ -488,13 +467,13 @@ class IncrementalContext( // Backup outputs.forEach { generated -> - copy(generated.abs(), generated.bak(), true) + copyWithTimestamp(generated.abs(), generated.bak(), true) } // Restore non-dirty outputs cleanOutputs.forEach { dst -> if (dst !in outputs) { - copy(dst.bak(), dst.abs(), false) + copyWithTimestamp(dst.bak(), dst.abs(), false) } } } diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/KotlinSymbolProcessingExtension.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/KotlinSymbolProcessingExtension.kt index d07db596ee..99bf72d067 100644 --- a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/KotlinSymbolProcessingExtension.kt +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/KotlinSymbolProcessingExtension.kt @@ -469,7 +469,7 @@ abstract class AbstractKotlinSymbolProcessingExtension( roundDir.walkTopDown().forEach { val dst = File(options.javaOutputDir, File(it.path).toRelativeString(roundDir)) if (dst.isFile || !dst.exists()) { - it.copyTo(dst) + copyWithTimestamp(it, dst, false) } } } diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/OutputDepsIt.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/OutputDepsIt.kt index 9c39e74311..955e6976cc 100644 --- a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/OutputDepsIt.kt +++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/OutputDepsIt.kt @@ -37,6 +37,9 @@ class OutputDepsIt { val src2Output = mapOf( "workload/src/main/java/p1/J1.java" to setOf( + "java/p1/J1Generated.java", + "java/p1/K1Generated.java", + "java/p1/K2Generated.java", "kotlin/p1/J1Generated.kt", "kotlin/p1/K1Generated.kt", "kotlin/p1/K2Generated.kt", @@ -44,11 +47,15 @@ class OutputDepsIt { "resources/p1.Anno2.log", ), "workload/src/main/java/p1/J2.java" to setOf( + "java/p1/J2Generated.java", "kotlin/p1/J2Generated.kt", "resources/p1.Anno1.log", "resources/p1.Anno2.log", ), "workload/src/main/kotlin/p1/K1.kt" to setOf( + "java/p1/J1Generated.java", + "java/p1/K1Generated.java", + "java/p1/K2Generated.java", "kotlin/p1/J1Generated.kt", "kotlin/p1/K1Generated.kt", "kotlin/p1/K2Generated.kt", @@ -56,6 +63,9 @@ class OutputDepsIt { "resources/p1.Anno2.log", ), "workload/src/main/kotlin/p1/K2.kt" to setOf( + "java/p1/J1Generated.java", + "java/p1/K1Generated.java", + "java/p1/K2Generated.java", "kotlin/p1/J1Generated.kt", "kotlin/p1/K1Generated.kt", "kotlin/p1/K2Generated.kt", @@ -66,6 +76,11 @@ class OutputDepsIt { val deletedSrc2Output = listOf( "workload/src/main/java/p1/J1.java" to listOf( + "java/p1/Anno1Generated.java", + "java/p1/Anno2Generated.java", + "java/p1/J2Generated.java", + "java/p1/K1Generated.java", + "java/p1/K2Generated.java", "kotlin/p1/Anno1Generated.kt", "kotlin/p1/Anno2Generated.kt", "kotlin/p1/J2Generated.kt", @@ -75,6 +90,10 @@ class OutputDepsIt { "resources/p1.Anno2.log", ), "workload/src/main/java/p1/J2.java" to listOf( + "java/p1/Anno1Generated.java", + "java/p1/Anno2Generated.java", + "java/p1/K1Generated.java", + "java/p1/K2Generated.java", "kotlin/p1/Anno1Generated.kt", "kotlin/p1/Anno2Generated.kt", "kotlin/p1/K1Generated.kt", @@ -83,6 +102,9 @@ class OutputDepsIt { "resources/p1.Anno2.log", ), "workload/src/main/kotlin/p1/K1.kt" to listOf( + "java/p1/Anno1Generated.java", + "java/p1/Anno2Generated.java", + "java/p1/K2Generated.java", "kotlin/p1/Anno1Generated.kt", "kotlin/p1/Anno2Generated.kt", "kotlin/p1/K2Generated.kt", @@ -90,6 +112,8 @@ class OutputDepsIt { "resources/p1.Anno2.log", ), "workload/src/main/kotlin/p1/K2.kt" to listOf( + "java/p1/Anno1Generated.java", + "java/p1/Anno2Generated.java", "kotlin/p1/Anno1Generated.kt", "kotlin/p1/Anno2Generated.kt", "resources/p1.Anno1.log", diff --git a/integration-tests/src/test/resources/output-deps/test-processor/src/main/kotlin/TestProcessor.kt b/integration-tests/src/test/resources/output-deps/test-processor/src/main/kotlin/TestProcessor.kt index 9e8436f931..a61b29a227 100644 --- a/integration-tests/src/test/resources/output-deps/test-processor/src/main/kotlin/TestProcessor.kt +++ b/integration-tests/src/test/resources/output-deps/test-processor/src/main/kotlin/TestProcessor.kt @@ -44,6 +44,12 @@ class TestProcessor : SymbolProcessor { writer.write("private val unused = \"unused\"") } } + codeGenerator.createNewFile(Dependencies(false, file), file.packageName.asString(), outputBaseFN, "java") + .use { output -> + OutputStreamWriter(output).use { writer -> + writer.write("class $outputBaseFN {}") + } + } } processed = true return emptyList()