銀の光と碧い空

クラウドなインフラとC#なアプリ開発の狭間にいるエンジニアの日々

VisualStudio 2015 のcsproj をいじるためのVS拡張 CSProjUtil をリリースしました

インストールはVisual Studio Galleryから。 visualstudiogallery.msdn.microsoft.com

コードはGitHubで公開しています。 github.com

使い方はVisual Studio Galleryの方に載せていますが、ソリューションエクスプローラーでプロジェクトを右クリックして「CsProj Util」から3つのコマンドを実行できます。それぞれ、SolutionDir という定義をPropertyGroupに定義するもの、HintPathをSolutionDirに変換するもの、Config Transformを追加するもの、です。

そもそも作った経緯ですが、いくつかの理由でVS2015にした後に、生成したcsprojを編集したいことが増えました。VSのデフォルト機能でcsprojを編集するには、いったんプロジェクトをアンロードする必要があって面倒だなあということで、編集したいパターンごとにcsprojを編集するコマンドを用意した拡張を作りました。

SolutionDir プロパティの追加

SolutionDir というプロパティがVS2013までは定義されていたのが、2015からはなくなってしまいました*1。通常はなくても問題ないのかもしれませんが、次の HintPath の定義をする部分などで必要なケースが出てきたので、プロパティを追加するコマンドを用意しました。

HintPathを SolutionDir を用いたパスに変換

次のHintPathの変換ですが、HintPathはNugetでライブラリを参照に追加すると、ダウンロードしたライブラリへの参照がcsprojに追加され、そのパスがHintPathに追加されます。パスは通常

<HintPath>..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll</HintPath> 

のように相対パスで定義されます。この相対パスは通常 csproj がある場所を基準に解釈されるため、csprojの1つ上のディレクトリ、つまりslnファイルがあるディレクトリの中のpackagesフォルダ以下を参照します。デフォルトの設定だと Nugetはslnファイルがあるフォルダの中にpackagesフォルダを作り、その中にダウンロードしたライブラリを配置するので問題ありません。 が、場合によってはcsprojがslnファイルの1つ下のフォルダには存在しない状況があってそのときに困ります。

一つの例が、あるソリューション Aに属するプロジェクトAが別のソリューション Libに属するLib プロジェクトをプロジェクトとして参照しているとします。そして、A.sln というフォルダの中に Libというフォルダがあり、その中にLib.sln が入っているとします*2

フォルダA
├フォルダpackages
├A.sln
├フォルダA
│├A.csproj
│└packages.config
└フォルダLib
 ├Lib.sln
 └フォルダLib
  ├Lib.csproj
  └packages.config

この場合、Lib.csproj は Lib.sln ではなく、A.sln に追加しています。そのため、Lib.csproj が必要なNugetライブラリは、A.slnと同じ場所にあるpackagesフォルダに格納されるため、 ../packages という一つ上のフォルダを見てもライブラリは存在しません。そこで、相対パスの替わりに SolutionDir を明示的に外部から指定するものとして、そこを基準にdllのパスを定義するように変換する機能を作りました。先ほどのHintPathの例だとこうなります。

<HintPath>$(SolutionDir)\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll</HintPath> 

そもそも Lib.csproj をNugetで配布すればこういうソリューション構成にならないのでは?という疑問もあるかと思いますが、この辺は弊社の開発の方針にあわせた構成になっています。このあたりの経緯は、「Build Insider MEETUP with Grani 第1回」のセッションレポートの中の「グラニのフレームワーク」に書いています。

www.buildinsider.net

Config変換の追加

Config変換の追加ですが、これはConsole アプリ向けの機能です。ASP.NET Webプロジェクトですと、Web.config をビルド構成およびデプロイ先に応じて変換することができます。それと同じことをConsoleアプリでも行いたいのですが、ビルド構成による変換であればいくつかのVS拡張が機能を提供しています。しかし、弊社ではコードそのものはビルド構成に依存しないものの、配置先に合わせたConfigを複数(最大10を超えるくらい)切り替えて展開したいケースが出てきました。この状況でビルド構成に応じた変換を使うと、ビルド構成が10も追加されて非常に扱いづらくなってしまいます。そこで、ビルド構成ではなく別のBuildTargetというプロパティをMSBuildで指定して、App..config というconfigファイルを元に変換するような仕組みを id:guitarrapc_tech に作ってもらいました。その仕組みを適用するのに csproj ファイルをいじる必要があったので、それを一発で実行するコマンドを作成しました。

最後に

project ファイルを任意に編集したいのであれば、この拡張機能が便利です。プロジェクトを開いたまま一時ファイルを作成してVSのテキストエディタとして編集できて、保存のタイミングでprojectを読み込み直してくれます。

EditProj: Visual Studio extension to edit project files - Ed's personal blog

visualstudiogallery.msdn.microsoft.com

*1:おそらくNugetの復元機能がcsprojの定義からはなくなった影響

*2:GitのSubmoduleやSubtreeを使うとこういうフォルダ構成になるかと思います