【问题标题】:Create .jar files deterministically (identical each time)确定性地创建 .jar 文件(每次都相同)
【发布时间】:2017-01-26 00:29:12
【问题描述】:

我使用 jar 命令来构建 jar 文件。在尝试使用 md5 签名缓存 jar 文件时,我发现从完全相同的源构建的 jar 具有不同的 md5 签名。
经过仔细检查,我发现每次创建 jar 时内容都完全相同(diff -qr 为空)。事实证明,创建的时间戳是在 jar 文件中编码的,它会抛出 md5 签名。其他人也发现了相同的here

甚至还有一个blog post 介绍如何每次使用 maven 创建相同的 jar 文件。但是,我想要一个使用命令行的简单解决方案,使用 jar 和 zip 等现成的命令(可能必须在没有安装权限的服务器上执行此操作),可能导致与我目前使用的“功能”jar 相同jar 命令。

编辑: 就我的目的而言,快速 找到 md5 就足够了,这样它在构建中是相同的,即使 jar 并不相同。到目前为止,我发现的唯一方法是提取 jar 中的文件并 md5 所有组件文件。但我担心对于更大的罐子来说这很慢,并且会破坏缓存它们以避免首先构建它们的目的。有没有更好更快的解决方案?

【问题讨论】:

  • 因此,如果您要将源代码编译到 jar 中,为什么不简单地执行一次,然后执行 xcopy 命令或不复制该 jar。
  • 为什么拥有位相同的容器很重要?
  • 问题是,我正在尝试缓存 jar - 一个 jar 是使用多个源(java 文件、其他 jar)构建的。为了确定是否需要构建特定的 jar,计算所有源 java 文件和 jar 的 md5。组件 jar 会在没有缓存的情况下重新构建,当这种情况发生时,它们的 md5 会发生变化,这会导致更大 jar 的缓存失效。
  • 这听起来像是在重新发明 Maven 和 Gradle。
  • 你是对的。这是具有自己的构建系统的项目的一部分。我只是研究它的人之一,对于它本质上是对 maven 的彻底改造这一事实无能为力。

标签: java ant jar zip md5


【解决方案1】:

主要问题是 jar 命令总是使用当前时间创建 META-INF\MANIFEST.MF。 文件时间保存在 zip 条目标题中。这就是为什么 MD5 值不同的原因,即使 jar 中的所有文件内容都保持不变: 不同的 zip 条目标题会产生不同的 zip 文件。

对于 jar 命令,唯一的解决方案是选项-M:不为条目创建清单文件。

【讨论】:

  • 确认这确实创建了具有相同 md5 的 jar。罐子也有效。您能否简要介绍一下缺少清单是否会引起任何担忧?
  • ZIP(jar 只是一个旋转的 zip 文件 IIRC)不也存储 .class 文件的元数据吗?就像您对其中一个 .class 文件进行 UNIX 触摸一样,它不会扰乱元数据并因此弄乱您的校验和吗?老实说,我不确定;只是问问。
  • @user34812。这取决于您打算如何使用 jar 文件,因为清单文件用于存储有关 jar 文件的信息。例如:如果您需要these features/创建一个 OSGi 捆绑包...等。需要清单文件。
  • @BJ 布莱克。正如您所说,其他文件的元数据也存储在 jar 文件中。更改.class 文件的文件上次修改时间会使 zip 条目标题发生更改。但与META-INF\MANIFEST.MF 不同,它们可以保留,它总是会更新。甚至jar -m 选项用于指定存在的清单文件。
  • 糟糕,@BJBlack 是正确的。我之前在一个较小的示例中尝试了 -M 标志,但没有更新类文件。一旦我更新类文件, md5 甚至与 -M 标志都不匹配。有没有办法在每次创建时覆盖 .jar 文件中的所有时间戳信息?
【解决方案2】:

Jar 命令总是使用当前时间创建 META-INF\MANIFEST.MF。 Zip 存储带有时间戳和文件属性的文件,因此两个工件的 sha256 或 MD5 将不同。

我们需要确保创建 jar 或 zip 所需的所有文件的创建、上次修改、访问时间戳和文件属性始终相同。

我创建了下面的脚本,它可以获取一个 jar 或 zip 文件,并通过使时间戳保持不变并设置正确的压缩级别和偏移量来使其具有确定性。

#!/bin/bash

usage() {
    echo "Usage : ./createDeterministicArtifact.sh <zip/jar file name>"
    exit 1
}

info() {
    echo "$1"
}

strip_artifact() {
    if [ -z ${file} ]; then
        usage
    fi
    if [ -f ${file} -a -s ${file} ]; then
        mkdir -p ${file}.tmp
        unzip -oq -d ${file}.tmp ${file}
        find ${file}.tmp -follow -exec touch -a -m -t 201912010000.00 {} \+
        if [ "$UNAME" == "Linux" ] ; then
            find ${file}.tmp -follow -exec chattr -a {} \+
        elif [[ "$UNAME" == CYGWIN* || "$UNAME" == MINGW* ]] ; then
            find ${file}.tmp -follow -exec attrib -A {} \+
        fi
        cd ${file}.tmp
        zip -rq -D -X -9 -A --compression-method deflate  ../${file}.new . 
        cd -
        rm -rf ${file}.tmp
        info "Recreated deterministic artifact: ${file}.new"
    else 
        info "Input file is empty. Please validate the file and try again"
    fi
}

file=$1

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-14
    相关资源
    最近更新 更多