Produce deterministic klib files.

Build systems like bazel rely on the output of a compilation being
deterministic. File checksums determine when an artifact is safe to
cache across builds. One can produce a klib deterministically by using a
fixed timestamp during klib compression.

Before this change:

```
$ kotlinc-native  -produce library -output /tmp/first/color.klib Color.kt
$ kotlinc-native  -produce library -output /tmp/second/color.klib Color.kt
$ shasum  /tmp/first/color.klib /tmp/second/color.klib
bc3f73678ff025cfbec9009f9a851a8ca74e1037  /tmp/first/color.klib
65aa37886fbd53285f2e449a4dab6a2ad02732e6  /tmp/second/color.klib
```

After this change:

```
$ kotlinc-native  -produce library -output /tmp/first/color.klib Color.kt
$ kotlinc-native  -produce library -output /tmp/second/color.klib Color.kt
$ shasum  /tmp/first/color.klib /tmp/second/color.klib
fcba304493916ae34d372188991f87b60a113cf3  /tmp/first/color.klib
fcba304493916ae34d372188991f87b60a113cf3  /tmp/second/color.klib
```
This commit is contained in:
Martin Petrov
2020-03-25 13:22:09 -04:00
committed by Roman
parent 923986d6a0
commit bc96e42585
2 changed files with 12 additions and 4 deletions
@@ -14,7 +14,9 @@ import java.lang.Exception
import java.nio.MappedByteBuffer
import java.nio.channels.FileChannel
import java.nio.file.*
import java.nio.file.attribute.BasicFileAttributeView
import java.nio.file.attribute.BasicFileAttributes
import java.nio.file.attribute.FileTime
data class File constructor(internal val javaPath: Path) {
constructor(parent: Path, child: String): this(parent.resolve(child))
@@ -62,10 +64,10 @@ data class File constructor(internal val javaPath: Path) {
Files.copy(javaPath, destination.javaPath, StandardCopyOption.REPLACE_EXISTING)
}
fun recursiveCopyTo(destination: File) {
fun recursiveCopyTo(destination: File, resetTimeAttributes: Boolean = false) {
val sourcePath = javaPath
val destPath = destination.javaPath
sourcePath.recursiveCopyTo(destPath)
sourcePath.recursiveCopyTo(destPath, resetTimeAttributes = resetTimeAttributes)
}
fun mkdirs() = Files.createDirectories(javaPath)
@@ -185,7 +187,7 @@ fun createTempFile(name: String, suffix: String? = null)
fun createTempDir(name: String): File
= Files.createTempDirectory(name).File()
fun Path.recursiveCopyTo(destPath: Path) {
fun Path.recursiveCopyTo(destPath: Path, resetTimeAttributes: Boolean = false) {
val sourcePath = this
Files.walk(sourcePath).forEach next@ { oldPath ->
@@ -202,6 +204,10 @@ fun Path.recursiveCopyTo(destPath: Path) {
} else {
Files.copy(oldPath, newPath, StandardCopyOption.REPLACE_EXISTING)
}
if (resetTimeAttributes) {
val zero = FileTime.fromMillis(0)
Files.getFileAttributeView(newPath, BasicFileAttributeView::class.java).setTimes(zero, zero, zero);
}
}
}
@@ -41,7 +41,9 @@ private fun File.toPath() = Paths.get(this.path)
fun File.zipDirAs(unixFile: File) {
unixFile.withMutableZipFileSystem {
this.recursiveCopyTo(it.file("/"))
// Time attributes are not preserved to ensure that the output zip file bytes deterministic for a fixed set of
// input files.
this.recursiveCopyTo(it.file("/"), resetTimeAttributes = true)
}
}