Почему двоичный вывод не равен при повторной компиляции? - PullRequest
33 голосов
/ 19 января 2012

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

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

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

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

Ответы [ 4 ]

33 голосов
/ 19 января 2012

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

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


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


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

Очень легко.

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

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

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

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

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

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

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

13 голосов
/ 19 января 2012

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

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

9 голосов
/ 24 июня 2017

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

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

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

Подробнее об этой функции https://github.com/dotnet/roslyn/blob/master/docs/compilers/Deterministic%20Inputs.md

2 голосов
/ 17 февраля 2016

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

...