如何使用javac在不同平台上创建二进制相同的类文件?

时间:2017-02-01 00:31:45

标签: java java-8 aws-lambda

我用Java编写AWS Lambda函数。 我用来上传我的lambdas(Terraform)的工具想要使用我的jar文件的SHA-256哈希来跟踪是否需要上传一个新版本的lambda。

问题是,不同操作系统平台(Windows和Linux)上的不同JDK会创建略微不同的字节码(即使使用相同的“更新”版本的JDK)。这意味着,如果我在Windows上上传lambda,然后在Linux上重新运行该进程 - 它将检测jar的不同哈希代码并不必要地重新上传lambda jar。

问题: 如何强制javac在不同的操作系统平台上创建相同的字节码?

3 个答案:

答案 0 :(得分:3)

你无法强制执行。有关生成的类文件的几个未指定的详细信息,例如某些源代码表达式的字节代码必须如何精确查看或成员或属性的顺序。

由于不需要在每次运行中生成完全相同的文件,因此编译器实现甚至都不会尝试。可以假设,当您使用完全相同的输入执行相同的软件(不仅是相同的源代码,而且是相同的选项)时,它将产生相同的输出,但这不仅需要相同的编译器版本,而且也是同样的JRE。

不幸的是,即使使用相同的实现和输入,可能也会有不同的行为。例如,有些尝试在一些Java 7实现中随机化java.util.HashMap的散列,如果javacHashMap中存储某些工件,那就不足为奇了。这不适用于Java 8,但可能适用于在Java 9中引入的不可变映射。编译器是否将使用该功能是不可预测的。

因此,如果您发现某个特定的jdk版本可以重复生成完全相同的字节代码,那么您现在可以使用它,但必须注意下一个版本可能没有该属性。

到目前为止还没有解决过,即使具有相同的字节码也不能保证具有相同的jar文件,因为未指定jar文件中的文件顺序。它可能取决于系统特定的文件迭代顺序。此外,由于jar文件是存储时间戳的zip文件,因此新编译的类文件肯定会产生不同的文件,除非您采取其他措施,例如:为所有条目强制执行特定的时间戳。

答案 1 :(得分:2)

对于遇到这个问题的任何机构:

  • 首先要看的是检查每个平台上JDK的供应商

原来我有相同的版本/更新级别,但实际上使用的是不同的JDK(duh)。

我在Windows上使用Oracle JDK,在Linux上使用OpenJDK。一旦我在Windows和Linux上将这些更改为Azul Zulu JDK(u112) - 看起来生成完全相同的字节码,至少对于我迄今为止编写的有限数量的Java代码(尽管根据Holger的答案,显然不应该依赖它。

答案 2 :(得分:0)

在Eclipse的构建过程中,会定期检查以下内容:从上一个基线以来没有git更改的项目中的任何已编译的类文件,与该基线的jar文件有任何二进制差异。经验告诉我们,只有在编译器中进行相关更改时才会出现差异。

这不是保证,但是经验证据通常相同的编译器版本在编译相同的源时会产生相同的字节。

在这种情况下,编译器是ecj。

从比较器(将很快删除)中查看recent example logfile,它确实发出意外的类文件更改信号,然后将其追溯到特定的编译器更改。相应的logs of a release预计为空。