しばやん雑記

Azure Serverless とメイドさんが大好きなフリーランスのプログラマーのブログ

.NET 6.0 にアップグレードすると ReadyToRun ビルドが失敗するようになったのを直す

.NET Core 3.0 からビルド時に Tier 0 のネイティブコードを生成する ReadyToRun が利用可能になっています。実行時に Tier 0 のコード生成を行わない分、スタートアップの高速化が期待できる機能です。

.NET 6.0 は Windows x64 環境なら全てのクロスコンパイルも出来るので、使い勝手がよくなっています。

起動速度が重要となるデスクトップアプリケーションでは基本的に ReadyToRun は有効化するようにしているのですが、WPF を使って MSIX でパッケージングしているアプリケーションを .NET 6.0 に移行すると GitHub Actions で行っているビルドが通らなくなりました。

使用しているのは Windows Server 2022 環境なので x64 の .NET SDK が使われているのですが、何故か R2R ビルドがサポートされないプラットフォームと言われてしまいます。

f:id:shiba-yan:20220211002057p:plain

error NETSDK1095: Optimizing assemblies for performance is not supported for the selected target platform or architecture. Please verify you are using a supported runtime identifier, or set the PublishReadyToRun property to false.

エラーメッセージにあるように PublishReadyToRunfalse にすればビルドが通るようになりますが、出来る限り有効化した状態で WPF アプリケーションはリリースしたいです。

最初は GitHub Actions 固有の問題かと思っていましたが、調べていると以下の Issue が見つかりました。どうやら .NET 6.0 から ReadyToRun ビルドを行うときの挙動が変更になっていたようです。

少し前に Breaking change としてドキュメントも公開されていました。.NET 6.0 からは ReadyToRun ビルドを行う際には復元時にも PublishReadyToRun プロパティを指定して、必要となる NuGet パッケージ*1をインストールする必要があるようです。

従って GitHub Actions や MSIX に関係なく、シンプルな WPF アプリケーションでもビルド・発行時に --no-restore を指定していると発生します。回避方法は dotnet restore 時に PublishReadyToRun を指定するか、dotnet publish で暗黙的に NuGet パッケージの復元を実行する必要があります。

.NET CLI で ReadyToRun ビルドを行う場合は以下のどちらかになることを覚えておけば良さそうです。

# --no-restore を指定する場合は restore 時に R2R を使う宣言をしないと crossgen2 がインストールされない
dotnet restore -p:PublishReadyToRun=true
dotnet publish -c Release --no-restore

# --no-restore を付けなければ自動で restore が行われるので問題なく動作する
dotnet publish -c Release -p:PublishReadyToRun=true

大きなソリューションでは最初に一回だけ dotnet restore を実行するようにして、暗黙的な復元を無効化した方がビルド時間が短縮できるので、--no-restore を使っているケースもあると思います。その場合は R2R との組み合わせに注意が必要です。

.NET CLI を使ったビルドの場合はここまでの方法で問題ないのですが、MSIX を使ったデスクトップアプリケーションのパッケージングには Visual Studio とインストールされた MSBuild が必要になります。具体的には wapprojdotnet msbuild を使ってもビルドは行えません。

最初に .NET CLI を使って dotnet restore だけ実行すれば良さそうではありますが、それもエラーになって上手く動きませんでした。なので MSBuild で NuGet パッケージの復元を行うことで対応します。

MSBuild は .NET CLI のように自動的に復元は実行してくれないですが -restore オプションを指定すると、ビルド前に NuGet パッケージの復元を行ってくれるようになります。その際に PublishReadyToRun を指定することで、必要なパッケージのインストールが行われるようになります。

# -restore を付けると dotnet build/publish のように自動で restore が行われる
msbuild -restore -p:Configuration=Release -p:PublishReadyToRun=true

Windows App SDK を使ったプロジェクトなど、最近の新しいプロジェクトでは各 RID 向けの Publish Profile が作成されて、それを wapproj から参照するようになっていますが、復元時にはその設定が考慮されないので個別に R2R の設定を指定しています。

実際にプロジェクトで使っている GitHub Actions のワークフローを修正した PR が以下になります。

GitHub 上で新しく Release を作成して MSIX のビルドを行わせると、ReadyToRun が有効な状態でも問題なくビルドが完了するようになりました。ARM64 向けの R2R ビルドも問題なく実行されています。

f:id:shiba-yan:20220211002212p:plain

原因が分からなかったので、一旦はローカル開発環境でビルドしたものをストアに申請していて非常に面倒でしたが、これで .NET 6.0 でも GitHub Actions を使った MSIX の自動化が利用出来るようになりました。

*1:crossgen2 が含まれているパッケージ