【问题标题】:Can the Java 8 compiler be forced into creating reproducible class files?可以强制 Java 8 编译器创建可重现的类文件吗?
【发布时间】:2019-03-13 20:36:35
【问题描述】:

我的雇主有业务需要使 Java 构建逐字节可重复。我知道使 JAR 文件可重现的困难(由于归档顺序和时间戳),但此时我正在谈论类文件。

我在 Mac 和 Linux 上都使用 Java 8u65 构建了相同的代码。类文件是二进制不同的。两个类都反编译回相同的源;要查看差异需要使用 javap 反汇编程序。

源代码好像是:

final TrustStrategy acceptingTrustStrategy =
              (X509Certificate[] chain, String authType) -> true;

在一次构建中,结果是:

private static boolean lambda$restTemplate$38(java.security.cert.X509Certificate[], java.lang.String) throws java.security.cert.CertificateException;
        Code:
           0: iconst_1
           1: ireturn
     

另一方面,它是:

private static boolean lambda$restTemplate$15(java.security.cert.X509Certificate[], java.lang.String) throws java.security.cert.CertificateException;
        Code:
           0: iconst_1
           1: ireturn

匿名 lambda 的名称中包含不同的数字(lambda$restTemplate$15lambda$restTemplate$38)。

看来,当我在同一主机上重建时,我得到了相同的字节。当主机不同时,数字会改变;两台 Linux 主机产生不同的字节。

是什么决定了这些数字?有没有办法强制每个编译在这个地方使用相同的数字,从而产生相同的类文件?还是 Java 8 类文件编译不确定?

【问题讨论】:

  • 同一位置的不同计数器值表示 lambda 以不同的顺序编译。序列保存在这里:hg.openjdk.java.net/jdk8/jdk8/langtools/file/1ff9d5118aae/src/…
  • 上面的“不同的计数器值”评论本身几乎就是一个答案。计数器值是否可以以任何方式控制?它是按源文件还是按 javac 调用重置?
  • @RobertMandeville:在不确切知道如何构建代码的情况下很难诊断,但有一些建议需要检查,大多数文件系统以任意顺序返回目录列表,这通常取决于文件/目录条目在磁盘上进行物理组织。当您在文件夹中列出文件时,Posix 系统不会定义文件的排序方式,因此您必须排序/请求特定的排序(例如字母数字)。

标签: java lambda javac binary-reproducibility


【解决方案1】:

我没有深入研究它,但this article 谈到了 Java 中的可重现构建,reproducible-builds 有一些工具可以帮助构建(和类)可重现。

您可能正在寻找的链接是 Reproducible Build Maven Plugin,专门为 Java 制作,以尝试“从生成的工件中剥离不可重现的数据”。

【讨论】:

  • 我看到了这个网站和这个工具。不幸的是,我没有看到任何关于可重现类的信息。从类文件中制作可重现的 JAR/WAR 是一个已知且可解决的问题,并且明确该插件解决了什么问题。自从 Lambda 在 Java 8 中引入以来,互联网上对可重现的类构建出奇地安静;它们曾经是给定的。
【解决方案2】:

lambda 表达式的计数由编译器完成,并在遇到其他 lambda 表达式时增加。

如果编译器以相同的顺序读取文件,它应该给出相同的编译类。

无论如何,由于您是自己构建代码,您可以简单地将 lambda 表达式更改为匿名类声明。

编辑:我刚刚注意到您指出这些类是建立在两个不同的操作系统上的。这可能会在代码的编译阶段引入差异。为了获得可重现的构建,它必须在相同的架构上执行。您是否有理由无法将工件部署为构建在一个架构(MacOS Linux)上?

【讨论】:

  • 我也许可以强制编译器读取文件的顺序。我怀疑我能否让我的开发人员将他们所有的 lambda 转换为类定义。就建筑而言,我遥遥领先。不幸的是,我的问题与平台无关。我在具有相同 Ubuntu 版本和相同 JDK 版本的两台主机上遇到这种情况。
【解决方案3】:

正如 DZone 文章中提到的,在 Major 的回答中链接,对于 gradle 这就是你所需要的:

tasks.withType(AbstractArchiveTask) {
    preserveFileTimestamps = false
    reproducibleFileOrder = true
}

将此添加到build.gradle 后,.jar 文件的 md5sum 在同一系统上的构建之间是稳定的。我无法使用其他系统进行测试,因为我询问的每个人都有不同的编译器版本,这使得构建不同。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-17
    • 2017-04-13
    • 2012-03-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多