Visual Studio for Macの.NET Coreサポートの実装について


## はじめに

本稿は[.NET Core Advent Calendar](http://qiita.com/advent-calendar/2016/netcore)1日目の投稿です。トップバッターなので、あまり.NET Coreの深い部分には立ち入らずに済ませようと思います。

Microsoft Connect 2016();で、新しいVSブランドの製品としてVisual Studio for Mac（以下VSMac）が公開され、プレビュー版が試せるようになりました。

お分かりの方も多いかと思いますが、VSMacは概ねXamarin Studioと言っても差し支えない存在です。MonoDevelopには元々UI上の「ブランディング」を簡単に変更する仕組みが用意されており、実態においてMonoDevelopとその上のアドインの集合体であるにすぎないXamarin Studioは、簡単に表示をXamarin Studioとしていられるわけです（かつてPlayStationSuiteという開発環境もありましたね）。VSMacもおそらく同様の仕組みに基づいて、UIを変更しているのでしょう。

というわけで、VSMacについて詳しく知りたければ、Xamarin StudioひいてはMonoDevelopについて調べればよいということになるわけですが、VSMacには、少なくとも現状のXamarin Studioには搭載されていない機能がいくつか存在します。そのひとつが本稿で言及する.NET Coreサポートです。

ちなみにVSMac自体についての基本的な使い方は、既に導入記事がいくつかあるので（[この辺](http://ytabuchi.hatenablog.com/entry/2016/11/18/003000)とか）、それらを参考にしてもらえればと思います。


## .NET Core toolingとバージョンの関係

2016年10月に[公表された](https://blogs.msdn.microsoft.com/dotnet/2016/10/19/net-core-tooling-in-visual-studio-15/)ところによると、VS2017（当該ブログで記述されているVS "15"は、VS2017となったようです）における.NET CoreプロジェクトのビルドツールはMSBuildということになって、project.jsonや.xprojファイルは.csprojファイルに移行することになったようです。古いプロジェクトは`dotnet migrate`コマンドで移行できるようです。

エェー、あの`.csproj`ファイルかよ…と思われたかもしれませんが、新しいファイルはだいぶすっきりしているので、そんなに悪くないかもしれませんよ?

	<Project Sdk="Microsoft.NET.Sdk">
	  <PropertyGroup>
	    <OutputType>Exe</OutputType>
	    <TargetFramework>netcoreapp1.1</TargetFramework>
	  </PropertyGroup>
	</Project>

ちなみに、以下が2016年11月時点での`.csproj`ファイルです。半年でだいぶ変わりましたね。

	<Project>
	  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
	  <PropertyGroup>
	    <OutputType>Exe</OutputType>
	    <TargetFramework>netcoreapp1.0</TargetFramework>
	  </PropertyGroup>
	  <ItemGroup>
	    <Compile Include="**\*.cs" />
	  </ItemGroup>
	  <ItemGroup>
	    <PackageReference Include="Microsoft.NETCore.App" Version="1.0.0" />
	    <PackageReference Include="Microsoft.NET.SDK" Version="1.0.0" />
	  </ItemGroup>
	  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
	</Project>

従来のMonoDevelopとこのVSMacでは、プロジェクトモデルの実装が異なるのですが（従来型のプロジェクトがいきなりプロジェクトのファイル追加・削除操作がワイルドカード ベースになったりはしない）、この辺については[こちらのblog post](http://ufcpp.net/blog/2017/5/newcsproj/)で詳しく説明されています。VSMacではこの新しいモデルに対応しています。

## .NET Coreをサポートするパッケージ管理

VSMacのアプリケーションの内容を見ていくと、./Contents/Resources/lib/monodevelop/AddIns 以下に、おなじみのmonodevelopのアドイン群が入っています。今回は（Xamarin.AzureMobileAppServiceなど別方面の新しいのは置いといて）、まず地味なパッケージ管理から入りましょう。

VSMacのソースコードは公開されておらず（もともとVSMacのベースとなっているXamarin Studioも非公開です）、どうやら公開されているmonodevelopとも少し異なるコードベースを持っているようです。とはいえ、今でも共通の部分が多く、パッケージ管理については公開コードにも含まれています。[MonoDevelop.PackageManagement](https://github.com/mono/monodevelop/tree/master/main/src/addins/MonoDevelop.PackageManagement)というアドインです。

これを見る限り、NuGet 3.5には対応しているようです。NuGet 3.5では、netcoreapp11やnetstandard17といったモニカで表されるtargetに対応できる状態になっています。（もっとも、後述の通り、VSMacで対応しているのはnetstandard14です。）

あと、実装がどこにあるかは分かりませんが（たぶんMonoDevelop.ProjectsとかMonoDevelop.Ide.Projectsとか、その辺だと思うのですが…）、csprojファイルに含まれるPackageReferenceビルドアクションもサポートされているので、パッケージ参照のやり方は前述のMSDN blogに書いてあるやり方と互換性があります。<del>ただし.NET Core 1.0互換なので、実際のビルドはproject.jsonで行っているでしょう。</del>12/1修正: ビルドは（相変わらず正確に調べてはいませんが）csprojのC#ビルドをMSBuildで行っている可能性のほうが高いと思っています。想像です。


## プロジェクトモデル

MonoDevelopは、.NETまわりのプロジェクトなら何でも自動的にサポートする、魔法のようなIDEではありません。monodevelopが新しい種類のプロジェクトをサポートするには、アドインによって新しいプロジェクト モデルを定義し、そのプロジェクトがどのようにビルド処理を行うのか、実行できる場合はどのように実行するのか、デバッグできる場合はどのデバッグエンジンを使用するのか、といった情報を、必要に応じてmonodevelopに渡せるようになっていなければなりません。

（MonoDevelopの「プロジェクトモデル」については、以前にも[勉強会](https://gist.github.com/atsushieno/3847291)や[記事](http://www.buildinsider.net/mobile/insidexamarin/11)で解説したことがあるので、もう少し詳しく知りたい方は、そちらを参照してください。）

というわけで、今回の.NET Coreサポートも、例外ではありません。

VSMacには、`MonoDevelop.DotNetCore`という新しいアドインのDLLが追加されています。これはどうやら.NET Coreのプロジェクトモデルを定義しているようです。このアドインのディレクトリの下にtemplatesのフォルダがあることから、それが窺えます。

[monodevelopのソース](https://github.com/mono/monodevelop/)では、`main/src/addins/MonoDevelop.DotNetCore`に存在しています。ちなみに、2016年11月の時点では、このアセンブリに相当するアドインのソースは存在しませんでした…と思われるかもしれませんが、実は[dotnetcoreというブランチ](https://github.com/mono/monodevelop/tree/dotnetcore/)が存在しており、この中には[MonoDevelop.DotNetCoreのソース](https://github.com/mono/monodevelop/tree/dotnetcore/main/src/addins/MonoDevelop.DotNetCore)が含まれていました。Xamarinでは、このように、新機能は基本的に別ブランチで開発していき、masterにマージできるようになったらマージする、という方針で開発が進められるので、新機能はブランチを眺めていると、何かしら新しいものが見えるようになるかもしれません(!)

MonoDevelop.DotNetCoreの具体的な実装内容について、少しずつコメントしていきましょう。

まず、**サポートされているフレームワーク**は `.NETCoreApp`と`.NETStandard`[です](https://github.com/mono/monodevelop/blob/e531760/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreProjectExtension.cs#L63)。とりあえず、.NET Core開発ツールとしてサポートする範囲としては、妥当なところかと思います。NuGetパッケージを追加しようとしても、netstandard14に対応していないものは追加できません。

**プロジェクト テンプレート**としては、Console App、Empty Web App、Libraryの3種類が用意されています。Web Appというのは、ASP.NET Coreアプリケーションのことです…ASP.NET Coreについては、今回ここで詳しく説明すると、もはやVSMacの話ではなくなってしまうので、今回はこの1行にとどめます。Libraryというのは、.NET Standard Libraryのことで…これも説明すると長くなるので、既にいろんな人が書いている解説を探して読んでみてください。

**ビルド**に関して言えば、前述の通り、VSMacの.NET Coreサポートはv1.0相当のものなので、MSBuildでcsprojベースのビルドを行っているわけではありません。実のところ、.NET Coreのプロジェクトのビルドで使われるのは、MSBuildではなくdotnetコマンドですね。というわけで、MonoDevelopでもプロジェクトビルダーでは[dotnetを](https://github.com/mono/monodevelop/blob/e531760/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCorePath.cs#L42)実行します。（てか、えらいコードですね、これ… `dotnet`が `/usr/local/dotnet` や `/usr/share/dotnet` に入っていないと無いことにされるのかよ…）

最後に**デバッグ実行**についてですが…これは次に節を改めて論じます。


## デバッガー

さて、今回の.NET Coreサポートで一番面白いところはここです（断言）。.NET Coreアプリケーションの実行は、（通常は）`dotnet run`で行いますが、デバッグ実行となると、全く別の様相を呈してきます。この意味では、.NET Coreのデバッグ実行は、`--debug`を付けて実行するだけのmonoランタイムによるデバッグ実行とは大違いです。

MonoDevelopの.NET Coreデバッグのサポートは、斜め上の方向から実装されています。MonoDevelop.DotNetCoreで実装されているデバッガー バックエンドは、DotNetCoreDebuggerEngineというクラスで、このCreateSession()メソッドはDotNetCoreDebuggerSessionというクラスのインスタンスを返します。このクラスはどう[定義されている](https://github.com/mono/monodevelop/blob/9babff2/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotnetCore.Debugger/DotNetCoreDebuggerSession.cs#L46)でしょうか?

	public class DotNetCoreDebuggerSession : VSCodeDebuggerSession

VSCode!? そう、VSCodeです。VSCodeはデバッグ プロトコルをオープンに実装しており、VSCodeをサポートするデバッガーエンジンで動作するdebuggeeアプリケーションは、VSCodeのデバッグ プロトコルをサポートする任意のエディタでデバッグできるのです。

![VSCode debug adapter (vscodeのサイトより)](https://code.visualstudio.com/images/example-debuggers_debug-arch.png)

このVSCodeのdebug protocolを実装するアドイン[MonoDevelop.Debugger.VSCodeDebugProtocol](https://github.com/mono/monodevelop/tree/dotnetcore/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol)が、VSMacには新しく追加されています。

MonoDevelopは、もともとmonoのデバッガー(sdb)に加えてgdbなどもサポートしており、VSCode debug protocolもこのアダプターのラインアップに新しく加わった、ということです。今後、VSCode debug protocolをサポートするプログラムは、そのデバッグ実行を開始さえ出来れば、このアドインを活用して簡単にデバッグをサポートできるようになった、と考えても良いでしょう。

VSMac（というかMonoDevelop）は、UIとしてはデバッガー クライアントであり、デバッグ対象プログラムを実行するアドインは、そのクライアントにとってはサーバーであり、このアドインは実際のプログラムにとっては、debug protocolの命令を転送するだけのクライアントである、ということになります。

さて、ではそのdebuggeeである.NET Coreプログラムは、どのようにしてデバッグ実行されるのでしょうか? vscodeでは、.NET Coreサポートにはomnisharp-vscodeというVSCode拡張が使用されます。C#サポートを実装するomnisharpの一部として実装されているということですね。デバッガー接続部分は[coreclr-debug](https://github.com/OmniSharp/omnisharp-vscode/tree/master/src/coreclr-debug)という部分で実装されていて、実際には`OpenDebugAD7`というプログラムをインストールして実行するようになっているようです。実際、MonoDevelop.DotNetCoreに含まれる[DotNetCoreDebuggerSession](https://github.com/mono/monodevelop/blob/9babff2/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotnetCore.Debugger/DotNetCoreDebuggerSession.cs#L51)でも、この`OpenDebugAD7`というプログラムが実行されているようです。一体この謎のプログラムは何なのでしょうか?

その答えは…まあぐぐればすぐに分かります。Microsoftが2015年にオープンソース化したMIEngineをご存知でしょうか?

https://github.com/Microsoft/MIEngine/

	The Visual Studio MI Debug Engine ("MIEngine") provides 
	an open-source Visual Studio Debugger extension that 
	works with MI-enabled debuggers such as gdb, lldb, and clrdbg.

これは、gdbのMIと呼ばれるテキストベースのプロトコルを、MicrosoftがVisual Studioのデバッガー拡張のひとつとして実装したものです。.NET Coreのデバッグエンジンであるclrdbgは、このMIEngineから起動でき、このMIEngineの[VSCode上での実体がOpenDebugAD7である](https://github.com/Microsoft/MIEngine/blob/d336b2b/tools/InstallToVSCode/InstallToVSCode.sh)ようです。

MonoDevelop.DotNetCoreのDotNetCoreDebuggerSessionは、このOpenDebugAD7を必要に応じてセットアップして接続しています。こう説明すると迂遠に見えるかもしれませんが、既存の汎用的なデバッグ サーバー・クライアントの仕組みを活用した、賢い戦略だと思いませんか?


## Moving Forward

VSMacの.NET Coreサポートの実装をざっくり(?)紹介しましたが、どうでしたか? 今回は特にdebug protocolについて詳しく紹介してみました。

Visual Studioの最新のトップページには、次のようなフレーズが使われています:

![visualstudio.com top page](https://i.imgur.com/RUyphhA.png)

"Any Developer, Any App, Any Platform"

VSMacも同じことを実現しようとしている、その未来の一端をこのdebug protocolサポートが示していると思いませんか? 現状ではXamarin Studioからrebrandingしただけとも言えるVSMacですが、新しいVisual Studioの方向性を組み込んだIDEとして、今後も発展していきそうです。