Почему двоичный вывод не равен при повторной компиляции?

Я использую скрипт сборки для компиляции нескольких проектов C#. Двоичный вывод копируется в папку результатов, перезаписывая предыдущую версию файлов, а затем добавляется / фиксируется в subversion.

Я заметил, что двоичный вывод компиляции отличается, даже если не было никаких изменений в источнике или среде вообще. Как это возможно? Разве двоичный результат не должен быть точно одинаковым для одного и того же входа?

Я нигде не намеренно использую какие-либо специальные метки времени, но может ли компилятор (Microsoft, входящий в.NET 4.0) добавлять метки времени сам?

Причина, по которой я спрашиваю, заключается в том, что я фиксирую вывод в subversion, и из-за того, как работает наш сервер сборки, проверенные изменения запускают перестройку, в результате чего вновь измененные двоичные файлы проверяются по кругу.

4 ответа

Решение

ДРУГОЕ ОБНОВЛЕНИЕ:

С 2015 года команда компиляторов прилагает усилия для извлечения источников недетерминированности из цепочки инструментов компилятора, чтобы идентичные входные данные действительно давали идентичные выходные данные. См. Тег "Концептуальный детерминизм" на Roslyn Github для более подробной информации.


ОБНОВЛЕНИЕ: Этот вопрос был темой моего блога в мае 2012 года. Спасибо за отличный вопрос!


Как это возможно?

Очень легко.

Разве двоичный результат не должен быть точно одинаковым для одного и того же входа?

Точно нет. Противоположность верна. Каждый раз, когда вы запускаете компилятор, вы должны получать разные выходные данные. Иначе откуда ты мог знать, что перекомпилируешь?

Компилятор C# внедряет недавно сгенерированный GUID в сборку при каждой компиляции, тем самым гарантируя, что никакие две компиляции не дадут абсолютно одинаковый результат.

Более того - даже без GUID компилятор не дает никаких гарантий, что две "одинаковые" компиляции будут давать одинаковые результаты.

В частности, порядок заполнения таблиц метаданных сильно зависит от деталей файловой системы; компилятор C# начинает генерировать метаданные в порядке, в котором ему передаются файлы, и это может быть слегка изменено различными факторами.

из-за того, как работает наш сервер сборки, проверенные изменения вызывают перестройку, вызывая повторную проверку измененных двоичных файлов по кругу.

Я бы исправил это на твоем месте.

Да, компилятор включает метку времени. Кроме того, в некоторых случаях компилятор автоматически увеличивает номер версии сборки. Я не видел нигде гарантии, что двоичный результат должен быть идентичным.

(Обратите внимание, что если исходный код уже находится в Subversion, я обычно избегаю добавления туда двоичных файлов. Обычно я включаю только выпуски сторонних библиотек. Это зависит от того, что именно вы делаете.)

Как уже упоминалось, компилятор генерирует отличную сборку, следовательно, другой результат. То, что вы ищете, - это способность создавать детерминированные сборки, и теперь это включено в состав компилятора Roslyn.

Параметры командной строки Roslyn

/terministic Создание детерминированной сборки (включая GUID версии модуля и метку времени)

Узнайте больше об этой функции https://github.com/dotnet/roslyn/blob/master/docs/compilers/Deterministic%20Inputs.md

Насколько я знаю, только двоичные файлы MS различаются на каждой компиляции. 20 лет назад это было не так. Двоичные файлы MS были одинаковыми после каждой компиляции (при условии, что исходный код был одинаковым).

Другие вопросы по тегам