pcbjam/scripts/build-wxuniversal-wasm.sh
Viktor Vaczi b46b4b69f1 fix(wasm): Fix asyncify rewind with asyncify-aware dynCall shims
Emscripten 4.x removed dynCall_* WASM exports, breaking asyncify
rewind through indirect calls (modal dialogs, event handlers).
Generate JS shims that track Asyncify.exportCallStack and register
in wasmExports so doRewind can find them.

Also fixes empty callback functions ((() => {})) generated by
Emscripten 4.x + pthreads for HTML5 events, pthread entry,
sighandler, async timer, and main loop callbacks.

Build pipeline improvements:
- Stub wasm-opt/finalize in Docker (RAM limits), run on host
- Add setup-emsdk.sh for reproducible Emscripten setup
- Simplify env.sh and version management

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 12:26:48 +01:00

196 lines
7.3 KiB
Shell
Executable file

#!/bin/bash
# Build wxWidgets with wxUniversal for WebAssembly
# Redirect all output to a log file (re-execs script with redirection)
source "$(dirname "$0")/common/logging.sh"
# Source common environment (sets up local emsdk)
source "$(dirname "$0")/common/env.sh"
# This builds the GUI-enabled wxWidgets needed for KiCad
#
# Prerequisites:
# - Emscripten SDK (auto-installed by env.sh via scripts/setup-emsdk.sh)
# - 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:
# ./build-wxuniversal-wasm.sh # Incremental build (default)
# ./build-wxuniversal-wasm.sh --clean # Clean build from scratch
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
BUILD_DIR="$PROJECT_ROOT/build-wasm/wxwidgets-universal"
WX_SOURCE="$PROJECT_ROOT/wxwidgets"
# 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"
# Disable autom4te cache to keep submodules clean
export AUTOM4TE="$SCRIPT_DIR/config/autom4te-wrapper.sh"
# Use JOBS from env.sh if set, otherwise use all available cores
JOBS="${JOBS:-$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)}"
echo "=== Building wxWidgets wxUniversal for WASM ==="
echo "Project root: $PROJECT_ROOT"
echo "Build dir: $BUILD_DIR"
echo "wxWidgets source: $WX_SOURCE"
# Verify we're in the right place
if [ ! -f "$WX_SOURCE/configure.in" ]; then
echo "ERROR: wxWidgets source not found at $WX_SOURCE"
echo "Make sure the wxwidgets submodule is initialized"
exit 1
fi
# Regenerate configure if configure.in is newer
if [ "$WX_SOURCE/configure.in" -nt "$WX_SOURCE/configure" ]; then
echo "configure.in is newer than configure, regenerating..."
(cd "$WX_SOURCE" && autoconf)
fi
# Incremental build by default, use --clean for full rebuild
if [ "$1" = "--clean" ]; then
echo "Cleaning build directory..."
rm -rf "$BUILD_DIR"
fi
# Create build directory
mkdir -p "$BUILD_DIR"
cd "$BUILD_DIR"
# 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
else
echo "Already configured, skipping configure (use clean build to reconfigure)"
fi
if [ $NEEDS_CONFIGURE -eq 1 ]; then
# Configure with emconfigure
# Key flags based on wxWidgets-wasm:
# --host=emscripten Host system (detected via config.sub)
# --enable-universal Use wxUniversal (draws widgets directly)
# --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
echo ""
echo "=== Configuring ==="
# 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
# 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
# Include emscripten cache sysroot for zlib headers
export CFLAGS="-DZ_HAVE_UNISTD_H=1 -I$EM_CACHE_SYSROOT/include ${WX_DEBUG_FLAGS} -fexceptions -pthread -matomics -mbulk-memory"
export CXXFLAGS="-DZ_HAVE_UNISTD_H=1 -I$EM_CACHE_SYSROOT/include -I$PCRE2_INCLUDE ${WX_DEBUG_FLAGS} -fexceptions -pthread -matomics -mbulk-memory"
export LDFLAGS="-L$EM_CACHE_SYSROOT/lib/wasm32-emscripten"
emconfigure "$WX_SOURCE/configure" \
--host=emscripten \
--without-subdirs \
--enable-universal \
--disable-shared \
--with-opengl \
--enable-exceptions \
--disable-richtext \
--without-libtiff \
--disable-xlocale \
--with-cxx=17 \
--enable-utf8 \
--with-zlib=sys \
${WX_CONFIGURE_DEBUG}
# 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) ==="
emmake make -C 3rdparty/pcre
fi
# Build wxWidgets
echo ""
echo "=== Building wxWidgets (using ${JOBS} parallel jobs) ==="
emmake make -j${JOBS}
# Create library symlinks (remove -emscripten suffix for CMake compatibility)
echo ""
echo "=== Creating library symlinks ==="
cd "$BUILD_DIR/lib"
for lib in *-emscripten.a; do
# Skip symlinks - only process real files (stub symlinks are handled below)
if [ -f "$lib" ] && [ ! -L "$lib" ]; then
newname="${lib/-emscripten/}"
rm -f "$newname" # Remove existing symlink/file to avoid conflicts
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
# Remove any existing symlinks first to avoid "same file" errors
rm -f "libwx_wasmunivu_${stub}-3.2.a" "libwx_wasmunivu_${stub}-3.2-emscripten.a"
emar rcs "libwx_wasmunivu_${stub}-3.2.a"
ln -sf "libwx_wasmunivu_${stub}-3.2.a" "libwx_wasmunivu_${stub}-3.2-emscripten.a"
done
cd "$BUILD_DIR"
echo ""
echo "=== Build complete ==="
ls -lh "$BUILD_DIR"/lib/*.a 2>/dev/null || echo "Libraries built in $BUILD_DIR/lib"