10

似乎从完全相同的源文件构建 jar 或 zip 总是会产生不同的文件。我使用 java jar 命令以及 ant 中的 jar 和 zip 任务都尝试了这个。

这似乎是由于新的 jars/zip 将时间戳设置为每个存储文件的当前时间。

有没有办法强制 zip 工具简单地使用文件系统上文件的时间戳来确保从完全相同的源构建的 jar 看起来完全相同?

4

8 回答 8

4

二进制差异是因为清单文件的时间戳。如果您让 jar 自己创建清单,它将动态创建清单并将创建的清单设置为 currentTimeMillis。

您可以通过以下方式解决它:
1. 不要添加清单(如果您使用 ant,则必须使用 zip 而不是 jar)
2. 像添加普通文件一样添加清单。(所以清单是文件系统上的一个文件,它不是动态创建的)

于 2009-10-25T13:16:58.027 回答
2

使用 Java java.util.zip.ZipOutputStream 标准库实用程序,可以创建具有可重现内容的 zip 文件。

唯一的技巧是必须使用这个技巧来修复 zip 条目的时间戳:

ZipOutputStream zos=...;
ZipEntry ze=new ZipEntry("Filename");
zipEntry.setTime(0);
zos.putNextEntry(ze);
try
{
   zos.write(data);
}finally
{
  zos.closeEntry();
}
于 2018-11-08T20:17:14.267 回答
1

我不认为有办法让 zip 做到这一点,但你当然可以在你创建之前将文件系统上文件的时间戳敲到一个已知的日期(在 unix 下使用'touch'命令 - 我不知道在 Windows 下是什么)罐子。

于 2009-04-15T22:00:09.823 回答
1

我有一个类似的问题,正如 pjz 建议的那样,我在将文件添加到 jar 之前通过“触摸”文件来解决它(所以,它对我有用:-))。如果需要,您可以在 GNU Windows Utilities、核心实用程序中找到 Windows 的触摸:http: //gnuwin32.sourceforge.net/packages/coreutils.htm,但它对于这个单一的包来说是一个大包(虽然您可能会喜欢许多其他有用的实用程序),或者下载类似http://www.softpedia.com/progClean/Touch-for-Windows-Clean-41086.html的东西。

于 2009-04-16T10:58:47.150 回答
1

好的,我和一位同事想出了一个适合我们的解决方案。

我们没有重新设计整个构建过程以不删除任何类或 jar 文件,而是使用以下过程:

  1. 构建新的工件。
  2. 使用 jardiff (jnlp的一部分)比较以前构建的更改。
  3. 如果 jardiff 生成的 diff jar 没有更改,请从以前的构建中获取工件。

是的,我知道这听起来很笨拙,但它肯定优于重写构建脚本以考虑到这一点。此外,我们可以在新机器上进行完全干净的构建(在服务器故障的情况下),这个过程将确保只生成实际更新的 jar。

于 2009-04-21T21:26:51.193 回答
0

这个答案是不够的。阅读我的另一个答案。我没有删除这个答案,因为它显示了非二进制兼容性的一些原因,但不是所有原因。

我有一个详尽的答案,但不幸的是德语:https ://www.vishia.org/SwEng/pdf/GenerateRepeatability_de.pdf 简短介绍:

echo compile javac
$JAVAC_HOME/bin/javac -d $TMPJAVAC/binjar -cp $CLASSPATH -sourcepath $SRCPATH $FILE1SRC 
mkdir $TMPJAVAC/binjar/META-INF
##Note: create the manifest file manually, not with jar, because of time stamp
cp $MANIFEST $TMPJAVAC/binjar/META-INF/MANIFEST.MF  
echo touch timestams to $VERSION
find $TMPJAVAC/binjar -exec touch -d $VERSION {} \;
echo build jar
$JAVAC_HOME/bin/jar -cvfM $JARFILE -C $TMPJAVAC/binjar . > $TMPJAVAC/jar.txt
if ! test "$MD5FILE" = ""; then echo output MD5 checksum
  md5sum -b $JARFILE > $MD5FILE
fi  
echo ok $JARFILE

它是由外部设置的脚本变量控制的通用 shell 脚本。它可以从另一个 shell 脚本或 gradle 调用。最重要的是清单的触摸命令和复制命令,以及 jar 的 M 选项(不是 m)。jar 文件中的二进制差异来自所包含文件的时间戳。哈特穆特·肖里格

于 2020-03-17T22:54:29.970 回答
0

不幸的是,我在 2020-03-17 的回答在所有情况下都不会产生可重现的 jar 文件(二进制兼容)。原因:jar 中文件的顺序取决于随机性。在另一台机器上生成,我使用过 Windows 和 Linux,在 jar/zip 文件中生成另一个文件顺序,从而产生另一个二进制内容。如果将 jar 文件逐个文件进行比较(解压缩后),它们是相同的。但是纯二进制jar不是这样的。我有一个解决方案,它不使用 JDK 中的 jar 命令,而是使用 JRE 功能的自己的 jar 算法。JRE 包含标准的 java.util.jar.* 和 java.util.zip.*。可以阅读https://vishia.org/Java/html5/source+build/reproducibleJar.html中的描述。你可以下载一个小https://www.vishia.org/Java/Download/versionArchive/vishiaZipJar-2020-03-23.jar,带有给定的 MD5 校验和(访问此下载/versionArchive 页面)。文章包含示例。我用 Windows 和 Linux 测试了不同的 JDK 版本,结果是二进制兼容的。

于 2020-03-25T20:42:17.860 回答
0

fwiw,任何使用 gradle 构建系统的人都可以廉价/轻松地获得二进制稳定的 jar:

tasks.withType(Jar).configureEach {
    Jar jar ->
        jar.preserveFileTimestamps = false
        jar.reproducibleFileOrder = true
}

这符合其他答案的建议:确保时间戳是稳定的,并确保 jar 中条目的顺序也是稳定的。我相信这里的大脑在jar命令本身之外,但我没有在这个问题上投入足够的精力来检查。

于 2020-07-02T23:48:57.107 回答