为什么每次构建时,相同的JAR文件都有不同的哈希值?

时间:2017-05-16 05:42:33

标签: java maven jar bytecode

我一直在考虑检查jar文件的哈希值,以确定它是否已更改,但事实证明,每次构建时,相同的jar文件都有不同的哈希值(导出为jar)来自eclipse的文件,或使用maven构建它。我删除了清单文件的日期值和内容,但它仍然不同。字节码生成中是否包含时间戳或其他内容?

4 个答案:

答案 0 :(得分:7)

JAR文件是ZIP文件,它包含local file headers和中央目录文件头中的最后修改日期。这将导致构建的不同哈希值。

如果在完全相同的文件集上运行JAR命令(具有相同的文件日期)并跳过清单文件创建,它应该为您提供完全相同的JAR文件(如果ZIP中的文件顺序不会更改)。

答案 1 :(得分:1)

我在Gradle版本中遇到了同样的问题。就我而言,我的.war文件包括许多内置的.jar文件。

在Gradle中,Jar和War任务本质上都是Zip任务的变体,Zip任务具有名为“ preserveFileTimestamps”(https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Zip.html#org.gradle.api.tasks.bundling.Zip:preserveFileTimestamps)的属性。 为了使SHA相同,请将此属性用于jar和war任务,例如,在build.gradle中的某个位置:

plugins.withType(WarPlugin).whenPluginAdded {
    war {
        preserveFileTimestamps = false
    }
}
jar {
    preserveFileTimestamps = false
}

还有一个有趣的注意事项,如果您是在MacOS上构建的,请确保.DS_Store文件不会进入构建的存档中,因为这也会导致不同的SHA。

要在MacOS上禁用,请在终端上运行此命令:

defaults write com.apple.desktopservices DSDontWriteNetworkStores true

然后重新启动它。您仍然必须删除现有的.DS_Store文件,因此从项目文件夹内部运行:

find . -name '.DS_Store' -exec rm {} \;

如果即使在不同的操作系统上构建后也要使SHA相同,则将war和jar任务的reproducibleFileOrder属性都设置为true,并确保umask相同您构建的两个系统(显然gradle都包含war / jar文件中的文件属性,并且当这些属性不同时,我具有不同的SHA)。

最后,无论在哪里构建,我都能获得相同的工件SHA。

欢呼

答案 2 :(得分:1)

使用Java获得可复制的版本,即始终产生相同二进制输出的内部版本需要进行一些调整,因为Java从一开始就不具有可复制性:jar文件(带有文件顺序和时间戳)是第一个自然的变化来源。除了Java引起的问题外,某些Maven插件还会引起其他变化:请参见Maven可复制/可验证的Build Wiki页面https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=74682318

您可以使用reproducible-build-maven-plugin:https://zlika.github.io/reproducible-build-maven-plugin  适用于受Java项目欢迎的Apache Maven构建工具或 sbt构建工具的sbt-reproducible-builds插件https://github.com/raboof/sbt-reproducible-builds,在Scala项目中很流行。 对于Gradle工具:https://docs.gradle.org/current/userguide/working_with_files.html#sec:reproducible_archives

有关“可复制的版本”的一般信息,请参见https://reproducible-builds.org

答案 3 :(得分:0)

对我来说最有效的解决方案是在我的gradle文件中进行以下操作(请注意,我还删除了可由某些任务更改的清单日期):

// Prevent manifest from changing every build
project.tasks.withType(Jar) {
    manifest.attributes Date: ''
}

// Prevent timestamps from appearing in JAR and use reproducible file order
tasks.withType(AbstractArchiveTask) {
    preserveFileTimestamps = false
    reproducibleFileOrder = true
}

灵感来自:https://dzone.com/articles/reproducible-builds-in-java