magic/toolchains/emscripten
Intubun d6438387ad Fixed a second emscripten 6.0.2 regression in the WASM build, this one at runtime rather than at compile time. emscripten 6.0.2 removed wasmBinary (along with a batch of GL/SDL members) from the default INCOMING_MODULE_JS_API list. The JS loaders in npm/examples pass Module.wasmBinary to embed the .wasm binary, and because the WASM build links with -sASSERTIONS=1, the now-unrecognized member triggers a hard runtime abort: "`Module.wasmBinary` was supplied but `wasmBinary` not included in INCOMING_MODULE_JS_API", failing every example test. Fixed by explicitly setting -sINCOMING_MODULE_JS_API in toolchains/emscripten/defs.mak to emscripten's full default list plus wasmBinary. Spelling out the whole default (rather than only the members our own loaders use) keeps external consumers of the npm package working if they supply locateFile, arguments, and similar Module options. 2026-07-08 09:48:10 -04:00
..
README.md Add WASM entry point and Emscripten build wiring 2026-05-11 14:20:47 -04:00
build-tcl-wasm.sh Fixed the WASM (emscripten) build, which broke starting with emscripten 6.0.2. The failure is not in magic itself but in the TCL dependency build: emscripten 6.0.2 began shipping a <sys/epoll.h> stub in its sysroot (6.0.1 did not), so TCL's configure now detects epoll on the Linux CI host via AC_CHECK_HEADERS([sys/epoll.h]), defines NOTIFIER_EPOLL, and compiles tclEpollNotfy.c. That file also requires <sys/queue.h>, which emscripten does not provide, so the build died with "fatal error: 'sys/queue.h' file not found". Fixed by passing ac_cv_header_sys_epoll_h=no to TCL's configure in toolchains/emscripten/build-tcl-wasm.sh, which forces the select()-based notifier. That notifier is the correct choice for single-threaded WASM anyway (emscripten's epoll is only a stub), and overriding the autoconf cache variable avoids patching the read-only TCL source tree and keeps the build working across future emscripten versions. 2026-07-08 09:48:10 -04:00
defs.mak Fixed a second emscripten 6.0.2 regression in the WASM build, this one at runtime rather than at compile time. emscripten 6.0.2 removed wasmBinary (along with a batch of GL/SDL members) from the default INCOMING_MODULE_JS_API list. The JS loaders in npm/examples pass Module.wasmBinary to embed the .wasm binary, and because the WASM build links with -sASSERTIONS=1, the now-unrecognized member triggers a hard runtime abort: "`Module.wasmBinary` was supplied but `wasmBinary` not included in INCOMING_MODULE_JS_API", failing every example test. Fixed by explicitly setting -sINCOMING_MODULE_JS_API in toolchains/emscripten/defs.mak to emscripten's full default list plus wasmBinary. Spelling out the whole default (rather than only the members our own loaders use) keeps external consumers of the npm package working if they supply locateFile, arguments, and similar Module options. 2026-07-08 09:48:10 -04:00
post-build.sh Add WASM entry point and Emscripten build wiring 2026-05-11 14:20:47 -04:00

README.md

Magic VLSI — Headless WASM Build

This toolchain builds Magic as a headless WebAssembly module using Emscripten. X11, Tk, OpenGL, and readline are all disabled. The resulting magic.js / magic.wasm pair can be loaded in Node.js, a browser, or a Web Worker.

Quick start (npm package)

The easiest way to build and use the WASM module is through the npm package:

# Build magic.js + magic.wasm and copy them into npm/
bash npm/build.sh

# Run the test suite (extract, GDS, DRC, CIF)
npm --prefix npm test

See npm/examples/ for usage examples.

Manual build

Prerequisites: an activated emsdk checkout (emcc, emar, emranlib on PATH), plus standard make and gcc.

# 1. Configure for Emscripten
CFLAGS="--std=c17 -D_DEFAULT_SOURCE=1 -DEMSCRIPTEN=1 -g" \
  emconfigure ./configure \
    --without-cairo --without-opengl --without-x --without-tk --without-tcl \
    --disable-readline --disable-compression \
    --host=asmjs-unknown-emscripten \
    --target=asmjs-unknown-emscripten

# 2. Append the Emscripten-specific make settings
cat toolchains/emscripten/defs.mak >> defs.mak

# 3. Build
emmake make depend
emmake make -j$(nproc) modules libs
emmake make techs
emmake make mains

The outputs are magic/magic.js and magic/magic.wasm.

Embedded files

The following runtime files are baked directly into the WASM binary via Emscripten's --embed-file mechanism and are available at startup without any host filesystem access:

Host path VFS path
scmos/ /magic/sys/current/
windows/windows7.glyphs /magic/sys/windows7.glyphs
windows/windows7.glyphs /magic/sys/bw.glyphs

To embed a custom technology file, add an --embed-file entry to TOP_EXTRA_LIBS in defs.mak.

Exported C API

The WASM module exports four functions:

Function Description
magic_wasm_init() Initialize Magic (idempotent — safe to call multiple times). Returns 0 on success.
magic_wasm_run_command(const char *cmd) Dispatch one Magic command. Calls magic_wasm_init() automatically if needed. Returns 0 on success.
magic_wasm_source_file(const char *path) Read and execute a command file from the virtual filesystem.
magic_wasm_update() Drive a display-update cycle. No-op in headless builds (null display suspends all redraws).

JavaScript usage

import createMagic from 'magic-vlsi-wasm';

const { runCommand, FS } = await createMagic();

// Write a layout file into the virtual filesystem
FS.writeFile('/work/inv.mag', layoutBytes);

// Run Magic commands
runCommand('tech load sky130A');
runCommand('load /work/inv');
runCommand('gds write /work/inv');

// Read the result back out
const gdsBytes = FS.readFile('/work/inv.gds');

Notes

  • CAD_ROOT is automatically set to / so that embedded system files are resolved under /magic/sys/.
  • The null display driver (-d null) sets GrDisplayStatus = DISPLAY_SUSPEND, which causes WindUpdate to return immediately without invoking any display callbacks. This is what makes the WASM build safe to run without a screen.
  • All POSIX signal/timer APIs (setitimer, SIGALRM, fcntl) are compiled out under __EMSCRIPTEN__; the display progress timer becomes a no-op.