magic/toolchains/emscripten
Darryl L. Miles 67d6a2ff5a build: address code-review findings (low-risk cleanups)
From an independent review of the branch:

* Makefile.in: `tcltk` is in both INSTALL_CAD_DIRS and PROGRAMS (Tcl build),
  so install@tcltk / install-tcl@tcltk were defined twice -> "given more than
  once" warnings on every command.  Wrap both install lists in $(sort ...) to
  dedup (as CLEAN_TARGETS already does).  Also fix a stale comment (module@ now
  waits for ${DATABASE_H}, not a `depend` phase).

* toolchains/emscripten/defs.mak: the scmos display-file embed globbed
  `mos.*.dstyle`, which misses mos.7bit.mraster_dstyle (an underscore, not a
  dot) -- so the mono-raster style was not embedded in WASM, and the comment
  wrongly called that file non-existent.  Glob `mos.*dstyle` to cover both.

* npm/build.sh: sed_strip_cr wrote the file back unconditionally, bumping the
  mtimes of configure and every scripts/ file on each WASM build (contradicting
  the "source never mutated" guarantee and able to force a coexisting native
  rebuild).  Only write back when a CR was actually stripped.

* graphics/Makefile.in: remove the dead DEPSRCS block left behind by retiring
  the Depend machinery (it also referenced an undefined ${THREE_D_SRCS}).

* scripts/defs.mak.in: clarify that only the SOURCE_DATE_EPOCH branch is UTC.

Verified: 0 duplicate-target warnings, wildcard now lists mos.7bit.mraster_dstyle,
full Tcl build+install rc=0 (375 files), graphics lib builds, build.sh syntax OK.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-26 14:54:11 -04:00
..
README.md Add WASM entry point and Emscripten build wiring 2026-05-11 14:20:47 -04:00
build-tcl-wasm.sh wasm: force TCL select() notifier (Emscripten has no working epoll) 2026-07-26 14:54:11 -04:00
defs.mak build: address code-review findings (low-risk cleanups) 2026-07-26 14:54:11 -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.