How to build WebAssembly C# Apps with the Mono AOT and Windows Subsystem for Linux
By: Uno Platform
Tags: Csharp, Mono, Linux, Ubuntu, Webassembly

How to build WebAssembly C# Apps with the Mono AOT and Windows Subsystem for Linux Uno Platform Mar 12, 2019·3 min read

Microsoft’s steady progress on WebAssembly gives an opportunity to test a lot of the new features regarding the payload size and performance balance.

Using the AOT provides a significant increase in performance over the mono interpreter, between 30x to 50x depending on the browser. This enables application such as the Uno Gallery or the Xaml Controls Gallery to run at very interesting performance.

Since the introduction of AOT, we noticed that the Wasm payload is particularly large. This is caused by multiple reasons, such as the fact that an LLVM backend is not used to generate WebAssembly or that some indirect calls are not supported by current Emscripten builds.

For those reasons, and a few others, the work that the Mono team is currently doing to support a mixed AOT/Interpreter Mode is going to play an important role to strike a good balance between payload size and performance.

At present time, the mono-wasm AOT tool chain is only compatible with Linux, and even though it will eventually support other platforms, it is required to setup a Linux machine or use the excellent Windows 10 Subsystem for Linux.

For those of you that have yet to experience WSL, Microsoft has added the ability for Windows to run native 64-Bits Linux binaries, untouched, non-virtualized. This means that most of the linux-compatible tooling, including mono, is able to run properly as if it were running on top of an actual Linux kernel.

We’re going to setup a WSL environment to build an Uno Platform application using Mono WebAssembly AOT.

Setting the build environment under WSL

Here’s what to do to, only once:

Install WSL on a Windows 10 box

Install Ubuntu 18.04 or later

Install a stable Mono toolchain

Run the following to add a new apt source :

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF

echo "deb https://download.mono-project.com/repo/ubuntu stable-bionic main" | sudo tee /etc/apt/sources.list.d/mono-official-stable.list

sudo apt update

Install python, mono and msbuild

sudo apt install python mono-devel msbuild

wget -q https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb

sudo dpkg -i packages-microsoft-prod.deb

sudo add-apt-repository universe

sudo apt-get install apt-transport-https

sudo apt-get update

sudo apt-get install dotnet-sdk-2.2

Install ninja

sudo apt install libc6 ninja-build

Install emscripten 1.38.28, with a mono patch

cd ~

git clone https://github.com/juj/emsdk.git

cd emsdk

./emsdk install sdk-1.38.28-64bit

./emsdk activate sdk-1.38.28-64bit

Build an Uno Platform WebAssembly head app

For each shell you’re opening afterwards, you’ll have to do the following:

Activate emscripten:

source ~/emsdk/emsdk_env.sh

Create an Cross-platform Application using the Uno Platform VSIX, follow the getting started guide for more details.

Make sure that the following property is added in the Wasm csproj :

<WasmShellEnableAOT Condition="$([MSBuild]::IsOsUnixLike()) and '$(Configuration)'=='Release'">true</WasmShellEnableAOT>

Navigate to the Wasm folder of your application using the wslpath utility:

cd `wslpath "C:\Users\my_user\source\repos\MyApp\MyApp.Wasm"`

Build the application:

msbuild /r /p:Configuration=Release

Serve the resulting application

cd `wslpath "C:\Users\my_user\source\repos\MyApp\MyApp.Wasm\bin\Release

etstandard2.0\dist\"`

python3 server.py

The application will be available at http://localhost:8000

You’ll notice that the mono.wasm is roughly between 20MB and 30MB, depending on the linker settings you've set, and using packages like Json.NET can significantly increase the binary size.

You’ll also probably notice that the build time can get pretty long, and the most time consuming step is Emscripten. This is known by the Mono team and will certainly be worked on in the future.

We’ll be discussing the use of the Mixed mode runtime in a next blog post, so we can get a performance/size balance for the generated binary.

Let us know what you think!