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

时间:2012-01-19 14:15:44

标签: c# compiler-construction .net-4.0 binaryfiles binary-reproducibility

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

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

我并不是故意在任何地方使用任何类型的特殊时间戳,但编译器(Microsoft,.NET 4.0中包含的那个)可能会自己添加时间戳吗?

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

4 个答案:

答案 0 :(得分:33)

另一个更新:

自2015年以来,编译器团队一直在努力从编译器工具链中获取非确定性的来源,因此相同的输入确实产生相同的输出。有关更多详细信息,请参阅Roslyn github上的“Concept-determinism”标记。


更新:This question was the subject of my blog in May 2012。谢谢你提出的好问题!


  

这怎么可能?

很容易。

  

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

绝对不是。反之亦然。 每次运行编译器时都应该得到不同的输出。否则你怎么知道你重新编译了?

C#编译器在每个编译中在程序集中嵌入一个新生成的GUID,从而保证没有两个编译产生完全相同的结果。

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

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

  

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

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

答案 1 :(得分:13)

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

(请注意,如果 source 已经在Subversion中,我通常会避免在那里添加二进制文件。我通常只包含第三方库的版本。取决于你正在做什么。)

答案 2 :(得分:9)

正如其他人所说,编译器会生成不同的构建,因此会产生不同的结果。您正在寻找的是能够创建确定性构建,现在它被包含在roslyn编译器中。

Roslyn command line options

  

/ deterministic生成确定性程序集(包括模块   版本GUID和时间戳)

详细了解此功能 https://github.com/dotnet/roslyn/blob/master/docs/compilers/Deterministic%20Inputs.md

答案 3 :(得分:2)

据我所知,每次编译时只有MS二进制文件不同。大约20年前,它不是那样的。每次编译后,MS二进制文件都是相同的(假设源代码相同)。