我的雇主有业务需要使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$15
与lambda$restTemplate$38
)。
看来,当我在同一主机上重建时,我得到相同的字节。当主机不同时,数字会改变;两个Linux主机产生不同的字节。
什么决定了这些数字?有没有办法强制每个编译在这个地方使用相同的数字,从而产生相同的类文件?或者Java 8类文件编译是不确定的?
我没有过多地研究它,但this article谈论了Java中可重现的构建,而reproducible-builds有一些工具可以帮助使构建(和类)可重现。
您可能正在寻找的链接是Reproducible Build Maven Plugin,专门用于Java,试图“从生成的工件中去除不可重现的数据”。
lambda表达式的计数由编译器完成,并在遇到其他lambda表达式时增加。
如果编译器以相同的顺序读取文件,则它应该提供相同的编译类。
在任何情况下,由于您自己构建代码,您只需将lambda表达式更改为匿名类声明即可。
编辑:我刚刚注意到你表示这些类是基于两个不同的操作系统构建的。这可能会在代码的编译阶段引入差异。为了具有可重现的构建,必须在相同的架构上执行。有没有理由不能将人工制品部署在一个架构(MacOS或Linux)上?
正如DZone文章中提到的那样,在Major的答案中,对于gradle来说,这就是你所需要的:
tasks.withType(AbstractArchiveTask) {
preserveFileTimestamps = false
reproducibleFileOrder = true
}
将此添加到build.gradle
后,.jar
文件的md5sum在同一系统上的构建之间保持稳定。我无法测试其他系统,因为我问的每个人都有不同的编译器版本,这使得构建不同。