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

  • 33
    いいね
  • 0
    コメント

はじめに

本稿は.NET Core Advent Calendar1日目の投稿です。トップバッターなので、あまり.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自体についての基本的な使い方は、既に導入記事がいくつかあるので(この辺とか)、それらを参考にしてもらえればと思います。

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

2016年10月に公表されたところによると、VS2017(当該ブログで記述されているVS "15"は、VS2017となったようです)における.NET CoreプロジェクトのビルドツールはMSBuildということになって、project.jsonや.xprojファイルは.csprojファイルに移行することになったようです。古いプロジェクトはdotnet migrateコマンドで移行できるようです。

エェー、あの.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>

とはいえ、残念ながら、今回リリースされたVSMac previewがサポートしているのは、.NET Core 1.0ベースのプロジェクトなので、VSMacの新規プロジェクトで作成された.csprojは、このスタイルにはなっていません。VS2017とMonoDevelopでは、プロジェクトモデルの実装が異なるので(いきなりプロジェクトのファイル追加・削除操作がワイルドカード ベースになったりはしない)、しばらくは従来型のプロジェクト ファイル構成になるのではないでしょうか。

現状のVSMacは、あくまでpreviewなので、VS2017の正式版が出てtoolingが.NET Core 1.1ベースに切り替わる頃には、こちらもアップデートされる可能性が十分にあると想像します。

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

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

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

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

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

プロジェクトモデル

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

(MonoDevelopの「プロジェクトモデル」については、以前にも勉強会記事で解説したことがあるので、もう少し詳しく知りたい方は、そちらを参照してください。)

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

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

monodevelopのソースを見ると、このアセンブリに相当するアドインのソースは存在しないようです…と思われるかもしれませんが、実はdotnetcoreというブランチが存在しており、この中にはMonoDevelop.DotNetCoreのソースが含まれています。Xamarinでは、このように、新機能は基本的に別ブランチで開発していき、masterにマージできるようになったらマージする、という方針で開発が進められるので、新機能はブランチを眺めていると、何かしら新しいものが見えるようになるかもしれません(!)

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

まず、サポートされているフレームワーク.NETCoreApp.NETStandardです。とりあえず、.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を実行します。(てか、えらいコードですね、これ… dotnet/usr/local/bin に入っていないと無いことにされるのかよ…)

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

デバッガー

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

MonoDevelopの.NET Coreデバッグのサポートは、斜め上の方向から実装されています。MonoDevelop.DotNetCoreで実装されているデバッガー バックエンドは、DotNetCoreDebuggerEngineというクラスで、このCreateSession()メソッドはDotNetCoreDebuggerSessionというクラスのインスタンスを返します。このクラスはどう定義されているでしょうか?

public class DotNetCoreDebuggerSession : VSCodeDebuggerSession

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

VSCode debug adapter (vscodeのサイトより)

このVSCodeのdebug protocolを実装するアドイン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という部分で実装されていて、実際にはOpenDebugAD7というプログラムをインストールして実行するようになっているようです。実際、MonoDevelop.DotNetCoreに含まれるDotNetCoreDebuggerSessionでも、この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であるようです。

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

Moving Forward

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

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

visualstudio.com top page

"Any Developer, Any App, Any Platform"

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