Emscripten is "the" toolchain for building WASM applications. Though there are several others available, e.g. wasi-sdk or even vanilla clang, Emscripten has several features which are required in order to get a completely-working copy of many types of C libraries out of the box, most notably their POSIX I/O API proxy support. sqlite3 can be built with other toolchains, but it cannot currently do much because getting the resulting WASM file loaded requires that the client provide implementations for the I/O-layer functions (a considerable undertaking!).

This page covers the process of building sqlite3 with the Emscripten SDK on Linux. It is untested on other platorms. Building using an Emscripten version installed via a Linux package manager may or may not work.

Emscripten SDK (a.k.a. emsdk)

First, install the Emscripten SDK, as documented here and summarized below for Linux environments:

# Clone the emscripten repository:
$ sudo apt install git
$ git clone https://github.com/emscripten-core/emsdk.git
$ cd emsdk

# Download and install the latest SDK tools:
$ ./emsdk install latest

# Make the "latest" SDK "active" for the current user:
$ ./emsdk activate latest

Those parts only need to be run once, but the SDK can be updated using:

$ git pull
$ ./emsdk install latest
$ ./emsdk activate latest

Reminder based on painful experience: if updating the SDK breaks things, be sure to check the the EMSDK changelog for incompatible changes (e.g. changes in default values like the stack memory size).

The following needs to be run for each shell instance which needs the emcc compiler:

# Activate PATH and other environment variables in the current terminal:
$ source /path/to/emsdk/emsdk_env.sh

$ which emcc

Optionally, add it to your login shell's resource file (~/.bashrc or equivalent):

if [ -f "$EMSDK_HOME/emsdk_env.sh" ]; then
    export EMSDK_HOME
    export EMSDK_QUIET=1
    source "$EMSDK_HOME/emsdk_env.sh"

WABT Tools (wasm-strip)

The wabt tools include wasm-strip, which is used by the canonical build process to "strip" the resulting .wasm files to make them smaller.

Because Emscripten's approach to mangling and minifying symbol names is incompatible with the sqlite3 build (resulting in completely non-functional JS files), the builds have to be created with full debugging info enabled using -g3. That inhibits Emscripten's minification of symbols but it also leads to huge .wasm files. wasm-strip can then shrink those by removing the debugging symbols. wasm-strip is automatically used if the build finds it. If it is not found, the build will warn but function, the main drawback being that the .wasm files will be extremely large.

If wasm-strip warns about "invalid code section" then the version of wasm-strip is too old and simply needs to be updated.

Building sqlite3 with Emscripten

At its simplest, do the following from the top of a checked-out copy of the sqlite3 source tree:

$ ./configure --enable-all
$ make sqlite3.c
$ cd ext/wasm
$ make

Noting that make here is GNU Make, which is named gmake on some platforms.

The GNUMakefile in that directory contains the complete details of which build options are used for creating the result WASM files. Emscripten offers a staggering number of settings and the makefile contains notes regarding several of them and why the are (or are not) used.

TODO: extend these docs to provide a "prose-oriented guide" to creating custom builds, beyond telling clients to "just play with the build options in the makefile."

Achtung: Code Minification

The build environment really likes to minimize all code, including reducing code symbols to small pseudo-random versions. That approach works fine when all participating code is locked away in the resulting JS/WASM module, but is fatal for libraries which are intended to be used by code outside of those libraries (like this project).

Emscripten, on all optimization levels higher than 1, attempts to minimize all JS code even if it's told not to via the --minify 0 flag. The workaround is to always build with -g3 (a high debugging-info level) and then strip the resulting WASM files using wasm-strip.