摘要
如何让ant从相同的.class文件中重复生成字节相同的jar文件?
背景
我们的构建过程如下:
- 从另一个应用程序的源存储库获取web服务定义(wsdl(文件
- 运行wsdl2java生成.java文件供web服务客户端(即我们的应用程序(使用
- 编译java文件
- 从编译器输出生成.jar文件
- 将"artifact"jar文件检查到源代码管理中
注意:我们完成了最后一步,这样开发人员就可以访问这个jar文件,而不用自己构建它。我们使用一个特殊的"派生"目录来区分源和工件。
问题
我们不能让ant生成字节相同的.jar文件,即使源文件没有改变,也就是说,每个构建都会生成一个略有不同的jar(具有不同的MD5(
我在网上查了一下,发现了这个5年前的问题:
如果我编译一些代码并使用ANT创建一个jar和相关的md5文件md5文件中的校验和每次都不同,即使代码没有更改。任何想法都是为什么这是如何规避的?我怀疑某个地方有一些时间戳信息。
http://www.velocityreviews.com/forums/t150783-creating-new-jar-same-code-different-md5.html
根据回复,我尝试了以下操作:
- 在jarring之前,将所有.class文件的时间戳设置为"0">
- 指定清单文件并将此清单的时间戳设置为0
【注:第二步似乎无效。见下文】
在每次构建之后,.jar文件仍然具有不同的MD5总和。
CSI:Jar文件
我已经取消捕获并检查了罐子,罐子的内容和时间戳在"不同"的罐子之间匹配,只有一个例外:META-INF/MMANIFEST.MF.的时间戳不同
代码
<-- touch classes and manifest to set consistent timestamp across builds -->
<touch millis="0">
<fileset dir="${mycompany.ws.classes.dir}"/>
</touch>
<touch millis="0" file="mymanifest.mf"/>
<jar destfile="${derived.lib.dir}/mycompanyws.jar"
manifest="mymanifest.mf"
basedir="${mycompany.ws.classes.dir}"
includes="**/com/mycompany/**,**/org/apache/xml/**"
/>
其他选项
如果.java文件发生了更改,我们可以使用更高级的ant编程只签入.jar文件。
我一直面临着类似的问题,但略有不同。我决定在这里分享它,因为它与问题的主题有关。为了在不同的时间生成两个字节相同的数字签名JAR文件,必须考虑以下几点:
-
时间戳:
**/*.class
文件必须具有相同的时间戳(java.util.zip.ZipEntry.setTime(long)
(。此外,META-INF/MANIFEST.MF
文件和证书文件(*.RSA
、*.DSA
和*.SF
(将添加到带有"现在"时间戳的JAR文件中。因此,即使您决定不编译类并使用已经编译的类(即具有原始JAR时间戳的类(,您得到的JAR也将是二进制的。 -
MANIFEST.MF
条目排序:请注意,MANIFEST.MF
文件中的键值对表示为java.util.HashMap
,而"does not guarantee that the order will remain constant over time."
。因此,当使用JDKv5和JDKv6jarsigner
工具对JAR文件进行签名时,可能会遇到另一个二进制差异,因为MANIFEST.MF
条目的顺序可能会发生变化(http://stackoverflow.com/questions/1879897/order-of-items-in-a-hashmap-differ-when-the-same-program-is-run-in-jvm5-vs-jvm6)。
所以基本上有两个层次的问题。首先,JAR/ZIP工具用文件系统时间戳对文件进行打包,从而为相同的一组Java类创建二进制的不同JAR文件,这些类是二进制的,但在不同的时间编译。其次,JAR签名器工具修改META-INF/MANIFEST-MF
文件,并将更多文件附加到JAR归档中(证书和类文件校验和(。
该解决方案可能是一个自定义的JAR签名器,它将所有JAR文件项的时间戳设置为一个恒定的时间,并对MANIFEST.MF
文件项进行排序(例如,按字母表(。据我所知,到目前为止,这是在不同时间点生成两个字节相同的数字签名JAR文件的唯一方法。
由于jar只是一个匿名的zip文件,您可以尝试使用zip
任务手动将清单文件添加到META-INF/
下。希望这能绕过与jar任务处理清单相关的任何内部魔法。
顺便说一句,因为听起来拥有相等的MD5是至关重要的,所以我建议您在构建过程中添加一个健全性测试,例如编译一些从未更改为jar的特殊"伪"代码,并检查jar MD5是否等于预期值。这将保护构建免受意外更改的影响(例如,升级到ant、JRE、OS、时区更改等之后(
遇到了同样的问题,出现在了这个页面上。Jiri Patera的上述回答非常有助于理解为什么在取消签名并放弃jar文件后,我无法获得预期的两个相同文件的md5sum。
这是我使用的解决方案:
jar-tvf$JARFILE|grep-v META-INF|perl-p-e's/^\s+(\d+(.*\s+([\w]+(/$1$2/g'|md5sum
它不能100%确定罐子是等效的,但它给出了一个相当可靠的指示。
它列出jarfile中的所有文件,减去META_INF文件,解析出文件大小和文件名,然后通过md5sum算法运行filesize加文件名的文本。