为什么再次编译时二进制输出不相等?

问题描述 投票:0回答:4

我正在使用构建脚本来编译多个 C# 项目。二进制输出被复制到结果文件夹,覆盖文件的先前版本,然后添加/提交到 subversion。

我注意到,即使源或环境根本没有变化,编译的二进制输出也是不同的。这怎么可能?对于相同的输入,二进制结果不是应该完全相等吗?

我无意在任何地方使用任何类型的特殊时间戳,但是编译器(Microsoft,.NET 4.0 中包含的编译器)是否可能自行添加时间戳?

我问的原因是我将输出提交给 subversion,并且由于我们的构建服务器的工作方式,签入的更改会触发重建,导致再次修改的二进制文件被循环签入。

c# .net-4.0 binaryfiles binary-reproducibility
4个回答
34
投票

另一个更新:

自 2015 年以来,编译器团队一直在努力将不确定性来源从编译器工具链中剔除,以便相同的输入确实产生相同的输出。有关更多详细信息,请参阅 Roslyn github 上的“概念决定论”标签。


更新:这个问题是我 2012 年 5 月博客的主题。感谢您提出这个好问题!


这怎么可能?

非常容易。

对于相同的输入,二进制结果不是应该完全相等吗?

绝对不是。事实恰恰相反。 每次运行编译器时,你都应该得到不同的输出。否则你怎么知道你已经重新编译了?

C# 编译器在每次编译时将新生成的 GUID 嵌入到程序集中,从而保证不会有两次编译产生完全相同的结果。

此外,即使没有 GUID,编译器也不保证两个“相同”编译会产生相同的结果。

特别是,元数据表的填充顺序高度依赖于文件系统的细节; C# 编译器开始按照文件提供给它的顺序生成元数据,并且可以通过多种因素巧妙地更改。

由于我们的构建服务器的工作方式,签入的更改会触发重建,导致再次修改的二进制文件被循环签入。

如果我是你,我会解决这个问题。


13
投票

是的,编译器包含时间戳。此外,在某些情况下,编译器会自动增加程序集版本号。我没有在任何地方看到任何保证二进制结果是相同的。

(请注意,如果 source 已经在 Subversion 中,我通常会避免在其中添加二进制文件。我通常只包含第三方库的版本。这取决于您的具体内容虽然在做。)


9
投票

正如其他人提到的,编译器确实生成了不同的构建,因此产生了不同的结果。 您正在寻找的是创建确定性构建的能力,现在它已作为 roslyn 编译器的一部分包含在内。

Roslyn 命令行选项

/definistic 产生一个确定性的程序集(包括模块 版本 GUID 和时间戳)

了解有关此功能的更多信息 https://github.com/dotnet/roslyn/blob/master/docs/compilers/Deterministic%20Inputs.md


2
投票

据我所知,只有 MS 二进制文件在每次编译时都不同。大约20年前,情况并非如此。每次编译后 MS 二进制文件都是相同的(假设源代码相同)。

© www.soinside.com 2019 - 2024. All rights reserved.