refactor(build): fold calculator into unified docker/build.sh dispatch
Convert the calculator port (commit 0464470) from the parallel/copy-paste
pattern to the dispatch pattern used by pcbnew + eeschema.
- docker/build.sh: <app> is now required (no pcbnew default); missing,
unknown, and --help paths all print "pcbnew | eeschema | calculator | all".
Adds calculator to build_app() via kicad_subdir_for() (pcb_calculator
inner subdir, calculator.* output basename). Also fixes the
EMSDK=/emsdkkicad-wasm-builder typo that snuck in during the calc merge.
- scripts/kicad/build-kicad-target.sh: accepts calculator; introduces
KICAD_TARGET (pcb_calculator for calc, == APP_NAME otherwise) used for
the make target, embind include path, and final-log line.
- scripts/kicad/build-calculator.sh: 305-line copy of build-pcbnew.sh
collapsed to a 7-line wrapper around build-kicad-target.sh.
- tests/scripts/setup-kicad-wasm.sh: copy_app calculator added with the
same pcb_calculator subdir mapping for the docker-volume fallback path.
- tests/package.json: test:calculator* routed through the shared
playwright-kicad.config.ts kicad/calculator.spec.ts (mirrors eeschema).
- Delete: docker/build-calculator.sh, tests/scripts/setup-calculator-wasm.sh,
tests/playwright-calculator.config.ts.
Bumps wxwidgets d1d1627 -> 6fb2eac (origin/wasm-port). The new sha includes
"unic/combobox: add GetCurrentSelection() inline default" which calc's
kicad/pcb_calculator/widgets/unit_selector.cpp needs to compile. Without
this bump the unified dispatch would expose calc as a buildable target but
the build itself would fail. Verified: build.sh all completes clean across
all three apps; pcbnew/eeschema e2e pass; calc compiles and launches (test
stability separate from this refactor).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
645f89b816
commit
d6d743340d
9 changed files with 108 additions and 587 deletions
|
|
@ -1,90 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
# Build KiCad PCB Calculator WASM inside Docker, then apply asyncify on host.
|
|
||||||
# Mirrors docker/build.sh; differences:
|
|
||||||
# - calls scripts/kicad/build-calculator.sh inside the container
|
|
||||||
# - copies pcb_calculator.* from build-wasm/kicad-calculator and renames to
|
|
||||||
# calculator.* on the host (keeps the kicad-fork patch minimal)
|
|
||||||
# - runs host-side passes against output/calculator.{js,wasm}
|
|
||||||
|
|
||||||
# Redirect all output to a log file (re-execs script with redirection)
|
|
||||||
source "$(dirname "$0")/../scripts/common/logging.sh"
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
cd "$(dirname "$0")/.."
|
|
||||||
|
|
||||||
# Use branch name as Docker Compose project name for isolated containers/volumes
|
|
||||||
BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD | tr '/' '-' | tr '[:upper:]' '[:lower:]')
|
|
||||||
export COMPOSE_PROJECT_NAME="kicad-wasm-${BRANCH_NAME}"
|
|
||||||
echo "Using Docker project: ${COMPOSE_PROJECT_NAME}"
|
|
||||||
|
|
||||||
# Add -j 10 by default if no -j flag is given
|
|
||||||
ARGS=("$@")
|
|
||||||
if [[ ! " ${ARGS[*]} " =~ " -j " ]]; then
|
|
||||||
ARGS+=("-j" "10")
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Start container if not running
|
|
||||||
docker compose -f docker/docker-compose.yml up -d
|
|
||||||
|
|
||||||
# Sync source code to container volume (same flake-tolerant retry as build.sh).
|
|
||||||
echo "Syncing source code to container..."
|
|
||||||
sync_rc=0
|
|
||||||
for sync_attempt in 1 2 3; do
|
|
||||||
if docker compose -f docker/docker-compose.yml exec kicad-wasm-builder \
|
|
||||||
rsync -r --delete --checksum --inplace \
|
|
||||||
--exclude="build-wasm" \
|
|
||||||
--exclude="output" \
|
|
||||||
--exclude=".git" \
|
|
||||||
--exclude="logs" \
|
|
||||||
--exclude=".idea" \
|
|
||||||
--exclude="node_modules" \
|
|
||||||
--exclude="tools/emsdk" \
|
|
||||||
/workspace-host/ /workspace/
|
|
||||||
then
|
|
||||||
sync_rc=0
|
|
||||||
else
|
|
||||||
sync_rc=$?
|
|
||||||
fi
|
|
||||||
{ [ $sync_rc -eq 0 ] || [ $sync_rc -eq 24 ]; } && break
|
|
||||||
echo "rsync attempt ${sync_attempt} failed (exit ${sync_rc}); retrying in 2s..."
|
|
||||||
sleep 2
|
|
||||||
done
|
|
||||||
if [ $sync_rc -ne 0 ] && [ $sync_rc -ne 24 ]; then
|
|
||||||
echo "ERROR: source sync failed after retries (exit ${sync_rc})"; exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Run build inside container.
|
|
||||||
docker compose -f docker/docker-compose.yml exec -e EMSDK=/emsdk kicad-wasm-builder \
|
|
||||||
/workspace/scripts/kicad/build-calculator.sh "${ARGS[@]}"
|
|
||||||
|
|
||||||
# Copy outputs. Artifacts are named calculator.* directly thanks to the
|
|
||||||
# OUTPUT_NAME calculator property we set on the pcb_calculator target for
|
|
||||||
# EMSCRIPTEN builds (kicad/pcb_calculator/CMakeLists.txt).
|
|
||||||
echo "Copying build output to ./output/..."
|
|
||||||
docker compose -f docker/docker-compose.yml exec kicad-wasm-builder \
|
|
||||||
bash -c '
|
|
||||||
set -e
|
|
||||||
mkdir -p /workspace/output
|
|
||||||
SRC=/workspace/build-wasm/kicad-calculator/pcb_calculator
|
|
||||||
cp "$SRC/calculator.js" /workspace/output/calculator.js
|
|
||||||
cp "$SRC/calculator.wasm" /workspace/output/calculator.wasm
|
|
||||||
[ -f "$SRC/calculator.worker.js" ] && cp "$SRC/calculator.worker.js" /workspace/output/calculator.worker.js || true
|
|
||||||
[ -f "$SRC/calculator.wasm.map" ] && cp "$SRC/calculator.wasm.map" /workspace/output/calculator.wasm.map || true
|
|
||||||
[ -f "$SRC/calculator.wasm.debug.wasm" ] && cp "$SRC/calculator.wasm.debug.wasm" /workspace/output/calculator.wasm.debug.wasm || true
|
|
||||||
cp /workspace/build-wasm/kicad-calculator/resources/images.tar.gz /workspace/output/images.tar.gz 2>/dev/null || true
|
|
||||||
cp /workspace/build-wasm/wxwidgets/build/wasm/wx.js /workspace/output/wx.js 2>/dev/null || true
|
|
||||||
'
|
|
||||||
|
|
||||||
# Inject dynCall shims into the JS loader (fixes Emscripten 4.x dynCall_* errors).
|
|
||||||
./scripts/common/inject-dyncall-shims.sh output/calculator.js
|
|
||||||
|
|
||||||
# Run wasm-emscripten-finalize on host (memory-intensive, skipped inside Docker).
|
|
||||||
./scripts/common/apply-finalize.sh output/calculator.wasm output/calculator.wasm
|
|
||||||
|
|
||||||
# Run wasm-opt --asyncify on host (~20-30GB RAM).
|
|
||||||
./scripts/common/apply-asyncify.sh output/calculator.wasm output/calculator.wasm
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "Build complete. Output files in ./output/"
|
|
||||||
ls -lh output/
|
|
||||||
|
|
@ -1,13 +1,18 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Build a KiCad editor (pcbnew or eeschema) inside Docker, then run asyncify
|
# Build a KiCad app (pcbnew, eeschema, calculator) inside Docker, then run
|
||||||
# and friends on the host.
|
# asyncify and friends on the host.
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# ./docker/build.sh # builds pcbnew (default)
|
# ./docker/build.sh <app> [args...]
|
||||||
# ./docker/build.sh pcbnew # explicit
|
#
|
||||||
# ./docker/build.sh eeschema # builds the schematic editor
|
# Apps:
|
||||||
# ./docker/build.sh all # builds both, sequentially
|
# pcbnew PCB editor
|
||||||
# ./docker/build.sh <app> -j 8 ... # any extra args are forwarded to build-*.sh
|
# eeschema schematic editor
|
||||||
|
# calculator PCB calculator
|
||||||
|
# all build pcbnew, eeschema, calculator sequentially
|
||||||
|
#
|
||||||
|
# Any extra args are forwarded to scripts/kicad/build-<app>.sh (e.g. -j 8,
|
||||||
|
# --full, --release, --diag=gal).
|
||||||
#
|
#
|
||||||
# The build is split into two phases:
|
# The build is split into two phases:
|
||||||
# 1. Docker: Compile KiCad to WASM (without asyncify)
|
# 1. Docker: Compile KiCad to WASM (without asyncify)
|
||||||
|
|
@ -15,25 +20,42 @@
|
||||||
#
|
#
|
||||||
# Binaryen is downloaded automatically - no prerequisites needed.
|
# Binaryen is downloaded automatically - no prerequisites needed.
|
||||||
|
|
||||||
# Redirect all output to a log file (re-execs script with redirection)
|
# Redirect all output to a log file (re-execs script with redirection).
|
||||||
|
# MUST be sourced before arg parsing — the re-exec relies on the original
|
||||||
|
# "$@", so any shifts before this point would strip args from the re-exec.
|
||||||
source "$(dirname "$0")/../scripts/common/logging.sh"
|
source "$(dirname "$0")/../scripts/common/logging.sh"
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
cd "$(dirname "$0")/.."
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
# First positional arg is the app name; everything else is forwarded to build-*.sh.
|
VALID_APPS="pcbnew | eeschema | calculator | all"
|
||||||
APP_NAME=""
|
|
||||||
if [[ $# -gt 0 ]] && [[ "$1" != -* ]]; then
|
usage() {
|
||||||
APP_NAME="$1"
|
echo "Usage: ./docker/build.sh <app> [args...]" >&2
|
||||||
shift
|
echo " <app>: ${VALID_APPS}" >&2
|
||||||
|
echo " args: forwarded to scripts/kicad/build-<app>.sh (e.g. -j 8, --release)" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
# First positional arg must be the app name. No default — picking one would
|
||||||
|
# silently build the wrong thing for someone who forgot the argument.
|
||||||
|
if [[ "${1:-}" == "-h" ]] || [[ "${1:-}" == "--help" ]]; then
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
fi
|
fi
|
||||||
APP_NAME="${APP_NAME:-pcbnew}"
|
if [[ $# -lt 1 ]] || [[ "$1" == -* ]]; then
|
||||||
|
echo "Error: missing <app> argument" >&2
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
APP_NAME="$1"
|
||||||
|
shift
|
||||||
|
|
||||||
case "$APP_NAME" in
|
case "$APP_NAME" in
|
||||||
pcbnew|eeschema|all) ;;
|
pcbnew|eeschema|calculator|all) ;;
|
||||||
*)
|
*)
|
||||||
echo "Error: unknown app '$APP_NAME' (expected: pcbnew | eeschema | all)" >&2
|
echo "Error: unknown app '$APP_NAME' (expected: ${VALID_APPS})" >&2
|
||||||
|
usage
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
@ -87,17 +109,29 @@ if [ $sync_rc -ne 0 ] && [ $sync_rc -ne 24 ]; then
|
||||||
echo "ERROR: source sync failed after retries (exit ${sync_rc})"; exit 1
|
echo "ERROR: source sync failed after retries (exit ${sync_rc})"; exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Map an app name to its inner CMake build subdirectory. Most apps share their
|
||||||
|
# subdir name with the app name; pcb_calculator emits OUTPUT_NAME=calculator
|
||||||
|
# but lives under the pcb_calculator/ subtree.
|
||||||
|
kicad_subdir_for() {
|
||||||
|
case "$1" in
|
||||||
|
calculator) echo "pcb_calculator" ;;
|
||||||
|
*) echo "$1" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
# Build one app: compile in container, then run host-side post-processing.
|
# Build one app: compile in container, then run host-side post-processing.
|
||||||
build_app() {
|
build_app() {
|
||||||
local app="$1"
|
local app="$1"
|
||||||
|
local subdir
|
||||||
|
subdir=$(kicad_subdir_for "$app")
|
||||||
echo ""
|
echo ""
|
||||||
echo "=== Building ${app} ==="
|
echo "=== Building ${app} ==="
|
||||||
|
|
||||||
# Run build command (without asyncify - handled on host due to memory requirements)
|
# Run build inside the container.
|
||||||
# -e EMSDK=/emsdk: `docker compose exec` bypasses the entrypoint that sources
|
# -e EMSDK=/emsdk: `docker compose exec` bypasses the entrypoint that sources
|
||||||
# emsdk_env.sh, so the build shell would lack emcc/embuilder on PATH. Setting
|
# emsdk_env.sh, so the build shell would lack emcc/embuilder on PATH. Setting
|
||||||
# EMSDK lets scripts/common/env.sh source /emsdk/emsdk_env.sh and activate the toolchain.
|
# EMSDK lets scripts/common/env.sh source /emsdk/emsdk_env.sh and activate the toolchain.
|
||||||
docker compose -f docker/docker-compose.yml exec -e EMSDK=/emsdkkicad-wasm-builder \
|
docker compose -f docker/docker-compose.yml exec -e EMSDK=/emsdk kicad-wasm-builder \
|
||||||
"/workspace/scripts/kicad/build-${app}.sh" "${ARGS[@]}"
|
"/workspace/scripts/kicad/build-${app}.sh" "${ARGS[@]}"
|
||||||
|
|
||||||
# Copy output to host-accessible directory.
|
# Copy output to host-accessible directory.
|
||||||
|
|
@ -105,8 +139,8 @@ docker compose -f docker/docker-compose.yml exec -e EMSDK=/emsdkkicad-wasm-build
|
||||||
echo "Copying ${app} build output to ./output/..."
|
echo "Copying ${app} build output to ./output/..."
|
||||||
docker compose -f docker/docker-compose.yml exec kicad-wasm-builder \
|
docker compose -f docker/docker-compose.yml exec kicad-wasm-builder \
|
||||||
bash -c "mkdir -p /workspace/output && \
|
bash -c "mkdir -p /workspace/output && \
|
||||||
cp /workspace/build-wasm/kicad-${app}/${app}/${app}.{js,wasm,wasm.debug.wasm,wasm.map,worker.js} /workspace/output/ 2>/dev/null || \
|
cp /workspace/build-wasm/kicad-${app}/${subdir}/${app}.{js,wasm,wasm.debug.wasm,wasm.map,worker.js} /workspace/output/ 2>/dev/null || \
|
||||||
cp /workspace/build-wasm/kicad-${app}/${app}/${app}.{js,wasm} /workspace/output/; \
|
cp /workspace/build-wasm/kicad-${app}/${subdir}/${app}.{js,wasm} /workspace/output/; \
|
||||||
cp /workspace/build-wasm/kicad-${app}/resources/images.tar.gz /workspace/output/ 2>/dev/null || true; \
|
cp /workspace/build-wasm/kicad-${app}/resources/images.tar.gz /workspace/output/ 2>/dev/null || true; \
|
||||||
cp /workspace/build-wasm/wxwidgets/build/wasm/wx.js /workspace/output/ 2>/dev/null || true"
|
cp /workspace/build-wasm/wxwidgets/build/wasm/wx.js /workspace/output/ 2>/dev/null || true"
|
||||||
|
|
||||||
|
|
@ -123,6 +157,7 @@ docker compose -f docker/docker-compose.yml exec -e EMSDK=/emsdkkicad-wasm-build
|
||||||
if [[ "${APP_NAME}" == "all" ]]; then
|
if [[ "${APP_NAME}" == "all" ]]; then
|
||||||
build_app pcbnew
|
build_app pcbnew
|
||||||
build_app eeschema
|
build_app eeschema
|
||||||
|
build_app calculator
|
||||||
else
|
else
|
||||||
build_app "${APP_NAME}"
|
build_app "${APP_NAME}"
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -1,305 +1,7 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Build KiCad PCB Calculator for WebAssembly.
|
# Build KiCad PCB Calculator for WebAssembly.
|
||||||
# This builds the standalone pcb_calculator app as a single WASM binary.
|
# Thin wrapper around build-kicad-target.sh — see that script for options.
|
||||||
#
|
|
||||||
# Mirrors scripts/kicad/build-pcbnew.sh almost verbatim; differences:
|
|
||||||
# - separate build tree: build-wasm/kicad-calculator
|
|
||||||
# - no pcbnew_scripting_stub (calculator doesn't link scripting)
|
|
||||||
# - embind compiles wasm/bindings/calculator_embind.cpp
|
|
||||||
# - final make target is `pcb_calculator` (single binary, no kiface MODULE,
|
|
||||||
# enabled by the EMSCRIPTEN branch we added to pcb_calculator/CMakeLists.txt)
|
|
||||||
#
|
|
||||||
# Usage:
|
|
||||||
# ./scripts/kicad/build-calculator.sh [options]
|
|
||||||
#
|
|
||||||
# Options (same as build-pcbnew.sh):
|
|
||||||
# --full Full clean rebuild (dependencies + KiCad)
|
|
||||||
# --clean-kicad Clean only KiCad-calculator build directory (not deps)
|
|
||||||
# --build-deps Build dependencies (default: skip)
|
|
||||||
# --debug Build with debug symbols (default)
|
|
||||||
# --release Build optimized without debug symbols
|
|
||||||
# -j N Parallel compilation jobs (default: 1)
|
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
source "${SCRIPT_DIR}/../common/env.sh"
|
exec "${SCRIPT_DIR}/build-kicad-target.sh" calculator "$@"
|
||||||
source "${SCRIPT_DIR}/../common/versions.sh"
|
|
||||||
source "${SCRIPT_DIR}/../common/functions.sh"
|
|
||||||
|
|
||||||
KICAD_DIR="${PROJECT_ROOT}/kicad"
|
|
||||||
KICAD_BUILD="${BUILD_ROOT}/kicad-calculator"
|
|
||||||
KICAD_STAMP="${BUILD_ROOT}/stamps/kicad-calculator.stamp"
|
|
||||||
WASM_LAYER="${PROJECT_ROOT}/wasm"
|
|
||||||
WX_BUILD="${BUILD_ROOT}/wxwidgets-universal"
|
|
||||||
|
|
||||||
# Parse arguments - incremental build by default (optimized for development)
|
|
||||||
NO_CLEAN=1
|
|
||||||
FULL_CLEAN=0
|
|
||||||
SKIP_DEPS=1
|
|
||||||
DEBUG=0
|
|
||||||
DIAG_LIST=""
|
|
||||||
while [[ $# -gt 0 ]]; do
|
|
||||||
case $1 in
|
|
||||||
--full)
|
|
||||||
FULL_CLEAN=1
|
|
||||||
NO_CLEAN=0
|
|
||||||
SKIP_DEPS=0
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
--clean-kicad)
|
|
||||||
NO_CLEAN=0
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
--build-deps)
|
|
||||||
SKIP_DEPS=0
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
--debug)
|
|
||||||
DEBUG=1
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
--release)
|
|
||||||
DEBUG_BUILD=0
|
|
||||||
export DEBUG_BUILD
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
--diag=*)
|
|
||||||
DIAG_LIST="${1#--diag=}"
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
--diag)
|
|
||||||
DIAG_LIST="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
-j)
|
|
||||||
export JOBS="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
-j*)
|
|
||||||
export JOBS="${1#-j}"
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
# Diagnostic preprocessor defines (same as build-pcbnew.sh)
|
|
||||||
DIAG_DEFINES=""
|
|
||||||
if [ -n "${DIAG_LIST}" ]; then
|
|
||||||
IFS=',' read -ra _diag_cats <<< "${DIAG_LIST}"
|
|
||||||
for _cat in "${_diag_cats[@]}"; do
|
|
||||||
case "${_cat}" in
|
|
||||||
gal) DIAG_DEFINES="${DIAG_DEFINES} -DKICAD_DIAG_GAL=1" ;;
|
|
||||||
coroutine) DIAG_DEFINES="${DIAG_DEFINES} -DKICAD_DIAG_COROUTINE=1" ;;
|
|
||||||
ctor) DIAG_DEFINES="${DIAG_DEFINES} -DKICAD_DIAG_CTOR=1" ;;
|
|
||||||
all) DIAG_DEFINES="${DIAG_DEFINES} -DKICAD_DIAG_GAL=1 -DKICAD_DIAG_COROUTINE=1 -DKICAD_DIAG_CTOR=1" ;;
|
|
||||||
"") ;;
|
|
||||||
*) log_warn "Unknown --diag category: '${_cat}' (valid: gal, coroutine, ctor, all)" ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
log_info "Diagnostic logging enabled:${DIAG_DEFINES}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
log_info "Using ${JOBS} parallel jobs"
|
|
||||||
|
|
||||||
# Step 1: Clean build directories
|
|
||||||
if [ $FULL_CLEAN -eq 1 ]; then
|
|
||||||
log_info "Full clean: removing all stamps and build directories..."
|
|
||||||
rm -rf "${STAMPS_DIR}"/*
|
|
||||||
rm -rf "${BUILD_ROOT}/deps"/*
|
|
||||||
rm -rf "${BUILD_ROOT}/wxwidgets-universal"
|
|
||||||
rm -rf "${BUILD_ROOT}/stubs"
|
|
||||||
rm -rf "${KICAD_BUILD}"
|
|
||||||
rm -rf "${SYSROOT}"/*
|
|
||||||
elif [ $NO_CLEAN -eq 0 ]; then
|
|
||||||
log_info "Cleaning KiCad Calculator build directory..."
|
|
||||||
rm -rf "${KICAD_BUILD}" "${KICAD_STAMP}"
|
|
||||||
else
|
|
||||||
log_info "Incremental build (use --clean-kicad or --full to clean)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Step 2: Build dependencies (shared sysroot with pcbnew)
|
|
||||||
if [ $SKIP_DEPS -eq 0 ]; then
|
|
||||||
log_info "Building dependencies..."
|
|
||||||
"${SCRIPT_DIR}/../deps/build-all-deps.sh" --with-occ
|
|
||||||
else
|
|
||||||
log_info "Skipping dependencies (use --build-deps or --full to build)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Step 4: Build wxWidgets (shared with pcbnew)
|
|
||||||
log_info "Building wxWidgets..."
|
|
||||||
"${SCRIPT_DIR}/../build-wxuniversal-wasm.sh" --no-clean
|
|
||||||
|
|
||||||
log_info "Building KiCad PCB Calculator ${KICAD_VERSION} for WASM..."
|
|
||||||
|
|
||||||
# Step 5: Set build type (same flags as pcbnew)
|
|
||||||
if [ "${DEBUG_BUILD:-0}" = "1" ] || [ $DEBUG -eq 1 ]; then
|
|
||||||
BUILD_TYPE="Debug"
|
|
||||||
EXTRA_FLAGS="-g -O1 -fexceptions -matomics -mbulk-memory"
|
|
||||||
LINKER_DEBUG_FLAGS="-O1 -g -gseparate-dwarf -fexceptions"
|
|
||||||
log_info "Building Calculator in DEBUG mode (separate DWARF for smaller main binary)"
|
|
||||||
else
|
|
||||||
BUILD_TYPE="Release"
|
|
||||||
EXTRA_FLAGS="-O2 -fexceptions -matomics -mbulk-memory"
|
|
||||||
LINKER_DEBUG_FLAGS="-O0 -fexceptions"
|
|
||||||
log_info "Building Calculator in RELEASE mode (skipping wasm-opt due to memory limits)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Step 6: Create build directory
|
|
||||||
mkdir -p "${KICAD_BUILD}"
|
|
||||||
cd "${KICAD_BUILD}"
|
|
||||||
|
|
||||||
# Step 6.1: Build stub libraries the calculator links against.
|
|
||||||
# pcbnew_scripting_stub is omitted — calculator doesn't link scripting.
|
|
||||||
STUBS_DIR="${PROJECT_ROOT}/wasm/stubs"
|
|
||||||
STUBS_BUILD="${BUILD_ROOT}/stubs"
|
|
||||||
mkdir -p "${STUBS_BUILD}"
|
|
||||||
|
|
||||||
log_info "Building stub libraries..."
|
|
||||||
# libgit2 stub: single_top.cpp calls git_libgit2_init / git_libgit2_shutdown.
|
|
||||||
emcc -c "${STUBS_DIR}/libgit2_stub.c" -o "${STUBS_BUILD}/libgit2_stub.o"
|
|
||||||
emar rcs "${STUBS_BUILD}/libgit2_stub.a" "${STUBS_BUILD}/libgit2_stub.o"
|
|
||||||
|
|
||||||
# curl stub: referenced by common/.
|
|
||||||
emcc -c "${STUBS_DIR}/curl_stub.c" -o "${STUBS_BUILD}/curl_stub.o"
|
|
||||||
emar rcs "${STUBS_BUILD}/libcurl_stub.a" "${STUBS_BUILD}/curl_stub.o"
|
|
||||||
|
|
||||||
# NNG stub: required because KICAD_IPC_API=ON. Sockets don't work in WASM.
|
|
||||||
emcc -c -I"${STUBS_DIR}" "${STUBS_DIR}/nng_stub.c" -o "${STUBS_BUILD}/nng_stub.o"
|
|
||||||
emar rcs "${STUBS_BUILD}/libnng_stub.a" "${STUBS_BUILD}/nng_stub.o"
|
|
||||||
|
|
||||||
log_info "Stub libraries built"
|
|
||||||
|
|
||||||
# Step 6.2: Swap EMSDK wasm-opt for stub (real one runs on host post-build).
|
|
||||||
if [ -z "${EMSDK}" ]; then
|
|
||||||
log_error "EMSDK environment variable is not set."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
EMSDK_WASM_OPT="${EMSDK}/upstream/bin/wasm-opt"
|
|
||||||
if [ -f "${EMSDK_WASM_OPT}" ] && [ ! -f "${EMSDK_WASM_OPT}.real" ]; then
|
|
||||||
log_info "Backing up real wasm-opt..."
|
|
||||||
mv "${EMSDK_WASM_OPT}" "${EMSDK_WASM_OPT}.real"
|
|
||||||
fi
|
|
||||||
cp "${STUBS_DIR}/wasm-opt-stub.sh" "${EMSDK_WASM_OPT}"
|
|
||||||
chmod +x "${EMSDK_WASM_OPT}"
|
|
||||||
log_info "wasm-opt stub installed (asyncify will run on host)"
|
|
||||||
|
|
||||||
# Step 6.3: Swap wasm-emscripten-finalize for stub (real one runs on host).
|
|
||||||
EMSDK_FINALIZE="${EMSDK}/upstream/bin/wasm-emscripten-finalize"
|
|
||||||
if [ -f "${EMSDK_FINALIZE}" ] && [ ! -f "${EMSDK_FINALIZE}.real" ]; then
|
|
||||||
log_info "Backing up real wasm-emscripten-finalize..."
|
|
||||||
mv "${EMSDK_FINALIZE}" "${EMSDK_FINALIZE}.real"
|
|
||||||
fi
|
|
||||||
cp "${STUBS_DIR}/wasm-emscripten-finalize-stub.sh" "${EMSDK_FINALIZE}"
|
|
||||||
chmod +x "${EMSDK_FINALIZE}"
|
|
||||||
log_info "wasm-emscripten-finalize stub installed (finalize will run on host)"
|
|
||||||
|
|
||||||
# Step 6.5: Verify WASM support is in KiCad fork
|
|
||||||
KICAD_CMAKE="${KICAD_DIR}/CMakeLists.txt"
|
|
||||||
if ! grep -q "msw|qt|gtk|osx|wasm" "${KICAD_CMAKE}"; then
|
|
||||||
log_error "KiCad fork is missing WASM port detection support."
|
|
||||||
log_error "Please ensure the kicad submodule has WASM modifications."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
KIPLATFORM_CMAKE="${KICAD_DIR}/libs/kiplatform/CMakeLists.txt"
|
|
||||||
if ! grep -q "KICAD_WX_PORT STREQUAL wasm" "${KIPLATFORM_CMAKE}"; then
|
|
||||||
log_error "KiCad fork is missing kiplatform WASM support."
|
|
||||||
log_error "Please ensure the kicad submodule has WASM modifications."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
log_info "KiCad WASM support verified"
|
|
||||||
|
|
||||||
# Step 7: Configure with CMake
|
|
||||||
log_info "Configuring KiCad Calculator with CMake..."
|
|
||||||
|
|
||||||
CCACHE_OPTS=""
|
|
||||||
if command -v ccache &> /dev/null; then
|
|
||||||
CCACHE_OPTS="-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache"
|
|
||||||
log_info "Using ccache for compilation"
|
|
||||||
fi
|
|
||||||
|
|
||||||
emcmake cmake "${KICAD_DIR}" \
|
|
||||||
${CCACHE_OPTS} \
|
|
||||||
-DCMAKE_BUILD_TYPE=${BUILD_TYPE} \
|
|
||||||
-DCMAKE_INSTALL_PREFIX="${SYSROOT}" \
|
|
||||||
-DCMAKE_MODULE_PATH="${WASM_LAYER}/cmake" \
|
|
||||||
-DSYSROOT="${SYSROOT}" \
|
|
||||||
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
|
|
||||||
-DCMAKE_CXX_FLAGS="${EXTRA_FLAGS} -pthread -sUSE_ZLIB=1 -DKICAD_USE_PLATFORM_WASM=1${DIAG_DEFINES} -I${SYSROOT}/include -I${STUBS_DIR}" \
|
|
||||||
-DCMAKE_C_FLAGS="${EXTRA_FLAGS} -pthread -sUSE_ZLIB=1 -I${SYSROOT}/include -I${STUBS_DIR}" \
|
|
||||||
-DCMAKE_EXE_LINKER_FLAGS="${LINKER_DEBUG_FLAGS} -pthread -sUSE_ZLIB=1 -sASYNCIFY=1 -sDYNCALLS=1 -sASYNCIFY_STACK_SIZE=65536 -sUSE_PTHREADS=1 -sPTHREAD_POOL_SIZE='navigator.hardwareConcurrency' -sPTHREAD_POOL_SIZE_STRICT=0 -sALLOW_MEMORY_GROWTH=1 -sINITIAL_MEMORY=256MB -sMAXIMUM_MEMORY=4GB -sMAX_WEBGL_VERSION=2 -sEXPORTED_RUNTIME_METHODS=['ccall','cwrap','UTF8ToString','stringToUTF8','lengthBytesUTF8','dynCall'] -sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=['\$dynCall'] --bind -L${SYSROOT}/lib ${STUBS_BUILD}/libgit2_stub.a ${STUBS_BUILD}/libcurl_stub.a ${STUBS_BUILD}/libnng_stub.a ${STUBS_BUILD}/calculator_embind.o" \
|
|
||||||
-DCMAKE_PREFIX_PATH="${SYSROOT};${WX_BUILD}" \
|
|
||||||
-DwxWidgets_CONFIG_EXECUTABLE="${WX_BUILD}/wx-config" \
|
|
||||||
\
|
|
||||||
-DKICAD_BUILD_QA_TESTS=OFF \
|
|
||||||
-DKICAD_SPICE=OFF \
|
|
||||||
-DKICAD_USE_EGL=OFF \
|
|
||||||
-DKICAD_USE_BUNDLED_GLEW=ON \
|
|
||||||
-DKICAD_BUILD_3D_VIEWER_WASM=OFF \
|
|
||||||
-DKICAD_IPC_API=ON \
|
|
||||||
\
|
|
||||||
-DZSTD_ROOT="${SYSROOT}" \
|
|
||||||
-DZSTD_INCLUDE_DIR="${SYSROOT}/include" \
|
|
||||||
-DZSTD_LIBRARY="${SYSROOT}/lib/libzstd.a" \
|
|
||||||
-DGLM_INCLUDE_DIR="${SYSROOT}/include" \
|
|
||||||
-DGLM_VERSION="0.9.9.8" \
|
|
||||||
-DBOOST_ROOT="${SYSROOT}" \
|
|
||||||
-DBoost_INCLUDE_DIR="${SYSROOT}/include" \
|
|
||||||
-DBoost_LIBRARY_DIR="${SYSROOT}/lib" \
|
|
||||||
-DBoost_NO_SYSTEM_PATHS=ON \
|
|
||||||
-DBoost_NO_BOOST_CMAKE=ON \
|
|
||||||
-DFREETYPE_INCLUDE_DIR_ft2build="${SYSROOT}/include/freetype2" \
|
|
||||||
-DFREETYPE_INCLUDE_DIR_freetype2="${SYSROOT}/include/freetype2" \
|
|
||||||
-DFREETYPE_LIBRARY="${SYSROOT}/lib/libfreetype.a" \
|
|
||||||
-DHarfBuzz_INCLUDE_DIR="${SYSROOT}/include/harfbuzz" \
|
|
||||||
-DHarfBuzz_LIBRARY="${SYSROOT}/lib/libharfbuzz.a" \
|
|
||||||
-DOCC_INCLUDE_DIR="${SYSROOT}/include/opencascade" \
|
|
||||||
-DOCC_LIBRARY_DIR="${SYSROOT}/lib" \
|
|
||||||
-DProtobuf_INCLUDE_DIR="${SYSROOT}/include" \
|
|
||||||
-DProtobuf_LIBRARY="${SYSROOT}/lib/libprotobuf.a" \
|
|
||||||
-DProtobuf_LITE_LIBRARY="${SYSROOT}/lib/libprotobuf-lite.a" \
|
|
||||||
-DProtobuf_PROTOC_EXECUTABLE="${SYSROOT}/bin/protoc" \
|
|
||||||
-DODBC_CONFIG:STRING="stub-for-wasm" \
|
|
||||||
-DODBCLIB:STRING="" \
|
|
||||||
-DODBC_CFLAGS:STRING="" \
|
|
||||||
-DODBC_LINK_FLAGS:STRING="" \
|
|
||||||
-DODBC_LIBRARIES:STRING="" \
|
|
||||||
\
|
|
||||||
-DBUILD_GITHUB_PLUGIN=OFF \
|
|
||||||
-DKICAD_PCM=OFF \
|
|
||||||
\
|
|
||||||
-DHAVE_STRCASECMP=1 \
|
|
||||||
-DHAVE_STRNCASECMP=1
|
|
||||||
|
|
||||||
# Step 7.1: Compile Embind bindings (after CMake so config.h exists).
|
|
||||||
EMBIND_SRC="${PROJECT_ROOT}/wasm/bindings/calculator_embind.cpp"
|
|
||||||
if [ -f "$EMBIND_SRC" ]; then
|
|
||||||
log_info "Compiling Embind bindings..."
|
|
||||||
WX_CXXFLAGS=$("${WX_BUILD}/wx-config" --cxxflags 2>/dev/null || echo "-I${WX_BUILD}/lib/wx/include/emscripten-unicode-static-3.2 -I${PROJECT_ROOT}/wxwidgets/include")
|
|
||||||
KICAD_INCLUDES="-I${KICAD_BUILD} -I${KICAD_DIR}/include -I${KICAD_DIR}/pcb_calculator -I${KICAD_DIR}/common"
|
|
||||||
KICAD_INCLUDES+=" -I${KICAD_DIR}/libs/core/include -I${KICAD_DIR}/libs/kimath/include -I${KICAD_DIR}/libs/kiplatform/include"
|
|
||||||
KICAD_INCLUDES+=" -I${KICAD_DIR}/thirdparty/clipper2/Clipper2Lib/include"
|
|
||||||
KICAD_INCLUDES+=" -I${KICAD_DIR}/thirdparty/nlohmann_json"
|
|
||||||
KICAD_INCLUDES+=" -I${KICAD_DIR}/thirdparty/dynamic_bitset"
|
|
||||||
KICAD_INCLUDES+=" -I${KICAD_DIR}/thirdparty/nanodbc"
|
|
||||||
KICAD_INCLUDES+=" -I${KICAD_DIR}/thirdparty/picosha2"
|
|
||||||
KICAD_INCLUDES+=" -I${KICAD_DIR}/thirdparty"
|
|
||||||
KICAD_INCLUDES+=" -I${SYSROOT}/include"
|
|
||||||
em++ -std=c++20 -c ${EXTRA_FLAGS} ${WX_CXXFLAGS} ${KICAD_INCLUDES} "$EMBIND_SRC" -o "${STUBS_BUILD}/calculator_embind.o"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Step 8: Build pcb_calculator target (single-binary WASM executable)
|
|
||||||
log_info "Building pcb_calculator..."
|
|
||||||
emmake make -j${JOBS} pcb_calculator
|
|
||||||
|
|
||||||
# Step 8.1: Build bitmap resources (images.tar.gz)
|
|
||||||
log_info "Building bitmap resources..."
|
|
||||||
emmake make bitmap_archive_build
|
|
||||||
|
|
||||||
# Step 9: Create stamp file
|
|
||||||
create_stamp "${KICAD_STAMP}"
|
|
||||||
log_info "KiCad Calculator build complete!"
|
|
||||||
log_info "Output: ${KICAD_BUILD}/pcb_calculator/pcb_calculator.js"
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Build a KiCad editor (pcbnew or eeschema) for WebAssembly.
|
# Build a KiCad app (pcbnew, eeschema, calculator) for WebAssembly.
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# ./scripts/kicad/build-kicad-target.sh <app> [options]
|
# ./scripts/kicad/build-kicad-target.sh <app> [options]
|
||||||
#
|
#
|
||||||
# Args:
|
# Args:
|
||||||
# <app> pcbnew | eeschema (required)
|
# <app> pcbnew | eeschema | calculator (required)
|
||||||
#
|
#
|
||||||
# Options:
|
# Options:
|
||||||
# --full Full clean rebuild (dependencies + KiCad)
|
# --full Full clean rebuild (dependencies + KiCad)
|
||||||
|
|
@ -16,25 +16,35 @@
|
||||||
# --diag=... Diagnostic preprocessor flags (gal, coroutine, ctor, all)
|
# --diag=... Diagnostic preprocessor flags (gal, coroutine, ctor, all)
|
||||||
# -j N Parallel compilation jobs (default: 1)
|
# -j N Parallel compilation jobs (default: 1)
|
||||||
#
|
#
|
||||||
# Each editor builds into its own tree: build-wasm/kicad-<app>/.
|
# Each app builds into its own tree: build-wasm/kicad-<app>/.
|
||||||
# Per-editor extras live alongside generic stubs:
|
# Per-app extras live alongside generic stubs:
|
||||||
# - wasm/bindings/<app>_embind.cpp (optional)
|
# - wasm/bindings/<app>_embind.cpp (optional)
|
||||||
# - wasm/stubs/<app>_frame_stub.cpp (optional, app-specific stubs)
|
# - wasm/stubs/<app>_frame_stub.cpp (optional, app-specific stubs)
|
||||||
# - wasm/stubs/<app>_scripting_stub.cpp (optional, app-specific scripting stubs)
|
# - wasm/stubs/<app>_scripting_stub.cpp (optional, app-specific scripting stubs)
|
||||||
|
#
|
||||||
|
# Most apps use the same name for the user-facing app, the CMake target, and
|
||||||
|
# the source subdirectory. Calculator is the exception: app=calculator but the
|
||||||
|
# upstream target and source subdir are both pcb_calculator (the OUTPUT_NAME
|
||||||
|
# property in pcb_calculator/CMakeLists.txt emits calculator.{js,wasm}).
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
if [ -z "$1" ]; then
|
if [ -z "$1" ]; then
|
||||||
echo "Error: missing <app> argument (pcbnew | eeschema)" >&2
|
echo "Error: missing <app> argument (pcbnew | eeschema | calculator)" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
APP_NAME="$1"
|
APP_NAME="$1"
|
||||||
shift
|
shift
|
||||||
|
|
||||||
case "$APP_NAME" in
|
case "$APP_NAME" in
|
||||||
pcbnew|eeschema) ;;
|
pcbnew|eeschema)
|
||||||
|
KICAD_TARGET="$APP_NAME"
|
||||||
|
;;
|
||||||
|
calculator)
|
||||||
|
KICAD_TARGET="pcb_calculator"
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Error: unknown app '$APP_NAME' (expected: pcbnew | eeschema)" >&2
|
echo "Error: unknown app '$APP_NAME' (expected: pcbnew | eeschema | calculator)" >&2
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
@ -362,7 +372,7 @@ emcmake cmake "${KICAD_DIR}" \
|
||||||
if [ -f "${EMBIND_SRC}" ]; then
|
if [ -f "${EMBIND_SRC}" ]; then
|
||||||
log_info "Compiling Embind bindings (${APP_NAME})..."
|
log_info "Compiling Embind bindings (${APP_NAME})..."
|
||||||
# Use the same includes and flags that KiCad uses
|
# Use the same includes and flags that KiCad uses
|
||||||
KICAD_INCLUDES="-I${KICAD_BUILD} -I${KICAD_DIR}/include -I${KICAD_DIR}/${APP_NAME} -I${KICAD_DIR}/common"
|
KICAD_INCLUDES="-I${KICAD_BUILD} -I${KICAD_DIR}/include -I${KICAD_DIR}/${KICAD_TARGET} -I${KICAD_DIR}/common"
|
||||||
KICAD_INCLUDES+=" -I${KICAD_DIR}/libs/core/include -I${KICAD_DIR}/libs/kimath/include -I${KICAD_DIR}/libs/kiplatform/include"
|
KICAD_INCLUDES+=" -I${KICAD_DIR}/libs/core/include -I${KICAD_DIR}/libs/kimath/include -I${KICAD_DIR}/libs/kiplatform/include"
|
||||||
KICAD_INCLUDES+=" -I${KICAD_DIR}/thirdparty/clipper2/Clipper2Lib/include"
|
KICAD_INCLUDES+=" -I${KICAD_DIR}/thirdparty/clipper2/Clipper2Lib/include"
|
||||||
KICAD_INCLUDES+=" -I${KICAD_DIR}/thirdparty/nlohmann_json"
|
KICAD_INCLUDES+=" -I${KICAD_DIR}/thirdparty/nlohmann_json"
|
||||||
|
|
@ -381,8 +391,8 @@ else
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Step 8: Build the app target
|
# Step 8: Build the app target
|
||||||
log_info "Building ${APP_NAME}..."
|
log_info "Building ${APP_NAME} (CMake target: ${KICAD_TARGET})..."
|
||||||
emmake make -j${JOBS} "${APP_NAME}"
|
emmake make -j${JOBS} "${KICAD_TARGET}"
|
||||||
|
|
||||||
# Step 8.1: Build bitmap resources (images.tar.gz)
|
# Step 8.1: Build bitmap resources (images.tar.gz)
|
||||||
# This creates the icon archive that KiCad loads at runtime
|
# This creates the icon archive that KiCad loads at runtime
|
||||||
|
|
@ -392,4 +402,4 @@ emmake make bitmap_archive_build
|
||||||
# Step 9: Create stamp file
|
# Step 9: Create stamp file
|
||||||
create_stamp "${KICAD_STAMP}"
|
create_stamp "${KICAD_STAMP}"
|
||||||
log_info "KiCad ${APP_NAME} build complete!"
|
log_info "KiCad ${APP_NAME} build complete!"
|
||||||
log_info "Output: ${KICAD_BUILD}/${APP_NAME}/${APP_NAME}.js"
|
log_info "Output: ${KICAD_BUILD}/${KICAD_TARGET}/${APP_NAME}.js"
|
||||||
|
|
|
||||||
|
|
@ -9,21 +9,20 @@
|
||||||
"build-wasm": "cd apps && make -f Makefile.wasm",
|
"build-wasm": "cd apps && make -f Makefile.wasm",
|
||||||
"serve": "npx serve apps -p 8080 -c ../serve.json",
|
"serve": "npx serve apps -p 8080 -c ../serve.json",
|
||||||
"setup:kicad": "./scripts/setup-kicad-wasm.sh",
|
"setup:kicad": "./scripts/setup-kicad-wasm.sh",
|
||||||
"setup:calculator": "./scripts/setup-calculator-wasm.sh",
|
|
||||||
"test:kicad:firefox": "npm run setup:kicad && playwright test --config=playwright-kicad.config.ts --project=firefox",
|
"test:kicad:firefox": "npm run setup:kicad && playwright test --config=playwright-kicad.config.ts --project=firefox",
|
||||||
"test:kicad:chrome": "npm run setup:kicad && playwright test --config=playwright-kicad.config.ts --project=chromium --headed",
|
"test:kicad:chrome": "npm run setup:kicad && playwright test --config=playwright-kicad.config.ts --project=chromium --headed",
|
||||||
"test:kicad": "npm run test:kicad:firefox",
|
"test:kicad": "npm run test:kicad:firefox",
|
||||||
"test:kicad:headed": "npm run test:kicad:chrome",
|
"test:kicad:headed": "npm run test:kicad:chrome",
|
||||||
"test:pcbnew:firefox": "npm run setup:kicad && playwright test --config=playwright-kicad.config.ts --project=firefox kicad/pcbnew.spec.ts",
|
"test:pcbnew:firefox": "npm run setup:kicad && playwright test --config=playwright-kicad.config.ts --project=firefox kicad/pcbnew.spec.ts",
|
||||||
"test:pcbnew:chrome": "npm run setup:kicad && playwright test --config=playwright-kicad.config.ts --project=chromium --headed kicad/pcbnew.spec.ts",
|
"test:pcbnew:chrome": "npm run setup:kicad && playwright test --config=playwright-kicad.config.ts --project=chromium --headed kicad/pcbnew.spec.ts",
|
||||||
"test:calculator:firefox": "npm run setup:calculator && playwright test --config=playwright-calculator.config.ts --project=firefox",
|
|
||||||
"test:calculator:chrome": "npm run setup:calculator && playwright test --config=playwright-calculator.config.ts --project=chromium --headed",
|
|
||||||
"test:calculator": "npm run test:calculator:firefox",
|
|
||||||
"test:calculator:headed": "npm run test:calculator:chrome",
|
|
||||||
"test:eeschema:firefox": "npm run setup:kicad && playwright test --config=playwright-kicad.config.ts --project=firefox kicad/eeschema.spec.ts",
|
"test:eeschema:firefox": "npm run setup:kicad && playwright test --config=playwright-kicad.config.ts --project=firefox kicad/eeschema.spec.ts",
|
||||||
"test:eeschema:chrome": "npm run setup:kicad && playwright test --config=playwright-kicad.config.ts --project=chromium --headed kicad/eeschema.spec.ts",
|
"test:eeschema:chrome": "npm run setup:kicad && playwright test --config=playwright-kicad.config.ts --project=chromium --headed kicad/eeschema.spec.ts",
|
||||||
"test:eeschema": "npm run test:eeschema:firefox",
|
"test:eeschema": "npm run test:eeschema:firefox",
|
||||||
"test:eeschema:headed": "npm run test:eeschema:chrome",
|
"test:eeschema:headed": "npm run test:eeschema:chrome",
|
||||||
|
"test:calculator:firefox": "npm run setup:kicad && playwright test --config=playwright-kicad.config.ts --project=firefox kicad/calculator.spec.ts",
|
||||||
|
"test:calculator:chrome": "npm run setup:kicad && playwright test --config=playwright-kicad.config.ts --project=chromium --headed kicad/calculator.spec.ts",
|
||||||
|
"test:calculator": "npm run test:calculator:firefox",
|
||||||
|
"test:calculator:headed": "npm run test:calculator:chrome",
|
||||||
"test:coroutine:firefox": "playwright test --config=playwright-coroutine.config.ts --project=firefox",
|
"test:coroutine:firefox": "playwright test --config=playwright-coroutine.config.ts --project=firefox",
|
||||||
"test:coroutine:chrome": "playwright test --config=playwright-coroutine.config.ts --project=chromium --headed"
|
"test:coroutine:chrome": "playwright test --config=playwright-coroutine.config.ts --project=chromium --headed"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,90 +0,0 @@
|
||||||
import { defineConfig, devices } from '@playwright/test';
|
|
||||||
import { execSync } from 'child_process';
|
|
||||||
import * as fs from 'fs';
|
|
||||||
import * as path from 'path';
|
|
||||||
|
|
||||||
// Mirrors playwright-kicad.config.ts. The only structural difference is
|
|
||||||
// testMatch — calculator.spec.ts lives next to pcbnew.spec.ts under ./kicad,
|
|
||||||
// and we filter to it so running `npm run test:calculator` doesn't drag in
|
|
||||||
// the pcbnew suite.
|
|
||||||
|
|
||||||
const PORT_FILE = path.join(__dirname, '.test-port');
|
|
||||||
|
|
||||||
// NOTE: Chrome headless crashes on ARM Mac due to SwiftShader WebGL bug
|
|
||||||
// (Chromium issues #1416283, #338414704). Firefox headless works reliably.
|
|
||||||
// Use --project=firefox for headless, --project=chromium for headed debugging.
|
|
||||||
|
|
||||||
function getOrFindPort(): number {
|
|
||||||
try {
|
|
||||||
const stat = fs.statSync(PORT_FILE);
|
|
||||||
const age = Date.now() - stat.mtimeMs;
|
|
||||||
if (age < 60000) {
|
|
||||||
const port = parseInt(fs.readFileSync(PORT_FILE, 'utf-8').trim());
|
|
||||||
if (port > 0 && port < 65536) {
|
|
||||||
return port;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// File doesn't exist or can't be read
|
|
||||||
}
|
|
||||||
|
|
||||||
const port = findFreePort();
|
|
||||||
fs.writeFileSync(PORT_FILE, port.toString());
|
|
||||||
return port;
|
|
||||||
}
|
|
||||||
|
|
||||||
function findFreePort(): number {
|
|
||||||
try {
|
|
||||||
const result = execSync(
|
|
||||||
'python3 -c "import socket; s=socket.socket(); s.bind((\'\',0)); print(s.getsockname()[1]); s.close()"',
|
|
||||||
{ encoding: 'utf-8' }
|
|
||||||
);
|
|
||||||
return parseInt(result.trim());
|
|
||||||
} catch {
|
|
||||||
return 9000 + Math.floor(Math.random() * 1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const port = getOrFindPort();
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
globalSetup: './global-setup.ts',
|
|
||||||
testDir: './kicad',
|
|
||||||
testMatch: 'calculator.spec.ts',
|
|
||||||
fullyParallel: true,
|
|
||||||
forbidOnly: !!process.env.CI,
|
|
||||||
retries: process.env.CI ? 2 : 0,
|
|
||||||
workers: process.env.CI ? 1 : undefined,
|
|
||||||
reporter: 'html',
|
|
||||||
timeout: 180000, // KiCad WASM needs more time to load (3 minutes)
|
|
||||||
|
|
||||||
use: {
|
|
||||||
baseURL: `http://localhost:${port}`,
|
|
||||||
trace: 'retain-on-failure',
|
|
||||||
screenshot: 'only-on-failure',
|
|
||||||
},
|
|
||||||
|
|
||||||
projects: [
|
|
||||||
{
|
|
||||||
name: 'firefox',
|
|
||||||
use: {
|
|
||||||
...devices['Desktop Firefox'],
|
|
||||||
viewport: { width: 1280, height: 720 },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Uses system Chrome (not bundled Chromium) so WebGL runs on the real GPU.
|
|
||||||
name: 'chromium',
|
|
||||||
use: {
|
|
||||||
channel: 'chrome',
|
|
||||||
viewport: { width: 1280, height: 720 },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
webServer: {
|
|
||||||
command: `npx serve apps -p ${port} -c ../serve.json`,
|
|
||||||
port: port,
|
|
||||||
reuseExistingServer: !process.env.CI,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
# Copy PCB Calculator WASM build output to the test directory.
|
|
||||||
# Mirrors setup-kicad-wasm.sh; differences:
|
|
||||||
# - copies calculator.{js,wasm,wasm.map,worker.js} instead of pcbnew.*
|
|
||||||
#
|
|
||||||
# Priority: use local output/ (populated by docker/build-calculator.sh).
|
|
||||||
# Fallback: copy from the Docker volume directly.
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
||||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
||||||
KICAD_TEST="$PROJECT_ROOT/tests/apps/kicad"
|
|
||||||
OUTPUT_DIR="$PROJECT_ROOT/output"
|
|
||||||
|
|
||||||
mkdir -p "$KICAD_TEST"
|
|
||||||
|
|
||||||
if [ -f "$OUTPUT_DIR/calculator.js" ] && [ -f "$OUTPUT_DIR/calculator.wasm" ]; then
|
|
||||||
echo "Copying Calculator WASM files from output directory..."
|
|
||||||
cp "$OUTPUT_DIR/calculator.js" "$KICAD_TEST/"
|
|
||||||
cp "$OUTPUT_DIR/calculator.wasm" "$KICAD_TEST/"
|
|
||||||
# Source map for debug symbols (optional)
|
|
||||||
cp "$OUTPUT_DIR/calculator.wasm.map" "$KICAD_TEST/" 2>/dev/null || true
|
|
||||||
# Worker file for pthreads (optional)
|
|
||||||
cp "$OUTPUT_DIR/calculator.worker.js" "$KICAD_TEST/" 2>/dev/null || true
|
|
||||||
# Bitmap resources for KiCad icons (shared with pcbnew; optional)
|
|
||||||
cp "$OUTPUT_DIR/images.tar.gz" "$KICAD_TEST/" 2>/dev/null || true
|
|
||||||
else
|
|
||||||
echo "Output directory not found, copying from Docker build..."
|
|
||||||
SRC=/workspace/build-wasm/kicad-calculator/pcb_calculator
|
|
||||||
docker compose -f "$PROJECT_ROOT/docker/docker-compose.yml" cp \
|
|
||||||
"kicad-wasm-builder:${SRC}/pcb_calculator.js" "$KICAD_TEST/calculator.js"
|
|
||||||
docker compose -f "$PROJECT_ROOT/docker/docker-compose.yml" cp \
|
|
||||||
"kicad-wasm-builder:${SRC}/pcb_calculator.wasm" "$KICAD_TEST/calculator.wasm"
|
|
||||||
# Optional artifacts (best-effort).
|
|
||||||
docker compose -f "$PROJECT_ROOT/docker/docker-compose.yml" cp \
|
|
||||||
"kicad-wasm-builder:${SRC}/pcb_calculator.wasm.map" "$KICAD_TEST/calculator.wasm.map" 2>/dev/null || true
|
|
||||||
docker compose -f "$PROJECT_ROOT/docker/docker-compose.yml" cp \
|
|
||||||
"kicad-wasm-builder:${SRC}/pcb_calculator.worker.js" "$KICAD_TEST/calculator.worker.js" 2>/dev/null || true
|
|
||||||
docker compose -f "$PROJECT_ROOT/docker/docker-compose.yml" cp \
|
|
||||||
kicad-wasm-builder:/workspace/build-wasm/kicad-calculator/resources/images.tar.gz "$KICAD_TEST/" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# wxWidgets WASM glue code (shared with pcbnew).
|
|
||||||
echo "Copying wxWidgets WASM glue code..."
|
|
||||||
if [ -f "$OUTPUT_DIR/wx.js" ]; then
|
|
||||||
cp "$OUTPUT_DIR/wx.js" "$KICAD_TEST/"
|
|
||||||
else
|
|
||||||
if docker compose -f "$PROJECT_ROOT/docker/docker-compose.yml" cp \
|
|
||||||
kicad-wasm-builder:/workspace/build-wasm/wxwidgets/build/wasm/wx.js "$KICAD_TEST/" 2>/dev/null; then
|
|
||||||
:
|
|
||||||
else
|
|
||||||
cp "$PROJECT_ROOT/wxwidgets/build/wasm/wx.js" "$KICAD_TEST/"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Calculator WASM files copied to $KICAD_TEST"
|
|
||||||
ls -lh "$KICAD_TEST"
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
# Priority: Use local output/ directory (populated by docker/build.sh)
|
# Priority: Use local output/ directory (populated by docker/build.sh)
|
||||||
# Fallback: Copy from Docker volume directly
|
# Fallback: Copy from Docker volume directly
|
||||||
#
|
#
|
||||||
# Copies whichever editors are present (pcbnew, eeschema).
|
# Copies whichever apps are present (pcbnew, eeschema, calculator).
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
|
@ -15,10 +15,22 @@ OUTPUT_DIR="$PROJECT_ROOT/output"
|
||||||
|
|
||||||
mkdir -p "$KICAD_TEST"
|
mkdir -p "$KICAD_TEST"
|
||||||
|
|
||||||
# Copy one editor's artifacts (js, wasm, optional debug/map/worker). Returns 0
|
# Map an app name to its inner CMake build subdirectory. Most apps share their
|
||||||
# if the editor was present, 1 if neither output/ nor the docker volume has it.
|
# subdir name with the app name; pcb_calculator emits OUTPUT_NAME=calculator
|
||||||
|
# but lives under the pcb_calculator/ subtree of the build dir.
|
||||||
|
kicad_subdir_for() {
|
||||||
|
case "$1" in
|
||||||
|
calculator) echo "pcb_calculator" ;;
|
||||||
|
*) echo "$1" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# Copy one app's artifacts (js, wasm, optional debug/map/worker). Returns 0
|
||||||
|
# if the app was present, 1 if neither output/ nor the docker volume has it.
|
||||||
copy_app() {
|
copy_app() {
|
||||||
local app="$1"
|
local app="$1"
|
||||||
|
local subdir
|
||||||
|
subdir=$(kicad_subdir_for "$app")
|
||||||
|
|
||||||
if [ -f "$OUTPUT_DIR/${app}.js" ] && [ -f "$OUTPUT_DIR/${app}.wasm" ]; then
|
if [ -f "$OUTPUT_DIR/${app}.js" ] && [ -f "$OUTPUT_DIR/${app}.wasm" ]; then
|
||||||
echo "Copying ${app} WASM files from output directory..."
|
echo "Copying ${app} WASM files from output directory..."
|
||||||
|
|
@ -32,13 +44,13 @@ copy_app() {
|
||||||
|
|
||||||
echo "Output ${app} not found locally, trying Docker volume..."
|
echo "Output ${app} not found locally, trying Docker volume..."
|
||||||
if docker compose -f "$PROJECT_ROOT/docker/docker-compose.yml" cp \
|
if docker compose -f "$PROJECT_ROOT/docker/docker-compose.yml" cp \
|
||||||
kicad-wasm-builder:/workspace/build-wasm/kicad-${app}/${app}/${app}.js "$KICAD_TEST/" 2>/dev/null \
|
kicad-wasm-builder:/workspace/build-wasm/kicad-${app}/${subdir}/${app}.js "$KICAD_TEST/" 2>/dev/null \
|
||||||
&& docker compose -f "$PROJECT_ROOT/docker/docker-compose.yml" cp \
|
&& docker compose -f "$PROJECT_ROOT/docker/docker-compose.yml" cp \
|
||||||
kicad-wasm-builder:/workspace/build-wasm/kicad-${app}/${app}/${app}.wasm "$KICAD_TEST/" 2>/dev/null; then
|
kicad-wasm-builder:/workspace/build-wasm/kicad-${app}/${subdir}/${app}.wasm "$KICAD_TEST/" 2>/dev/null; then
|
||||||
docker compose -f "$PROJECT_ROOT/docker/docker-compose.yml" cp \
|
docker compose -f "$PROJECT_ROOT/docker/docker-compose.yml" cp \
|
||||||
kicad-wasm-builder:/workspace/build-wasm/kicad-${app}/${app}/${app}.wasm.map "$KICAD_TEST/" 2>/dev/null || true
|
kicad-wasm-builder:/workspace/build-wasm/kicad-${app}/${subdir}/${app}.wasm.map "$KICAD_TEST/" 2>/dev/null || true
|
||||||
docker compose -f "$PROJECT_ROOT/docker/docker-compose.yml" cp \
|
docker compose -f "$PROJECT_ROOT/docker/docker-compose.yml" cp \
|
||||||
kicad-wasm-builder:/workspace/build-wasm/kicad-${app}/${app}/${app}.worker.js "$KICAD_TEST/" 2>/dev/null || true
|
kicad-wasm-builder:/workspace/build-wasm/kicad-${app}/${subdir}/${app}.worker.js "$KICAD_TEST/" 2>/dev/null || true
|
||||||
docker compose -f "$PROJECT_ROOT/docker/docker-compose.yml" cp \
|
docker compose -f "$PROJECT_ROOT/docker/docker-compose.yml" cp \
|
||||||
kicad-wasm-builder:/workspace/build-wasm/kicad-${app}/resources/images.tar.gz "$KICAD_TEST/" 2>/dev/null || true
|
kicad-wasm-builder:/workspace/build-wasm/kicad-${app}/resources/images.tar.gz "$KICAD_TEST/" 2>/dev/null || true
|
||||||
return 0
|
return 0
|
||||||
|
|
@ -49,11 +61,12 @@ copy_app() {
|
||||||
}
|
}
|
||||||
|
|
||||||
found_any=0
|
found_any=0
|
||||||
copy_app pcbnew && found_any=1
|
copy_app pcbnew && found_any=1
|
||||||
copy_app eeschema && found_any=1
|
copy_app eeschema && found_any=1
|
||||||
|
copy_app calculator && found_any=1
|
||||||
|
|
||||||
if [ "$found_any" -eq 0 ]; then
|
if [ "$found_any" -eq 0 ]; then
|
||||||
echo "Error: neither pcbnew nor eeschema artifacts found in output/ or docker volume" >&2
|
echo "Error: no pcbnew/eeschema/calculator artifacts found in output/ or docker volume" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit d1d1627b279672fc71deb4ff4512a7771dfc2cc8
|
Subproject commit 6fb2eac2572cf0d3964ba8bec8d73e017d311733
|
||||||
Loading…
Reference in a new issue