Introduction

同一ソースから常に同じバイナリが生成できれば、ある 2 つのリビジョンで生成されたバイナリまたはその集合を比較することで、差分を把握することができる。
これを利用してパッチを作ることができるのではと考えたのだが…

この手の質問は Microsoft の Japan Developer Support Core チームによく問い合わせが来るそうで。

こちらのブログでは C# と MSVC での成果物について記載している。

How to do?

C# の場合

*.csproj<Deterministic>false</Deterministic> を追加することで、生成されるバイナリに埋め込まれるタイムスタンプが変化するようになる。
つまり、C# の場合、デフォルトで生成されるバイナリが同一になっている。
少なくとも、 .NET Framework 4.8.NET 8.0 において、デフォルトで Deterministictrue になっているいることを確認した。

1
2
3
4
5
6
7
8
9
10
11
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Deterministic>false</Deterministic>
</PropertyGroup>

</Project>

dumpbin でバイナリのヘッダーを確認出来る。

1
2
3
$ dotnet build -c Release
$ "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\dumpbin.exe" /headers bin\Release\net8.0\Test.dll | findstr "time date stamp"
E349BE6B time date stamp

ここで <Deterministic>false</Deterministic> を追加してみる。

1
2
3
4
5
$ "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\dumpbin.exe" /headers net8.0\Test.dll | findstr "time date stamp"
65EC9D9E time date stamp Sun Mar 10 02:34:22 2024

$ "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\dumpbin.exe" /headers net8.0\Test.dll | findstr "time date stamp"
65EC9E39 time date stamp Sun Mar 10 02:36:57 2024

タイムスタンプが変化した。
注意しなくてはいけないのは、obj 内のオブジェクトファイルにタイムスタンプが埋め込まれるのか、ソースに変更せずにビルドすると、タイムスタンプが変化しない。
バイナリを生成する前は obj を削除する必要がある。

msbuild.exe を使えば、引数で制御もできる。

1
2
3
$ "C:\Program Files\Microsoft Visual Studio\2022\Community\Msbuild\Current\Bin\MSBuild.exe" -t:clean,build -p:Configuration=Release -p:deterministic=false
$ "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\dumpbin.exe" /headers bin\Release\Demo.exe | findstr "time date stamp"
65ECA0D9 time date stamp Sun Mar 10 02:48:09 2024

C++ の場合

上記ブログにもあったように、deterministic は非対応の模様。

1
2
3
$ "C:\Program Files\Microsoft Visual Studio\2022\Community\Msbuild\Current\Bin\MSBuild.exe" -t:clean,build -p:Configuration=Release -p:deterministic=true
$ "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\dumpbin.exe" /headers bin\Win32\Release\Demo.exe | findstr "time date stamp"
65ECA349 time date stamp Sun Mar 10 02:58:33 2024

ただし、正式な文書はないが別のオプションがある。
ビルドオプションではなく、リンカーオプションに /brepro を付与する。

ios

オプションの前後でヘッダーが変化したことが確認出来る。

1
2
3
4
5
$ "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\dumpbin.exe" /headers bin\Win32\Release\Demo.exe | findstr "time date stamp"
65ECAA53 time date stamp Sun Mar 10 03:28:35 2024

$ "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\dumpbin.exe" /headers bin\Win32\Release\Demo.exe | findstr "time date stamp"
6996B0D9 time date stamp

またはコマンドラインを使ってリンカーオプションを書き換えてもいい。
まず、下記のような *.props ファイルを用意する。

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemDefinitionGroup>
<Link>
<AdditionalOptions>/Brepro %(AdditionalOptions)</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
</Project>

最後に、msbuild.exe に先のファイルを 絶対パス で指定する。

1
2
3
4
$ "C:\Program Files\Microsoft Visual Studio\2022\Community\Msbuild\Current\Bin\MSBuild.exe" -t:clean,build -p:Configuration=Release /p:ForceImportBeforeCppTargets="F:\Demo\profile.props"

$ "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\dumpbin.exe" /headers bin\Win32\Release\Demo.exe | findstr "time date stamp"
6996B0D9 time date stamp

上手くいった…が、非公式なオプションであることが気になる。
事実、コミュニティでもこのオプションを文書化する要請が出ているが…芳しくない…

また、副作用としてビルド時間が延びるとある。