#!/usr/bin/env bash set -euo pipefail cd "$(dirname "$0")" # LinuxCNC source basis: this guard ties generated switchkins/remap aliases and # config cases to LinuxCNC configs/sim INI REMAP entries, remap_subs # # assignments, and halcmd loadusr parsing sources. It validates # generator/table policy only; it does not add CNC behavior, expand smoke # parsing, or interpret G-code. linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} linuxcnc_root=$(cd "$linuxcnc_root" && pwd) grep -F 'LinuxCNC source basis: generated switchkins/remap tables are extracted from' \ generate-linuxcnc-switchkins-remap-table.sh >/dev/null grep -F 'src/hal/utils/halcmd_commands.cc do_loadusr_cmd()' \ generate-linuxcnc-switchkins-remap-table.sh >/dev/null grep -F 'getopt("+wWin:")' generate-linuxcnc-switchkins-remap-table.sh >/dev/null grep -F 'src/hal/utils/halcmd_completion.c lists the common -W/-Wn/-w/-iw forms' \ generate-linuxcnc-switchkins-remap-table.sh >/dev/null grep -F 'LinuxCNC TOOL_TABLE source is not listed as tooldata' \ generate-linuxcnc-switchkins-remap-table.sh >/dev/null grep -F 'LinuxCNC source basis: switchkins/remap table validation derives M428/M429/M430' \ check-linuxcnc-switchkins-remap-table.sh >/dev/null grep -F 'validate_tool_table_source_coverage_only' \ check-linuxcnc-switchkins-remap-table.sh >/dev/null grep -F 'exposes TOOL_TABLE through switchkins API/web bridge' \ check-linuxcnc-switchkins-remap-table.sh >/dev/null grep -F 'int do_loadusr_cmd(const char *args[])' "$linuxcnc_root/src/hal/utils/halcmd_commands.cc" >/dev/null grep -F 'getopt(argc,' "$linuxcnc_root/src/hal/utils/halcmd_commands.cc" >/dev/null grep -F '"+wWin:"' "$linuxcnc_root/src/hal/utils/halcmd_commands.cc" >/dev/null grep -F -- '-Wn' "$linuxcnc_root/src/hal/utils/halcmd_completion.c" >/dev/null python3 - "$linuxcnc_root" <<'PY' from pathlib import Path import json import re import sys linuxcnc_root = Path(sys.argv[1]) source_map = Path("docs/linuxcnc-switchkins-remap-source-map.md").read_text(encoding="utf-8") table = Path("core/src/linuxcnc_switchkins_remap_table.inc").read_text(encoding="utf-8") cpp_cases = Path("core/tests/linuxcnc_switchkins_remap_config_cases.inc").read_text(encoding="utf-8") json_cases = json.loads(Path("web/public/linuxcnc_switchkins_remap_config_cases.json").read_text(encoding="utf-8")) counts = { "core/src/linuxcnc_switchkins_remap_table.inc": len(re.findall(r'^\s*\{"', table, re.M)), "core/tests/linuxcnc_switchkins_remap_config_cases.inc": len(re.findall(r'^\s*\{"', cpp_cases, re.M)), "web/public/linuxcnc_switchkins_remap_config_cases.json": len(json_cases), } for path, count in counts.items(): if f"| `{path}` | {count} |" not in source_map: raise SystemExit(f"docs/linuxcnc-switchkins-remap-source-map.md count mismatch for {path}") remap_sources = [ path for path in (linuxcnc_root / "configs/sim").rglob("*remap.ngc") if path.name in {"428remap.ngc", "429remap.ngc", "430remap.ngc"} ] remap_ini_re = re.compile(r"REMAP\s*=\s*M(428|429|430)(?:\D|$)", re.I) remap_inis = [ path for path in (linuxcnc_root / "configs/sim").rglob("*.ini") if remap_ini_re.search(path.read_text(encoding="utf-8", errors="ignore")) ] if f"| LinuxCNC M428/M429/M430 remap sources | {len(remap_sources)} |" not in source_map: raise SystemExit("docs/linuxcnc-switchkins-remap-source-map.md remap source count mismatch") if f"| LinuxCNC switchkins REMAP INI sources | {len(remap_inis)} |" not in source_map: raise SystemExit("docs/linuxcnc-switchkins-remap-source-map.md REMAP INI source count mismatch") required_phrases = [ "must not add CNC behavior", "must not expand the temporary smoke parser", "`#` assignments", "[EMCIO] TOOL_TABLE` entries are source coverage only", "must not become generated switchkins aliases", "./check-linuxcnc-switchkins-remap-source-map.sh", "./check-linuxcnc-switchkins-remap-table.sh linuxcnc-kinematics-source-files.txt", "does not interpret G-code", ] for phrase in required_phrases: if phrase not in source_map: raise SystemExit( f"docs/linuxcnc-switchkins-remap-source-map.md is missing boundary phrase: {phrase}" ) for entry in [ "test-native.sh", "test-linuxcnc-source-link.sh", "build-wasm.sh", ]: text = Path(entry).read_text(encoding="utf-8") if "./check-linuxcnc-switchkins-remap-source-map.sh" not in text: raise SystemExit(f"{entry} must run ./check-linuxcnc-switchkins-remap-source-map.sh") case_fields = {case["field"] for case in json_cases} source_only_field_re = re.compile(r"asset|metadata|tool|tooldata|tool_table|TOOL_TABLE", re.I) bad_fields = [field for field in case_fields if source_only_field_re.search(field)] if bad_fields: raise SystemExit(f"generated switchkins config cases expose source-only fields: {bad_fields}") api_surface_re = re.compile(r'\b(?:asset|metadata|toolData|tooldata|toolTable|tooltable|tool_table|TOOL_TABLE)\b') for api_path in ["web/src/wasm-core.js", "web/src/index.ts", "core/include/cnc_sim_api.h"]: if api_surface_re.search(Path(api_path).read_text(encoding="utf-8")): raise SystemExit(f"{api_path} exposes source-only switchkins inputs through API surface") PY