How to Build ZXing C++ to Wasm using WASI SDK in Linux

WASI is a modular system interface, which aims to build runnable .wasm modules for any WASI-compliant runtime, not only for Node.js and web browsers. Although WASI is still in development and not stable yet, it is available for some experimental projects. In this article, I will share how to use WASI SDK to build a .wasm barcode reader module by porting ZXing C++.

Installing WASI Runtime

Both Wasmer and Wasmtime support WASI.

Wasmer

curl https://get.wasmer.io -sSfL | sh

Wasmtime

Get the source code and build wasmtime with Rust compiler.

Setting Up WASI Building Environment

Let’s get started with wasi-libc.

To build WASI Libc, you have to use clang 8 or above. If you don’t have clang installed before, you may find the latest clang version is not listed in the apt software repositories.

install clang in debian

The workaround is to add “deb http://deb.debian.org/debian/ testing main” to “/etc/apt/sources.list” and update the sources:

sudo apt update

Then the latest clang 9 is visible:

install clang 9 in debian

Install clang 9 and create relevant symlinks:

sudo apt install clang-9
sudo ln -s /usr/bin/clang-9 /usr/bin/clang
sudo ln -s /usr/bin/clang++-9 /usr/bin/clang++

Now we can build WASI Libs from the source code.

If you just want to use the toolchain, a more convenient way is to install wasi-sdk:

sudo dpkg -i wasi-sdk_7.0_amd64.deb
export PATH=/opt/wasi-sdk/bin:$PATH
export CC=/opt/wasi-sdk/bin/clang
export CXX=/opt/wasi-sdk/bin/clang++

Create a ‘hello world’ program for the test:

#include <stdio.h>

int main()
{
    printf("hello wasi libc\n");
    return 0;
}

Build the code:

$ clang - target=wasm32-wasi - sysroot=/opt/wasi-sdk/share/wasi-sysroot/ test.c -o test.wasm

Run the app:

wasmer run test.wasm
wasmtime test.wasm

Porting ZXing C++ for WASI SDK

Get the source code of zxing-cpp.

Since the current WASI libc does not yet support C++ exceptions, we need to add -fno-exceptions to CMakeLists.txt:

set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} - target=wasm32-wasi -Wall -Wextra -fno-exceptions")

In addition, set sysroot as follows:

set (CMAKE_SYSROOT /opt/wasi-sdk/share/wasi-sysroot)

To pass the build, I have disabled all C++ exception-relevant code and adjusted the project structure.

Build the project to a wasm file:

mkdir build
cd build
cmake ..
cmake --build .

Run the app under the build folder:

qr code
$ wasmer run zxing_barcode_reader.wasm - dir=$(pwd)/../ $(pwd)/../test.png
Text: MEBKM:URL:http\://en.wikipedia.org/wiki/Main_Page;;
Format: QR_CODE
Position: 190x367 205x162 422x165 405x342
EC Level: M

$ wasmtime zxing_barcode_reader.wasm - dir=$(pwd)/../ $(pwd)/../test.png
Text: MEBKM:URL:http\://en.wikipedia.org/wiki/Main_Page;;
Format: QR_CODE
Position: 190x367 205x162 422x165 405x342
EC Level: M

How to Use Wapm to Publish and Run the Wasm File

Generate a wapm.toml file with init command:

$ wapm init zxing_barcode_reader

Edit wapm.toml:

[package]
name = "yushulx/zxing_barcode_reader"
version = "0.1.4"
description = "A barcode reader app built with ZXing C/C++ and wasi-sdk"
readme = "README.md"
repository = "https://github.com/yushulx/wasi-zxing-wasm"

[[module]]
name = "zxing_barcode_reader"
source = "dist/zxing_barcode_reader.wasm"
abi = "wasi"

[[command]]
name = "zxing_barcode_reader"
module = "zxing_barcode_reader"

Publish the package to wapm.io:

$ wapm login
$ wapm publish

Install the package via wapm and read barcodes from a PNG image:

$ wapm install yushulx/zxing_barcode_reader
$ wapm run zxing_barcode_reader --dir=. test.png
zxing barcode reader built with wasi

Related Articles

My First Wasmtime Experience in Windows 10

Reference

https://github.com/CraneStation/wasmtime/blob/master/docs/WASI-tutorial.md

Source Code

https://github.com/yushulx/wasi-zxing-wasm