RFR: 8276766: Enable jar and jmod to produce deterministic timestamped content

John Neffenger jgneff at openjdk.java.net
Mon Nov 22 02:39:09 UTC 2021


On Fri, 19 Nov 2021 16:52:36 GMT, Andrew Leonard <aleonard at openjdk.org> wrote:

> Add a new --source-date <TIMESTAMP> (epoch milliseconds) option to jar and jmod to allow specification of time to use for created/updated jar/jmod entries. This then allows the ability to make the content deterministic.
> 
> Signed-off-by: Andrew Leonard <anleonar at redhat.com>

Thank you for this timely pull request, Andrew! I need this pull request and also #6395 to [enable reproducible builds in JavaFX](https://github.com/openjdk/jfx/pull/446). I drove myself crazy this weekend with time zones, and if I understand your proposed changes correctly, it looks as if you're hitting the same problems as I did:

1. The [`SOURCE_DATE_EPOCH` environment variable](https://reproducible-builds.org/specs/source-date-epoch/) is defined as the number of **seconds** since the epoch of 1970-01-01T00:00:00Z, but the new command option is defined as the number of milliseconds. That makes it difficult to set `--source-date=$SOURCE_DATE_EPOCH` on the command line.

2. Calling the method `ZipEntry.setTime(long)` will not allow for reproducible builds when the builds run in different time zones.

For the second problem, run the included Java Time program as shown below:


$ javac Time.java 
$ echo $SOURCE_DATE_EPOCH
1637085342
$ date --date="@$SOURCE_DATE_EPOCH"
Tue 16 Nov 2021 09:55:42 AM PST
$ java Time
Build timestamp = 2021-11-16T17:55:42Z
$ for f in *.zip; do zipinfo -v $f | grep -e Archive -e modified; done
Archive:  FailsInNome.zip
  file last modified on (DOS date/time):          2021 Nov 16 08:55:42
Archive:  FailsInRome.zip
  file last modified on (DOS date/time):          2021 Nov 16 18:55:42
Archive:  WorksInNome.zip
  file last modified on (DOS date/time):          2021 Nov 16 17:55:42
Archive:  WorksInRome.zip
  file last modified on (DOS date/time):          2021 Nov 16 17:55:42


import java.io.FileOutputStream;
import java.io.IOException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.TimeZone;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class Time {

    static void writeZipFile(String name, ZipEntry entry) throws IOException {
        var output = new ZipOutputStream(new FileOutputStream(name));
        output.putNextEntry(entry);
        output.closeEntry();
        output.close();
    }

    public static void main(String[] args) throws IOException {
        var instant = Instant.now().truncatedTo(ChronoUnit.SECONDS);
        var sourceDateEpoch = System.getenv("SOURCE_DATE_EPOCH");
        if (sourceDateEpoch != null) {
            long seconds = Long.parseLong(sourceDateEpoch);
            instant = Instant.ofEpochSecond(seconds);
        }
        System.out.println("Build timestamp = " + instant);

        var entry = new ZipEntry("Entry");

        long newTime = 1000 * instant.getEpochSecond();
        TimeZone.setDefault(TimeZone.getTimeZone("America/Nome"));
        entry.setTime(newTime);
        writeZipFile("FailsInNome.zip", entry);
        TimeZone.setDefault(TimeZone.getTimeZone("Europe/Rome"));
        entry.setTime(newTime);
        writeZipFile("FailsInRome.zip", entry);

        var dosTime = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);
        TimeZone.setDefault(TimeZone.getTimeZone("America/Nome"));
        entry.setTimeLocal(dosTime);
        writeZipFile("WorksInNome.zip", entry);
        TimeZone.setDefault(TimeZone.getTimeZone("Europe/Rome"));
        entry.setTimeLocal(dosTime);
        writeZipFile("WorksInRome.zip", entry);
    }
}

-------------

PR: https://git.openjdk.java.net/jdk/pull/6481


More information about the core-libs-dev mailing list