In this article I will show how to use Bazel as the single build tool for a Javascript application with C++ WebAssembly.

Bazel

Note: If you are new to Bazel, I recommend checking out one of my other Bazel articles first.

The tldr; is that Bazel is a language agnostic and incremental build toolchain, designed to be the single build tool for your entire tech stack. In my previous articles you will see examples of how to use Bazel to build full stack projects in Java, Typescript, Svelte, Angular, React, C#, Go and C++. In this article I will show an example of how to use Bazel to build C++ WebAssembly (Emscripten) and a Svelte web application.

Demo Project

My demo application is basic and experimental, but should at least highlight some of the benefits of using Bazel as your build tool in a mixed tech stack.

Let’s take a look at the C++ and Svelte code below:

C++ (add.cc)

I have intentionally kept the C++ code very simple, but I have more advanced C++ examples in my other articles if you are interested.

extern "C" long add_numbers(long operand1, long operand2) { return operand1 + operand2; }

All this code does is add two numbers and return the result.

Svelte (App.svelte and add-service.js)

On the Svelte side I am using the C++ function to add two user entered numbers. Notice how I am using Cwrap to wrap the C++ method in a Javascript method.

<script> import { AddService } from './add-service'; const addService = new AddService(); let operand1 = 10; let operand2 = 20; let result; function add() { result = addService.add(operand1, operand2); } </script> <h2>Add</h2> <input type="number" bind:value={operand1}> <input type="number" bind:value={operand2}> <button on:click={add}>Add</button> <div>Result: {result || ''}</div> export class AddService { constructor() { this.add_numbers = Module.cwrap("add_numbers", "number", ["number", "number"]); } add(operand1, operand2) { return this.add_numbers(operand1, operand2); } }

Bazel Configurations

Integrating Emscripten with Bazel is pretty easy now that the Github repo includes a Bazel toolchain for pointing the C++ Bazel rules to Emscripten. My demo is just a slightly modified version of the original setup. In order to set the necessary compiler flags I have configured a new flag_set as seen below.

flag_set( actions = all_compile_actions + all_link_actions, flags = [ "-Os", #Tweak this for appropriate optimizations "-sWASM", "-sEXTRA_EXPORTED_RUNTIME_METHODS=['cwrap']", "-sEXPORTED_FUNCTIONS=['_add_numbers']" ], features = ["default_feature"], ),

This part should be made more generic and parametrized, but as a POC it works just fine. Two important flags here are EXTRA_EXPORTED_RUNTIME_METHODS and EXPORTED_FUNCTIONS since these are needed to expose cwrap and my custom add_numbers method.

One the Svelte side I use my own experimental Bazel rules like so:

svelte( name = "App", srcs = [ "main.js", "add-service.js", "externs.js" ], entry_point = "App.svelte", deps = [] ) bundle_prod( name = "bundle", entry_point = "main.js", closure_config = ":closure-config.json", #Bundle created using the Closure Compiler deps = [ ":App" ] )

Finally I am setting up a simple Node/Express server for serving the Svelte/C++ application.

nodejs_binary( name = "server", data = [ "@npm_app//express", ":bundle", ":index.html", ":add" ], entry_point = ":server.js", )

Demo

The code can be downloaded from Github

The end result is a Svelte application with WASM integration where Bazel is the single build tool for both languages. My current setup uses Docker for all Bazel dependencies, so there is no need to install anything manually.