#!/usr/bin/env bash set -euo pipefail cd "$(dirname "$0")" # LinuxCNC source basis: the wasm build preflights and compiles the # linuxcnc-rs274-wasm-source-files.txt manifest entries from # src/emc/rs274ngc/tooldata plus source-basis-checked shims before copying the # browser artifacts; it must not add independent interpreter behavior. linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} manifest=${1:-linuxcnc-rs274-wasm-source-files.txt} build_jobs=${CNC_SIM_BUILD_JOBS:-8} if ! [[ "$build_jobs" =~ ^[1-9][0-9]*$ ]]; then echo "CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs" >&2 exit 1 fi cmake_compiler_launcher_mode=none cmake_compiler_launcher_args=() if [[ -z ${CXX:-} ]] && command -v ccache >/dev/null 2>&1; then cmake_compiler_launcher_mode=ccache cmake_compiler_launcher_args+=( -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache ) fi preflight_cache_dir=build/wasm-preflight-cache LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" "$manifest" LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-switchkins-remap-table-cached.sh --cache-dir "$preflight_cache_dir" linuxcnc-kinematics-source-files.txt manifest=$(cd "$(dirname "$manifest")" && pwd)/$(basename "$manifest") linuxcnc_root=$(cd "$linuxcnc_root" && pwd) default_manifest="$PWD/linuxcnc-rs274-wasm-source-files.txt" count_nonempty_lines() { local text=${1-} local count=0 local line while IFS= read -r line; do if [[ -n "$line" ]]; then count=$((count + 1)) fi done <<<"$text" printf '%s\n' "$count" } parallel_probe_labels=() parallel_probe_logs=() parallel_probe_pids=() run_parallel_wasm_probe() { local label=$1 shift local probe_index=${#parallel_probe_pids[@]} local log_dir=build/wasm-preflight-cache/parallel-probe-logs local log="$log_dir/probe_${probe_index}.log" mkdir -p "$log_dir" ( CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" "$@" ) >"$log" 2>&1 & parallel_probe_labels+=("$label") parallel_probe_logs+=("$log") parallel_probe_pids+=("$!") } wait_parallel_wasm_probes() { local failed=0 local probe_index local status for probe_index in "${!parallel_probe_pids[@]}"; do echo "${parallel_probe_labels[$probe_index]}" if wait "${parallel_probe_pids[$probe_index]}"; then cat "${parallel_probe_logs[$probe_index]}" else status=$? cat "${parallel_probe_logs[$probe_index]}" >&2 || true echo "${parallel_probe_labels[$probe_index]} failed with status $status" >&2 failed=1 fi done parallel_probe_labels=() parallel_probe_logs=() parallel_probe_pids=() if [[ "$failed" -ne 0 ]]; then exit 1 fi } # This script writes fixed build and public artifact paths, so serialize it. if [[ "${CNC_SIM_BUILD_WASM_SKIP_LOCK:-0}" != "1" ]]; then exec 9>"${TMPDIR:-/tmp}/cnc_sim_build_wasm.lock" flock 9 fi manifest_core_count=0 manifest_blocked_count=0 manifest_core_output=$(CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core) manifest_blocked_output=$(CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" blocked) manifest_core_count=$(count_nonempty_lines "$manifest_core_output") manifest_blocked_count=$(count_nonempty_lines "$manifest_blocked_output") if [[ "$manifest_core_count" -eq 0 || "$manifest_blocked_count" -eq 0 ]]; then echo "unexpected wasm manifest partition: core=$manifest_core_count blocked=$manifest_blocked_count" >&2 exit 1 fi if [[ "$manifest" == "$default_manifest" ]] && [[ "$manifest_core_count" -ne 26 || "$manifest_blocked_count" -ne 9 ]]; then echo "unexpected wasm manifest partition: core=$manifest_core_count blocked=$manifest_blocked_count" >&2 exit 1 fi LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" "$manifest" --require-rs274-complete echo "LinuxCNC wasm blocker scan" CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" ./test-linuxcnc-wasm-blockers.sh "$manifest" echo "LinuxCNC wasm-safe source syntax probe" CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" ./test-linuxcnc-wasm-source-syntax.sh "$manifest" echo "LinuxCNC wasm-safe source object probe" CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" ./test-linuxcnc-wasm-source-objects.sh "$manifest" echo "LinuxCNC wasm-safe CMake probe target" CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" ./test-linuxcnc-wasm-cmake-safe-probe.sh "$manifest" run_parallel_wasm_probe "LinuxCNC wasm tooldata shim link probe" ./test-linuxcnc-wasm-tooldata-link.sh run_parallel_wasm_probe "LinuxCNC wasm tooldata_common link probe" ./test-linuxcnc-wasm-tooldata-common-link.sh run_parallel_wasm_probe "LinuxCNC wasm tooldata_mmap symbol probe" ./test-linuxcnc-wasm-tooldata-mmap-symbols.sh run_parallel_wasm_probe "LinuxCNC wasm tooldata runtime symbol probe" ./test-linuxcnc-wasm-tooldata-runtime-symbols.sh run_parallel_wasm_probe "LinuxCNC wasm interp_base link probe" ./test-linuxcnc-wasm-interp-base-link.sh run_parallel_wasm_probe "LinuxCNC wasm interp_find link probe" ./test-linuxcnc-wasm-interp-find-link.sh run_parallel_wasm_probe "LinuxCNC wasm python_plugin link probe" ./test-linuxcnc-wasm-python-plugin-link.sh run_parallel_wasm_probe "LinuxCNC wasm Python C API symbol probe" ./test-linuxcnc-wasm-python-c-api-symbols.sh run_parallel_wasm_probe "LinuxCNC wasm rs274ngc_pre object probe" ./test-linuxcnc-wasm-rs274ngc-pre-object.sh wait_parallel_wasm_probes echo "LinuxCNC wasm rs274ngc_pre link blocker scan" CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" ./test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh "$manifest" echo "LinuxCNC wasm rs274ngc_pre link probe" CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" ./test-linuxcnc-wasm-rs274ngc-pre-link.sh "$manifest" echo "LinuxCNC wasm runtime link probe" CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" ./test-linuxcnc-wasm-runtime-link.sh "$manifest" missing_tools=() for required_tool in cmake emcmake emcc node; do if ! command -v "$required_tool" >/dev/null 2>&1; then missing_tools+=("$required_tool") fi done if ((${#missing_tools[@]} > 0)); then echo "WASM build tools are required. Missing: ${missing_tools[*]}." >&2 echo "Install/activate cmake, emsdk, and Node.js so cmake, emcmake, emcc, and node are in PATH." >&2 exit 1 fi wasm_build_signature=build/wasm/wasm-build.signature wasm_build_next_signature=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_wasm_build.signature.XXXXXX") { printf 'CXX=%s\n' "${CXX:-g++}" printf 'PWD=%s\n' "$PWD" printf 'LINUXCNC_ROOT=%s\n' "$linuxcnc_root" printf 'MANIFEST=%s\n' "$manifest" printf 'MANIFEST_HASH=' sha256sum "$manifest" printf 'COMPILER_LAUNCHER=%s\n' "$cmake_compiler_launcher_mode" printf 'CMAKE_LISTS=' sha256sum core/CMakeLists.txt printf 'CONFIGURE_RULE_VERSION=1\n' } > "$wasm_build_next_signature" configure_wasm=1 if [[ ! -f "$wasm_build_signature" ]] || ! cmp -s "$wasm_build_signature" "$wasm_build_next_signature"; then mkdir -p build/wasm elif [[ -f build/wasm/CMakeCache.txt ]]; then configure_wasm=0 fi if [[ "$configure_wasm" == "1" ]]; then emcmake cmake -S core -B build/wasm \ -DCMAKE_BUILD_TYPE=Release \ -DCNC_SIM_LINUXCNC_ROOT="$linuxcnc_root" \ -DCNC_SIM_LINUXCNC_WASM_SOURCE_MANIFEST="$manifest" \ -DCNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE=ON \ "${cmake_compiler_launcher_args[@]}" fi mv "$wasm_build_next_signature" "$wasm_build_signature" cmake --build build/wasm --target cnc_sim_wasm_runtime_probe cnc_sim_wasm --parallel "$build_jobs" mkdir -p web/public cp build/wasm/cnc_sim.js web/public/cnc_sim.js cp build/wasm/cnc_sim.wasm web/public/cnc_sim.wasm cmp -s build/wasm/cnc_sim.js web/public/cnc_sim.js cmp -s build/wasm/cnc_sim.wasm web/public/cnc_sim.wasm ./test-web-wasm-node-smoke.sh ./test-web-wasm-browser-smoke.sh