Roslyn の構文解析を使ってデバッガーを自作する

// C# Advent Calendar 2018 の 23 日目の記事です。

デバッガーのようなものを自作してみました。

動機

  • 普段は Visual Studio を使っているが、デバッグ時に手動でステップ実行するのが面倒
    • ループなどでステップ数が多い場合
    • 分岐の様子や変数の状態を軽くチェックしたい場合

解決案

  • ステップの時間間隔だけを指定して、デバッガーを自動で実行させる
    • 変数の一覧が表示される
    • 時間間隔をリアルタイムで調節できる
  • .NET Compiler Platform (Roslyn) の構文解析の機能を使い、各ステップの間にデバッグ用のコードを差し込めば実現できそう

結果

というわけで、WPF でプロトタイプ「Tick-tack Debugger」を作ってみた結果、このようになりました。
例として、ニュートン法で平方根を求めています。 (クリックで拡大)

 

解説

以下は概略の技術解説です。
WPF アプリを作成する前に、まず .NET Framework 上のコンソール アプリで実験してみます。
C# の構文解析を使うには、NuGet で Microsoft.CodeAnalysis.CSharp をインストールします。

デバッグ対象となるソースコードにデバッグ コードを挿入し、それを動的にコンパイルして実行する、という方針です。
コンソール アプリのソースコードを以下に示します (全体のソリューションは SyntaxTreeSample にあります)。

SyntaxHelper クラスでは、デバッグ対象の C# ソースコードを構文ツリー (SyntaxTree) に変換して走査し、
各ステートメントの前にデバッグ用のコード行を挿入していきます。

CSharpSyntaxTree.ParseText メソッドを使うことで、ソースコードを構文ツリーに変換できます。
また、メソッド・ステートメント・式など、すべてのノードを表す親クラスは SyntaxNode クラスであり、

  • Parent プロパティ: 親
  • Ancestors メソッド: 祖先
  • ChildNodes メソッド: 子
  • DescendantNodes メソッド: 子孫

が存在することを知っておけば、だいたいの探索ができるでしょう。

この他に、デバッグ用のコードから呼び出されるメソッドを定義するクラス ライブラリとして DebuggerLib を作成しています。
各ステートメントの位置、およびその直前で存在する変数とその値を通知するために、このライブラリを経由させます。

Program クラスでは、生成されたデバッグ用のソースコードをファイルに保存したら、
System.CodeDom.Compiler 名前空間の CodeDomProvider を使ってこれをコンパイルし、
そのエントリ ポイント (Main メソッド) を呼び出します。
また、デバッグ コードが実行されたときのイベントハンドラーを登録しておき、
Thread.Sleep メソッドを使って、指定した時間だけ停止させます。

これで、デバッグ対象の元のソースコードが次の Program.cs だとすると、
デバッグ用のソースコードとして下の Program.g.cs が生成されます。

作成したコンソール アプリを実行すると、次の図のようになります (時間間隔は 0.3 秒)。

 

以上をもとに、WPF アプリでデバッグ ツールを作成しました。
左側の C# ソースコードの部分は TextBox で、編集もできます。
デバッグ実行時は、各ステートメントを選択状態にすることでハイライトしています。
右側の変数一覧が表示される部分は DataGrid です。

(図は円周率を求める例)

今回は上記の方法でプロトタイプを作ってみましたが、
デバッグ コードの挿入やコンパイルに関しては、よりスマートな方法があるのではないかと思います。

注意点
  • 考えられうるすべてのステートメントには対応できていません。また、Main メソッドしか構文解析していません。
  • コンパイル時に生成されるアセンブリ (EXE) は、%TEMP% フォルダー (ユーザーの AppData\Local\Temp) に保存されていきます。
  • TextBox で、IsInactiveSelectionHighlightEnabled を True に設定しても利かないことがあります。
    また、選択状態のハイライトがずれることがあります。
    RichTextBox で Run などを使うのがよいかもしれません。

 

作成したサンプル
バージョン情報
参照
広告

T4 でクラスやプロパティを自動生成する

Text Template Transformation Toolkit (T4) はテンプレート エンジンの一つで、主に Visual Studio で使われているものです。
これを使うと、ソースコードやデータの集合などのファイルを自動生成できます。

今回は例として、次のような Markdown を記述したら、
それに対応するクラスやプロパティを C# のソースコードとして生成することを考えます。

この Markdown の仕様を次のように定めます。

  • 空行は無視
  • 箇条書きでない行がクラス名
  • その下に続く箇条書きはプロパティ
    • 「- Type PropertyName」の形式
    • プロパティ名が省略された場合は、型名と同じ
  • クラス名およびプロパティ名は、PascalCase, camelCase のどちらを指定してもよい

以下では、このような .md ファイルを入力として、
プロパティおよびコンストラクターを持つ部分クラスを .cs ファイルに出力するように T4 で実装していきます。

まず、プロジェクトに上記の .md ファイルを追加しておきます。
そしてプロジェクトに「テキスト テンプレート (.tt)」 を追加します。

Add New Item

追加された .tt ファイルを、仕様に従って次のように実装します。

注意点は以下の通りです。

  • 初期状態では出力の拡張子が .txt になっているため、.cs に変更する
  • プロジェクト内のファイルのパスを取得するには、hostspecific="true" を指定して Host.ResolvePath メソッドを使う
  • <#= #> :テキストの出力
  • <# #> :コードを書ける、変数を使える
  • <#+ #> :メソッド、クラスなどを定義できる

.tt ファイルを保存したときに処理が実行されます。
または、.tt ファイルを右クリックして [カスタム ツールの実行] を選択すれば実行されます。

Run Custom Tool

これで、以下のように RecordTypes.cs が生成されます。

 

作成したサンプル

テストしたバージョン

  • Visual Studio 2017

参照

カテゴリー: ツール. タグ: , . Leave a Comment »

.NET Core 向けビルド スクリプト

ビルド用 PowerShell スクリプトの Build Release (GitHub) を .NET Framework プロジェクト形式向けに提供していましたが、
今回は .NET Core プロジェクト形式向けのビルドツールを追加しました。

ツールの内容:

(1) Version 1up

アセンブリのバージョン (x.y.z の z の部分) を 1 だけ増加させます。
.NET Core プロジェクト形式では、プロジェクト ファイル (.csproj) でバージョンを書き換えます。

(2) Zip Release

プロジェクトを Release でビルドして、ZIP ファイルを作成します。
ビルド前にアセンブリのバージョンを増加させます。

(3) NuGet Packup

プロジェクトを Release でビルドして、NuGet パッケージを作成します。
ビルド前にアセンブリのバージョンを増加させます。

これらのツール (PowerShell スクリプト) を使う方法としては、Visual Studio の「外部ツール」に登録するのが便利だと思います。
前回にビルド用のスクリプトを Visual Studio の外部ツールに登録する方法について書きましたが、
.NET Core 版の手順も改めて以下に書いておきます。

セットアップ

Build-Release/Downloads (GitHub) からツールの最新版をダウンロードして任意のフォルダーに展開します。

Explorer

Visual Studio のメニューで [ツール] – [外部ツール] を選択して各スクリプトを追加していきます。

  • タイトル: 任意
  • コマンド: powershell.exe
  • 引数: -ExecutionPolicy Unrestricted "C:\scripts_folder\KTools.xxx.ps1"
  • 初期ディレクトリ: $(ProjectDir)
    • 右の ▶ ボタンで選択できる
    • Version 1up では $(SolutionDir) でもよい
  • 出力ウィンドウを使用: オン

External Tools

 

プロジェクトの作成

.NET Core 向けのプロジェクト テンプレートを選択してプロジェクトを作成します。

New Project

.NET Framework プロジェクトではバージョン番号などを AssemblyInfo.cs に記述しますが、
.NET Core プロジェクトではプロジェクト ファイル (.csproj) に記述します。
初期状態ではバージョンが設定されていない (その場合は 1.0.0 と判定される) ため、
プロジェクトのプロパティで [パッケージ バージョン] の値を設定しておきます。

Project Property

上記の設定をして保存すると、.csproj ファイルの <Version> に反映されます。

.csproj

なお、.NET Core のプロジェクト形式でも、

<TargetFramework>net45</TargetFramework>

のようにすれば .NET Framework をターゲットにすることができます。
詳細は .NET Core と .NET Standard を参照してください。

 

ツールの実行:

(1) Version 1up

対象のプロジェクト内のファイルを開いた状態で、メニューからスクリプトを選択すると実行されます。

External Tools Menu

実行すると、ログが Visual Studio に出力されます。

Version 1up Output

 

(2) Zip Release

同様に、メニューから Zip Release を実行します。
zip フォルダーに ZIP ファイルが作成されます。

Zip Release

 

(3) NuGet Packup

クラス ライブラリ プロジェクトを対象に NuGet Packup を実行します。
pkg フォルダーに NuGet パッケージが作成されます。

NuGet Packup

 

注意点

  • .NET Framework プロジェクト形式向けには NuGet 経由でプロジェクトに PowerShell スクリプトを追加する
    方法 (KTools.ZipRelease) も提供していますが、.NET Core プロジェクト形式では NuGet で同様の方法で追加できませんでした。

 

前回: ビルド用のスクリプトを Visual Studio の外部ツールに登録する

テスト済バージョン
Visual Studio 2017

参照
Build Release (GitHub)
外部ツールの管理
.nuspec File Reference for NuGet

カテゴリー: ALM, ツール. タグ: , . 3 Comments »

ビルド用のスクリプトを Visual Studio の外部ツールに登録する

以前にビルドして ZIP にする PowerShell スクリプトを作成しましたが、
そのときはプロジェクトごとに NuGet でインストールする方法を前提としていました。
今回は各スクリプトを Visual Studio の外部ツールとして登録する方法も便利だとわかったため、その利用手順を紹介します。

設定手順:

  • Build-Release/Downloads (GitHub) から最新版をダウンロードして、任意のフォルダーに PowerShell スクリプトを展開する
  • Visual Studio のメニューで [ツール] – [外部ツール] を選択して各スクリプトを追加する
    • タイトル: 任意
    • コマンド: powershell.exe
    • 引数: -ExecutionPolicy Unrestricted "C:\scripts_folder\KTools.xxx.ps1"
    • 初期ディレクトリ: $(ProjectDir)
      • 右の ▶ ボタンで選択できる
      • KTools.VersionIncrement.ps1 は $(SolutionDir) でもよい
    • 出力ウィンドウを使用: オン

ExternalTools

 

以上の設定で、「プロジェクト フォルダー上で PowerShell スクリプトを実行する」ためのメニューが
Visual Studio の [ツール] メニューに追加されました。
実行するには、対象のプロジェクトのファイルを開いているときにメニューからそれらを選択します。

ExternalTools-Menu

[出力ウィンドウを使用] がオンに設定されていると、ログが Visual Studio に出力されます。

ExternalTools-Output

ExternalTools-Zip

 

このように Visual Studio の外部ツールを利用することで、
バージョンアップ、Release ビルド、ZIP 作成が Visual Studio から簡単にできるようになりました。

このツールは .NET Framework プロジェクト形式向けに提供していますが、
次回は .NET Core プロジェクト形式向けのツールを追加します。

次回: .NET Core 向けビルド スクリプト

テスト済バージョン
Visual Studio 2017

参照
Build Release (GitHub)
外部ツールの管理
Visual Studioの外部ツール機能を活用してみよう
ビルドして ZIP にする PowerShell スクリプト

カテゴリー: ALM, ツール. タグ: , . 3 Comments »

dotnet コマンドによるビルド

前回の .NET Core と .NET Standard に引き続き、今回はコマンドラインでアプリやライブラリをビルドする方法を検証しました。
まず、ビルドに関連する dotnet コマンドの一覧を挙げます。
基本的にはプロジェクト フォルダー上で実行しますが、build や pack などは ソリューション フォルダー上でも実行できます。

  • dotnet restore
    • NuGet 参照を解決する
  • dotnet build
    • MSBuild.exe を実行する
    • 内部で restore する (ソースコードしかない状態でも実行できる)
  • dotnet msbuild
    • MSBuild.exe と同じ引数を指定する
    • 内部で restore しない (ソースコードしかない状態では失敗)
  • dotnet publish
    • publish フォルダーに発行する
    • 内部で build する (ソースコードしかない状態でも実行できる)
  • dotnet pack
    • NuGet パッケージを作成する
    • 参照先の DLL は含まれず、依存関係が設定される
    • 内部で build しない (ソースコードしかない状態では失敗)
  • dotnet clean
    • 前回のビルド結果を消去する
    • restore の結果は残る
  • dotnet run
    • ソースコードからアプリを実行する
    • 内部で build する
  • dotnet App1.dll
    • ビルド済みのアプリを実行する

 

以下、詳細について記述していきます。

dotnet msbuild と msbuild

dotnet msbuild と msbuild の動作は同じです。

dotnet msbuild /p:Configuration=Release /t:Rebuild
msbuild /p:Configuration=Release /t:Rebuild

ただし、msbuild は環境変数の PATH に設定されていないため、
cmd や PowerShell で実行するにはそのパスを指定しなければなりませんが、
dotnet は PATH に設定されているため cmd や PowerShell でそのまま実行できて便利です。

アセンブリのビルド・発行

リビルドするには --no-incremental を指定します。

dotnet build -c Release --no-incremental

ただし build では、.NET Core を対象とする場合、NuGet 参照の DLL がコピーされません。
build では開発環境が想定されており、.dev.json ファイルに NuGet 参照が記述されます。
(.NET Framework を対象とする場合は NuGet 参照の DLL もコピーされます。)

配置用にすべての DLL を含めるには publish を使います。
プロジェクトに対象のフレームワークが複数ある場合、-f で一つだけ指定します。

dotnet clean -c Release
dotnet publish -c Release -f netcoreapp2.0

なお、publish 単独ではリビルドができないため、先に clean を実行しています。

NuGet パッケージ作成

出力先のディレクトリを変更するには -o を指定します。

dotnet pack -c Release -o pkg

または、

dotnet msbuild /p:Configuration=Release /t:pack

[構築時に NuGet パッケージを生成する] (.csproj では GeneratePackageOnBuild) を設定して build する方法もあります。

dotnet build -c Release --no-incremental

GeneratePackageOnBuild

 

前回: .NET Core と .NET Standard
次回: ASP.NET Core Web API の Tips

作成したサンプル

バージョン情報

  • .NET Core 2.0

参照

カテゴリー: .NET Core, .NET Framework, ツール. タグ: . 2 Comments »

ビルドして ZIP にする PowerShell スクリプト

以前に .NET ビルド小技集 (4) という記事を書き、
PowerShell でバージョンをインクリメントしてビルドする方法を紹介しました。
今回は、そのツールを改良したうえで NuGet で公開しました。

Visual Studio のプロジェクトに対して、NuGet で KTools.ZipRelease をインストールすると、
次の PowerShell ファイルがプロジェクトに追加されます。

  • KTools.VersionIncrement.ps1
  • KTools.ZipRelease.ps1

image

エクスプローラー上で PowerShell スクリプトを実行できます。

image

KTools.ZipRelease.ps1 により、以下の処理が実行されます。

  • バージョン番号のインクリメント (KTools.VersionIncrement.ps1 の呼び出し)
  • MSBuild.exe を利用して Release ビルド
  • ビルドの結果を ZIP ファイルにする
    • ファイル名は「AssemblyName-x.y.z.zip」の形式
    • 既定ではプロジェクト フォルダーの下の「zip」フォルダーに作成される

image

 

KTools.VersionIncrement.ps1 により、AssemblyInfo.cs 内の
AssemblyVersion 属性および AssemblyFileVersion 属性の値のビルド番号を 1 だけ増加させています。
例えば、1.0.2.0 が 1.0.3.0 に、1.0.2 が 1.0.3 に、1.0.2-alpha が 1.0.3-alpha に変わります。

ちなみに、バージョン番号は .exe および .dll ファイルのプロパティに反映されます。

image

PowerShell スクリプトのため、各自の要件に合わせてカスタマイズできるでしょう。
また、バージョン番号のインクリメントについては、
KTools.VersionIncrement として単独でインストールして使うことができます。

 

技術的には、以下の特徴が挙げられます。
ソースコードは Build Release (GitHub) にあります。

  • PowerShell の中で C# を利用
  • 値を変更する処理で正規表現を利用
  • .csproj ファイルからの値の読み込みに XPath を利用
  • MSBuild.exe のパスを探索 (たいへん)
    • .NET Framework 付属の MSBuild より Visual Studio 付属の MSBuild を優先

 

追記: ビルド用のスクリプトを Visual Studio の「外部ツール」に登録すると便利です。
また、.NET Core プロジェクト形式向けのビルド スクリプトも追加しました。

作成したツール
Build Release (GitHub)

参照
.NET ビルド小技集 (4)
.NET Framework の正規表現
.nuspec リファレンス
NuGet Package Version Reference

SIR 感染症モデルのシミュレーター

// この投稿は ライフゲーム Advent Calendar 2017 の 19 日目の記事です。

2017年11月11日の稲葉寿先生還暦記念祝賀研究集会「数理人口学・数理疫学・構造化個体群モデル」で講演してきました。
そのときに発表したシミュレーション ツールを紹介します。

数理生物学で「SIR 感染症モデル」という数理モデルがあり、

S(t): 未感染者の人口
I(t): 感染者の人口
R(t): 回復者の人口

としたとき、

S’ = θR – βSI
I’ = βSI – γI
R’ = γI – θR

のような微分方程式系で表されるものを指します。ここで、各定数は

β: 感染率
γ: 回復率
θ: 免疫喪失率

を表します (このように免疫喪失を考慮する場合は SIRS ともいう)。

このモデルをもとに、感染症の伝播を視覚的に表現するセルオートマトンを WPF で作成しました。
各ファイルは GitHub にあります。

EpidemicSimulator.exe を実行し、右下のトグルスイッチを押せばシミュレーションを開始できます。
感染率などのいくつかのパラメーターは実行時にリアルタイムに変更できます。

S と I が隣り合っているとき、一定の割合で感染が発生するようになっています (したがって、数式で表した状況とは厳密には異なります)。
また、I と R は一定の割合でそれぞれ R と S に移動します。

実行前に設定するパラメーター:

  • 高さ
  • SIR の初期人口比

実行中も設定できるパラメーター:

  • 感染率
  • 回復率
  • 免疫喪失率
  • Looping Map: マップの端でループするかどうか
  • ターンの時間間隔

Epidemic Simulator

 

実装方法については技術的に目新しいところはありませんが、特徴としては以下が挙げられます。

(1) シミュレーションの演算は UI スレッドではなく、バックグラウンド スレッドで実行
(2) 各フレームで画像データを生成して、Image コントロールで表示

(1) については、重い処理を UI スレッドで実行するとアプリケーションがフリーズしてしまうため、
各フレームで非同期的にデータのスナップショットを取得しています。
技術的な説明は、以前に

で書いた通りです。

バージョン情報
.NET Framework 4.5
ReactiveProperty 3.6.0
ToggleSwitch 1.1.2

参照
稲葉寿先生還暦記念祝賀研究集会「数理人口学・数理疫学・構造化個体群モデル」

カテゴリー: ツール, 数学. タグ: . Leave a Comment »