Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

WebAssembly interface and Emscripten builds

The WebAssembly build compiles the standard conjure-oxide executable for wasm32-unknown-emscripten. A small browser wrapper passes CLI arguments and input files, then returns the exit code, stdout, and stderr. The normal Essence parser, released minion-sys 0.1.2, and BatSat are included.

Build and try the minimal page

First follow Install the build tools, then run from the repository root:

bash tools/build-wasm.sh
python3 -m http.server 8767 --bind 127.0.0.1 --directory target/wasm-package

Open http://127.0.0.1:8767/minimal.html. It has three text boxes and a Submit button, with no CSS or npm setup. The build writes a self-contained target/wasm-package directory. An optional first argument to the build script chooses another package directory. Copy the whole directory into your application.

Use the JavaScript interface

import { run } from "./conjure/index.mjs";
const result = await run(
  ["solve", "/model.essence", "--solver", "sat", "-n", "20"],
  { "/model.essence": "find x, y : bool\nsuch that x != y\n" },
);
console.log(result.exitCode, result.stdout, result.stderr);

run(args, files?, options?) accepts ordinary CLI arguments without the executable name and a map of input paths to text. Files live in each worker’s private in-memory filesystem. For a parameter file, supply its text in files and add its path after the model path, just as with the native CLI. Use --output-format json if your page wants to parse JSON instead of displaying the usual Essence output. await run(["--help"]) returns CLI help.

Results contain exitCode, stdout, stderr, elapsedMs, and files. Pass captureFiles: ["/solutions.json"] in the options to return that file’s UTF-8 text in result.files["/solutions.json"], for example after writing JSON solutions with --output-format json -o /solutions.json. Requested files are read before the worker terminates; missing files reject with an error identifying the path and CLI exit code. With no requested files, files is an empty object. A nonzero exit code resolves normally with the CLI’s diagnostics if requested files can be read. Worker, Wasm loading, and filesystem failures reject the Promise. Optional signal supports cancellation; timeoutMs defaults to 30000 and includes startup. Cancellation and timeout terminate the worker, discard partial output, and reject with AbortError and TimeoutError respectively. onStage receives "loading" and "running". Each invocation has a fresh worker and engine; concurrent calls are independent and leave the page responsive.

See tools/wasm/README.md for the full API and student integration instructions. To run the browser regression suite, serve the repository root over HTTP and open /tools/wasm/tests/browser.html. The full React playground is maintained in the separate conjure-oxide-web repository, which imports the built package without Rust dependencies.

CLI build features

The script compiles conjure-cp-cli with --no-default-features --features wasm-target. This selects BatSat; Minion remains available. Z3, the language server, mimalloc, and extra rule checks are omitted. Requesting Z3 or server-lsp reports that support was not compiled in, with the feature needed to enable it. These feature choices also work natively.

The generated conjure.mjs exposes Emscripten’s FS and callMain to worker.mjs, which writes the input files, runs the standard main, and captures stdout and stderr. There is no dedicated Wasm Rust executable. CLI parsing, parameters, solution limits, output formats, and help use the same code as native builds. Commands that launch external programs, such as --parser via-conjure, compile but return an operating-system error in a browser.

Library backend features

Minion remains available unconditionally. SAT requires at least one compiled backend: sat-cadical or sat-batsat. Native defaults include both and enable Z3. Sat::default() selects CaDiCaL when available, otherwise BatSat; Sat::new(SatBackend::Batsat) selects a backend explicitly.

The CLI selects the backend at runtime:

conjure-oxide solve model.essence --solver sat --sat-backend cadical
conjure-oxide solve model.essence --solver sat --sat-backend batsat

--sat-backend defaults to CaDiCaL on native builds and BatSat on Wasm. Its help lists only the compiled backends, and selecting an unavailable backend is a command-line error. Both backends share SAT encoding and solution enumeration.

To build with only BatSat, disable defaults and enable sat-batsat on conjure-cp / conjure-cp-rules. Re-enable z3 explicitly if needed on native builds. Selecting neither SAT feature is a compile error.

Wasm requires sat-batsat; CaDiCaL remains a native-only dependency. Parser features forward the backend features. Procedural macros run on the host and use the parser’s native defaults independently of the Wasm target’s selection.

BatSat supports timeout termination through its callback API; nonzero solver seeds return an explicit unsupported error rather than being ignored. Default seed zero uses BatSat’s defaults. CaDiCaL retains its seed and timeout support. CLI builds also expose optional z3, lsp, and mimalloc features. Native defaults include Z3, the language server, and mimalloc. The language server itself still uses its native dependencies.

Install the build tools

If you have an already-built package, you can serve it and use the JavaScript interface without installing Rust or Emscripten. Building from source needs Python 3 and native C/C++ build tools, libclang, and CMake for Rust dependencies.

With Rust installed through rustup, run this from the repository root to install the target for the repository’s selected Rust version:

rustup target add wasm32-unknown-emscripten

This installs Rust’s target standard library. Install Emscripten separately. On macOS with Homebrew available, use the Homebrew package:

brew install emscripten

For macOS or Linux without administrator access, use the official Emscripten SDK installation instructions. With Git and Python 3 available, these commands install the SDK in your home directory without sudo:

git clone https://github.com/emscripten-core/emsdk.git "$HOME/emsdk"
cd "$HOME/emsdk"
./emsdk install 6.0.10
./emsdk activate 6.0.10
source ./emsdk_env.sh

The commands select a tested Emscripten version. In each new Bash or Zsh terminal, run source "$HOME/emsdk/emsdk_env.sh" again before building. Windows users can use an existing WSL Linux environment; native Windows builds have not been tested. On a managed machine, ask course staff about any missing native build tools.

Return to the repository root and check the installation:

python3 --version
command -v emcc
emcc --version
emcc --check
rustup target list --installed

These should identify Python 3, the Emscripten compiler and version, and wasm32-unknown-emscripten in the installed target list. emcc --check verifies Emscripten’s supporting tools. If emcc is not found, source the SDK environment in this terminal. Resolve any check errors before running bash tools/build-wasm.sh.

Tree-sitter dependency fix

Tree-sitter 0.27.0 incorrectly enables its bare-Wasm libc replacements for Emscripten. The workspace and lockfile pin upstream revision b2f00cb0b5942ff10c45dc77d3a9c7e9c7515da8, which limits those replacements to wasm32-unknown-unknown. The runtime declares version 0.28.0. Its language crate is pinned to the same source to keep grammar and runtime types consistent. No manifest edits, local vendor patch, or command-line Cargo override are needed. Downstream Rust workspaces must carry the same patches until an upstream release contains this fix; Cargo does not inherit patches from dependencies.

Engine linking

If CARGO_TARGET_DIR is overridden, use that output directory instead of target. Use the Python configured by the Emscripten SDK; set EMSDK_PYTHON explicitly if its launcher picks an incompatible Python installation.

The linker wrapper uses --whole-archive for the built-in rules archive, retaining objects referenced only through inventory constructors. Additional downstream rule crates would need equivalent retention. Emscripten runs the constructors at startup; no explicit registry or initialisation call is needed in Rust.

The C++ link setting supplies Minion’s runtime. The larger stack supports parser, rewriter and solver recursion. Memory growth allows Minion to allocate its search storage rather than abort at the default fixed heap limit. std::time::Instant works through Emscripten, so this path does not need a Wasm timing replacement.