2025-11-27 15:30:10 +01:00
|
|
|
#!/bin/bash
|
2026-06-12 10:18:16 +02:00
|
|
|
# Build wxWidgets for WebAssembly (the DOM port: widgets are real HTML
|
|
|
|
|
# elements; owner-drawn widgets render into per-window canvas islands)
|
2026-07-29 17:42:45 +02:00
|
|
|
#
|
|
|
|
|
# CI cache epoch: 2 (2026-07-29). CI's wx cache key hashes THIS FILE
|
|
|
|
|
# (.github/workflows/wasm-build.yml "Restore wx build cache"), so editing this
|
|
|
|
|
# header is how a poisoned build-wasm/wxwidgets cache gets discarded. Bumped
|
|
|
|
|
# after a restored cache failed with "No rule to make target
|
|
|
|
|
# 3rdparty/pcre/src/pcre2_chartables.c": pcre's makefile rm's that path and
|
|
|
|
|
# re-links it to pcre2_chartables.c.dist at configure time, so a cache captured
|
|
|
|
|
# around that window restores a build tree whose pcre rule can no longer be
|
|
|
|
|
# satisfied from a clean checkout. Bump the number to force a fresh wx build.
|
2026-01-03 11:55:54 +01:00
|
|
|
|
|
|
|
|
# Redirect all output to a log file (re-execs script with redirection)
|
|
|
|
|
source "$(dirname "$0")/common/logging.sh"
|
2026-01-09 14:48:23 +01:00
|
|
|
|
perf(build): use all cores in host builds — wx lib, test apps, gal-webgl
env.sh exports a docker-safe JOBS=1 default, and every host build silently
inherited it (the docker kicad path dodges it by passing -j explicitly):
- build-wx-wasm.sh: the intended "${JOBS:-nproc}" fallback sat BELOW the
env.sh source, so it was dead code — full wx builds ran make -j1 on the
Mac and on CI. Compute the all-cores default before env.sh instead
(explicit JOBS/PARALLEL_JOBS still wins); also -j the PCRE pre-build.
- build-wasm-test.sh: same default fix, plus fan the post-link
hoist+asyncify loop out across JOBS with xargs -P. Per-app wasm-opt
can't feed many cores (small modules), so serial stays ~4min even with
BINARYEN_CORES=16; fanning across the 74 independent apps is what
scales. Safe: apply-asyncify is in-place per wasm, injector tmp is
per-js, HOIST_WASMOPT resolved once up front.
- build-gal-webgl-test.sh: make had no -j at all.
Measured on a 16-core M4 Max, clean test-app build: 15m36s (-j1) → 2m08s,
peak RAM 10.7 GB summed across all build processes (64 GB machine; CI
runners have 120 GB). Core counts are always derived from nproc at
runtime — nothing hardcoded, CI workers differ.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XeiSRRScdaox5jBueJNcyG
2026-07-02 10:54:14 +02:00
|
|
|
# Default to all cores BEFORE sourcing env.sh — env.sh exports a docker-safe
|
|
|
|
|
# JOBS=1 when unset, which would make the "${JOBS:-nproc}" fallback below it
|
|
|
|
|
# dead code (this build ran make -j1 everywhere for that reason). An explicit
|
|
|
|
|
# JOBS/PARALLEL_JOBS from the caller still wins (e.g. the docker pipeline's -j
|
|
|
|
|
# via build-kicad-target.sh).
|
|
|
|
|
JOBS="${JOBS:-$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)}"
|
|
|
|
|
|
2026-03-13 12:26:48 +01:00
|
|
|
# Source common environment (sets up local emsdk)
|
2026-01-09 14:48:23 +01:00
|
|
|
source "$(dirname "$0")/common/env.sh"
|
2026-06-01 17:31:03 +02:00
|
|
|
# Build-progress markers (parsed by scripts/build-monitor.sh).
|
|
|
|
|
source "$(dirname "$0")/common/stages.sh"
|
2025-11-27 15:30:10 +01:00
|
|
|
# This builds the GUI-enabled wxWidgets needed for KiCad
|
2025-11-28 16:42:35 +01:00
|
|
|
#
|
|
|
|
|
# Prerequisites:
|
2026-03-13 12:26:48 +01:00
|
|
|
# - Emscripten SDK (auto-installed by env.sh via scripts/setup-emsdk.sh)
|
2025-11-28 16:42:35 +01:00
|
|
|
# - autoconf (for regenerating configure from configure.in)
|
|
|
|
|
#
|
|
|
|
|
# To regenerate Makefile.in from bakefiles (after modifying files.bkl):
|
|
|
|
|
# cd wxwidgets/build/bakefiles
|
|
|
|
|
# docker run --rm -v "$(pwd)/../..":"$(pwd)/../.." -w "$(pwd)" \
|
|
|
|
|
# ghcr.io/vslavik/bakefile:0.2 bakefile_gen
|
|
|
|
|
#
|
|
|
|
|
# Usage:
|
2026-06-12 10:18:16 +02:00
|
|
|
# ./build-wx-wasm.sh # Incremental build (default)
|
|
|
|
|
# ./build-wx-wasm.sh --clean # Clean build from scratch
|
2025-11-27 15:30:10 +01:00
|
|
|
|
|
|
|
|
set -e
|
|
|
|
|
|
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
|
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
|
|
|
|
WX_SOURCE="$PROJECT_ROOT/wxwidgets"
|
|
|
|
|
|
2026-06-12 10:18:16 +02:00
|
|
|
# Parse arguments: --clean or --no-clean (default; kept for the kicad
|
|
|
|
|
# pipeline's explicit call), in any order.
|
2026-06-10 17:17:57 +02:00
|
|
|
CLEAN_BUILD=0
|
|
|
|
|
for arg in "$@"; do
|
|
|
|
|
case "$arg" in
|
2026-06-11 00:19:53 +02:00
|
|
|
--clean) CLEAN_BUILD=1 ;;
|
|
|
|
|
--no-clean) CLEAN_BUILD=0 ;;
|
2026-06-10 17:17:57 +02:00
|
|
|
*) echo "Unknown argument: $arg"; exit 1 ;;
|
|
|
|
|
esac
|
|
|
|
|
done
|
|
|
|
|
|
2026-06-12 10:18:16 +02:00
|
|
|
BUILD_DIR="$PROJECT_ROOT/build-wasm/wxwidgets"
|
|
|
|
|
WXLIB_PREFIX="libwx_wasmu"
|
2026-06-10 17:17:57 +02:00
|
|
|
|
jspi: retire the asyncify pipeline — knob, post-link tail, binaryen hooks
Phase 8 in the parent repo. Deleted: asyncify-scheduler.js, apply-asyncify.sh,
apply-finalize.sh, inject-dyncall-shims.sh, asyncify-imports/removelist.txt,
the wasm-opt/finalize stub pair, scripts/binaryen-hoist-pass/ (the fork stays
a dormant submodule; removal is a follow-up), bench/wasm-opt-bench.sh (README
marked historical), wasm/shims/context_sleep.cpp, and the sched-context
harness app + Makefile targets.
PCBJAM_ASYNC_BACKEND is gone: build-wx-wasm.sh hardcodes the jspi stamp
(still force-cleans pre-migration trees), build-kicad-target.sh gives editors
the JSPI link surface and the CLIs nothing (they pin ASYNCIFY=0), the stub
dance is replaced by an unconditional .real-restore, build-wasm-test.sh lost
its whole post-link loop, docker/build.sh's postprocess is the ENV shim only,
and Makefile.wasm links every app JSPI with the scheduler shim as a tracked
prerequisite. pcbjam_async_policy.h keys on __EMSCRIPTEN__.
jspi-scheduler.js: wxWasmMainLoopPump dropped from the wrap census (the
export died with the D5 detach); inert [TRACE] instrumentation removed.
CI: wasm-build.yml rewritten for the single-cache pipeline (one output cache
keyed on compile inputs; post-processed bytes cached after the shim);
opt_level input removed from both callers. wasm-cache-hash.mjs inputs now
cover patch-env-shim.mjs + jspi-scheduler.js + jspi-exports.txt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NDeBaKKhQztd8KiVtHuyXr
2026-08-13 08:39:12 +02:00
|
|
|
# Async-backend stamp: the JSPI migration changed COMPILE flags in ways the
|
|
|
|
|
# configure-cached incremental build cannot see — building over a stale
|
|
|
|
|
# asyncify-era tree silently links a MIXED library. Force a clean build
|
|
|
|
|
# whenever the stamp disagrees.
|
jspi: migration phases 0-7 — build knob, scheduler shim, test successor suite
Toolchain: emsdk 6.0.6 (versions.sh; cache-hash keys on it). Build knob
PCBJAM_ASYNC_BACKEND=jspi|asyncify: build-kicad-target.sh links editors with
-sJSPI + -sJSPI_EXPORTS=@scripts/common/jspi-exports.txt + --pre-js
jspi-scheduler.js (no DYNCALLS, no post-link asyncify pipeline); wx build
stamps the backend and forces clean on flip or unknown provenance;
docker/build.sh passes the knob, seeds the emscripten ports cache from the
volume every launch, jspi postprocess = patch-env-shim only.
scripts/common/shims/jspi-scheduler.js: the JSPI successor scheduler —
token-wait registry, resume turnstile (one armed resume between engine
re-entries, SP swaps only at microtask boundaries), green-region spill
stacks (16-aligned tops), S1 embind mutator FIFO lane + parker wraps, S6
shutdown, libctx integration hooks (suspend/end/quarantine + g_current
arm/clear), SuspendError attributor, lost-wake + stuck-window watchdogs,
__wxWaitDump observability.
Embind: PARKER registrations get emscripten::async() under PCBJAM_JSPI
(wasm/bindings/pcbjam_async_policy.h). nanosleep yields route via the shim.
Tests: tests/asyncify -> tests/jspi successor suite (jspi-stack red/green
shadow-stack battery, jspi-coroutine MiniCoro harness, suspend-races
semantic scenarios + __wxWaitDump books coherence); projects jspi-firefox/
jspi-chrome (asyncify-webkit retired — no JSPI in WebKit); unconditional
Firefox JSPI pref; guard-beacons -> wait-beacons (+wxScheduler/libctxJspi
families); Makefile.wasm links test apps against JSPI with the shim as a
tracked link prerequisite.
Web: WasmTool setRo await + __wxWaitDump forensics, open-flow contained
promise, scheduler-shim.test.ts retargeted (8 green).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NDeBaKKhQztd8KiVtHuyXr
2026-08-13 07:06:24 +02:00
|
|
|
BACKEND_STAMP="$BUILD_DIR/.pcbjam-async-backend"
|
jspi: retire the asyncify pipeline — knob, post-link tail, binaryen hooks
Phase 8 in the parent repo. Deleted: asyncify-scheduler.js, apply-asyncify.sh,
apply-finalize.sh, inject-dyncall-shims.sh, asyncify-imports/removelist.txt,
the wasm-opt/finalize stub pair, scripts/binaryen-hoist-pass/ (the fork stays
a dormant submodule; removal is a follow-up), bench/wasm-opt-bench.sh (README
marked historical), wasm/shims/context_sleep.cpp, and the sched-context
harness app + Makefile targets.
PCBJAM_ASYNC_BACKEND is gone: build-wx-wasm.sh hardcodes the jspi stamp
(still force-cleans pre-migration trees), build-kicad-target.sh gives editors
the JSPI link surface and the CLIs nothing (they pin ASYNCIFY=0), the stub
dance is replaced by an unconditional .real-restore, build-wasm-test.sh lost
its whole post-link loop, docker/build.sh's postprocess is the ENV shim only,
and Makefile.wasm links every app JSPI with the scheduler shim as a tracked
prerequisite. pcbjam_async_policy.h keys on __EMSCRIPTEN__.
jspi-scheduler.js: wxWasmMainLoopPump dropped from the wrap census (the
export died with the D5 detach); inert [TRACE] instrumentation removed.
CI: wasm-build.yml rewritten for the single-cache pipeline (one output cache
keyed on compile inputs; post-processed bytes cached after the shim);
opt_level input removed from both callers. wasm-cache-hash.mjs inputs now
cover patch-env-shim.mjs + jspi-scheduler.js + jspi-exports.txt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NDeBaKKhQztd8KiVtHuyXr
2026-08-13 08:39:12 +02:00
|
|
|
# Single backend since the JSPI migration: the stamp still force-cleans any
|
|
|
|
|
# pre-migration tree (asyncify objects would silently mix into the library).
|
|
|
|
|
CURRENT_BACKEND="jspi"
|
jspi: migration phases 0-7 — build knob, scheduler shim, test successor suite
Toolchain: emsdk 6.0.6 (versions.sh; cache-hash keys on it). Build knob
PCBJAM_ASYNC_BACKEND=jspi|asyncify: build-kicad-target.sh links editors with
-sJSPI + -sJSPI_EXPORTS=@scripts/common/jspi-exports.txt + --pre-js
jspi-scheduler.js (no DYNCALLS, no post-link asyncify pipeline); wx build
stamps the backend and forces clean on flip or unknown provenance;
docker/build.sh passes the knob, seeds the emscripten ports cache from the
volume every launch, jspi postprocess = patch-env-shim only.
scripts/common/shims/jspi-scheduler.js: the JSPI successor scheduler —
token-wait registry, resume turnstile (one armed resume between engine
re-entries, SP swaps only at microtask boundaries), green-region spill
stacks (16-aligned tops), S1 embind mutator FIFO lane + parker wraps, S6
shutdown, libctx integration hooks (suspend/end/quarantine + g_current
arm/clear), SuspendError attributor, lost-wake + stuck-window watchdogs,
__wxWaitDump observability.
Embind: PARKER registrations get emscripten::async() under PCBJAM_JSPI
(wasm/bindings/pcbjam_async_policy.h). nanosleep yields route via the shim.
Tests: tests/asyncify -> tests/jspi successor suite (jspi-stack red/green
shadow-stack battery, jspi-coroutine MiniCoro harness, suspend-races
semantic scenarios + __wxWaitDump books coherence); projects jspi-firefox/
jspi-chrome (asyncify-webkit retired — no JSPI in WebKit); unconditional
Firefox JSPI pref; guard-beacons -> wait-beacons (+wxScheduler/libctxJspi
families); Makefile.wasm links test apps against JSPI with the shim as a
tracked link prerequisite.
Web: WasmTool setRo await + __wxWaitDump forensics, open-flow contained
promise, scheduler-shim.test.ts retargeted (8 green).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NDeBaKKhQztd8KiVtHuyXr
2026-08-13 07:06:24 +02:00
|
|
|
# ABSENT stamp = unknown provenance = same as a mismatch: an incremental build
|
|
|
|
|
# over objects of unknown backend produced a MIXED library once (jspi evtloop
|
|
|
|
|
# EM_JS in the glue next to live fiber dispatch — --allow-multiple-definition
|
|
|
|
|
# hid it and the editor took the fiber branch at runtime under JSPI).
|
|
|
|
|
if [ -d "$BUILD_DIR" ] && [ "$(cat "$BACKEND_STAMP" 2>/dev/null)" != "$CURRENT_BACKEND" ]; then
|
|
|
|
|
echo "=== Async backend changed ($(cat "$BACKEND_STAMP" 2>/dev/null || echo unknown) -> $CURRENT_BACKEND): forcing clean wx build ==="
|
|
|
|
|
CLEAN_BUILD=1
|
|
|
|
|
fi
|
|
|
|
|
|
2025-12-27 01:07:44 +01:00
|
|
|
# Use our config.sub wrapper for autoconf projects
|
|
|
|
|
# CONFIG_SHELL is critical: nested configures (pcre, etc.) do SHELL=${CONFIG_SHELL-/bin/sh}
|
|
|
|
|
# Without CONFIG_SHELL, nested configures would reset SHELL to /bin/sh and bypass our wrapper
|
|
|
|
|
export SHELL="$SCRIPT_DIR/config/config-sub-wrapper.sh"
|
|
|
|
|
export CONFIG_SHELL="$SCRIPT_DIR/config/config-sub-wrapper.sh"
|
|
|
|
|
|
2025-12-27 09:21:31 +01:00
|
|
|
# Disable autom4te cache to keep submodules clean
|
|
|
|
|
export AUTOM4TE="$SCRIPT_DIR/config/autom4te-wrapper.sh"
|
|
|
|
|
|
2026-06-12 10:18:16 +02:00
|
|
|
echo "=== Building wxWidgets for WASM ==="
|
2025-11-27 15:30:10 +01:00
|
|
|
echo "Project root: $PROJECT_ROOT"
|
|
|
|
|
echo "Build dir: $BUILD_DIR"
|
|
|
|
|
echo "wxWidgets source: $WX_SOURCE"
|
|
|
|
|
|
|
|
|
|
# Verify we're in the right place
|
2025-11-28 16:42:35 +01:00
|
|
|
if [ ! -f "$WX_SOURCE/configure.in" ]; then
|
2025-11-27 15:30:10 +01:00
|
|
|
echo "ERROR: wxWidgets source not found at $WX_SOURCE"
|
|
|
|
|
echo "Make sure the wxwidgets submodule is initialized"
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
|
2026-06-10 17:17:57 +02:00
|
|
|
# Regenerate configure if configure.in or autoconf_inc.m4 is newer.
|
|
|
|
|
# (configure.in sincludes autoconf_inc.m4, which bakefile regenerates from
|
|
|
|
|
# build/bakefiles/files.bkl — new build conditions live there.)
|
|
|
|
|
if [ "$WX_SOURCE/configure.in" -nt "$WX_SOURCE/configure" ] || \
|
|
|
|
|
[ "$WX_SOURCE/autoconf_inc.m4" -nt "$WX_SOURCE/configure" ]; then
|
|
|
|
|
echo "configure inputs changed, regenerating configure..."
|
2025-11-28 16:42:35 +01:00
|
|
|
(cd "$WX_SOURCE" && autoconf)
|
|
|
|
|
fi
|
|
|
|
|
|
2025-12-27 11:26:25 +01:00
|
|
|
# Incremental build by default, use --clean for full rebuild
|
2026-06-10 17:17:57 +02:00
|
|
|
if [ "$CLEAN_BUILD" = "1" ]; then
|
2025-12-27 11:26:25 +01:00
|
|
|
echo "Cleaning build directory..."
|
2025-11-27 15:30:10 +01:00
|
|
|
rm -rf "$BUILD_DIR"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Create build directory
|
|
|
|
|
mkdir -p "$BUILD_DIR"
|
|
|
|
|
cd "$BUILD_DIR"
|
|
|
|
|
|
2025-12-26 20:51:58 +01:00
|
|
|
# Determine if we need to run configure
|
|
|
|
|
# Skip configure if:
|
|
|
|
|
# 1. Makefile exists (already configured)
|
|
|
|
|
# 2. configure.in hasn't changed since last configure
|
|
|
|
|
NEEDS_CONFIGURE=0
|
|
|
|
|
if [ ! -f "$BUILD_DIR/Makefile" ]; then
|
|
|
|
|
echo "Not configured yet, will run configure..."
|
|
|
|
|
NEEDS_CONFIGURE=1
|
|
|
|
|
elif [ "$WX_SOURCE/configure.in" -nt "$BUILD_DIR/Makefile" ]; then
|
|
|
|
|
echo "configure.in changed since last configure, will reconfigure..."
|
|
|
|
|
NEEDS_CONFIGURE=1
|
|
|
|
|
elif [ "$WX_SOURCE/configure" -nt "$BUILD_DIR/Makefile" ]; then
|
|
|
|
|
echo "configure script changed, will reconfigure..."
|
|
|
|
|
NEEDS_CONFIGURE=1
|
2026-06-10 17:17:57 +02:00
|
|
|
elif [ "$WX_SOURCE/Makefile.in" -nt "$BUILD_DIR/Makefile" ]; then
|
|
|
|
|
# Makefile.in is regenerated from build/bakefiles/files.bkl (see header);
|
|
|
|
|
# the build Makefile must be re-derived or new source files are ignored.
|
|
|
|
|
echo "Makefile.in changed since last configure, will reconfigure..."
|
|
|
|
|
NEEDS_CONFIGURE=1
|
2025-12-12 14:51:38 +01:00
|
|
|
else
|
2025-12-26 20:51:58 +01:00
|
|
|
echo "Already configured, skipping configure (use clean build to reconfigure)"
|
2025-12-12 14:51:38 +01:00
|
|
|
fi
|
|
|
|
|
|
2025-12-26 20:51:58 +01:00
|
|
|
if [ $NEEDS_CONFIGURE -eq 1 ]; then
|
|
|
|
|
# Configure with emconfigure
|
|
|
|
|
# Key flags based on wxWidgets-wasm:
|
|
|
|
|
# --host=emscripten Host system (detected via config.sub)
|
|
|
|
|
# --disable-shared Build static libraries
|
|
|
|
|
# --with-opengl Enable OpenGL/WebGL support
|
|
|
|
|
# --enable-exceptions Enable C++ exceptions (needed for KiCad debug builds)
|
|
|
|
|
# --disable-richtext Not needed for KiCad, simplifies build
|
|
|
|
|
# --without-libtiff Avoid external dependencies
|
|
|
|
|
# --disable-xlocale Browser environment handles locale
|
|
|
|
|
|
2026-06-01 17:31:03 +02:00
|
|
|
kw_stage wxwidgets-configure
|
2025-12-26 20:51:58 +01:00
|
|
|
echo ""
|
|
|
|
|
echo "=== Configuring ==="
|
|
|
|
|
|
2025-12-27 15:28:20 +01:00
|
|
|
# Regenerate autotools files in bundled PCRE to fix version mismatch
|
|
|
|
|
# The bundled PCRE was generated with automake 1.16.1 but build systems
|
|
|
|
|
# may have different versions. Running autoreconf ensures compatibility.
|
|
|
|
|
if command -v autoreconf &> /dev/null; then
|
|
|
|
|
echo "Regenerating autotools files for bundled PCRE..."
|
|
|
|
|
(cd "$WX_SOURCE/3rdparty/pcre" && autoreconf -fi 2>/dev/null || true)
|
|
|
|
|
fi
|
|
|
|
|
|
2025-12-26 20:51:58 +01:00
|
|
|
# Ensure Emscripten's zlib port is built (works in Docker and on host)
|
|
|
|
|
# This populates the cache sysroot with zlib.h and libz.a
|
|
|
|
|
echo "Building Emscripten zlib port..."
|
|
|
|
|
embuilder build zlib
|
|
|
|
|
|
|
|
|
|
# Get Emscripten cache sysroot path (portable across environments)
|
|
|
|
|
EM_CACHE_SYSROOT="$(em-config CACHE)/sysroot"
|
|
|
|
|
echo "Emscripten cache sysroot: $EM_CACHE_SYSROOT"
|
|
|
|
|
|
|
|
|
|
# Set flags for Emscripten compatibility
|
|
|
|
|
# Z_HAVE_UNISTD_H ensures zlib includes <unistd.h> for read/write/lseek
|
|
|
|
|
# Include pcre2 headers from the build directory (generated during configure)
|
|
|
|
|
PCRE2_INCLUDE="$BUILD_DIR/3rdparty/pcre/src"
|
|
|
|
|
|
|
|
|
|
# Configure debug/release flags based on DEBUG_BUILD environment variable
|
|
|
|
|
if [ "${DEBUG_BUILD:-1}" = "1" ]; then
|
|
|
|
|
WX_DEBUG_FLAGS="-g -O1"
|
|
|
|
|
WX_CONFIGURE_DEBUG="--enable-debug"
|
|
|
|
|
echo "Building wxWidgets in DEBUG mode"
|
|
|
|
|
else
|
|
|
|
|
WX_DEBUG_FLAGS="-O2"
|
|
|
|
|
WX_CONFIGURE_DEBUG=""
|
|
|
|
|
echo "Building wxWidgets in RELEASE mode"
|
|
|
|
|
fi
|
|
|
|
|
|
2026-06-29 19:50:18 +02:00
|
|
|
# Exception model: native WebAssembly exceptions (legacy binary encoding) + wasm setjmp/longjmp,
|
jspi cleanup: remove the asyncify-era residue — dead code, conditionals, pipeline scaffolding, stale prose
The runtime is JSPI-only; this removes everything that still pretended
otherwise. Three exhaustive sweeps (C++/JS+build+CI/tests+docs) drove
the inventory; every deletion verified by grep closure + full gates.
Broken-right-now fixes:
- deploy-staging.yml passed the retired opt_level input — the workflow
could not even start. Removed.
- env.sh carried dead exports with a live -sASYNCIFY=1 inside
(WASM_LDFLAGS/PTHREAD_LDFLAGS, zero consumers). Removed; the
WASM_LEGACY_EXCEPTIONS rationale rewritten to the real reason.
- docker/build.sh exported PCBJAM_ASYNC_BACKEND (read nowhere). Gone.
Dead weight removed:
- binaryen submodule (nothing builds or invokes it), wasm-opt-bench
workflow + scripts/bench/, get-wasm-opt.sh, diagnostics.js (242 lines
of Asyncify-API-only code), the KICAD_PIPELINE background-postprocess
scaffolding (existed to parallelize the deleted wasm-opt phase; the
postprocess is a seconds-long node script and now runs inline),
build-monitor's dead asyncify rows, sched-context orphan build
output, dead .gitignore entries, the .jspi-assets spike dir (the two
wf-result research JSONs moved to docs/features/async/migration-evidence/).
- bindings: fiber_park.h + its 12 embind registrations (broken-if-
called under JSPI), the kicadOpenFileStart/OPEN_JOB starter route,
main_stack_runner.h + 5 includes, the always-null context-sleep weak
hook in nanosleep_yield.c.
- shim: the backend field (installed-flag idempotency instead),
noteContextWait (dead both sides), the __wxAsyncifyDump alias (+ the
WasmTool fallback and string-dump normalize branch).
- web: the emscripten-6-ignored mainScriptUrlOrBlob option in boot.ts
(gerber-demo keeps it: it loads the deployed CDN release, which
predates emscripten 6 — noted inline).
Conditionals: all 'backend === jspi' checks reduced to scheduler-
presence checks; races_quiescent re-keyed from Asyncify.state (vacuous)
to real backlog quiescence (resumeReady/mutatorQueue — NOT _windowLive,
which is the probing activation's own window by definition).
Renames (identifiers only, no file renames): ASYNC_LINK_FLAGS→
JSPI_LINK_FLAGS and Makefile ASYNC_LDFLAGS→JSPI_LDFLAGS,
kicadCollabFiberBusy→kicadCollabBusy (embind + web + tests),
collab_common.h fiber*→apply*/coroutine naming, asyncifySignatures→
wasmTrapSignatures (lists byte-identical).
Tests: the two remaining vacuous [wx-asyncify]/fiber-resume-refused
asserts re-keyed to live JSPI beacons; eeschema-load's failure message
no longer sends the developer to a deleted script; wait-beacons' dead
families/parser deleted; lane-0 legacy-glue guards removed (lane 0 is
unconstructible); the embind test.fail re-gated with the JSPI reason
(plain embind invokers cannot suspend — verified still failing);
lint-determinism now scans tests/jspi (166 files clean);
eeschema-collab local-move gated to chromium (~50% flaky on FF even
solo; pcbnew twin covers both engines).
Docs: DEBUG.md rewritten as the JSPI debugging guide; build.md
describes the single-phase build; docs/features/async/README.md
banner-marked historical and repointed at the NEW
23-jspi-runtime.md (current architecture: export census, turnstile,
libcontext ownership + refusal contract, embind call shapes, the
em-pthread service-wrapper trick, exception policy, known gaps).
Gates on the cleaned tree: test:e2e 725 passed / 0 failed (after the
quiescence-probe fix; the 3 other reds were verified contention flakes
solo-green or the documented FF gate), web 76/0, jspi 18/18 both
engines, vitest 295/295 + 17/17, all lints green, live-app census
clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016X9eh1s5sTx1o9Em9KBuwR
2026-08-14 09:25:32 +02:00
|
|
|
# single-sourced from scripts/common/env.sh. -sWASM_LEGACY_EXCEPTIONS=1 pins the EH binary
|
|
|
|
|
# encoding (exnref is not adopted across our pinned emsdk/browsers). See docs/features/wasm-exceptions/.
|
2026-06-29 19:50:18 +02:00
|
|
|
WX_EH_FLAGS="$DEPS_EH_FLAGS"
|
|
|
|
|
echo "wx EH model flags: ${WX_EH_FLAGS}"
|
|
|
|
|
|
2025-12-26 20:51:58 +01:00
|
|
|
# Include emscripten cache sysroot for zlib headers
|
2026-06-29 19:50:18 +02:00
|
|
|
export CFLAGS="-DZ_HAVE_UNISTD_H=1 -I$EM_CACHE_SYSROOT/include ${WX_DEBUG_FLAGS} ${WX_EH_FLAGS} -pthread -matomics -mbulk-memory"
|
|
|
|
|
export CXXFLAGS="-DZ_HAVE_UNISTD_H=1 -I$EM_CACHE_SYSROOT/include -I$PCRE2_INCLUDE ${WX_DEBUG_FLAGS} ${WX_EH_FLAGS} -pthread -matomics -mbulk-memory"
|
2025-12-26 20:51:58 +01:00
|
|
|
export LDFLAGS="-L$EM_CACHE_SYSROOT/lib/wasm32-emscripten"
|
|
|
|
|
|
|
|
|
|
emconfigure "$WX_SOURCE/configure" \
|
|
|
|
|
--host=emscripten \
|
|
|
|
|
--without-subdirs \
|
|
|
|
|
--disable-shared \
|
|
|
|
|
--with-opengl \
|
|
|
|
|
--enable-exceptions \
|
|
|
|
|
--disable-richtext \
|
|
|
|
|
--without-libtiff \
|
|
|
|
|
--disable-xlocale \
|
|
|
|
|
--with-cxx=17 \
|
|
|
|
|
--enable-utf8 \
|
|
|
|
|
--with-zlib=sys \
|
jspi: migration phases 0-7 — build knob, scheduler shim, test successor suite
Toolchain: emsdk 6.0.6 (versions.sh; cache-hash keys on it). Build knob
PCBJAM_ASYNC_BACKEND=jspi|asyncify: build-kicad-target.sh links editors with
-sJSPI + -sJSPI_EXPORTS=@scripts/common/jspi-exports.txt + --pre-js
jspi-scheduler.js (no DYNCALLS, no post-link asyncify pipeline); wx build
stamps the backend and forces clean on flip or unknown provenance;
docker/build.sh passes the knob, seeds the emscripten ports cache from the
volume every launch, jspi postprocess = patch-env-shim only.
scripts/common/shims/jspi-scheduler.js: the JSPI successor scheduler —
token-wait registry, resume turnstile (one armed resume between engine
re-entries, SP swaps only at microtask boundaries), green-region spill
stacks (16-aligned tops), S1 embind mutator FIFO lane + parker wraps, S6
shutdown, libctx integration hooks (suspend/end/quarantine + g_current
arm/clear), SuspendError attributor, lost-wake + stuck-window watchdogs,
__wxWaitDump observability.
Embind: PARKER registrations get emscripten::async() under PCBJAM_JSPI
(wasm/bindings/pcbjam_async_policy.h). nanosleep yields route via the shim.
Tests: tests/asyncify -> tests/jspi successor suite (jspi-stack red/green
shadow-stack battery, jspi-coroutine MiniCoro harness, suspend-races
semantic scenarios + __wxWaitDump books coherence); projects jspi-firefox/
jspi-chrome (asyncify-webkit retired — no JSPI in WebKit); unconditional
Firefox JSPI pref; guard-beacons -> wait-beacons (+wxScheduler/libctxJspi
families); Makefile.wasm links test apps against JSPI with the shim as a
tracked link prerequisite.
Web: WasmTool setRo await + __wxWaitDump forensics, open-flow contained
promise, scheduler-shim.test.ts retargeted (8 green).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NDeBaKKhQztd8KiVtHuyXr
2026-08-13 07:06:24 +02:00
|
|
|
--with-regex=builtin \
|
2025-12-26 20:51:58 +01:00
|
|
|
${WX_CONFIGURE_DEBUG}
|
jspi: migration phases 0-7 — build knob, scheduler shim, test successor suite
Toolchain: emsdk 6.0.6 (versions.sh; cache-hash keys on it). Build knob
PCBJAM_ASYNC_BACKEND=jspi|asyncify: build-kicad-target.sh links editors with
-sJSPI + -sJSPI_EXPORTS=@scripts/common/jspi-exports.txt + --pre-js
jspi-scheduler.js (no DYNCALLS, no post-link asyncify pipeline); wx build
stamps the backend and forces clean on flip or unknown provenance;
docker/build.sh passes the knob, seeds the emscripten ports cache from the
volume every launch, jspi postprocess = patch-env-shim only.
scripts/common/shims/jspi-scheduler.js: the JSPI successor scheduler —
token-wait registry, resume turnstile (one armed resume between engine
re-entries, SP swaps only at microtask boundaries), green-region spill
stacks (16-aligned tops), S1 embind mutator FIFO lane + parker wraps, S6
shutdown, libctx integration hooks (suspend/end/quarantine + g_current
arm/clear), SuspendError attributor, lost-wake + stuck-window watchdogs,
__wxWaitDump observability.
Embind: PARKER registrations get emscripten::async() under PCBJAM_JSPI
(wasm/bindings/pcbjam_async_policy.h). nanosleep yields route via the shim.
Tests: tests/asyncify -> tests/jspi successor suite (jspi-stack red/green
shadow-stack battery, jspi-coroutine MiniCoro harness, suspend-races
semantic scenarios + __wxWaitDump books coherence); projects jspi-firefox/
jspi-chrome (asyncify-webkit retired — no JSPI in WebKit); unconditional
Firefox JSPI pref; guard-beacons -> wait-beacons (+wxScheduler/libctxJspi
families); Makefile.wasm links test apps against JSPI with the shim as a
tracked link prerequisite.
Web: WasmTool setRo await + __wxWaitDump forensics, open-flow contained
promise, scheduler-shim.test.ts retargeted (8 green).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NDeBaKKhQztd8KiVtHuyXr
2026-08-13 07:06:24 +02:00
|
|
|
# --with-regex=builtin: without it a host pkg-config can find the host's
|
|
|
|
|
# pcre2 (homebrew on macOS) and configure silently picks "regex sys", which
|
|
|
|
|
# skips the bundled 3rdparty/pcre build dir the next step requires.
|
2025-12-26 20:51:58 +01:00
|
|
|
|
|
|
|
|
# Build PCRE first to avoid race condition with parallel builds
|
|
|
|
|
# PCRE headers (pcre2.h) must be generated before regex.cpp compiles
|
|
|
|
|
echo ""
|
|
|
|
|
echo "=== Building PCRE first (dependency) ==="
|
perf(build): use all cores in host builds — wx lib, test apps, gal-webgl
env.sh exports a docker-safe JOBS=1 default, and every host build silently
inherited it (the docker kicad path dodges it by passing -j explicitly):
- build-wx-wasm.sh: the intended "${JOBS:-nproc}" fallback sat BELOW the
env.sh source, so it was dead code — full wx builds ran make -j1 on the
Mac and on CI. Compute the all-cores default before env.sh instead
(explicit JOBS/PARALLEL_JOBS still wins); also -j the PCRE pre-build.
- build-wasm-test.sh: same default fix, plus fan the post-link
hoist+asyncify loop out across JOBS with xargs -P. Per-app wasm-opt
can't feed many cores (small modules), so serial stays ~4min even with
BINARYEN_CORES=16; fanning across the 74 independent apps is what
scales. Safe: apply-asyncify is in-place per wasm, injector tmp is
per-js, HOIST_WASMOPT resolved once up front.
- build-gal-webgl-test.sh: make had no -j at all.
Measured on a 16-core M4 Max, clean test-app build: 15m36s (-j1) → 2m08s,
peak RAM 10.7 GB summed across all build processes (64 GB machine; CI
runners have 120 GB). Core counts are always derived from nproc at
runtime — nothing hardcoded, CI workers differ.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XeiSRRScdaox5jBueJNcyG
2026-07-02 10:54:14 +02:00
|
|
|
# Serial relative to the main build (it fully completes first); parallel inside.
|
|
|
|
|
emmake make -j${JOBS} -C 3rdparty/pcre
|
2025-12-26 20:51:58 +01:00
|
|
|
fi
|
2025-11-28 16:42:35 +01:00
|
|
|
|
2026-07-02 09:59:32 +02:00
|
|
|
# Ensure the Emscripten zlib port exists before compiling. We configure with
|
|
|
|
|
# --with-zlib=sys, which resolves zlib.h/libz.a to the emsdk cache sysroot —
|
|
|
|
|
# populated only by `embuilder build zlib` in the configure branch above. When
|
|
|
|
|
# the build dir arrives pre-configured (e.g. CI restores build-wasm/wxwidgets
|
|
|
|
|
# from cache onto a runner with a freshly installed emsdk), configure is
|
|
|
|
|
# skipped and any recompile of a zlib-using TU (zipstrm.cpp, zstream.cpp, ...)
|
|
|
|
|
# fails with "'zlib.h' file not found" — deterministically, so the serial
|
|
|
|
|
# retry below can't clear it.
|
|
|
|
|
EM_CACHE_SYSROOT="$(em-config CACHE)/sysroot"
|
|
|
|
|
if [ ! -f "$EM_CACHE_SYSROOT/include/zlib.h" ]; then
|
|
|
|
|
echo "Emscripten zlib port missing from $EM_CACHE_SYSROOT; building it..."
|
|
|
|
|
# --force: embuilder stamps the port as built on libz.a alone, so a
|
|
|
|
|
# half-populated cache (lib present, headers gone) would no-op without it.
|
|
|
|
|
embuilder build zlib --force
|
|
|
|
|
fi
|
|
|
|
|
|
2025-11-28 16:42:35 +01:00
|
|
|
# Build wxWidgets
|
2026-06-01 17:31:03 +02:00
|
|
|
kw_stage wxwidgets-compile
|
2025-11-27 15:30:10 +01:00
|
|
|
echo ""
|
2025-12-12 14:51:38 +01:00
|
|
|
echo "=== Building wxWidgets (using ${JOBS} parallel jobs) ==="
|
2026-06-13 23:15:49 +02:00
|
|
|
# A clean -jN build occasionally fails non-deterministically: a burst of GUI
|
|
|
|
|
# translation units (toplevel.cpp, dirdlgg.cpp, the generic colour/dir dialogs,
|
|
|
|
|
# ...) all abort at once with bogus "incomplete type 'wxBitmap'" / "wxIcon has
|
|
|
|
|
# no member IsOk" / "unknown type 'wxTranslations'" errors, while neighbouring
|
|
|
|
|
# files compile fine. It never happens at -j1 (the host default) — it's a
|
|
|
|
|
# parallel-build race over a generated/regenerated header (config.status can
|
|
|
|
|
# re-emit wx/setup.h, and emscripten warms its cache on first use), so some
|
|
|
|
|
# compiles read a file mid-rewrite. The same source builds cleanly on a retry.
|
|
|
|
|
# Rather than serialize the whole (slow) build, fall back to a serial pass only
|
|
|
|
|
# when the parallel one trips: it resumes from the already-built objects, so it
|
|
|
|
|
# just recompiles the few that lost the race. A genuine error still fails the
|
|
|
|
|
# -j1 pass and surfaces. (Same spirit as the serial PCRE pre-build above.)
|
|
|
|
|
if ! emmake make -j${JOBS}; then
|
|
|
|
|
echo ""
|
|
|
|
|
echo "=== Parallel build failed; retrying serially to clear the clean-build race ==="
|
|
|
|
|
emmake make -j1
|
|
|
|
|
fi
|
2025-11-27 15:30:10 +01:00
|
|
|
|
2025-12-08 18:26:57 +01:00
|
|
|
# Create library symlinks (remove -emscripten suffix for CMake compatibility)
|
|
|
|
|
echo ""
|
|
|
|
|
echo "=== Creating library symlinks ==="
|
|
|
|
|
cd "$BUILD_DIR/lib"
|
|
|
|
|
for lib in *-emscripten.a; do
|
2025-12-14 15:33:54 +01:00
|
|
|
# Skip symlinks - only process real files (stub symlinks are handled below)
|
|
|
|
|
if [ -f "$lib" ] && [ ! -L "$lib" ]; then
|
2025-12-08 18:26:57 +01:00
|
|
|
newname="${lib/-emscripten/}"
|
2025-12-14 15:33:54 +01:00
|
|
|
rm -f "$newname" # Remove existing symlink/file to avoid conflicts
|
2025-12-08 18:26:57 +01:00
|
|
|
ln -sf "$lib" "$newname"
|
|
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
# Create stub libraries for components wx-config reports but we didn't build
|
|
|
|
|
# KiCad doesn't use these directly
|
|
|
|
|
echo "Creating stub libraries..."
|
|
|
|
|
for stub in richtext webview; do
|
2025-12-14 15:33:54 +01:00
|
|
|
# Remove any existing symlinks first to avoid "same file" errors
|
2026-06-10 17:17:57 +02:00
|
|
|
rm -f "${WXLIB_PREFIX}_${stub}-3.2.a" "${WXLIB_PREFIX}_${stub}-3.2-emscripten.a"
|
|
|
|
|
emar rcs "${WXLIB_PREFIX}_${stub}-3.2.a"
|
|
|
|
|
ln -sf "${WXLIB_PREFIX}_${stub}-3.2.a" "${WXLIB_PREFIX}_${stub}-3.2-emscripten.a"
|
2025-12-08 18:26:57 +01:00
|
|
|
done
|
|
|
|
|
|
|
|
|
|
cd "$BUILD_DIR"
|
|
|
|
|
|
2025-11-27 15:30:10 +01:00
|
|
|
echo ""
|
jspi: migration phases 0-7 — build knob, scheduler shim, test successor suite
Toolchain: emsdk 6.0.6 (versions.sh; cache-hash keys on it). Build knob
PCBJAM_ASYNC_BACKEND=jspi|asyncify: build-kicad-target.sh links editors with
-sJSPI + -sJSPI_EXPORTS=@scripts/common/jspi-exports.txt + --pre-js
jspi-scheduler.js (no DYNCALLS, no post-link asyncify pipeline); wx build
stamps the backend and forces clean on flip or unknown provenance;
docker/build.sh passes the knob, seeds the emscripten ports cache from the
volume every launch, jspi postprocess = patch-env-shim only.
scripts/common/shims/jspi-scheduler.js: the JSPI successor scheduler —
token-wait registry, resume turnstile (one armed resume between engine
re-entries, SP swaps only at microtask boundaries), green-region spill
stacks (16-aligned tops), S1 embind mutator FIFO lane + parker wraps, S6
shutdown, libctx integration hooks (suspend/end/quarantine + g_current
arm/clear), SuspendError attributor, lost-wake + stuck-window watchdogs,
__wxWaitDump observability.
Embind: PARKER registrations get emscripten::async() under PCBJAM_JSPI
(wasm/bindings/pcbjam_async_policy.h). nanosleep yields route via the shim.
Tests: tests/asyncify -> tests/jspi successor suite (jspi-stack red/green
shadow-stack battery, jspi-coroutine MiniCoro harness, suspend-races
semantic scenarios + __wxWaitDump books coherence); projects jspi-firefox/
jspi-chrome (asyncify-webkit retired — no JSPI in WebKit); unconditional
Firefox JSPI pref; guard-beacons -> wait-beacons (+wxScheduler/libctxJspi
families); Makefile.wasm links test apps against JSPI with the shim as a
tracked link prerequisite.
Web: WasmTool setRo await + __wxWaitDump forensics, open-flow contained
promise, scheduler-shim.test.ts retargeted (8 green).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NDeBaKKhQztd8KiVtHuyXr
2026-08-13 07:06:24 +02:00
|
|
|
mkdir -p "$BUILD_DIR" && printf %s "$CURRENT_BACKEND" > "$BACKEND_STAMP"
|
2025-11-27 15:30:10 +01:00
|
|
|
echo "=== Build complete ==="
|
|
|
|
|
ls -lh "$BUILD_DIR"/lib/*.a 2>/dev/null || echo "Libraries built in $BUILD_DIR/lib"
|