ぷろじぇくと、みすじら。

Blazor Web Assembly を publish すると AssemblyResolutionException でエラーとなる

Created at:

現象

Visual Studio からの発行や dotnet publish -c Release などを実行したとき、IL Linker 実行中に以下のような AssemblyResolutionException がスローされます。

  Fatal error in Mono IL Linker
C:\Users\Tomoyo\.nuget\packages\microsoft.aspnetcore.components.webassembly.build\3.2.1\targets\Blazor.MonoRuntime.targets(326,5): error : Unhandled exception. Mono.Cecil.AssemblyResolutionException: Failed to resolve assembly: 'SQLitePCLRaw.core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1488e028ca7ab535' [C:\Users\Tomoyo\Source\Repos\BlazorApp6\BlazorApp6\BlazorApp6.csproj]
   ---> Mono.Cecil.AssemblyResolutionException: Failed to resolve assembly: 'SQLitePCLRaw.core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1488e028ca7ab535'
     at Mono.Cecil.BaseAssemblyResolver.Resolve(AssemblyNameReference name, ReaderParameters parameters)
     at Mono.Linker.AssemblyResolver.Resolve(AssemblyNameReference name, ReaderParameters parameters)
     at Mono.Linker.LinkContext.Resolve(IMetadataScope scope)
     at Mono.Linker.LinkContext.Resolve(IMetadataScope scope)
     at Mono.Linker.LinkContext.ResolveReferences(AssemblyDefinition assembly)
     at Mono.Linker.Steps.LoadReferencesStep.ProcessReferences(AssemblyDefinition assembly)
     at Mono.Linker.Steps.LoadReferencesStep.ProcessAssembly(AssemblyDefinition assembly)
     at Mono.Linker.Steps.BaseStep.Process(LinkContext context)
     at Mono.Linker.Pipeline.ProcessStep(LinkContext context, IStep step)
     at Mono.Linker.Pipeline.Process(LinkContext context)
     at Mono.Linker.Driver.Run(ILogger customLogger)
     at Mono.Linker.Driver.Execute(String[] args, ILogger customLogger)
     at Mono.Linker.Driver.Main(String[] args)

原因

IL Linker は依存しているアセンブリ参照をすべて検索して、不要な IL を削っていくというビルドステップです。

この例外(エラー)は依存先のアセンブリを探しているときにアセンブリが見つからなかった場合に発生するもので、通常 NuGet でパッケージ参照されているものに対しては発生しません。

しかし依存パッケージの作り次第では例外が発生する場合があります。例えば Microsoft.CodeAnalysis.Workspaces.Common のようなパッケージを参照すると発生します。

これは Microsoft.CodeAnalysis.Workspaces.CommonSQLitePCLRaw.bundle_green パッケージを PrivateAssets="all" として参照しているため、実際のパッケージにはパッケージ参照として SQLitePCLRaw.bundle_green が含まれないことでアプリケーションから参照したときにパッケージが解決されないのでアセンブリが見つからないということが発生します。

つまりパッケージの依存としては扱わないもののアセンブリを参照はあるという状況で発生します。必須ではないパッケージなどの場合にはこういった構成になります。

解決方法

解決方法としては次の2つの方法があります。

1. 必要なパッケージをプロジェクトで参照する

見つからないといわれているアセンブリが含まれているパッケージをプロジェクトから参照します。

2. IL Linker を無効にする

SQLitePCLRaw.bundle_green のようなものは参照してもエラーとなるのでそういった場合には IL Linker 自体を無効にします。

https://docs.microsoft.com/ja-jp/aspnet/core/blazor/host-and-deploy/configure-linker?view=aspnetcore-3.1

<PropertyGroup>
  <BlazorWebAssemblyEnableLinking>false</BlazorWebAssemblyEnableLinking>
</PropertyGroup>

ARM64 Windows 版 Visual Studio Code をビルドする

Created at:

Surface Pro X も発売され早数か月、今や多くの方が ARM64 版 Windows をご利用中かと思いますが(要出典)、Visual Studio Code の ARM64 版はまだリリースされていない状況です。もちろん x86 版をエミュレーションで利用できますが、Electron アプリはパフォーマンス的にかなり不利ですので ARM64 ネイティブなものが欲しいところです。

Visual Studio Code のリポジトリで ARM64 対応がないかなと眺めていたところ ARM64 ビルドをやっていく PR が作られているのを発見し、マージされるのを watch していたのですが先日ついにマージされました。

Add gulp targets, fix build for Windows on Arm. by richard-townsend-arm · Pull Request #85326 · microsoft/vscode

というわけで自分で VSCode リポジトリからビルドしてみたところ、手順が意外とわかりにくかったのでまとめておきます。

ビルド準備 (ツール)

大体は vscode リポジトリの今トリビュートガイドのビルド手順に従って環境を用意します。

まず、クロスコンパイルする形になるので x64 の Windows 環境が必要になります。もしかするとコンパイラーなどは動くかもしれませんが死ぬほど遅いでしょうし、Node.js もメモリーの都合なのか x64 版を用意するように書かれているので素直に x64 環境でビルドするのがよいでしょう(AzureやAWSで適当にVM立ててビルドするとか)。

Python とコンパイラーは npm install -g windows-build-tools でインストールできます。VSCode のガイドには --vs2015 を指定して Visual Studio 2015 で…のようなことが書いてありますがそのまま Visual Studio 2017 でもビルドできます。

コンパイラーは windows-build-tools ではなく Visual Studio 2017 Build Tools を入れることでも大丈夫です。Visual Studio 2019 の場合でも 2017 のコンパイラーが入っていればビルドできそうな気がします。

さらに ARM64 版ビルドを作る場合には Visual Studio Installer から個別のコンポーネントとして Visual C++ compilers and libraries for ARM64 というものを入れておく必要があります。

Visual C++ 2010 Redistributable (x86) も必要です。これはビルド途中で使われるリソース書き換えツールの rcedit を動かすのに必要です。

ビルド準備 (Node)

ツールがそろったらコードを clone して、yarn でモジュールをインストールします。

git clone https://github.com/microsoft/vscode.git

と、yarn でモジュールをインストールする前にビルド対象のアーキテクチャを環境変数で設定しておきます。

cd vscode
set npm_config_arch=arm64
set npm_config_target_arch=arm64

設定したら yarn を実行します。

yarn install

すると多分途中で %USERPROFILE%\AppData\Local\node-gyp\Cache\12.4.0\arm64\node.lib がないというようなエラーになるかと思います。

node-gyp で使われる lib がないということなので次の場所からダウンロードしてきて放り込みます。

https://unofficial-builds.nodejs.org/download/release/v12.15.0/win-arm64/

本当はバージョンを合わせるべきな気がしますが、必ずしも同じバージョンのバイナリがあるわけではないのでそれっぽいバージョンを放り込みます。

ファイルを置いたらもう一度 yarn install を実行すると最後まで通るはずです。

ビルド準備 (VSCode)

ここまできたらあとはビルドするだけですが、その前に少し VSCode のビルド設定を変更しておきます。

まず前提として VSCode リポジトリは Microsoft からリリースされている Visual Studio Code そのものではないのです。

VSCode リポジトリは Code - OSS というオープンソースな Visual Studio Code 部分です。一方 Microsoft がバイナリでリリースしている Visual Studio Code は Code - OSS にブランディングや Marketplace のエンドポイント設定、テレメトリーの有効化などのカスタマイズを行って、プロプラエタリなライセンスでリリースしているものとなっています。Chromium と Google Chrome とかも似たような感じかもしれません。

つまり Code - OSS そのままだと Marketplace を使えないので、API エンドポイントを設定してあげます。そのあたりは VSCode リポジトリを元にコミュニティーバイナリビルドを作っている VSCodium を参考にして product.json を編集します。

diff --git a/product.json b/product.json
index 075a1d0ada..093f517644 100644
--- a/product.json
+++ b/product.json
@@ -20,6 +20,9 @@
        "licenseFileName": "LICENSE.txt",
        "reportIssueUrl": "https://github.com/Microsoft/vscode/issues/new",
        "urlProtocol": "code-oss",
+       "quality": "stable",
+       "extensionAllowedBadgeProviders": ["api.bintray.com", "api.travis-ci.com", "api.travis-ci.org", "app.fossa.io", "badge.fury.io", "badge.waffle.io", "badgen.net", "badges.frapsoft.com", "badges.gitter.im", "badges.greenkeeper.io", "cdn.travis-ci.com", "cdn.travis-ci.org", "ci.appveyor.com", "circleci.com", "cla.opensource.microsoft.com", "codacy.com", "codeclimate.com", "codecov.io", "coveralls.io", "david-dm.org", "deepscan.io", "dev.azure.com", "flat.badgen.net", "gemnasium.com", "githost.io", "gitlab.com", "godoc.org", "goreportcard.com", "img.shields.io", "isitmaintained.com", "marketplace.visualstudio.com", "nodesecurity.io", "opencollective.com", "snyk.io", "travis-ci.com", "travis-ci.org", "visualstudio.com", "vsmarketplacebadge.apphb.com", "www.bithound.io", "www.versioneye.com"],
+       "extensionsGallery": {"serviceUrl": "https://marketplace.visualstudio.com/_apis/public/gallery", "cacheUrl": "https://vscode.blob.core.windows.net/gallery/index", "itemUrl": "https://marketplace.visualstudio.com/items"},
        "extensionAllowedProposedApi": [
                "ms-vscode.references-view"
        ],

ビルド

準備ができたらビルドを実行してコンパイルして配布物一式を作成します。

set NODE_ENV=production
yarn gulp vscode-win32-arm64
yarn gulp vscode-win32-arm64-archive

vscode-win32-arm64-archive を実行すると .build\win32-arm64\archive\VSCode-win32-arm64.zip という ZIP ファイルが出来上がります。

というわけでこの一式を ARM64 環境へもっていって展開すれば Visual Studio Code 的なものを使えます(インストーラーは2020年4月現在ビルドできません)。

ARM64 版の制約

Microsoft Visual Studio Code との違い

ビルドの準備の途中でも書きましたが、手元でビルドしたものは Microsoft からリリースされるものとは異なります。

名前が違うこともあり設定は Visual Studio Code とは別の場所に保存されるものになります(つまり公式リリースが出た場合でも設定は別になります)。

ライセンスが違うのが実はちょっと罠なので注意が必要です。というのも Microsoft がリリースしている VSCode 拡張のライセンスは Microsoft 公式から配布されている Microsoft Visual Studio Code とともに使うことが許可されているものがあります(例えば Remote とか)ので注意してください。