#!/usr/bin/env bash set -euo pipefail cd "$(dirname "$0")" linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} cxx=${CXX:-g++} build_dir=${TMPDIR:-/tmp}/cnc_sim_rs274_source_link python_includes=$(python3.13-config --includes 2>/dev/null || python3-config --includes) rm -rf "$build_dir" mkdir -p "$build_dir" common_flags=( -std=c++17 -DULAPI $python_includes -I core/include -I core/src -I "$linuxcnc_root/src" -I "$linuxcnc_root/src/emc" -I "$linuxcnc_root/src/emc/nml_intf" -I "$linuxcnc_root/src/emc/rs274ngc" -I "$linuxcnc_root/src/emc/motion" -I "$linuxcnc_root/src/emc/pythonplugin" -I "$linuxcnc_root/include" ) objects=() while IFS=: read -r group path note; do case "$group" in core) obj="$build_dir/$(basename "$path" .cc).o" "$cxx" "${common_flags[@]}" -c "$linuxcnc_root/$path" -o "$obj" objects+=("$obj") ;; ""|\#*|binding|tooldata) ;; *) echo "unknown manifest group: $group" >&2 exit 1 ;; esac done < linuxcnc-rs274-source-files.txt for source in \ core/src/canon_event_sink.cpp \ core/src/linuxcnc_canon_bridge.cpp \ core/src/rtcp_kinematics.cpp \ core/src/simulator_gcode_controls.cpp \ core/tools/linuxcnc_rs274_dump.cpp; do obj="$build_dir/$(basename "$source" .cpp).o" "$cxx" "${common_flags[@]}" -c "$source" -o "$obj" objects+=("$obj") done support_objects=( "$linuxcnc_root/src/objects/emc/rs274ngc/interpmodule.o" "$linuxcnc_root/src/objects/emc/rs274ngc/canonmodule.o" "$linuxcnc_root/src/objects/emc/rs274ngc/pyarrays.o" "$linuxcnc_root/src/objects/emc/rs274ngc/pyblock.o" "$linuxcnc_root/src/objects/emc/rs274ngc/pyemctypes.o" "$linuxcnc_root/src/objects/emc/rs274ngc/pyinterp1.o" "$linuxcnc_root/src/objects/emc/rs274ngc/pyparamclass.o" "$linuxcnc_root/src/objects/emc/nml_intf/emcops.o" "$linuxcnc_root/src/objects/emc/sai/dummyemcstat.o" "$linuxcnc_root/src/objects/libnml/nml/stat_msg.o" ) for obj in "${support_objects[@]}"; do if [[ ! -f "$obj" ]]; then echo "missing LinuxCNC support object: $obj" >&2 echo "build LinuxCNC first or set LINUXCNC_ROOT to a built tree" >&2 exit 1 fi done "$cxx" "${objects[@]}" "${support_objects[@]}" \ -L "$linuxcnc_root/lib" \ -Wl,-rpath,"$linuxcnc_root/lib" \ -llinuxcnc-uspace-posix \ -llinuxcncini \ -llinuxcnchal \ -lpyplugin \ -ltooldata \ -lnml \ -lposemath \ -lboost_python313 \ -lpython3.13 \ -lfmt \ -ldl \ -o "$build_dir/linuxcnc_rs274_source_dump" var_file="$build_dir/rs274ngc-source.var" cp "$linuxcnc_root/tests/halui/jogging/sim.var" "$var_file" CNC_SIM_RS274_VAR="$var_file" \ "$build_dir/linuxcnc_rs274_source_dump" tests/gcode/basic_mill.ngc \ >/tmp/cnc_sim_linuxcnc_source_basic_mill.json CNC_SIM_RS274_VAR="$var_file" \ "$build_dir/linuxcnc_rs274_source_dump" tests/gcode/linuxcnc_rtcp_controls.ngc \ >/tmp/cnc_sim_linuxcnc_source_rtcp_controls.json CNC_SIM_RS274_VAR="$var_file" \ "$build_dir/linuxcnc_rs274_source_dump" tests/gcode/linuxcnc_canned_cycle.ngc \ >/tmp/cnc_sim_linuxcnc_source_canned_cycle.json python3 - <<'PY' import json from pathlib import Path events = json.loads(Path("/tmp/cnc_sim_linuxcnc_source_basic_mill.json").read_text()) types = {event["type"] for event in events} required = { "set-units", "set-plane", "set-spindle", "tool-change", "rapid", "set-feed", "linear-feed", "arc-feed", "program-end", } missing = sorted(required - types) if missing: raise SystemExit("missing source-linked events: " + ", ".join(missing)) final_motion = [event for event in events if event["type"] in {"rapid", "linear-feed", "arc-feed"}][-1] if final_motion["end"]["x"] != 0 or final_motion["end"]["y"] != 0: raise SystemExit("unexpected source-linked final XY position") controls = json.loads(Path("/tmp/cnc_sim_linuxcnc_source_rtcp_controls.json").read_text()) kin = [event for event in controls if event["type"] == "kinematics-switch"] if [(event["reserved"], event["feed"]) for event in kin] != [(1, 1), (0, 0), (2, 1)]: raise SystemExit("unexpected source-linked kinematics switch sequence") rtcp = [event for event in controls if event["type"] == "rtcp-state"] if [(event["line"], event["tool"], event["feed"]) for event in rtcp] != [(3, 0, 0), (4, 7, 1)]: raise SystemExit("unexpected source-linked RTCP state sequence") cycle = json.loads(Path("/tmp/cnc_sim_linuxcnc_source_canned_cycle.json").read_text()) motions = [event for event in cycle if event["type"] in {"rapid", "linear-feed"}] expected_cycle = [ ("rapid", 0, 0, 5), ("rapid", 20, 0, 5), ("rapid", 20, 0, 3), ("linear-feed", 20, 0, -2), ("rapid", 20, 0, 3), ] actual_cycle = [(event["type"], event["end"]["x"], event["end"]["y"], event["end"]["z"]) for event in motions] if actual_cycle != expected_cycle: raise SystemExit(f"unexpected source-linked G81/G80 expansion: {actual_cycle!r}") PY echo "linuxcnc rs274 source link smoke passed (${#objects[@]} local objects)" echo "dumped /tmp/cnc_sim_linuxcnc_source_basic_mill.json" echo "dumped /tmp/cnc_sim_linuxcnc_source_rtcp_controls.json" echo "dumped /tmp/cnc_sim_linuxcnc_source_canned_cycle.json"