magic/toolchains/emscripten/README.md

101 lines
3.3 KiB
Markdown
Raw Normal View History

Add WASM entry point and Emscripten build wiring The pieces that make Magic actually buildable as a WASM library. * magic/magicWasm.c — new headless entry point exporting four functions used by the JS wrapper: - magic_wasm_init() idempotent initialisation - magic_wasm_run_command(s) dispatch one Magic command - magic_wasm_source_file(p) execute a script from the VFS - magic_wasm_update() drive a display-update cycle Sets CAD_ROOT=/ if unset, so embedded technology files under /magic/sys/ resolve correctly. Centers the command point inside GrScreenRect so commands route to the layout window client rather than the border/window-management client. * utils/main.c, utils/main.h — split magicMain() into magicMainInit() + the dispatch loop. magicMainInit is idempotent (a static flag guards against re-initialisation) so JS callers can call any of the four wasm entry points first without sequencing. * magic/Makefile — adds the WASM link target, gated by MAKE_WASM=1 set from toolchains/emscripten/defs.mak. Conditionally compiles magicWasm.c into the main binary, links to magic.js and runs post-build.sh on the result. * toolchains/emscripten/defs.mak — Emscripten linker flags (WASM=1, MODULARIZE, EXPORT_ES6, ALLOW_MEMORY_GROWTH, INITIAL_MEMORY=32M, STACK_SIZE=5M), the four EXPORTED_FUNCTIONS, and the embed-file bindings for the technology files under /magic/sys/. * toolchains/emscripten/post-build.sh — patches Emscripten's ESM output so it works in pure Node.js ESM: aliases require() through createRequire, injects __filename / __dirname shims, and resyncs the ___emscripten_embedded_file_data constant from the wasm global section if Emscripten emitted a stale value. Idempotent and pinned to emsdk 3.1.56 (see WARNING in the header). * toolchains/emscripten/README.md — full build documentation: quick-start via npm/build.sh, manual build, list of embedded files, exported C API, JavaScript usage example, and notes on CAD_ROOT, DISPLAY_SUSPEND, and the signal-API stubs. * .gitignore — adds the WASM artefacts (magic.js, magic.wasm, magic.symbols), tightens the editor/OS cruft list, and keeps toolchains/emscripten/defs.mak tracked despite the `defs.mak` ignore rule.
2026-05-04 13:31:41 +02:00
# 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:
```bash
# 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/`](../../npm/examples/) for usage examples.
## Manual build
Prerequisites: an activated [emsdk](https://emscripten.org/docs/getting_started/downloads.html)
checkout (`emcc`, `emar`, `emranlib` on `PATH`), plus standard `make` and `gcc`.
```bash
# 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`](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
```js
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.