是否可以强制Java 8编译器创建可重现的类文件?

问题描述 投票:7回答:3

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

我使用Java 8u65构建了相同的代码,无论是在Mac上还是在Linux上。类文件是二元不同的。两个类都反编译回同一个源;看到差异需要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

匿名的lambdas正在获得不同数字的名字(lambda$restTemplate$15lambda$restTemplate$38)。

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

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

java lambda javac binary-reproducibility
3个回答
3
投票

我没有过多地研究它,但this article谈论了Java中可重现的构建,而reproducible-builds有一些工具可以帮助使构建(和类)可重现。

您可能正在寻找的链接是Reproducible Build Maven Plugin,专门用于Java,试图“从生成的工件中去除不可重现的数据”。


3
投票

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

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

在任何情况下,由于您自己构建代码,您只需将lambda表达式更改为匿名类声明即可。

编辑:我刚刚注意到你表示这些类是基于两个不同的操作系统构建的。这可能会在代码的编译阶段引入差异。为了具有可重现的构建,必须在相同的架构上执行。有没有理由不能将人工制品部署在一个架构(MacOS或Linux)上?


1
投票

正如DZone文章中提到的那样,在Major的答案中,对于gradle来说,这就是你所需要的:

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

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

© www.soinside.com 2019 - 2024. All rights reserved.