#!/usr/bin/env bash set -euo pipefail cd "$(dirname "$0")" # LinuxCNC source basis: src/emc/rs274ngc/canonmodule.cc, interpmodule.cc, # and py*.cc are blocked Python/Boost.Python binding sources. This probe links # the non-gcodemodule binding objects against inert Python/runtime shims, while # keeping src/emc/rs274ngc/gcodemodule.cc isolated because it defines Canon # callbacks that collide with the browser runtime bridge. manifest=${1:-linuxcnc-rs274-wasm-source-files.txt} preflight_cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-build/wasm-probe-preflight-cache} python_build_dir=${CNC_SIM_WASM_PYTHON_C_API_BUILD_DIR:-build/wasm-python-c-api-symbols} if [[ -z "$python_build_dir" ]]; then echo "CNC_SIM_WASM_PYTHON_C_API_BUILD_DIR must not be empty" >&2 exit 1 fi if [[ "$python_build_dir" =~ [[:space:]] ]]; then echo "CNC_SIM_WASM_PYTHON_C_API_BUILD_DIR must not contain whitespace: $python_build_dir" >&2 exit 1 fi mkdir -p "$python_build_dir" python_build_dir=$(cd "$python_build_dir" && pwd) if [[ "$python_build_dir" == "/" ]]; then echo "CNC_SIM_WASM_PYTHON_C_API_BUILD_DIR must not be the filesystem root" >&2 exit 1 fi exec 9>"$python_build_dir/python-binding-link.lock" flock 9 CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" \ CNC_SIM_WASM_PYTHON_C_API_BUILD_DIR="$python_build_dir" \ ./test-linuxcnc-wasm-python-c-api-symbols.sh runtime_probe=$(CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" ./build-linuxcnc-wasm-link-probe.sh --mode runtime "$manifest") runtime_dir=$(dirname "$runtime_probe") runtime_signature="$runtime_dir/link.signature" if [[ ! -f "$runtime_signature" ]]; then echo "missing runtime link probe signature: $runtime_signature" >&2 exit 1 fi mapfile -t runtime_objects < <(awk 'flag{print} /^OBJECTS:$/{flag=1; next}' "$runtime_signature" | sed '/^$/d') if [[ "${#runtime_objects[@]}" -eq 0 ]]; then echo "runtime link probe signature did not list objects" >&2 exit 1 fi non_gcode_binding_objects=( "$python_build_dir/canonmodule.o" "$python_build_dir/interpmodule.o" "$python_build_dir/pyarrays.o" "$python_build_dir/pyblock.o" "$python_build_dir/pyemctypes.o" "$python_build_dir/pyinterp1.o" "$python_build_dir/pyparamclass.o" ) for object in "${non_gcode_binding_objects[@]}" "$python_build_dir/gcodemodule.o"; do if [[ ! -f "$object" ]]; then echo "missing Python binding object: $object" >&2 exit 1 fi done main_source="$python_build_dir/python_binding_link_probe_main.cc" main_object="$python_build_dir/python_binding_link_probe_main.o" target="$python_build_dir/python_binding_link_probe" cat > "$main_source" <<'EOF' int main() { return 0; } EOF trap 'rm -f "$main_source" "$main_object"' EXIT cxx=${CXX:-g++} if [[ -z ${CXX:-} ]] && command -v ccache >/dev/null 2>&1; then cxx="ccache $cxx" fi $cxx -std=c++17 -c "$main_source" -o "$main_object" $cxx "${runtime_objects[@]}" "${non_gcode_binding_objects[@]}" "$main_object" \ -Wl,--no-gc-sections -lfmt -o "$target" negative_log="$python_build_dir/python_binding_gcodemodule_collision.err" if $cxx "${runtime_objects[@]}" "${non_gcode_binding_objects[@]}" "$python_build_dir/gcodemodule.o" "$main_object" \ -Wl,--no-gc-sections -lfmt -o "$python_build_dir/python_binding_link_probe_with_gcodemodule" \ 2>"$negative_log"; then echo "gcodemodule binding unexpectedly linked with runtime bridge Canon callbacks" >&2 exit 1 fi grep -F "multiple definition of" "$negative_log" >/dev/null grep -F "gcodemodule.o" "$negative_log" >/dev/null grep -F "linuxcnc_canon_bridge.cpp" "$negative_log" >/dev/null "$target" echo "linuxcnc wasm Python binding link probe passed"