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

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

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

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

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

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

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

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

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

对于遇到这个问题的任何身体:

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

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

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

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

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

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

查看来自比较器的最新示例日志文件 (将很快删除),它确实发出了意外的类文件更改信号,然后将其追溯到特定的编译器更改。 发布的相应日志应为空。