kazuk は null に触れてしまった

C# / .NET 系技術ネタ縛りでお送りしております

タグアーカイブ: NuGetCustomBuilder

NuGet で MsBuild ターゲットが展開できるのが便利すぎて全俺が(ry


というわけで、 NuGet & MsBuild なネタ。

NuGet 2.5 から MsBuild の targets と props の展開ができるようになりました。

NuGet 2.5 のリリースノート

んで何が便利かというと、ビルド時処理のカスタマイズを部品化できて、 csproj を人がいじらないでも、その部品の付け外しが NuGet パッケージのインストール/アンインストールで簡単にできるという事で、これまでは一部のビルドヲタとかビルドをカスタムすることができる人に覆いかぶさっていた作業が NuGet パッケージ入れろよとか、外せよの一言で済んでしまうという事である。

取りあえず、便利すぎて濡れたので作った物

そういうカスタムビルド処理用の NuGet パッケージを書くためのプロジェクトテンプレートを作ってみたのが NuGetCustomBuilder になる。

んで、このプロジェクトテンプレートを元に、一般的によく使いそうなビルド拡張を NuGet パッケージとしてどんどんこさえちゃうよ!ってのが OnBuild だ。

これで何がどう変わるのか

NuGet を絡めてちょっと大きな規模の開発とかをどう変える事ができるのか書いてみたい。

その昔は Visual Studio がいっぱいいっぱいになって落ちるか落ちないかまで、1つのソリューションにライブラリプロジェクトとかどんどん放り込んでいったはずだ。なんでそんなことをしたのかというと、ビルドの依存性とかのつながりがソリューションの単位で閉じていて、ビルド順序の制御とか依存関係を追いかけてやる事ってのはソリューションで閉じた単位でしか、開発環境としては面倒が見れなかったからだ。

これを複数のソリューションに簡単に分割でき、そしてさらにパッケージ間で依存関係が定義できるようになったのが NuGet のいいところでおいしい所で、それ以前はビルドイベントを使ってビルド後にアセンブリをどっかにまとめて出して、それを参照している別のソリューションでビルド前にそれを取り込むような事をやってきたはずだ。

NuGet でそれが可能になったはずでも、旧来通りにソリューション間で依存アセンブリを受渡ししていたりすることは非常に多かった。

まず、最初の要因として底辺揃えが横行する現場では新機能なんてそうそう使えるもんじゃない。ましてや、外部からのモジュールを NuGet で勝手に取り込んでしまっては困るという都合があったりするかもしれない。

その第一の障壁を乗り越えたとしても、取り込めるけど、自分たちで NuGet パッケージを作るのはあまりやられてこなかった。

NuGet パッケージを作るうえでのパッケージングや、パッケージをリポジトリに出すところの問題だ、ビルドイベントを使うと Visual Studio にとっては依存関係のトラッキングの利かないタスクが増えた=何がどう変わるか予想ができないから常にそのタスクを動かさざるを得ない=ビルド時間が延びる。ビルド時間を延ばさずにできる方法といえば、MsBuild の知識を身に着けて csproj を直接編集せざるを得なかった。

結果として、そこまでやるのは必要知識の増大でしかなく(底辺揃えの横行している現場では必要知識の増大は最大の導入障壁である)、64bit版OSにメモリ乗っければ現行路線を進んでも扱えるソリューションもメモリに大きくなるし問題ないでしょうっていうムーアの法則頼りの解決法も 64bit への移行期であることも幸いして機能しているのでパッケージシステムに頼らないでも回る所は回っているからよっぽどでない限り問題無いのだ。

さて NuGet パッケージを作ってみよう、それをローカルリポジトリに配置してみよう

簡単にクラスライブラリを NuGet パッケージにしてみよう。時代にしたがってポータブルクラスライブラリをベースに作ろう。

imageimage

パッケージマネージャーコンソールで、 Install-Package OnBuild.PackageAssemblyForNuget とするとパッケージングの準備がされる。

imageimage

ビルドすると nuspec が作られるので、プロジェクトに追加する。

image

ビルドすると Properties\AssemblyInfo.cs の記述不足が指摘されるので直す。

imageimage

Warning だけになればパッケージは作成されている。(直す直さないは任せる)

image

ご覧のとおりで csproj には直接には手を入れてないので、 MsBuild の知識はなくともこれは可能になったと言える。

続いて、 OnBuild.PublishNugetPackage を入れる。

image

Readme が表示されるので、それに従って設定する。プロジェクト名.csproj.nupublish ファイルをサンプルからコピペで作る。

imageimage

リリースモードでだけリポジトリにコピーが走るようになっているので Release に切り替えてビルドすると、NuGet リポジトリへコピーが走る。

image

ローカル NuGet リポジトリへの発行も、csproj を直接変更する必要はなく実現されたという事になる。

これでローカルなNuGet リポジトリを使って、パッケージを管理する事で共通モジュールの実装等を行う上でのネックだったビルドカスタマイズに関する手間は追放できたし、csproj を直接編集するとか素で言ったら km 単位で引かれるような事をやらせないでOK になったわけだ。

ビルドサーバクラスターという夢

「C#はビルドが早い!」って言われるけどさ、C++等と比べれば確かにそうなんだけど、規模が大きくなればそこそこ遅い。

企業向けの一連のプログラム大き目なシステムで exe を数百個ビルドするとか、共通のアセンブリに手が入ると当然に数百個の exe をビルドしなおさなければならない。

話を戻して1ソリューションに exe を一杯おかれると、ビルドの並列化とかしたくても詰む、なんで詰むかってそりゃ依存関係を追いながらビルドする単位がデカいから詰む。頑張っても Debug 版はこっちのビルドサーバ、Release 版はあっちと分けれて二つとかになって、いまどきの安いマシンを大量投入とか頑張りたくても頑張れない。

んで、ソースはソース管理に、細かいパッケージはパッケージリポジトリにって恰好になると、パッケージの依存関係を追いながら、ビルドをすることができる。んで、小分けパッケージ間での依存関係でビルドしていくなら、ソリューションの単位が小さくなって、数が増えるので並行ビルドができるようになる。

いまどきのビルドサーバーさんは TFS にしても TeamCity にしても Jenkins にしても、ビルドエージェントは必要なら複数持てて並行ビルドが可能なら並行ビルドをやってのけるので、並行度を上げれば上げただけビルドが早くなる構成に持っていくには、小分けパッケージの依存関係で管理するのがおすすめって事。

規模のデカいシステムをメンテしていて、ビルドの遅さに泣いている人はこの辺取り組んでおくんなまし。ってか取り組むような相談受けて作ってみた感なので、頑張ってくださいませ(to 該当者)。

まとめ

OnBuild にあったほうがいいんじゃない?というネタとか、 pull request くれればどんどんマージするし、自分で必要だなーって思ったネタとか入れていくつもりだったりする。プロジェクトテンプレートを元に勝手に作って放流してくれても別に構わない。

んで、csproj のカスタムで悩んでるとかあれば Issues · kazuk/OnBuild に request とかラベル作ってあるんでラベル付けて入れてくれれば、「あぁ、それ俺も使う」とかならさくっと作るかもしれんし、そうでなければ放置するかもしれん。

ちゅうわけで、ソーシャルコーディングしたい。

(まとまっているのかはよくわからない。)