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

本文关键字:二进制 输出 不相等 编译 为什么 | 更新日期: 2023-09-27 18:20:52

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

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

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

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

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

另一个更新:

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


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


这怎么可能?

非常容易。

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

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

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

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

特别是,元数据表的填充顺序在很大程度上取决于文件系统的细节;C#编译器开始按照文件的顺序生成元数据,这可能会因各种因素而发生微妙的变化。

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

如果我是你,我会解决的。

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

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

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

Roslyn命令行选项

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

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

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