kazuk は null に触れてしまった

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

カテゴリーアーカイブ: NuGet

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 とかラベル作ってあるんでラベル付けて入れてくれれば、「あぁ、それ俺も使う」とかならさくっと作るかもしれんし、そうでなければ放置するかもしれん。

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

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

NuGet でのパッケージングの注意事項諸々


NuGet で pack する時に失敗しそうな事を色々と見つけたので blog

web.config.transform が指定した通りには入らないっぽい

現象の発生する NuGet.exe

C:\Users\kazuk\Documents\DocOfCode\DocOfCode>..\.nuget\NuGet.exe
NuGet Version: 2.5.40416.9020

ASP.NET プロジェクトのディレクトリで nuget spec 叩いて nuspec をちょっと編集してパッケージを作れるようになりましたって状態で、web.config に関する変換を web.config.transform として作成

nuget pack を叩く

Successfully created package ‘C:\Users\kazuk\Documents\DocOfCode\DocOfCode\DocOfCode.1.0.0.33115.nupkg’.

んでNuget package explorer で中身見てみると自分が指定した web.config.transform じゃなくて、web.config の内容が入ってる。 Exclude で web.config 指定してもダメっぽい。

image

Nuget package explorer で狙った格好の web.config.transform を入れなおさないと現状だめっぽい。

web.config の中にセキュリティ的にヤバい事書いてると nuget 経由で漏れそうな気がするので注意、超注意

NuGet pack –IncludeReferencedProjects を不用意に使うと危険

なんでか知らんけど、依存プロジェクトに含まれる出力ディレクトリにコピーのついてないコンテンツまで nuget パッケージに入れられちゃう。

nuget pack –IncludeReferencedProjects を使ってパッケージ

Successfully created package ‘C:\Users\kazuk\Documents\DocOfCode\DocOfCode\DocOfCode.1.0.0.33115.nupkg’.

CreateRasKey.txt は依存プロジェクトのコンテンツなんだけど、パッケージに入っちゃう。

image

クラスライブラリプロジェクトの中に txt で作り中のメモとか置いて出力ディレクトリにコピーしてないから大丈夫だと思ってるとダダ漏れになる。(RSA秘密鍵とかそういうのをうっかりコンテンツで置いてて漏れそうになった罠)

ビルドオプションをちゃんと None にしないとダメよと。

まとめ

便利な物だけど、ファイルが表に出ていく物である以上は慎重に使ってあげないと、セキュリティ的にヤバいので、中身はちゃんと確認しましょう。

NuGet パッケージの作成とローカルリポジトリへの発行


Gistしましたって事でお知らせ。

https://gist.github.com/kazuk/5089069

何をするものですか?

ビルドすると NuGet パッケージを作り、ローカルリポジトリに発行します。

使い方

  1. ソリューションを右クリックし、「NuGetパッケージの復元の有効化」を行います。(又は NuGet.exe をソリューション配下、 .nuget ディレクトリに置いてください)
  2. ソリューションにGistから取ったファイルを突っ込んでください。
  3. csproj の末尾で Import してください
  4. 必要に応じて NuGetRepository を PropertyGroup 定義してください(定義しないと \\nugetrepos.local\NuGetRepos にパッケージを発行します)
  5. プロジェクトに nuspec を加え、ビルドアクションに NuSpec を指定します。

nuspec 作るってどうやって?

csproj のある場所で NuGet.exe spec すればいいんじゃないかな?

そのままパッケージしようとすると author とか色々設定されてないよって言われるはずだけど、文句言われた所直せばいいと思うよ。

csproj の末尾でImportって?

プロジェクトを右クリックして「プロジェクトのアンロード」、もう一回右クリックで csproj の編集すればいいよ。

ファイル末尾の </Project> の前でローカルリポジトリの場所と targets ファイル置いた場所によって設定してね。

  <PropertyGroup>
    <NuGetRepository>\\FileServer.local\NuGetRepos</NuGetRepository>
  </PropertyGroup>
  <Import Project="..\Build.Utilities\CustomBuild.targets" />

発行って Copy してるだけに見えるけど

ファイルシステムフォルダをローカルリポジトリとして使ってるならコピーだけでいいんだよ。

NuGet.org とかに出す前に色々確認して欲しいからローカルにしか出さないよ。

Visual Studio プロジェクトテンプレートの作り方


NuGet のプロジェクトテンプレートを公開してたりする人なので、レシピを公開しておきましょう。

最も簡単な方法は Visual Studio のファイルメニューからテンプレートの作成なんですが、今回はちっと難しい方法を。

Visual Studio SDK をインストールします。Download details: Visual Studio 2010 SDK

こいつをインストールすると、プロジェクトの種類として Extensibility が選べるようになり、Visual Studio の拡張パッケージを作成したり色々できる他に、その他色々のおまけがついてます。

image

今回は ASP.NET MVC3 のHttpModule を作成する為のプロジェクトテンプレートを書いてみます。プロジェクト名を設定して出てくる画面はこんな感じ。

image

いきなり vstemplate が開かれてますね。ソリューションエクスプローラーでの表示にはProjectTemplate.csproj 含め幾つかファイルが入ってますね。

というわけで、目的に応じて「あとは好きに致したまえ(ぇ」なんで、ASP.NET MVC3で使うHttpModuleの作成に使うテンプレートになるようにカスタムしていきます。

まず、Class1.cs で最初にできるべきコードを考える為に、本物を作ってみましょう。

クラスライブラリプロジェクトを追加して、そこの上で HttpModule を作るための作業をします。

image

System.Web とかを参照設定して

image

Class1.csをHttpModule1.csにリネームして IHttpModuleを派生して実装するのに十分な事をやります。

IHttpModule.DisposeのDisposeパターンを実装してやばそうなら Debug.Fail を呼ぶぐらいをデバッグ版でやるまでが今回想定できる範囲でしょうか。

image

ビルドしてちゃんとビルドできると解ったら、いきなり作ったクラスライブラリプロジェクトをアンロードします。

ソリューションエクスプローラーからプロジェクトのコンテキストメニューから「プロジェクトのアンロード」を選びます。

アンロードしたプロジェクトを右クリックで編集を選ぶと csproj の編集になります。(が見るだけで編集するわけではありません)

これを ProjectTemplate.csproj と並べたのがこんな感じ。

image

後はこの両者を比べながらProjectTemplate.csprojが「いい感じになるようにします(ぉぃ」ここでビルドシーケンスに仕込みとかができますね。NuGetパッケージプロジェクトの時はここで仕込みを入れました。(ちょっとだけ後述)

まぁ、参照設定の Reference の所が一番とっつき良いでしょうね。(MVC3入ってないじゃん、うん、裏でインストールやってて止まる罠にはまった為、単純にASP.NET HttpModuleのプロジェクトになってる)

いい感じになったらプロジェクトのコンテンツに行きましょう。

今回は HttpModule1.cs がコンテンツになりますのでコンテンツになるようにまた左右に並べて「いい感じになるようにします(ぉぃ」

image

こんな感じに。

$safeprojectname$ とかの暗号めいた物のリファレンスはこちら。

テンプレート名 ($if$ とかのドキュメントが無いように感じるのは自分だけでしょうか?)

お気づきかもですが、 NuGet の Configuration File and Source-Code Transformations がこの $で記号を挟む形式って事で、NuGetはVS内にあるテンプレート機構にオンラインリポジトリを結びつけた物という見方も出来たりするかもしれませんね。

んでこれをビルドするとプロジェクトテンプレートの zip ファイルが bin/構成 配下に吐き出されます。

これを自分の ドキュメントの Visual Studio 2010\Templates 配下の適切な場所に配置するとそれをベースとしてプロジェクトを作る事ができるようになります。

もともとビルド成功してた物を参考に「いい感じになるようにします(ぉぃ」だったので、プロジェクトを作ってみてビルドできない様であればなんかが間違ってるって事ですね。

csprojでのビルドシーケンスへの仕込みをする時の Tips

Visual Studio は結構激しく csproj と関連依存ファイルをキャッシュするので、csprojへの仕込みとデバッグは大変です。MSBuildのImportを使って分離した編集用ファイルを見るようにしておいてそれを編集しながらビルドを試すとかはこのキャッシュ機構によって、いくら編集して保存しても反映されない事になり「直したのに直らない→何がわるいのか解らない→挫折しちゃってもいいかなモードがひたすら上昇」の負帰還ループにはまります。

「csprojへの仕込みのデバッグは Visual Studio ではできない」これを最初に前提として進みましょう。

プロジェクトのアンロードをして csproj の編集に入ったら、スタートメニューから Visual Studio のコマンドプロンプトを開き、cd プロジェクトのフォルダで msbuild をコマンドプロンプトから叩けるようにしておいて(これをやってくれるVS拡張は色々あります) csproj を編集して保存したらビルドはコマンドプロンプトから実行します。ビルドがいい感じでできるようになるまではこの状況で進むのがおすすめです。

csproj内では msbuild のTaskやTargetを書く事になりますが、Visual Studio 2010 では MsBuild 4.0 の BeforeTarget/AfterTargetによって既存の Microsoft.Common.Tagets に手を入れずにシーケンスの途中に割り込めるようになりました。この機能の効果は結構絶大で MsBuild インラインタスクとかと組み合わせるとビルドシーケンスは結構思うがままです。(参考: MsBuild 4.0の新機能でビルド時コード生成とか)

注意事項ですが最終的にいい感じな csproj を作ったらテキストの複製を取って Visual Studio に csproj を再読み込みさせてすぐにアンロードして再度編集に入って比較するのがおすすめです。Visual Studioは自分に都合の悪い記述をデフォルト値に戻す動きを暗黙にやります。このようなVisual Studioによって削除されたりデフォルト値に戻された値についてはVisual Studioでのビルド、デバッグに支障がでない何らかの方策を実施する必要な項目として重要な情報ですが比較してみないことには解らないというのが結構厄介です。(この動作についてはMsBuild とVSの連携に関するところにドキュメントで書いてあった記憶はありますが見つからない…)

作ったプロジェクトテンプレートを NuGet 配布しちゃうぞ

って手前味噌ですが、自分の流した NuGetPackageProject を取ってください、とり方はNuGet パッケージプロジェクトテンプレートを参照。

するとNuGetパッケージの為のプロジェクトを作れます、これを作ってるソリューションに追加します。

image

プロジェクトテンプレートプロジェクトの出力、今回は bin\Debug\ に作成されている Mvc3HttpModuleProject.zip を Tools 配下に置くようにします。

今回はパッケージプロジェクトのビルド前イベントでcopy コマンドを使いました。

image

Package/content 配下に readme.htm を作ります。(本来的にはいらないんですが、何もないとinstall.ps1が動かないので)

image image

ソリューション配下の packages\NuGetPackageProject\Tools\install.ps1 からプロジェクトテンプレートへのコピー操作を取ってきて Package\Tools\install.ps1 に貼り付けます。(うわ、install.ps1が動かねーってデバッグしてた時のまんまになってる)

param($installPath, $toolsPath, $package, $project)

Copy-Item $toolsPath\*.zip -destination ([System.Environment]::ExpandEnvironmentVariables("%VisualStudioDir%\Templates\ProjectTemplates\Visual C#\"))Write-Host "Project Template installed"

って感じでどうでしょう。

NuGetPackage.tt を編集して好みの nuspec を書いてビルドすると貴方のプロジェクトテンプレートを配布する nupkg がめでたく出来上がったはずです。

ローカルリポジトリを使ってテストして後は nuget.org に流すだけです。アカウントとってアカウント承認してもらったらさっくり放流です。

ローカルリポジトリからの一連のあたりは neue cc – NuGetパッケージの作り方、或いはXmlエディタとしてのVisual Studio が詳しく書いてるのでそちらをどうぞ。

という訳で Visual Studio プロジェクトテンプレートの作り方からcsprojへの仕込み tips 、手前味噌の NuGet パッケージ作成の仕方でした。

NuGet パッケージプロジェクトテンプレート


日本最速?

ってわけで NuGet パッケージを作るためのプロジェクトテンプレートをインストールしてくれる NuGet パッケージを作ってNuGet.org に放り込んでみました。

インストールスクリプトを使ってるんでコンソールから入れろって言われます。

image

おとなしくコンソールから行ってください(お願いします)。

イカ、コンソールでのコマンドとレスポンスでゲソ。

以下と変換しようとしたらイカが出たので侵略されたゲソ。太字が入力ゲソ。

PM> Get-Help NuGet
TOPIC
    about_NuGet
   
SHORT DESCRIPTION
    Provides information about NuGet Package Manager commands.
          
LONG DESCRIPTION
    This topic describes the NuGet Package Manager commands. NuGet is an integrated package
management tool for adding libraries and tools to .NET projects.

                
The following NuGet Cmdlets for are included.

Cmdlet Description
—————— ———————————————-
Get-Package Lists the set of packages available from the package source.

List-Package An alias for Get-Package. This is the more widely used command
for listing packages.

Install-Package Installs a package and its dependencies into the project.

Uninstall-Package Uninstalls a package. If other packages depend on this package,
the command will fail unless the –Force option is specified.

Update-Package Updates a package and its dependencies to a newer version.

New-Package Creates a new package when supplied with a Nuspec package specification file.

Add-BindingRedirects Examines all assemblies within the output path for a project and adds binding
redirects to the application (or web) configuration file where necessary.
                           
Get-Project Returns a reference to the DTE (Development Tools Environment) for the active
or specified project.

SEE ALSO
    Online documentation: http://go.microsoft.com/fwlink/?LinkID=206619
    Get-Package
    Install-Package
    Uninstall-Package
    Update-Package
    New-Package
PM>

dir ‘C:\Users\kikuchi\Documents\Visual Studio 2010\Templates\ProjectTemplates\Visual C#’
PM> List-Package -Remote -Filter NuGet

Id                                    Version                               Description                         
—                                    ——-                               ———–                         
NuGet.CommandLine                     1.0.11220.26                          NuGet command line tool used to cr…
NuGet.CommandLine                     1.1.2113.118                          NuGet command line tool used to cr…
NuGet.CommandLine                     1.1.2120.134                          NuGet command line tool used to cr…
NuGet.CommandLine                     1.1.2120.136                          NuGet command line tool used to cr…
NuGet.CommandLine                     1.1.2121.140                          NuGet command line tool used to cr…
Nuget.Core                            1.0.1120.104                          NuGet.Core is the core framework a…
NuGetPackageProject                   0.1.0                                 Customized build for NuGet pack.    
WebActivator                          1.0.0.0                               A NuGet package that allows other …

 

PM> Install-Package NuGetPackageProject
Successfully installed ‘NuGetPackageProject 0.1.0’
Successfully added ‘NuGetPackageProject 0.1.0’ to MvcApplication1
Installing Project Template! not executing now
PM> dir ‘C:\Users\kikuchi\Documents\Visual Studio 2010\Templates\ProjectTemplates\Visual C#’

    ディレクトリ: C:\Users\kikuchi\Documents\Visual Studio 2010\Templates\ProjectTemplates\Visual C#

Mode                LastWriteTime     Length Name                                                               
—-                ————-     —— —-                                                               
-a—        2011/01/21     17:52     103760 NuGetPackageProject.zip                                            

PM>

ってわけで、個人の ProjectTemplates に NuGetPackageProject.zip が追加されてるんで後はプロジェクトを追加する事の NuGetPackageProject を選ぶ。

image

NuGetPackage.tt が現れるので編集して説明その他を足す。

image

ソリューションエクスプローラーで適当にファイルを放り込むなりなんなりしてから華麗にビルド

image

ビルド成功ってわけ。

bin\Debug bin\Release に nupkg ができるので、nuget.org にポイするなり、社内のローカルパッケージリポジトリに放り込むと作業完了ですね。

ってわけで、このパッケージもこのパッケージで作られておりますな自己再帰的関係をむにゃむにゃやってみました。

パッケージ開発の注意事項

Install.ps1 / Uninstall.ps1 は Contents が空だと動きません。(これで結構はまりました。)

ローカルリポジトリを使ってインストール動作等は確認しましょう。

ディレクトリ構造はかなり決め打ちされてますので NuGet のドキュメントをよく読みましょう。

インストールスクリプトを使うと UI でのインストールはできなくなります。