.NET のコンパイラーとバージョン情報


最近 FAKE でビルドスクリプトを書いています。その際にコンパイラーによってバージョン情報の扱いが違うらしいことに気が付いたので,ここにまとめます。

.NET Framework のアセンブリーには 3 つのバージョン情報があり,属性により指定します。

バージョン 属性
アセンブリバージョン AssemblyVersionAttribute
Win32 ファイルバージョン AssemblyFileVersionAttribute
製品バージョン AssemblyInformationalVersionAttribute

ファイルバージョンは FileVersionInfo.ProductVersion で,製品バージョンは FileVersionInfo.ProductVersion でそれぞれ取得できます[A]

さて, .NET Framework ではバージョン番号の形式は メジャー.マイナー[.ビルド[.リビジョン]] という決められた形式があります。しかしファイルバージョンや製品バージョンは厳密にはアセンブリバージョンでないようなテキストでも良いことになっています。そこで製品バージョン,つまり AssemblyInformationalVersionAttribute にアセンブリバージョン互換でない文字列,例えば "1.0-beta" を与えたらどうなるでしょうか。

結果から言うと,コンパイラーごとに挙動が異なる模様です。 Windows の C# コンパイラー (csc), F# コンパイラー (fsc), Linux 上での mono[B] の C# コンパイラー (mcs),コミュニティ版 F# コンパイラー (fsharpc)[C] の結果をまとめます。

ケース バージョン Windows Linux
csc fsc mcs fsharpc
  • AV, AssemblyVersion
  • AFV, AssemblyFileVersion
  • AIV, AssemblyInformationalVersion
互換値 ファイル AFV AFV AV null
製品 AIV AIV AFV null
非互換値 ファイル AFV AV AV null
製品 AIV AV AFV null

csc が常に正しい結果を返し, fsc はアセンブリーバージョン非互換値を指定すると AssemblyFileVersion を製品バージョンとして使用し, mcs はファイルバージョンに AssemblyVersion で製品バージョンに AssemblyFileVersion を使い, fsharpi は常に無視されます。なお,どのコンパイラーでもアセンブリー中の属性は 3 属性とも正常に記録されている模様です[D]

もともと行っていたのがビルドしたアセンブリーの情報から NuGet パッケージのバージョンを決定するということでした。 NuGet のバージョンは 1.0-beta のようなハイフン区切りのバージョンをプレリリース版としてみなすので, AssemblyVersion だとそのような指定ができないのです。 FAKE スクリプトはバージョンを明示的に指定しないと 0.1 になってしまいます。これを回避するためにアセンブリーからバージョンを取得するということをしようとしていたのですが…。おそらく nuspec に正しいバージョンを書いておいて,それをマニュアルで読み込んで指定するということになりそうです。面倒くさいですね。

[2013-08-10 12:30 追記] よく考えたら,製品バージョンをアセンブリーから取得するのが目的なので,ファイルにバージョンが正常に書き込まれていなくても,アセンブリー自体には属性が含まれているのだから, FileVersionInfo を使わずに Assembly からリフレクションでバージョン情報を取得すれば良いのでした。

脚注

  1. ちなみにアセンブリバージョンは Assembly.ReflectionOnlyLoadFrom して GetName して Version で取得できます。 []
  2. Windows の mono は未チェック []
  3. Linux 上で実行 []
  4. F# コンパイラーは Microsoft 版もコミュニティ版も AssemblyVersion が二重に記録されている気がします。 []