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

C# 为什么再次编译时二进制输出不相等?,c#,compiler-construction,.net-4.0,binaryfiles,binary-reproducibility,C#,Compiler Construction,.net 4.0,Binaryfiles,Binary Reproducibility,我正在使用一个构建脚本来编译几个C#项目。二进制输出被复制到结果文件夹,覆盖文件的早期版本,然后添加/提交到subversion 我注意到编译的二进制输出是不同的,即使源代码或环境没有任何变化。这怎么可能?对于相同的输入,二进制结果不是应该完全相等吗 我不是故意在任何地方使用任何类型的特殊时间戳,但是编译器(Microsoft,包含在.NET4.0中的编译器)可能会添加时间戳吗 我询问的原因是我将输出提交给subversion,并且由于构建服务器的工作方式,签入的更改触发重建,导致再次修改的二进

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

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

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


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

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

(请注意,如果源代码已经在Subversion中,我通常不会在其中添加二进制文件。我通常只包括第三方库的版本。这完全取决于您正在执行的操作。)

另一个更新:

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


更新:。谢谢你的提问


这怎么可能

很容易

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

绝对不是。恰恰相反每次运行编译器时,您都会得到不同的输出。否则,您怎么知道重新编译了

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

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

特别是,元数据表的填充顺序高度依赖于文件系统的详细信息;C#编译器开始按照文件的顺序生成元数据,这些元数据可以被各种因素微妙地改变

由于构建服务器的工作方式,签入的更改会触发重建,导致再次修改的二进制文件在一个圆圈中签入


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

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

正如其他人所提到的,编译器确实生成了不同的构建,因此产生了不同的结果。您需要的是能够创建确定性构建,现在它已包含在roslyn编译器中

/确定性生成确定性组件(包括模块 版本(GUID和时间戳)

了解有关此功能的更多信息

有没有一种简单的方法可以避免这种情况?二进制文件被用作驻留在同一存储库中的不同项目的输入。(事实上,还有很多项目。)您可以在输出目录以外的其他位置复制二进制文件,链接到副本,并将二进制文件的副本(但不是输出目录本身)置于版本控制中。当然,如果你这样做了,你必须决定谁的责任是更新二进制文件夹。如果这两个项目密切相关,也可以将项目本身添加到解决方案文件中,然后添加对项目的引用。我认为VS可能会明智地处理这种情况。对我来说,源代码和二进制文件的颠覆听起来是多余的,你不应该只将源代码保存在subversion下吗?您可以尝试通过解决方案根据需要聚合程序集,避免版本生成输出的需要(我在sourcesafe环境下做类似的事情)@alex由于项目规模巨大,我们的团队如何工作,这在我的情况下并不容易,但我一定会尝试朝着这个方向走。我向MS创建了一个请求,请投票:Alex Nolasco的答案包含指向确定性构建文档的链接。你还需要什么?我记得gcc产生了相同的二进制文件(不确定是否保证),所以.NET的行为让我很惊讶。“不过这是有道理的。@mafurtct:人们有时确实会对此感到惊讶。例如,审查进入赌博机的代码的政府机构期望他们能够从供应商处获得源代码和二进制文件,并自己重新编译源代码并获得相同的二进制文件,作为二进制文件和源文件匹配的“证据”。不幸的是,对于他们来说,二进制匹配其源代码的证明并不是C#团队声称提供的服务,因此他们必须找到另一种解决方案。@EricLippert:这很有趣。我无法通过web搜索找到有关您的示例的任何信息。网上有关于这方面的文章吗?我偶尔会想,编译器是否会保留发送到元数据中的每个
类型
的GUID属性。。。我见过GUID在重新编译之间发生更改的情况,也见过GUID保持不变的情况。由于/deterministic开关的可用性,这个答案现在已过时。我看到这适用于带VS2015的MSBuild。这也可以通过devenv.com完成吗?我好像找不到开关。我问的原因是MSBuild