検索
リンク
タグ
ASP.NET
.NET
ASP.NET MVC
F#
Visual Studio
Azure
ASP.NET Core
ライトニングトーク
Plone
AJAX
Selenium
C#
jQuery
ADO.NET Entity Framework
SQL Server
JavaScript
LINQ
WebMatrix
EFCore
Fizz-Buzz
カテゴリ
最新の記事
最新のコメント
記事ランキング
最新のトラックバック
以前の記事
2022年 12月 2022年 11月 2022年 10月 2022年 09月 2022年 08月 2022年 07月 2022年 06月 2022年 05月 2022年 04月 2022年 03月 2022年 02月 2022年 01月 2021年 12月 2021年 11月 2021年 10月 2021年 09月 2021年 08月 2021年 07月 2021年 06月 2021年 05月 2021年 04月 2021年 03月 2021年 02月 2021年 01月 2020年 12月 2020年 11月 2020年 10月 2020年 09月 2020年 08月 2020年 07月 2020年 06月 2020年 05月 2020年 04月 2020年 03月 2020年 02月 2020年 01月 2019年 12月 2019年 11月 2019年 10月 2019年 09月 2019年 08月 2019年 07月 2019年 06月 2019年 05月 2019年 04月 2019年 03月 2019年 02月 2019年 01月 2018年 12月 2018年 11月 2018年 10月 2018年 09月 2018年 08月 2018年 07月 2018年 06月 2018年 05月 2018年 04月 2018年 03月 2018年 02月 2018年 01月 2017年 12月 2017年 11月 2017年 10月 2017年 09月 2017年 08月 2017年 07月 2017年 06月 2017年 05月 2017年 04月 2017年 02月 2017年 01月 2016年 12月 2016年 11月 2016年 10月 2016年 09月 2016年 08月 2016年 07月 2016年 06月 2016年 05月 2016年 04月 2016年 03月 2016年 02月 2016年 01月 2015年 12月 2015年 11月 2015年 10月 2015年 09月 2015年 08月 2015年 07月 2015年 05月 2015年 04月 2015年 03月 2015年 02月 2015年 01月 2014年 12月 2014年 11月 2014年 10月 2014年 09月 2014年 08月 2014年 06月 2014年 04月 2014年 03月 2014年 02月 2014年 01月 2013年 12月 2013年 10月 2013年 09月 2013年 08月 2013年 07月 2013年 06月 2013年 05月 2013年 04月 2013年 03月 2013年 02月 2013年 01月 2012年 12月 2012年 11月 2012年 10月 2012年 09月 2012年 08月 2012年 07月 2012年 06月 2012年 05月 2012年 04月 2012年 03月 2012年 02月 2012年 01月 2011年 12月 2011年 11月 2011年 10月 2011年 09月 2011年 08月 2011年 07月 2011年 06月 2011年 05月 2011年 04月 2011年 03月 2011年 02月 2011年 01月 2010年 12月 2010年 11月 2010年 10月 2010年 09月 2010年 08月 2010年 07月 2010年 06月 2010年 05月 2010年 04月 2010年 03月 2010年 02月 2010年 01月 2009年 12月 2009年 10月 2009年 09月 2009年 07月 2009年 06月 2009年 05月 2009年 04月 2009年 03月 2009年 02月 2009年 01月 2008年 12月 2008年 11月 2008年 10月 2008年 09月 2008年 08月 2008年 07月 2008年 06月 2008年 05月 2008年 04月 2008年 03月 2008年 02月 2008年 01月 2007年 12月 2007年 11月 2007年 04月 2007年 03月 2007年 02月 2007年 01月 2006年 11月 2006年 10月 2006年 09月 2006年 08月 2006年 07月 ファン
ブログジャンル
画像一覧
|
2022年 10月 30日
こんなシナリオ例えばこんなフォルダ構成の .NET アプリケーションプロジェクト = AppA と AppB の 2 本があったとして、 📂Projects ├ 📄Projects.sln ├ 📂AppA │ └ 📄AppA.csproj └ 📂AppB └ 📄AppB.csproj これら AppA と AppB には、常に同じバージョン番号を設定したい、みたいな需要があったとする。 (上記は C# の例だが、VB や F# などに適宜読み替えてもらって大丈夫) さて、.NET Core 以降、SDK スタイルの .NET プロジェクトの場合は、プロジェクトファイル中における "Version" MSBuild プロパティの指定がバージョン番号の指定になる (下記例)。 <Project> <PropertyGroup> ... <Version>1.2.3</Version> ... なので、バージョン番号が変更になるたびに、各プロジェクトのプロジェクトファイル中の "<Version>~</Version>" の箇所を都度書き換えればそれでいい話である。しかしそうはいっても、同じ作業を複数回愚直に繰り返すのはなんだか愚行のような気がしてならないし、単に気持ちの問題というだけではなく、単純に設定漏れや設定違いは普通に発生する危険がある。 ということで、共通の1箇所でバージョン番号を指定することで、複数の .NET プロジェクトに対し同じバージョン番号を設定できる方法を考えてみる。 なお、以下で紹介するいずれの方式も、Microsoft の公式ドキュメントサイトやネット上の情報で既に知られているような内容だ。そのため、本記事はそれら一次情報の劣化版に留まるような気もする。とはいいつつも、実は別途このテーマで相談を受けていたこともあり、改めて自分なりに再検証の上、まとめてみる。 方法 1. Directory.Build.props を使う.NET プロジェクトファイルのビルド開始時、実は、そのビルド対象プロジェクトファイルがあるフォルダから親フォルダへとさかのぼって、Directory.Build.props という名前のファイルがないか、探索される。そして Directory.Build.props が見つかると、その内容を MSBuild スクリプトとして追加で取り込む (インポートする) こととなっている。 例えば下記のようにプロジェクト AppA と AppB より上の階層のフォルダに Directory.Build.props を置いておき、 📂Projects ├ 📄Projects.sln ├ 📄Directory.Build.props 👈 コレ! ├ 📂AppA │ └ 📄AppA.csproj └ 📂AppB └ 📄AppB.csproj Directory.Build.props 内に Version MSBuild プロパティ指定を書いておくと、 <!-- 📄Directory.Build.props --> <Project> <PropertyGroup> <Version>1.2.3</Version> </PropertyGroup> </Project> この Directory.Build.props があるフォルダ以下の階層にいるすべての .NET プロジェクトに読み込まれてバージョン番号指定が反映される、という仕組みだ。バージョン番号に変更があったときは、Directory.Build.props だけを書き換えて再ビルドすればよい。 詳しい仕様は下記公式ドキュメントサイトをぜひ一読頂きたい。 方法 2. 適当な .props ファイルを作り、各アプリのプロジェクトファイルからインポートするさて、大抵の場合は、前述の Directory.Build.props 方式で用が足りることと思う。しかし場合によっては、Directory.Build.props では効果がありすぎる場面があるかもしれない。つまり、Directory.Build.props は、その配下の階層の .NET プロジェクトすべてに自動で取り込まれてしまうからだ。場合によっては、フォルダ構成上、配下の階層にあるだけの関係の無いプロジェクト (例えばビルド支援のための専用カスタムツールのプロジェクトであったり、単体テストプロジェクトであったり) にまで適用されてしまうことが問題になることがあるかもしれない。 そのような場合は、"自動で読み込まれる" Directory.Build.props の代わりに、自分でカスタム MSBuild スクリプトを読み込むように手配するとよい。 具体的には、ファイル名は任意でよいのだが例えば VersionInfo.props というファイルを用意してこれに "Version" MSBuild プロパティを指定することとして (フォルダ構成は下記)、 📂Projects ├ 📄Projects.sln ├ 📄VersionInfo.props 👈 コレ! ├ 📂AppA │ └ 📄AppA.csproj └ 📂AppB └ 📄AppB.csproj AppA、AppB それぞれのプロジェクトファイル中から、プロジェクトファイルそれ自身を基準とした相対パス指定で、その VersionInfo.props を "取り込む" (インポートする) ようにすればよい。具体的には、各プロジェクトファイル中の冒頭に、以下のように Import ノードを記述する。 <!-- 📄AppA.csproj や 📄AppB.csproj --> <Project Sdk="Microsoft.NET.Sdk"> <Import Project="../VersionInfo.props" /> ... こうすることで、明示的に VersionInfo.props をインポートしているプロジェクトでのみ、共通のバージョン情報が設定されるようになる。バージョン番号に変更があったときは、VersionInfo.props だけを書き換えて再ビルドすればよい。 方法 3. AssemblyFileVersion 属性指定を含む共通の .cs ソースコードを取り込む実のところ、プロジェクトファイル (MSBuild スクリプト) 上の Version プロパティで指定されたバージョン情報は、アセンブリに対する AssemblyFileVersionAttribute 属性指定を行なうソースコードを自動生成するのに使われている。実際、自動生成されたソースコードは、例えば C# プロジェクトの場合、obj フォルダ以下に "{アセンブリ名}.AssemblyInfo.cs" という名前のファイルで保存されているのを確認できる。 ということで、バージョン番号の指定は、MSBuild スクリプトで指定するだけでなく、直接プログラムのソースコード中から、アセンブリに対する AssemblyFileVersionAttribute 属性を指定することでも可能だ。(というか、.NET Core より前の世代だと、この方式でバージョン番号を設定していた。) 詳細は下記公式ドキュメントサイトなどが参考になるかもしれない。 そこから次のようなやり方が可能である。 C# プロジェクトを例とするが、まず以下のようなフォルダ構成で、新たな C# ソースコードファイルを用意し (C# ソースコードファイルの名前は任意だが、ここでは AssemblyInfo.cs としてみた)、 📂Projects ├ 📄Projects.sln ├ 📄AssemblyInfo.cs 👈 コレ! ├ 📂AppA │ └ 📄AppA.csproj └ 📂AppB └ 📄AppB.csproj このソースコードに、アセンブリに対する AssemblyFileVersionAttribute 属性指定を記述する。 using System.Reflection; [assembly: AssemblyFileVersion("1.2.3")] あとは、AppA、AppB それぞれのプロジェクトファイル中から、プロジェクトファイルそれ自身を基準とした相対パス指定で、そのソースコードファイル (この例では AssemblyInfo.cs) を参照すればよい。具体的には、各プロジェクトファイル中に、以下のようにコンパイル対象アイテムの指定を記述する。 <!-- 📄AppA.csproj や 📄AppB.csproj --> <Project Sdk="Microsoft.NET.Sdk"> ... <ItemGroup> <Compile Include="..\AssemblyInfo.cs" Link="AssemblyInfo.cs" /> </ItemGroup> ... なお、このままビルド実行すると、前述のとおり、SDK スタイルのプロジェクトファイル (MSBuild スクリプト) によって自動生成されるコードによる AssemblyFileVersionAttribute 属性と重複指定になってしまい、下記ビルドエラーとなる。 error CS0579: Duplicate 'System.Reflection.AssemblyFileVersionAttribute' attribute そこでプロジェクトファイルによる自動生成コードに、AssemblyFileVersionAttribute 属性指定を含めないよう追加の指示を行なう。具体的には、GenerateAssemblyFileVersionAttribute MSBuild プロパティに false を指定してやるとよい。 <!-- 📄AppA.csproj や 📄AppB.csproj --> <Project Sdk="Microsoft.NET.Sdk"> ... <PropertyGroup> ... <GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute> ... GenerateAssemblyFileVersionAttribute MSBuild プロパティをはじめ、SDK スタイルのプロジェクトファイルによって自動生成されるアセンブリ情報の制御について詳細は公式ドキュメントサイト (下記リンク先) を一読されたい。 以上で、AssemblyInfo.cs をコンパイル対象アイテムに明示指摘に指定しているプロジェクトに対し、AssemblyInfo.cs 内で指定された共通のバージョン情報が設定されるようになる。バージョン番号に変更があったときは、AssemblyInfo.cs だけを書き換えて再ビルドすればよい。 ところで、このソースコードでバージョン番号を指定する方式については、もう一つ面白い実装方法があって、AssemblyFileVersionAttribute 属性のコンストラクタ引数に指定するバージョン番号文字列を、直接即値で指定するのではなく、別のクラスの定数値として定義することもできる。下記が C# でのその実装例。 using System.Reflection; [assembly: AssemblyFileVersion(AssemblyInfo.Version)] internal static class AssemblyInfo { internal const string Version = "1.2.3"; } こうしておくと、このソースコードファイルをコンパイル対象アイテムに参照しているプログラム内では、リフレクション技法を使わずに、自アセンブリのバージョン番号を (タイトル表示とかバージョン情報表記とかの場面で) "AssemblyInfo.Version" という文字列定数として直接利用できるのだ。もちろん、この方式も良し悪しあるのだが、簡易なツールを手早く作っているときは便利だったりするので、ご参考までに。 方法 4. リリースノートテキストファイルの記載中、1行目のバージョン番号を読み取る例えば、リリースノートを "RELEASE-NOTES.txt" という名前のテキストファイルに、以下の書式で保存しているプロジェクト群があったとしよう。 v.1.2.3 - Lorem ipsum dolor sit amet takimata qui elitr ut - nonummy duo et no consetetur lorem amet lorem et. v.1.2.2 - Sanctus vero takimata et. Lorem consequat sit magna duo kasd v.1.2.1 - gubergren erat et. Dolores at sadipscing. - Ea consetetur eu luptatum augue magna ea lobortis justo vero. 上記例からわかるとおり、現在のバージョン番号は、このリリースノートテキストファイルの1行目に記載されることになる。および、上記 RELEASE-NOTES.txt の配置先フォルダは以下のとおりとする。 📂Projects ├ 📄Projects.sln ├ 📄RELEASE-NOTES.txt 👈 コレ! ├ 📂AppA │ └ 📄AppA.csproj └ 📂AppB └ 📄AppB.csproj さてこのリリースノートテキストファイルの1行目に記載のバージョン番号を、各アプリケーションのバージョン番号の指定とすることも可能だ。各プロジェクトファイル (MSBuild スクリプト) 内にて、このテキストファイル "RELEASE-NOTES.txt" を読み取り、正規表現検索と組み合わせて、1行目のバージョン番号部分を抜き出し、"Version" MSBuild プロパティに設定する、という作戦である。 具体的には、以下のような Target をプロジェクトファイル内に記述する。 <!-- 📄AppA.csproj や 📄AppB.csproj --> <Project Sdk="Microsoft.NET.Sdk"> ... <!-- ビルドの直前に実行される Target を定義 --> <Target Name="SetupVersion" BeforeTargets="BeforeBuild"> <!-- 親フォルダにある RELEASE-NOTES.txt の内容を1行ずつ読み取って、 ReleaseNoteLines という名前の項目に、読み取った各行を格納 --> <ReadLinesFromFile File="../RELEASE-NOTES.txt"> <Output TaskParameter="Lines" ItemName="ReleaseNoteLines" /> </ReadLinesFromFile> <PropertyGroup> <!-- 正規表現検索のために、読み取った各行の項目を LF 区切りで単一の 文字列に連結し、ReleaseNoteText という名前のプロパティに格納 --> <ReleaseNoteText>@(ReleaseNoteLines, '%0a')</ReleaseNoteText> <!-- ReleaseNoteText プロパティ値に対し正規表現検索で1行目の バージョン番号部分を取り出し、Version プロパティ値に設定 --> <Version>$([System.Text.RegularExpressions.Regex]::Match($(ReleaseNoteText), "^v\.([^\n]+)\n", System.Text.RegularExpressions.RegexOptions.Singleline).Groups[1].Value)</Version> </PropertyGroup> </Target> </Project> なお、若干ボリュームのある MSBuild スクリプトなので、上記 Target を方法 2 で紹介したのと同じやり方で共通の MSBuild スクリプトファイルに抜き出し、各アプリのプロジェクトファイルからはそれを Import するだけとするのがよいかもしれない。 いずれにせよ、上記 Target を仕込んであるプロジェクトに対し、RELEASE-NOTES.txt 内の1行目に記載されたバージョン情報が設定されるようになる。バージョン番号に変更があったときは、RELEASE-NOTES.txt だけを書き換えて再ビルドすればよい。 方法 5. dotnet build 時のコマンドラインで指定するさて、ここまでの方法はいずれも、バージョン情報の指定を、MSBuild スクリプトやプログラムのソースコードなど、プログラムを構成するファイルに固定で記述する方法を紹介してきた。しかし場合によっては、そういったプログラムを構成するファイルに固定でバージョン情報を記述することは好ましくないことがあるかもしれない。 そのような場合、とりわけ、ビルドに dotnet CLI を使っている場合は、"dotnet build" コマンドによるビルド実行時に、そのコマンドライン引数にてバージョン番号を指定できる。"dotnet build" や "dotnet publish" コマンドでは、そのコマンドライン引数として、"-p:{MSBuild プロパティ名}={値}" という書式で、コマンド実行時に MSBuild プロパティを指定できる。なので、バージョン番号、すなわち、"Version" MSBuild プロパティ値も "-p:Version=1.2.3" のようにコマンドライン引数を指定することで、ビルド実行時に指定が可能だ。 例えば、ソリューションファイル Projects.sln に、AppA と AppB の両方のプロジェクトが含まれている場合は、以下の dotnet コマンドを実行するとことで、AppA、AppB の両方に、コマンドライン引数で指定された同じバージョン番号が設定されてビルドされる。 dotnet build Projects.sln -p:Version=1.2.3 方法 6. 環境変数に設定する実は、MSBuild スクリプトにおけるプロパティ値として、ビルド実行時のプロセスの環境変数が自動で取り込まれている (下記リンク先参照)。 そのため、"dotnet build" (または "dotnet publish") コマンド実行時のコマンドライン引数に指定する以外に、環境変数を通して、同名の MSBuild プロパティ値を設定できる。例えば PwoserShell 上の場合、以下の様に環境変数 Version を設定すると、これがそのまま同名の MSBuild プロパティに設定され、結果として環境変数 Version に指定したバージョン番号で、ソリューション Projects.sln に含まれるプロジェクトがビルドされる。 PS> $env:Version="1.2.3" PS> dotnet build Projects.sln この方法 6: 環境変数でのバージョン番号指定、および、ひとつ前の方法 5: ビルドコマンド実行時の引数でのバージョン番号指定は、CI/CD 環境だと使い勝手がよいかもしれない。 まとめと補足以上、複数の .NET プロジェクトに対し、共通の1箇所で同じバージョン情報を設定する、6 つの方法について紹介した。もっと他にもいろいろやり方はあるのかもしれない、何かお気づきの点があればコメントまたは Twitter 上でお知らせいただきたい。 なお、ここまでの説明では "Version" MSBuild プロパティ、すなわち、アセンブリのバージョン番号の設定についてのみ言及してきたが、もちろん、バージョン番号以外の任意のアセンブリ情報の設定、例えば Copyright であったり Description であったり、に対しても同じように共通の1箇所で複数プロジェクトに一斉適用できる。なので、バージョン番号に限らず、「同一の共通設定を繰り返し別々のプロジェクトファイルに書き散らかしたくない」という要件があれば、これら 6 つの方法のうちいくかが参考になるかもしれない。
by developer-adjust
| 2022-10-30 17:45
| .NET
|
Comments(0)
|
ファン申請 |
||