Notes on building SWI-Prolog for WebAssembly (WASM)
Table of Contents
Because I encountered difficulties compiling version 10.1.1 of SWI-Prolog for WASM, I have decided to publish these notes as a complement to the official web page on this topic. I am sincerely thankful to Jan Wielemaker for his patient help, hence this webpage as a return favor. I am also indebted to claude.ai who helped me, both to install SWI-Prolog for WASM and to complete this “howto”.
Prerequisites for Debian/Ubuntu/MX Linux users
Minimal dependencies for WASM compilation only
If you only want to compile SWI-Prolog for WASM (not the native version), install these essential packages:
# Essential build tools sudo apt-get update sudo apt-get install -y build-essential cmake ninja-build git # Minimal required libraries for WASM sudo apt-get install -y \ zlib1g-dev \ libgmp-dev \ libpcre2-dev \ curl \ ca-certificates \ pkg-config
Note: Node.js is required to test the WASM build. Install it if not already present:
# Install Node.js (version 16 or higher recommended) curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - sudo apt-get install -y nodejs
Full dependencies (for native SWI-Prolog compilation)
If you also want to compile the native version of SWI-Prolog, install these additional packages. See Prerequisites for Debian based systems for the most up-to-date list:
# Graphics and GUI (SDL3-based, replacing old X11 dependencies) sudo apt-get install -y \ libsdl3-dev \ libpango1.0-dev \ libcairo2-dev \ libfreetype6-dev \ libfontconfig1-dev # Additional libraries sudo apt-get install -y \ libreadline-dev \ libedit-dev \ libunwind-dev \ libssl-dev \ unixodbc-dev \ libarchive-dev \ libossp-uuid-dev \ libdb-dev \ libyaml-dev # Optional but recommended sudo apt-get install -y \ junit \ texlive-latex-base \ graphviz
Note for MX Linux / Debian Stable users: If libsdl3-dev is not available in your repositories,
you can skip the GUI dependencies - they are not needed for WASM compilation or for server-based usage.
To Linux Users (especially MX Linux users)
Do not choose /usr/local/ as installation directory. When installing a
new major version, few distributions allow you to preserve
this path, which belongs to the root partition. This means that, if you
are like me a Linux MX user, to update from version 23 to 25, you will
have to reinstall everything you put in /usr/local/. That is why I
have finally decided to put the installation directory /wasm/ inside
my home: /home/joseph/wasm/. So, inside your home:
mkdir ~/wasm
cd ~/wasm
and everything related to wasm is downloaded and compiled inside ~/wasm.
Preparation
You need to download the Emscripten compiler (inside ~/wasm!).
Follow the instructions on its home page. But, DO NOT INSTALL the
latest version; instead:
cd ~/wasm git clone https://github.com/emscripten-core/emsdk.git cd emsdk ./emsdk install 4.0.15 ./emsdk activate 4.0.15 source ./emsdk_env.sh
Important: You’ll need to run source ~/wasm/emsdk/emsdk_env.sh in each new terminal session where you want to compile WASM code.
Dependencies installation
zlib (mandatory)
The link https://zlib.net/current/zlib.tar.gz
is a permalink for the most recent release. Download this package
inside ~/wasm and then:
cd ~/wasm wget https://zlib.net/current/zlib.tar.gz tar xvfz zlib.tar.gz cd zlib-1.3.1 # (or zlib-x.y.z for a newer version) emconfigure ./configure --static --prefix=$HOME/wasm emmake make emmake make install
pcre2 (optional but recommended)
Inside ~/wasm:
cd ~/wasm git clone https://github.com/PCRE2Project/pcre2.git mkdir -p pcre2-build cd pcre2-build emcmake cmake -DCMAKE_INSTALL_PREFIX=$HOME/wasm -DPCRE2GREP_SUPPORT_JIT=OFF -G Ninja ../pcre2 ninja ninja install
GMP library (optional but recommended for arithmetic performance)
Inside ~/wasm, download the latest version
https://gmplib.org/download/gmp/gmp-6.3.0.tar.xz and:
cd ~/wasm wget https://gmplib.org/download/gmp/gmp-6.3.0.tar.xz tar xvf gmp-6.3.0.tar.xz cd gmp-6.3.0 emconfigure ./configure \ --prefix=$HOME/wasm \ --host=none \ --disable-assembly \ --enable-fat=no \ ABI=longlong emmake make emmake make install
SWI-Prolog sources
Inside ~/wasm, download the latest version of SWI-Prolog:
cd ~/wasm git clone https://github.com/SWI-Prolog/swipl-devel.git cd swipl-devel
Once the sources are downloaded, create the WASM build directory:
cd ~/wasm/swipl-devel mkdir build.wasm cd build.wasm
Create a configure file with the following content:
#!/bin/bash WASM_HOME=$HOME/wasm source $WASM_HOME/emsdk/emsdk_env.sh TOOLCHAIN=$EMSDK/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake cmake -DCMAKE_TOOLCHAIN_FILE=$TOOLCHAIN \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_FIND_ROOT_PATH="$HOME/wasm" \ -DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=ONLY \ -DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY \ -DGMP_ROOT="$HOME/wasm" \ -DINSTALL_DOCUMENTATION=OFF \ -G Ninja ..
Now configure, build and test:
bash configure ninja ctest -j 8 node src/swipl.js
The last command should give something like this:
joseph@mx:~/wasm/swipl-devel/build.wasm$ node src/swipl.js
Welcome to SWI-Prolog (32 bits, version 10.1.1-27-ga959da416)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
Please run ?- license. for legal details.
CMake built from "/home/joseph/wasm/swipl-devel/build.wasm"
For online help and background, visit https://www.swi-prolog.org
?-
Tinker Installation and Configuration
Tinker is the web-based playground for SWI-Prolog. Here’s how to set it up:
Installing Tinker
Clone the Tinker repository:
cd ~/wasm/swipl-devel/src/wasm/demos
git clone https://github.com/SWI-Prolog/tinker
Or, if you have a custom fork:
cd ~/wasm/swipl-devel/src/wasm/ # Remove the default demos and replace with your version mv demos demos.original_backup git clone https://github.com/your-username/your-tinker-fork demos
Copying WASM files to demos
The demos directory needs access to the compiled WASM files:
cp ~/wasm/swipl-devel/build.wasm/src/swipl-bundle.js ~/wasm/swipl-devel/src/wasm/demos/ cp ~/wasm/swipl-devel/build.wasm/src/swipl-web.* ~/wasm/swipl-devel/src/wasm/demos/
Configuring the HTTP server
The server.pl file in ~/wasm/swipl-devel/src/wasm/ should point to the correct paths.
Verify/update these lines:
user:file_search_path(web, '/home/YOUR_USERNAME/wasm/swipl-devel/src/wasm/demos'). user:file_search_path(web, '/home/YOUR_USERNAME/wasm/swipl-devel/src/wasm/demos/tinker').
Testing manually
You can test the server manually:
cd ~/wasm/swipl-devel/build.wasm
swipl ../src/wasm/server.pl --port=8080
Then open your browser to http://localhost:8080/wasm/tinker
Setting up automatic startup (for MX Linux / SysVinit systems)
Create an init script for automatic startup:
- Create the init script:
sudo nano /etc/init.d/swipl-tinker
- Add this content (adjust YOURUSERNAME):
#!/bin/sh ### BEGIN INIT INFO # Provides: swipl-tinker # Required-Start: $network # Required-Stop: $network # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Description: SWI-Prolog Tinker WASM Server ### END INIT INFO DAEMON="/usr/local/bin/swipl" # or /usr/bin/swipl ARGS="../src/wasm/server.pl --port=8080" WORKDIR="/home/YOUR_USERNAME/wasm/swipl-devel/build.wasm" NAME="swipl-tinker" PIDFILE="/var/run/$NAME.pid" case "$1" in start) echo "Starting $NAME..." cd "$WORKDIR" nohup $DAEMON $ARGS > /dev/null 2>&1 & echo $! > "$PIDFILE" ;; stop) echo "Stopping $NAME..." [ -f "$PIDFILE" ] && kill $(cat "$PIDFILE") 2>/dev/null rm -f "$PIDFILE" ;; restart) $0 stop sleep 2 $0 start ;; status) if [ -f "$PIDFILE" ]; then PID=$(cat "$PIDFILE") if kill -0 $PID 2>/dev/null; then echo "$NAME is running (PID: $PID)" else echo "$NAME is not running (stale PID file)" fi else echo "$NAME is not running" fi ;; *) echo "Usage: $0 {start|stop|restart|status}" exit 1 ;; esac
- Make it executable and enable:
sudo chmod +x /etc/init.d/swipl-tinker sudo update-rc.d swipl-tinker defaults
- Start the service:
sudo /etc/init.d/swipl-tinker start
- Check status:
sudo /etc/init.d/swipl-tinker status
The server will now start automatically on boot.
For systemd-based systems
If your system uses systemd instead of SysVinit, create a systemd service:
- Create the service file:
sudo nano /etc/systemd/system/swipl-tinker.service
- Add this content (adjust YOURUSERNAME):
[Unit] Description=SWI-Prolog Tinker WASM Server After=network.target [Service] Type=simple User=YOUR_USERNAME WorkingDirectory=/home/YOUR_USERNAME/wasm/swipl-devel/build.wasm ExecStart=/usr/local/bin/swipl ../src/wasm/server.pl --port=8080 Restart=always RestartSec=5 [Install] WantedBy=multi-user.target
- Enable and start:
sudo systemctl daemon-reload sudo systemctl enable swipl-tinker.service sudo systemctl start swipl-tinker.service sudo systemctl status swipl-tinker.service
Verification
After installation and service setup:
Check that the server is running:
ps aux | grep swipl
- Test in browser: http://localhost:8080/wasm/tinker
Verify SWI-Prolog version in the console:
?- current_prolog_flag(version, V).Should return the version number (e.g.,
V = 100101for version 10.1.1)
Troubleshooting
Browser cache issues
If you update the WASM files but still see an old version:
- Clear browser cache completely
- Or use hard refresh:
Ctrl+Shift+R(Firefox/Chrome on Linux)
Port already in use
If port 8080 is occupied:
sudo lsof -i :8080 # Find what's using the port sudo kill PID # Kill the process
Service won’t start
Check logs:
# For SysVinit tail -f /var/log/syslog # For systemd sudo journalctl -u swipl-tinker.service -f
Additional Resources
Updating to a newer SWI-Prolog version
When a new version is released:
cd ~/wasm/swipl-devel git pull cd build.wasm bash configure ninja # Copy updated WASM files cp src/swipl-bundle.js ../src/wasm/demos/ cp src/swipl-web.* ../src/wasm/demos/ # Restart service sudo /etc/init.d/swipl-tinker restart # or systemctl restart swipl-tinker