Enable exceptions and pthreads atomics for KiCad WASM build

Build fixes:
- Enable C++ exceptions in wxWidgets (--enable-exceptions, -fexceptions)
  Required for KiCad's OnExceptionInMainLoop override in debug builds
- Add -matomics -mbulk-memory to all dependency builds for shared memory
  Required for pthreads support with Emscripten
- Fix docker/build.sh output copy path (pcbnew/ not bin/)
- Make workspace mount read-only in docker-compose.yml

Updated dependency scripts:
- build-freetype.sh, build-harfbuzz.sh, build-protobuf.sh
- build-zstd.sh, build-opencascade.sh, build-boost.sh
- build-pixman.sh, build-cairo.sh (meson cross-file)
- build-wxuniversal-wasm.sh, build-pcbnew.sh

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Viktor Vaczi 2025-12-13 11:29:45 +01:00
commit d8d934ffca
13 changed files with 100 additions and 34 deletions

View file

@ -59,7 +59,7 @@ cd "$BUILD_DIR"
# --enable-universal Use wxUniversal (draws widgets directly)
# --disable-shared Build static libraries
# --with-opengl Enable OpenGL/WebGL support
# --disable-exceptions Emscripten doesn't support C++ exceptions well
# --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
@ -83,8 +83,8 @@ else
echo "Building wxWidgets in RELEASE mode"
fi
export CFLAGS="-DZ_HAVE_UNISTD_H=1 ${WX_DEBUG_FLAGS}"
export CXXFLAGS="-DZ_HAVE_UNISTD_H=1 -I$PCRE2_INCLUDE ${WX_DEBUG_FLAGS}"
export CFLAGS="-DZ_HAVE_UNISTD_H=1 ${WX_DEBUG_FLAGS} -fexceptions -pthread -matomics -mbulk-memory"
export CXXFLAGS="-DZ_HAVE_UNISTD_H=1 -I$PCRE2_INCLUDE ${WX_DEBUG_FLAGS} -fexceptions -pthread -matomics -mbulk-memory"
emconfigure "$WX_SOURCE/configure" \
--host=emscripten \
@ -92,7 +92,7 @@ emconfigure "$WX_SOURCE/configure" \
--enable-universal \
--disable-shared \
--with-opengl \
--disable-exceptions \
--enable-exceptions \
--disable-richtext \
--without-libtiff \
--disable-xlocale \

View file

@ -43,6 +43,7 @@ log_info "=== Phase 1: Header-only libraries ==="
log_info "=== Phase 2: Basic compression/serialization ==="
"${SCRIPT_DIR}/build-zstd.sh" ${CLEAN}
"${SCRIPT_DIR}/build-protobuf.sh" ${CLEAN}
"${SCRIPT_DIR}/build-boost.sh" ${CLEAN}
# 3. Font rendering stack
log_info "=== Phase 3: Font rendering ==="
@ -54,6 +55,11 @@ log_info "=== Phase 4: Graphics libraries ==="
"${SCRIPT_DIR}/build-pixman.sh" ${CLEAN}
"${SCRIPT_DIR}/build-cairo.sh" ${CLEAN}
# 4b. Stub headers (needed for KiCad compilation but functionality is stubbed)
log_info "=== Phase 4b: Stub dependencies (headers only) ==="
"${SCRIPT_DIR}/build-curl-headers.sh" ${CLEAN}
"${SCRIPT_DIR}/build-libgit2-headers.sh" ${CLEAN}
# 5. Optional heavy dependencies
if [ $WITH_OCC -eq 1 ]; then
log_info "=== Phase 5a: OpenCASCADE (3D/STEP support) ==="

View file

@ -76,7 +76,7 @@ fi
cat > user-config.jam << EOF
using clang : emscripten
: em++
: <cxxflags>"${BOOST_DEBUG_FLAGS} -pthread"
: <cxxflags>"${BOOST_DEBUG_FLAGS} -pthread -matomics -mbulk-memory"
<linkflags>"-pthread"
;
EOF
@ -94,7 +94,7 @@ log_info "Building Boost.Locale (${BOOST_VARIANT})..."
link=static \
threading=multi \
runtime-link=static \
cxxflags="${BOOST_DEBUG_FLAGS} -pthread" \
cxxflags="${BOOST_DEBUG_FLAGS} -pthread -matomics -mbulk-memory" \
install
create_stamp "${BOOST_STAMP}"

View file

@ -82,15 +82,19 @@ cpu = 'wasm32'
endian = 'little'
[properties]
# Prevent finding system libraries when cross-compiling
sys_root = '${SYSROOT}'
# Don't set sys_root - it gets prepended to pkg-config paths which are already absolute
# Just use pkg_config_libdir to control where we find .pc files
pkg_config_libdir = '${SYSROOT}/lib/pkgconfig'
[built-in options]
default_library = 'static'
b_staticpic = false
b_pie = false
c_args = [${MESON_DEBUG_FLAGS}, '-pthread', '-I${SYSROOT}/include', '-I${SYSROOT}/include/freetype2', '-I${SYSROOT}/include/pixman-1']
# Emscripten has these functions but meson checks fail - provide HAVE_ defines
# to prevent Cairo from defining its own conflicting implementations
# Include ft2build.h and ftcolor.h to fix FT_Color forward declaration bug in cairo-ft-private.h
# (the forward declaration is inside HAVE_FT_SVG_DOCUMENT but used in HAVE_FT_COLR_V1)
c_args = [${MESON_DEBUG_FLAGS}, '-pthread', '-matomics', '-mbulk-memory', '-I${SYSROOT}/include', '-I${SYSROOT}/include/freetype2', '-I${SYSROOT}/include/pixman-1', '-DHAVE_CTIME_R=1', '-DHAVE_LOCALTIME_R=1', '-DHAVE_GMTIME_R=1', '-DHAVE_STRNDUP=1', '-include', 'ft2build.h', '-include', 'freetype/ftcolor.h']
c_link_args = ['-pthread', '-L${SYSROOT}/lib']
pkg_config_path = '${SYSROOT}/lib/pkgconfig'
EOF
@ -119,8 +123,38 @@ meson setup "${CAIRO_DIR}" \
-Dlibpng:default_library=static
# JOBS is set in env.sh (default: 1 for sequential builds, use -j N to override)
ninja -j${JOBS}
ninja install
# Build only the library targets we need - utility executables fail to link and aren't needed
ninja -j${JOBS} src/libcairo.a
# Manual installation - ninja install tries to build everything including utilities
# which fail due to freetype atomics issues
log_info "Installing Cairo library and headers..."
mkdir -p "${SYSROOT}/lib" "${SYSROOT}/include/cairo" "${SYSROOT}/lib/pkgconfig"
cp src/libcairo.a "${SYSROOT}/lib/"
cp "${CAIRO_DIR}/src/cairo.h" "${SYSROOT}/include/cairo/"
cp "${CAIRO_DIR}/src/cairo-deprecated.h" "${SYSROOT}/include/cairo/"
cp "${CAIRO_DIR}/src/cairo-version.h" "${SYSROOT}/include/cairo/"
# cairo-features.h is generated during configure in the build directory
cp src/cairo-features.h "${SYSROOT}/include/cairo/"
cp "${CAIRO_DIR}/src/cairo-ft.h" "${SYSROOT}/include/cairo/"
cp "${CAIRO_DIR}/src/cairo-pdf.h" "${SYSROOT}/include/cairo/"
cp "${CAIRO_DIR}/src/cairo-ps.h" "${SYSROOT}/include/cairo/"
cp "${CAIRO_DIR}/src/cairo-script.h" "${SYSROOT}/include/cairo/"
# Create pkg-config file
cat > "${SYSROOT}/lib/pkgconfig/cairo.pc" << PKGEOF
prefix=${SYSROOT}
exec_prefix=\${prefix}
libdir=\${prefix}/lib
includedir=\${prefix}/include
Name: cairo
Description: Multi-platform 2D graphics library
Version: 1.18.0
Libs: -L\${libdir} -lcairo
Cflags: -I\${includedir}/cairo
Requires.private: freetype2 pixman-1
PKGEOF
create_stamp "${CAIRO_STAMP}"
log_info "Cairo build complete!"

View file

@ -56,8 +56,8 @@ emcmake cmake "${FREETYPE_DIR}" \
-DCMAKE_BUILD_TYPE=${BUILD_TYPE:-Debug} \
-DCMAKE_INSTALL_PREFIX="${SYSROOT}" \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DCMAKE_C_FLAGS="${DEBUG_CFLAGS:--g -O0}" \
-DCMAKE_CXX_FLAGS="${DEBUG_CFLAGS:--g -O0}" \
-DCMAKE_C_FLAGS="${DEBUG_CFLAGS:--g -O0} -pthread -matomics -mbulk-memory" \
-DCMAKE_CXX_FLAGS="${DEBUG_CFLAGS:--g -O0} -pthread -matomics -mbulk-memory" \
-DFT_DISABLE_BZIP2=ON \
-DFT_DISABLE_BROTLI=ON \
-DFT_DISABLE_HARFBUZZ=ON \
@ -68,5 +68,12 @@ emcmake cmake "${FREETYPE_DIR}" \
emmake make -j${JOBS}
emmake make install
# Create symlink without debug suffix for pkg-config compatibility
# FreeType CMake adds 'd' suffix for Debug builds but pkg-config expects 'libfreetype'
if [ -f "${SYSROOT}/lib/libfreetyped.a" ] && [ ! -f "${SYSROOT}/lib/libfreetype.a" ]; then
ln -sf libfreetyped.a "${SYSROOT}/lib/libfreetype.a"
log_info "Created libfreetype.a symlink for pkg-config"
fi
create_stamp "${FREETYPE_STAMP}"
log_info "FreeType build complete!"

View file

@ -60,8 +60,8 @@ emcmake cmake "${HARFBUZZ_DIR}" \
-DCMAKE_BUILD_TYPE=${BUILD_TYPE:-Debug} \
-DCMAKE_INSTALL_PREFIX="${SYSROOT}" \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DCMAKE_C_FLAGS="${DEBUG_CFLAGS:--g -O0}" \
-DCMAKE_CXX_FLAGS="${DEBUG_CFLAGS:--g -O0}" \
-DCMAKE_C_FLAGS="${DEBUG_CFLAGS:--g -O0} -pthread -matomics -mbulk-memory" \
-DCMAKE_CXX_FLAGS="${DEBUG_CFLAGS:--g -O0} -pthread -matomics -mbulk-memory" \
-DHB_HAVE_FREETYPE=ON \
-DHB_HAVE_GLIB=OFF \
-DHB_HAVE_ICU=OFF \

View file

@ -63,8 +63,8 @@ emcmake cmake "${OCC_DIR}" \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DCMAKE_BUILD_TYPE=${BUILD_TYPE:-Debug} \
-DCMAKE_INSTALL_PREFIX="${SYSROOT}" \
-DCMAKE_CXX_FLAGS="${DEBUG_CFLAGS:--g -O0} -pthread" \
-DCMAKE_C_FLAGS="${DEBUG_CFLAGS:--g -O0} -pthread" \
-DCMAKE_CXX_FLAGS="${DEBUG_CFLAGS:--g -O0} -pthread -matomics -mbulk-memory" \
-DCMAKE_C_FLAGS="${DEBUG_CFLAGS:--g -O0} -pthread -matomics -mbulk-memory" \
-DBUILD_LIBRARY_TYPE=Static \
-DBUILD_MODULE_ApplicationFramework=OFF \
-DBUILD_MODULE_Draw=OFF \

View file

@ -77,7 +77,7 @@ cpu = 'wasm32'
endian = 'little'
[built-in options]
c_args = [${MESON_DEBUG_FLAGS}, '-pthread']
c_args = [${MESON_DEBUG_FLAGS}, '-pthread', '-matomics', '-mbulk-memory']
c_link_args = ['-pthread']
EOF

View file

@ -57,8 +57,8 @@ cd "${PROTOBUF_BUILD}"
emcmake cmake "${PROTOBUF_DIR}" \
-DCMAKE_BUILD_TYPE=${BUILD_TYPE:-Debug} \
-DCMAKE_INSTALL_PREFIX="${SYSROOT}" \
-DCMAKE_C_FLAGS="${DEBUG_CFLAGS:--g -O0} -pthread" \
-DCMAKE_CXX_FLAGS="${DEBUG_CFLAGS:--g -O0} -pthread" \
-DCMAKE_C_FLAGS="${DEBUG_CFLAGS:--g -O0} -pthread -matomics -mbulk-memory" \
-DCMAKE_CXX_FLAGS="${DEBUG_CFLAGS:--g -O0} -pthread -matomics -mbulk-memory" \
-Dprotobuf_BUILD_TESTS=OFF \
-Dprotobuf_BUILD_EXAMPLES=OFF \
-Dprotobuf_BUILD_PROTOC_BINARIES=OFF \
@ -68,5 +68,16 @@ emcmake cmake "${PROTOBUF_DIR}" \
emmake make -j${JOBS}
emmake make install
# Create symlinks without debug suffix for pkg-config compatibility
# Protobuf CMake adds 'd' suffix for Debug builds but pkg-config expects 'libprotobuf'
if [ -f "${SYSROOT}/lib/libprotobufd.a" ] && [ ! -f "${SYSROOT}/lib/libprotobuf.a" ]; then
ln -sf libprotobufd.a "${SYSROOT}/lib/libprotobuf.a"
log_info "Created libprotobuf.a symlink for pkg-config"
fi
if [ -f "${SYSROOT}/lib/libprotobuf-lited.a" ] && [ ! -f "${SYSROOT}/lib/libprotobuf-lite.a" ]; then
ln -sf libprotobuf-lited.a "${SYSROOT}/lib/libprotobuf-lite.a"
log_info "Created libprotobuf-lite.a symlink for pkg-config"
fi
create_stamp "${PROTOBUF_STAMP}"
log_info "Protobuf build complete!"

View file

@ -62,8 +62,8 @@ emcmake cmake "${ZSTD_DIR}/build/cmake" \
-DZSTD_BUILD_SHARED=OFF \
-DZSTD_BUILD_STATIC=ON \
-DZSTD_MULTITHREAD_SUPPORT=ON \
-DCMAKE_C_FLAGS="${DEBUG_CFLAGS:--g -O0} -pthread" \
-DCMAKE_CXX_FLAGS="${DEBUG_CFLAGS:--g -O0} -pthread"
-DCMAKE_C_FLAGS="${DEBUG_CFLAGS:--g -O0} -pthread -matomics -mbulk-memory" \
-DCMAKE_CXX_FLAGS="${DEBUG_CFLAGS:--g -O0} -pthread -matomics -mbulk-memory"
emmake make -j${JOBS}
emmake make install

View file

@ -87,9 +87,10 @@ else
fi
# Step 2: Build dependencies
# Note: --with-occ for OpenCASCADE, but NOT ngspice since KICAD_SPICE=OFF
if [ $SKIP_DEPS -eq 0 ]; then
log_info "Building dependencies..."
"${SCRIPT_DIR}/../deps/build-all-deps.sh" --all
"${SCRIPT_DIR}/../deps/build-all-deps.sh" --with-occ
else
log_info "Skipping dependencies (--skip-deps specified)"
fi
@ -100,26 +101,33 @@ if [ $NO_CLEAN -eq 1 ] && check_stamp "${KICAD_STAMP}"; then
exit 0
fi
# Step 4: Verify wxWidgets is built
# Step 4: Build wxWidgets if not present
WXWIDGETS_STAMP="${BUILD_ROOT}/stamps/wxwidgets.stamp"
if [ ! -f "${WX_BUILD}/lib/libwx_baseu-3.2.a" ]; then
log_error "wxWidgets not found. Please build wxWidgets first with:"
log_error " ./scripts/build-wxuniversal-wasm.sh"
exit 1
log_info "Building wxWidgets..."
"${SCRIPT_DIR}/../build-wxuniversal-wasm.sh" --no-clean
# Create stamp after successful build
touch "${WXWIDGETS_STAMP}"
elif [ ! -f "${WXWIDGETS_STAMP}" ]; then
# Library exists but no stamp - create one
touch "${WXWIDGETS_STAMP}"
fi
log_info "Building KiCad PCBnew ${KICAD_VERSION} for WASM..."
# Step 5: Set build type
# Use environment DEBUG_BUILD if set, otherwise check local --debug flag
# -fexceptions is required because wxWidgets is built with exceptions enabled
# -matomics -mbulk-memory are required for shared memory (pthreads)
if [ "${DEBUG_BUILD:-0}" = "1" ] || [ $DEBUG -eq 1 ]; then
BUILD_TYPE="Debug"
EXTRA_FLAGS="-g -O0"
LINKER_DEBUG_FLAGS="-g -gsource-map"
EXTRA_FLAGS="-g -O0 -fexceptions -matomics -mbulk-memory"
LINKER_DEBUG_FLAGS="-g -gsource-map -fexceptions"
log_info "Building KiCad in DEBUG mode (with source maps)"
else
BUILD_TYPE="Release"
EXTRA_FLAGS="-O2"
LINKER_DEBUG_FLAGS=""
EXTRA_FLAGS="-O2 -fexceptions -matomics -mbulk-memory"
LINKER_DEBUG_FLAGS="-fexceptions"
log_info "Building KiCad in RELEASE mode"
fi