#!/usr/bin/env bash set -euo pipefail cd "$(dirname "$0")" # LinuxCNC source basis: object probes compile the core entries selected from # linuxcnc-rs274-wasm-source-files.txt, including src/emc/rs274ngc, # src/emc/tooldata/tooldata_common.cc, src/emc/ini/inifile.cc, and # src/emc/nml_intf/emcops.cc, together with CMake-listed wasm-safe project # bridge sources and shims that replace the manifest's blocked Python, dlopen, # tooldata mmap, HAL, and runtime edges. usage() { echo "usage: $0 --mode source-objects|rs274ngc-pre|runtime [manifest]" >&2 } mode= manifest=linuxcnc-rs274-wasm-source-files.txt while [[ "$#" -gt 0 ]]; do case "$1" in --mode) if [[ "$#" -lt 2 ]]; then echo "missing --mode value" >&2 exit 1 fi mode=$2 shift 2 ;; --help|-h) usage exit 0 ;; --*) usage echo "unknown wasm probe object cache option: $1" >&2 exit 1 ;; *) manifest=$1 shift ;; esac done case "$mode" in source-objects|rs274ngc-pre|runtime) ;; "") usage echo "missing --mode" >&2 exit 1 ;; *) usage echo "unknown wasm probe object cache mode: $mode" >&2 exit 1 ;; esac linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} cxx=${CXX:-g++} if [[ -z ${CXX:-} ]] && command -v ccache >/dev/null 2>&1; then cxx="ccache $cxx" fi 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 cache_dir=${CNC_SIM_WASM_PROBE_OBJECT_CACHE_DIR-build/wasm-probe-object-cache} if [[ -z "$cache_dir" ]]; then echo "CNC_SIM_WASM_PROBE_OBJECT_CACHE_DIR must not be empty" >&2 exit 1 fi if [[ "$cache_dir" =~ [[:space:]] ]]; then echo "CNC_SIM_WASM_PROBE_OBJECT_CACHE_DIR must not contain whitespace: $cache_dir" >&2 exit 1 fi mkdir -p "$cache_dir" cache_dir=$(cd "$cache_dir" && pwd) if [[ "$cache_dir" == "/" ]]; then echo "CNC_SIM_WASM_PROBE_OBJECT_CACHE_DIR must not be the filesystem root" >&2 exit 1 fi preflight_cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-"$cache_dir/preflight-cache"} LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" "$manifest" --require-rs274-complete manifest=$(cd "$(dirname "$manifest")" && pwd)/$(basename "$manifest") linuxcnc_root=$(cd "$linuxcnc_root" && pwd) common_flag_profile=core20-sections case "$mode" in source-objects) common_flag_profile=core20 ;; esac common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh --profile "$common_flag_profile") mapfile -t common_flags <<<"$common_flag_output" case "$mode" in runtime) common_flags+=(-DCNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND) ;; esac grep -F 'LinuxCNC source basis: wasm-safe project probe sources are the thin bridge' list-linuxcnc-wasm-safe-project-sources.sh >/dev/null grep -F 'LinuxCNC source basis: wasm-safe shim probe sources are limited to the' list-linuxcnc-wasm-safe-shims.sh >/dev/null sources=() shim_output=$(./list-linuxcnc-wasm-safe-shims.sh) mapfile -t shim_sources <<<"$shim_output" sources+=("${shim_sources[@]}") case "$mode" in source-objects) ;; rs274ngc-pre|runtime) project_source_output=$(./list-linuxcnc-wasm-safe-project-sources.sh) if [[ -n "$project_source_output" ]]; then mapfile -t project_sources <<<"$project_source_output" sources+=("${project_sources[@]}") fi ;; esac source_output=$(CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core full) mapfile -t linuxcnc_sources <<<"$source_output" sources+=("${linuxcnc_sources[@]}") for source in "${sources[@]}"; do if [[ -z "$source" ]]; then echo "wasm probe object source list contains an empty path" >&2 exit 1 fi done mode_dir="$cache_dir/$mode" mkdir -p "$mode_dir" exec 8>"$mode_dir/objects.lock" flock 8 objects=() object_index=0 for source in "${sources[@]}"; do objects+=("$mode_dir/source_${object_index}.o") object_index=$((object_index + 1)) done signature="$mode_dir/objects.signature" next_signature="$mode_dir/objects.signature.next" { printf 'CXX=%s\n' "$cxx" printf 'PWD=%s\n' "$PWD" printf 'LINUXCNC_ROOT=%s\n' "$linuxcnc_root" printf 'MANIFEST=%s\n' "$manifest" printf 'MODE=%s\n' "$mode" printf 'BUILD_RULE_VERSION=1\n' printf 'COMMON_FLAGS=' printf ' %s' "${common_flags[@]}" printf '\n' printf 'SOURCES:\n' printf '%s\n' "${sources[@]}" } > "$next_signature" if [[ ! -f "$signature" ]] || ! cmp -s "$signature" "$next_signature"; then rm -f "$mode_dir"/source_*.o "$mode_dir"/source_*.d fi mv "$next_signature" "$signature" makefile="$mode_dir/objects.mk" { printf 'CXX := %s\n' "$cxx" printf 'COMMON_FLAGS :=' printf ' %s' "${common_flags[@]}" printf '\n\n' printf 'OBJECTS :=' printf ' %s' "${objects[@]}" printf '\n\n' printf 'DEP_FILES :=\n' for object in "${objects[@]}"; do printf 'DEP_FILES += %s.d\n' "$object" done printf '\n.PHONY: all\n' printf 'all: $(OBJECTS)\n\n' object_index=0 for source in "${sources[@]}"; do source_flags=() case "$source" in core/src/linuxcnc_corexykins_adapter.c|core/src/linuxcnc_genserfuncs_adapter.c|core/src/linuxcnc_xyzab_tdr_kins_adapter.c) source_flags+=(-fpermissive) ;; esac printf '%s: %s\n' "${objects[$object_index]}" "$source" printf '\t@$(CXX) $(COMMON_FLAGS)' printf ' %s' "${source_flags[@]}" printf ' -MMD -MP -MF %s.d -c %s -o %s\n\n' "${objects[$object_index]}" "$source" "${objects[$object_index]}" object_index=$((object_index + 1)) done printf '\n-include $(DEP_FILES)\n' } > "$makefile" make --output-sync=target -j"$build_jobs" -f "$makefile" >/dev/null printf '%s\n' "${objects[@]}"