Uno.Wasm で mono-wasm AOT ビルドをする (2020/2 現在版)

## はじめに

[Uno Platform](https://platform.uno/) の Web Assembly 対応 (Uno.Wasm) では現在 AOT ビルド対応が進んでいます (現在使用できるテンプレートを普通に使っている場合はインタープリター実行になります) 。

Uno.Wasm のバックエンドである mono-wasm の AOT 対応は現在も開発中で、 AOT ビルドは一応可能ですが非常に面倒くさいです。

* [How to build WebAssembly C# Apps with the Mono AOT and Windows Subsystem for Linux](https://platform.uno/blog/how-to-build-webassembly-c-apps-with-the-mono-aot-and-windows-subsystem-for-linux/)

上記記事は Uno.Wasm で AOT ビルドをするための記事ですが、この通りに進めても作業を完遂することができませんでした。その後試行錯誤して AOT ビルドをやりきる事ができたので記事にまとめておきます。

ほぼ自分用備忘録で、各種開発ツールの状況が変わったりして通用しなくなる可能性もありますのでご了承ください。

上記記事は Windows 10 + WSL 環境となっていますが作業のほとんどは WSL (Linux) 環境で行います。純粋な Linux 環境でも Uno アプリのプロジェクト (.csproj) を用意すれば作業は行えるのではないかと思います。

## 事前準備

Visual Studio に Uno Platform のプロジェクトテンプレートをインストールし、 Uno のプロジェクトを作成してください。前回記事を参照してください。

* [Uno Platform のススメ](https://qiita.com/tan-y/items/b240252870a4d7aa9d34)

Wasm プロジェクトのビルドには "Uno.Wasm.Bootstrap" パッケージが必要になりますが、 **このバージョンが "1.0.10" (1.0 系) であることを確認してください。** バージョンが異なると今回の記事は通らなくなります (理由は後述) 。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211064/20acca4f-0dae-9505-0d1f-eb8106205318.png)

## 手順

差異だけの要点だけ記述します。実際はほとんど手順通りでいけますが、一番重要なところで躓きます。

### Setting the build environment under WSL

このタイトルのパートについて、

>Install ninja

まではそのまま行ってください。

>Install emscripten 1.38.28, with a mono patch

これですが、 "sdk-1.38.28-64bit" が存在しないのでインストールすることができません。代わりに Emscripten で通常入れる latest バージョンをインストールしようと

```
emsdk install latest
emsdk activate latest
```

とやりたくなりますが、これだと成功しません。現在 Emscripten は 1.39 系が最新となっていますが、 

* 1.39 は 1.38 と比較してツールのパラメーターなどで影響の大きい変更がされている (同じ手続きでは失敗する事がある)
* Uno.Wasm.Bootstrap 1.0 系の AOT は 1.38 に依存した実装になっている

という理由により 1.38 系を入れる必要があります。私が試した時点では下記手順で必要なものを install 及び activate しました。

```
emsdk install emscripten-1.38.31
emsdk activate emscripten-1.38.31
emsdk install fastcomp-clang-e1.38.31-64bit
emsdk activate fastcomp-clang-e1.38.31-64bit
emsdk install node-12.9.1-64bit
emsdk activate node-12.9.1-64bit
```

また、現在の Uno.Wasm.Bootstrap の master HEAD ("2.0.1-test.3" となっています) ではすでに Emscripten 1.39 系を前提としたコードになっており、 2.0 系が stable になった時に手順が変わることは確定的と思われます (現状のような面倒な手順のまま stable にすること自体がないとは思いますが・・・) 。

### .csproj の設定

.csproj のプロパティにパラメーターを追加する必要があります。

```xml
<PropertyGroup>
  <OutputType>Exe</OutputType>
  <!-- 略 -->
  <UseUnoXamlParser>true</UseUnoXamlParser>
  <WasmShellMonoRuntimeExecutionMode>InterpreterAndAOT</WasmShellMonoRuntimeExecutionMode>
</PropertyGroup>
```

"UseUnoXamlParser" と "WasmShellMonoRuntimeExecutionMode" を追加します。 WasmShellMonoRuntimeExecutionMode は "AOT" でもいけそうな気もしますが、確認していません。

必須ではないですが、 "WasmShellMonoTempFolder" というプロパティでテンポラリパスを指定できます。 Uno.Wasm.Bootstrap はテンポラリに mono-wasm の SDK の prebuilt バイナリをダウンロード、展開して作業を行いますが、二度目以降に展開済みパスが存在していると高い確率で (というか私の場合は確実に) 失敗するのでその場合は削除してリトライします。なのでテンポラリを専用パスに指定しておくと作業がしやすくなります。未指定時はシステムのテンポラリが使われます。

```xml
<PropertyGroup>
  <WasmShellMonoTempFolder>/mnt/d/Temp/UnoBuild</WasmShellMonoTempFolder>
</PropertyGroup>
```

### ビルド

記載の通りです。

1. WSL にログインして emsdk を使用できるようにする。
2. msbuild でビルドする。

ビルドは原因がよくわからず失敗する事が多発したので、 -verbosity オプションで詳細を出力するようにしていました (これで調べないと分らなかった事が多々ありました) 。

```
msbuild /r /p:Configuration=Release -verbosity:d | tee buildlog-unowasm.txt
```

## ビルド結果ついて

通常のインタープリター実行のビルドと異なり、出力された mono.wasm ファイルが明らかに大きいものになっており、 AOT ビルドはされているものと思われます。

実際に実行をしてみると動作は確認できましたが、 Chrome のデバッガで見てみると AOT の方は明らかにダウンロードサイズが大きく、動作速度面の確認はしていませんが、サイズだけを見ると AOT の方が不利な状況のようです。 AOT でも .NET アセンブリのダウンロードをしており (型情報のため？) 、単純に mono.wasm のファイルサイズが響いているように見えます。

* インタープリター
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211064/8e1f4018-b4f0-87fd-54fa-433201564875.png)
* AOT
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211064/33dddac5-2150-71cf-7cfb-a68ae326187b.png)

プロジェクトテンプレートで生成された初期コード ("Hello, world!" が出るだけ) の状態でのビルドです。 mono.wasm のサイズ差がそのまま出ている感じです。 .clr ファイルは .NET アセンブリ (.dll) そのものです。ファイルサイズ的にはインタープリターと AOT とでほぼ同じ .clr が配置されています。

AOT は開発中で現時点で評価する事にあまり意味はないですので、今後の改善に期待というところでしょうか。

## おわりに

mono-wasm の AOT は軽い気持ちで調べ始めたのですが、深い沼状態でなかなか大変です。現時点では簡単に試すというわけもいかなさそうです。

mono-wasm SDK 「だけ」で AOT ビルドするのはかなり面倒くさそうですが、 Uno.Wasm.Bootstrap はまだ面倒なものの一応ほぼ自動で一通りのビルドがまわせるので、現状で mono-wasm AOT を試すなら Uno.Wasm を使うのが一番確実かと思われます。