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

maf*_*afu 34 c# compiler-construction binaryfiles .net-4.0 binary-reproducibility

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

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

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

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

Eri*_*ert 33

另一个更新:

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


更新:这个问题是我2012年5月的博客主题.谢谢你这个好问题!


这怎么可能?

非常简单地.

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

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

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

而且 - 即使没有GUID,编译器也不保证两个"相同"的编译将产生相同的结果.

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

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

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

  • @mafutrct:人们确实有时会对此感到惊讶.例如,审查进入赌博机器的代码的政府机构期望他们应该能够从供应商处获取源代码和二进制文件,并且自己重新编译源代码并获得相同的二进制文件,如"证明"二进制和源匹配.不幸的是,证明二进制文件与其来源匹配并不是C#团队声称提供的服务,因此他们必须找到另一种解决方案. (16认同)
  • 现在,由于/ deterministic开关的可用性,这个答案已经过时了. (4认同)
  • 我记得gcc生成相同的二进制文件(不确定它是否有保证),因此.NET行为让我感到惊讶.虽然这很有道理. (2认同)
  • 我偶尔想知道编译器是否保留了发送到元数据中的每个`Type`的GUID属性...我已经看到GUID在重新编译之间发生变化的情况以及它保持不变的情况. (2认同)

Jon*_*eet 13

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

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


Ale*_*sco 9

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

Roslyn命令行选项

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

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

  • 发现你可以通过添加属性组到.csproj` <Project> <PropertyGroup> <Deterministic> true </ Deterministic> </ PropertyGroup> </ Project>`按照https://gist.github.com/aelij/b20271f4bd0ab1298e49068b388b54ae (3认同)