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
/path/to/emsdk/upstream/emscripten/emcc
Optionally, add it to your login shell's resource file (~/.bashrc
or equivalent):
EMSDK_HOME=$HOME/src/emsdk
if [ -f "$EMSDK_HOME/emsdk_env.sh" ]; then
export EMSDK_HOME
export EMSDK_QUIET=1
source "$EMSDK_HOME/emsdk_env.sh"
fi
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
.