PowerShellをコードリーディングするためにデバッグ環境まで整える

[PowerShell](https://github.com/PowerShell/PowerShell)をコードリーディングをするために環境構築で行ったことを書いておきます。

PowerShellはv5まではOSSではなかったのですが、
2016年2月頃からOSSとなり、GitHub上でソースコードが公開されるようになっています。

この記事で書いている内容は同リポジトリ内にある[Build PowerShell on Windows for .NET Core](https://github.com/PowerShell/PowerShell/blob/master/docs/building/windows-core.md)とほぼ同じ内容です。

macOSやLinux用の環境構築手順書も提供されているので、適宜ご参照ください。

# 必要となる環境
以下、Windows用に必要となるツール一覧です。（他のOSでは未検証）

- Git
- pwsh 6（PowerShell v5でもたぶん可能）
- Visual Studio Code、または、Visual Studio 2019（2017でもたぶん可能）
  ※ Visual Studioの場合、「.NET デスクトップ開発」「.NET Core クロスプラットフォームの開発」のワークロードがインストール済みであること。

# リポジトリのclone

では、ソースコードをcloneします。

```powershell
cd ~/Repos/
git clone "https://github.com/PowerShell/PowerShell.git"
cd PowerShell
```

# チェックアウト

次にコードリーディングしたいバージョンを`git tag`で表示されるタグの中から選んでいきます。
一つ一つのタグについての説明は[Releases・PowerShell/PowerShell - GitHub](https://github.com/PowerShell/PowerShell/releases)に記載されています。

そのままmasterブランチをコードリーディングすると、あとでバージョンの更新があった場合、
自分の読んだことのあるバージョンからどういう変更が入っているのか追いづらくなってしまうため、
コードリーディングするときはいつもタグでcheckoutして読むようにしています。

```powershell
git tag
#=>...
#=>v6.2.1
#=>v6.2.2
#=>v7.0.0-preview.1
#=>v7.0.0-preview.2
#=>v7.0.0-preview.3

git checkout v6.2.2
```

今回は直近にリリースされた正規リリース版であるv6.2.2を読んでいきます。
※v7.0.0-preview.3も試そうとしましたが、上手くビルドできなかったので諦めました。（自分の環境だけ？）

`cloc`で算出されるプロジェクトの規模としては以下のようになります。
総コード行数は100万を突破してました。けっこう大規模ですね。

```bash
github.com/AlDanial/cloc v 1.82  T=18.00 s (122.6 files/s, 79339.3 lines/s)
-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
C\#                           1369         104612         191513         511145
XML                             37           8470            674         335512
JSON                            81             10              0         147721
PowerShell                     478          15086           7113          79530
Markdown                        73           2066              0           7257
XSD                             49              2            396           6283
WiX source                       3             19             58           3195
YAML                            32            360             71           2161
Bourne Shell                    14            205            190            968
SVG                              7             26             89            956
MSBuild script                  29             99             19            697
Dockerfile                      20            101             73            440
XSLT                             2             44             55            292
Razor                            8             56              0            236
Pascal                           1              0              0            201
JavaScript                       1              7              2             51
RobotFramework                   1              0              0             26
Python                           1              3              1             15
DOS Batch                        1              0              0              1
-------------------------------------------------------------------------------
SUM:                          2207         131166         200254        1096687
-------------------------------------------------------------------------------
```

# ビルド

まず、ビルドに必要となる`dotnet`コマンドをプロジェクトから提供されている以下のコマンドでインストールします。
コマンド実行後はインストールされた`dotnet`コマンドへPATHが追加されます。

※現在のセッション上ではPATHが追加されますが、一度セッションを終了すると消えるので、
　Visual Studio上でデバッグなど実行したい場合は、`%LOCALAPPDATA%\Microsoft\dotnet`をPATHへ追加しておく必要があります。

```powershell
Import-Module ./build.psm1
Start-PSBootstrap
```

こちらのコマンドで、ビルド＆実行に必要となる.NET Coreのバージョンとpwsh、[rcedit](https://github.com/electron/rcedit)がインストールされ、必要に応じてPATHに追加されます。

次にビルドします。

```powershell
Start-PSBuild -Clean
```

以上でビルドまで完了できました。
これで既にVisual Studio CodeやVisual Studioでデバッグ実行することが可能となります。

# デバッグ

VS CodeとVisual Studioとでそれぞれデバッグ実行できるか試していきます。

## Visual Studio Code

VS Codeの場合、PowerShellのプロジェクトのフォルダを開き、
そのままデバッグ実行（F5）をすれば、デバッグが開始されます。

「このワークスペースには拡張機能の推奨事項があります。」と表示される場合は、
「推奨事項を表示」をクリックしあらかじめインストールしておいてください。

launch.json内で設定されてあるオプションに`stopAtEntry`が`true`となっているので、
ブレークポイントを貼っておかなくても、最初のMainメソッドの開始ブロックで止まってくれるようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114728/67f48b7b-cc31-27c7-9cef-8dd6b43aa6a6.png)

## Visual Studio

Visual Studioでは、以下のようにメニューを選んで実行することになります。
今回はWindowsでの実施なのでエントリポイントがpowershell-win-coreプロジェクトになってますが、
ここはWindows以外であれば、powershell-unixプロジェクトとなります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114728/897ef4cf-a274-e860-44c3-b5beea84f59b.png)

「デバッグ実行」をクリックすると、ビルドが始まり以下のダイアログボックスが表示されました。
どうやら、`build.psm1`の`Start-PSBuild`でないとそのままではビルドできないようです。

これまでの手順でコマンド経由でビルドはできているので、「はい」を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114728/5f6118ef-963b-9065-7e7c-f1b7ee77c528.png)

すると、VS Codeと同様、デバッグ実行が開始できました。
※Visual Studioの場合は、ブレークポイントをステップ実行を開始したい場所に貼っておく必要があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114728/6505ed27-2faf-c00f-bd07-025f452246c3.png)

# 遭遇したエラー

以下、遭遇したエラーです。

## 依存するPowerShellモジュールが`~\.nuget`配下にないケース

```powershell
Restore PowerShell modules to C:\Users\xxxxx\Repos\PowerShell\src\powershell-win-core\bin\Debug\netcoreapp2.1\win7-x64\publish
Name='PowerShellGet', Version='2.1.3', Destination='C:\Users\xxxxx\Repos\PowerShell\src\powershell-win-core\bin\Debug\netcoreapp2.1\win7-x64\publish\Modules'
Copy-Item : パス 'C:\Users\xxxxx\.nuget\packages\powershellget\2.1.3' が存在しないため検出できません。
発生場所 C:\Users\xxxxx\Repos\PowerShell\build.psm1:2353 文字:9
+         Copy-Item -Exclude $dontCopy -Recurse $src/* $dest
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (C:\Users\xxxxx...rshellget\2.1.3:String) [Copy-Item], ItemNotFoundException
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.CopyItemCommand
```

実行すると`.\src\powershell-win-core\bin\Debug\netcoreapp2.1\win7-x64\publish\pwsh.exe`に実行ファイルが生成されるところまでは正常に完了しましたが、
後続の処理でエラーが起きたようです。

`build.psm1`の以下の箇所に`-Force`を追加すれば、ひとまず解決できました。
バグなのかは不明です。

```powershell
function Restore-PSModuleToBuild
{
    ...
    Copy-PSGalleryModules -Destination $modulesDir -CsProjPath "$PSScriptRoot\src\Modules\PSGalleryModules.csproj" -Force
```

# 参考
[PowerShell Core 6.0をデバッグ実行する - Visual Studio Code編 - しばたテックブログ](https://blog.shibata.tech/entry/2017/12/11/000500)
[Build PowerShell on Windows for .NET Core - GitHub](https://github.com/PowerShell/PowerShell/blob/master/docs/building/windows-core.md) @v6.2.2