WebAssembly "Hello World"

Use the latest Clang compiler to create your first 'Hello World' WebAssembly module.

Pre-requisites

To compile and produce our first WebAssembly module, we'll be using Docker. Docker makes it super easy to run a pre-packaged virtual machine like environment which enables you to install all manner of packages without affecting your main system.

You will also need to be comfortable with using the command line as some of the steps require you to run commands.

Naturally, you will also need a browser capable of running WebAssembly to test out our WebAssembly module. Any post-2017 browser should work.

Preparing to compile your first WebAssembly module

Using Docker, we'll spin-up a Debian Linux host image (debian:stretch) with the latest CLang compiler that will enable us to produce our WebAssembly module from source code.

Before we do anything, let's first create a webassembly_helloworld directory in our local system for our project. Start a terminal (or command prompt) and run the following commands:

mkdir webassembly_helloworld 
cd webassembly_helloworld

For your convenience, I have prepared all the source files we'll be using in this guide in this zip file . You can download this file and extract its contents or create the files manually by following the next steps.

Our 'Hello World' WebAssembly module's source code

We'll need some source code that can be compiled into our "Hello World" WebAssembly module. So, let's create a helloworld.c source file in the webassembly_helloworld directory with the following contents:

#include <stdio.h>
#include <errno.h>

int main(int argc, char **argv) {

    fprintf(stdout, "Hello World from your first WebAssembly module!\n");
    return 0;
}

Configuring a Debian Linux host to on Docker to compile WebAssembly

We have our source file, now we need a way of compiling this into WebAssembly. As mentioned above, we'll start a Debian Linux host (using Docker) that will have everything setup to compile WebAssembly.

To setup the Debian Linux image with everything we need, create a new Dockerfile file in the webassembly_helloworld directory with the following contents:

FROM debian:stretch-20190506-slim
RUN apt-get update \ 
   && apt-get install -y curl gnupg xz-utils

RUN curl -O https://apt.llvm.org/llvm-snapshot.gpg.key \
   && apt-key add llvm-snapshot.gpg.key \
   && echo  "deb http://apt.llvm.org/stretch/ llvm-toolchain-stretch-9 main" >> /etc/apt/sources.list \
   && echo "deb-src http://apt.llvm.org/stretch/ llvm-toolchain-stretch-9 main" >> /etc/apt/sources.list \
   && apt-get update \
   && apt-get -y install clang-9 lld-9

RUN mkdir /wasi-src ; cd /wasi-src \ 
   && curl -O -L "https://github.com/CraneStation/wasi-sdk/releases/download/wasi-sdk-5/wasi-sdk-5.0-linux.tar.gz" ; mkdir wasi-sdk ; cd wasi-sdk ; tar -zxvf ../wasi-sdk-* 

RUN cd /wasi-src \
   && curl -O -L "https://github.com/CraneStation/wasi-sysroot/releases/download/v0.1-alpha/wasi-sysroot.tar.xz" ; mkdir wasi-sysroot ; cd wasi-sysroot ; tar -xvf ../wasi-sysroot.*

RUN update-alternatives --install /usr/bin/clang clang /usr/bin/clang-9 100 \
   && update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-9 100 \
   && update-alternatives --install /usr/bin/wasm-ld wasm-ld /usr/bin/wasm-ld-9 100

RUN dirname `clang --target=wasm32-wasi -print-libgcc-file-name` | xargs mkdir \
    && clang --target=wasm32-wasi -print-libgcc-file-name | xargs cp /wasi-src/wasi-sdk/wasi-sdk-5.0/opt/wasi-sdk/lib/clang/8.0.0/lib/wasi/libclang_rt.builtins-wasm32.a 

CMD ["/bin/bash"]
WORKDIR /src

With the Dockerfile file ready to go, We are now ready to prepare what we'll need to compile WebAssembly in the Debian Linux host.

Putting it all together

From the webassembly_helloworld directory, let's prepare the WebAssembly build host by running the following command:

docker build -t webassemblytutor_helloworld .

After running the above command, Docker will download a pre-packaged debian:stretch host image and setup everything we need to compile WebAssembly. Once the command has completed, let's start the Debian Linux host with the following command:

# start Docker in interactive mode
# map our local machine directory (webassembly_helloworld ) to the /src directory in our Debian Linux host
# ensure permissions inside the Debian Linux host are the same as the running machine
# use our shiny new webassemblytutor_helloworld we prepared in the previous step
# start a bash shell for us to enter our compile commands
docker run \
    -it --rm \
    -v $PWD:/src \
    --user root -e NB_UID=$UID -e NB_GID=$GID \
    webassemblytutor_helloworld \
    /bin/bash

Yes! We're now ready to compile our first WebAssembly module

Now we are ready to compile our "Hello World" WebAssembly module. Let's compile the helloworld.c we created above with the following command:

clang --target=wasm32-wasi --sysroot=/wasi-src/wasi-sysroot/sysroot helloworld.c -o helloworld.wasm

Finally, let's test our shiny new WebAssembly module with the WASI web polyfill tester at https://wasi.dev/polyfill.

Click choose file and navigate to the webassembly_helloworld directory we created on the very first step and select the helloworld.wasm file.

You should see "Hello World from your first WebAssembly module!" appear in the text box. Congratulations, you have compiled your first WebAssembly module.

Where to next?

Using the latest Clang compiler, you have compiled and ran your first "Hello World" WebAssembly module in a web browser. This basic module is a great starting point that you can build upon to compile your own advanced projects.

What do you think?
Have you done anything cool with WebAssembly?
Get in touch via twitter.

Read more articles and tutorials about WebAssembly.

Contact

If you have any questions feel free to contact me directly via: