#!/usr/bin/env bash set -euo pipefail cd "$(dirname "$0")" # LinuxCNC source basis: this guard ties LinuxCNC tooldata/Submakefile, # src/emc/tooldata sources, native/WASM manifests, and wasm-safe tooldata shims # to the tooldata source map. It validates source coverage 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 'LIBTOOLDATA_SRCS += emc/tooldata/tooldata_nml.cc' \ "$linuxcnc_root/src/emc/tooldata/Submakefile" >/dev/null grep -F 'LIBTOOLDATA_SRCS += emc/tooldata/tooldata_mmap.cc' \ "$linuxcnc_root/src/emc/tooldata/Submakefile" >/dev/null grep -F 'LIBTOOLDATA_SRCS += emc/tooldata/tooldata_common.cc' \ "$linuxcnc_root/src/emc/tooldata/Submakefile" >/dev/null grep -F 'LIBTOOLDATA_SRCS += emc/tooldata/tooldata_db.cc' \ "$linuxcnc_root/src/emc/tooldata/Submakefile" >/dev/null grep -F 'TOOL_MMAP_READ_SRCS = emc/tooldata/tool_mmap_read.cc' \ "$linuxcnc_root/src/emc/tooldata/Submakefile" >/dev/null grep -F 'TOOL_WATCH_SRCS = emc/tooldata/tool_watch.cc' \ "$linuxcnc_root/src/emc/tooldata/Submakefile" >/dev/null grep -F 'int main (int argc,char**argv)' \ "$linuxcnc_root/src/emc/tooldata/tool_mmap_read.cc" >/dev/null grep -F 'if (tool_mmap_user())' \ "$linuxcnc_root/src/emc/tooldata/tool_mmap_read.cc" >/dev/null grep -F 'idx_last = tooldata_last_index_get();' \ "$linuxcnc_root/src/emc/tooldata/tool_mmap_read.cc" >/dev/null grep -F 'if (tooldata_get(&tdata,idx) != IDX_OK)' \ "$linuxcnc_root/src/emc/tooldata/tool_mmap_read.cc" >/dev/null grep -F 'RCS_STAT_CHANNEL *stat = 0;' \ "$linuxcnc_root/src/emc/tooldata/tool_watch.cc" >/dev/null grep -F 'stat = new RCS_STAT_CHANNEL(emcFormat, "emcStatus", "xemc", nmlfile);' \ "$linuxcnc_root/src/emc/tooldata/tool_watch.cc" >/dev/null grep -F 'EMC_STAT *emcStatus = static_cast(stat->get_address());' \ "$linuxcnc_root/src/emc/tooldata/tool_watch.cc" >/dev/null grep -F 'emcStatus->io.tool.pocketPrepped' \ "$linuxcnc_root/src/emc/tooldata/tool_watch.cc" >/dev/null grep -F '#define TOOL_MMAP_FILENAME ".tool.mmap"' \ "$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null grep -F 'bool tool_mmap_is_random_toolchanger(void)' \ "$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null grep -F 'ans = hptr->is_random_toolchanger;' \ "$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null grep -F 'int tool_mmap_creator(EMC_TOOL_STAT const * ptr,int random_toolchanger)' \ "$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null grep -F 'static int inited=0;' \ "$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null grep -F 'if (inited) {' \ "$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null grep -F 'inited = 1;' \ "$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null grep -F 'hptr->is_random_toolchanger = random_toolchanger;' \ "$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null grep -F 'hptr->last_index = 0;' \ "$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null grep -F 'int tool_mmap_user()' \ "$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null grep -F 'fprintf(stderr,"tool_mmap_user(): tool mmap not available\n");' \ "$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null grep -F 'return(-1);' \ "$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null grep -F 'void tool_mmap_close()' \ "$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null grep -F 'if (!tool_mmap_base) { return; }' \ "$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null grep -F 'int tooldata_last_index_get(void)' \ "$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null grep -F 'tool_mmap_mutex_give(); return -1;' \ "$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null grep -F 'int tooldata_find_index_for_tool(int toolno)' \ "$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null grep -F 'if (toolno == -1) {tool_mmap_mutex_give(); return -1;}' \ "$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null grep -F 'if (!hptr->is_random_toolchanger && toolno == 0)' \ "$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null grep -F 'int tool_nml_register(CANON_TOOL_TABLE *tblptr)' \ "$linuxcnc_root/src/emc/tooldata/tooldata_nml.cc" >/dev/null grep -F 'int tooldata_db_init(char progname_plus_args[]' \ "$linuxcnc_root/src/emc/tooldata/tooldata_db.cc" >/dev/null grep -F '#define MAX_DB_PROGRAM_ARGS 10' \ "$linuxcnc_root/src/emc/tooldata/tooldata_db.cc" >/dev/null grep -F 'char* token = strtok_r(progname_plus_args, " ", &saveptr);' \ "$linuxcnc_root/src/emc/tooldata/tooldata_db.cc" >/dev/null grep -F 'if (child_argc >= MAX_DB_PROGRAM_ARGS)' \ "$linuxcnc_root/src/emc/tooldata/tooldata_db.cc" >/dev/null grep -F 'if (access(child_argv[0],X_OK))' \ "$linuxcnc_root/src/emc/tooldata/tooldata_db.cc" >/dev/null grep -F 'int tooldata_db_getall() {' \ "$linuxcnc_root/src/emc/tooldata/tooldata_db.cc" >/dev/null grep -F 'if (!db_live) {return -1;}' \ "$linuxcnc_root/src/emc/tooldata/tooldata_db.cc" >/dev/null grep -F 'int tooldata_db_notify(tool_notify_t ntype,' \ "$linuxcnc_root/src/emc/tooldata/tooldata_db.cc" >/dev/null grep -F 'if (!db_live) return 0;' \ "$linuxcnc_root/src/emc/tooldata/tooldata_db.cc" >/dev/null grep -F 'int tooldata_read_entry(const char *input_line)' \ "$linuxcnc_root/src/emc/tooldata/tooldata_common.cc" >/dev/null grep -F 'LinuxCNC tooldata source map' docs/linuxcnc-tooldata-source-map.md >/dev/null grep -F 'must not add CNC behavior' docs/linuxcnc-tooldata-source-map.md >/dev/null grep -F 'must not expand the temporary smoke parser' docs/linuxcnc-tooldata-source-map.md >/dev/null grep -F '`src/emc/tooldata/Submakefile` selects `tooldata_mmap.cc`' \ docs/linuxcnc-tooldata-source-map.md >/dev/null grep -F 'Tool table files referenced by switchkins INI files are source coverage only' \ docs/linuxcnc-tooldata-source-map.md >/dev/null grep -F '`src/emc/tooldata/tool_mmap_read.cc` is the mmap tool table reader utility' \ docs/linuxcnc-tooldata-source-map.md >/dev/null grep -F '`tool_mmap_user()`, `tooldata_last_index_get()`, and `tooldata_get()`' \ docs/linuxcnc-tooldata-source-map.md >/dev/null grep -F "single-creator process boundary from" docs/linuxcnc-tooldata-source-map.md >/dev/null grep -F "including after" docs/linuxcnc-tooldata-source-map.md >/dev/null grep -F '`src/emc/tooldata/tool_watch.cc` is the NML/mmap tool table watcher utility' \ docs/linuxcnc-tooldata-source-map.md >/dev/null grep -F '`EMC_STAT::io.tool` fields through `RCS_STAT_CHANNEL`' \ docs/linuxcnc-tooldata-source-map.md >/dev/null grep -F 'tooldata:src/emc/tooldata/tooldata_common.cc:shared tool table parser/format logic' \ linuxcnc-rs274-source-files.txt >/dev/null grep -F 'tooldata:src/emc/tooldata/tooldata_mmap.cc:native mmap implementation; replace for wasm' \ linuxcnc-rs274-source-files.txt >/dev/null grep -F 'tooldata:src/emc/tooldata/tooldata_db.cc:external database integration; exclude from wasm core' \ linuxcnc-rs274-source-files.txt >/dev/null grep -F 'tooldata:src/emc/tooldata/tooldata_nml.cc:NML tool table backend selected by LinuxCNC TOOL_NML_FLAG' \ linuxcnc-rs274-source-files.txt >/dev/null grep -F 'header:src/emc/tooldata/tooldata.hh:LinuxCNC tooldata declarations used by RS274 and wasm-safe tooldata shims' \ linuxcnc-rs274-source-files.txt >/dev/null grep -F 'metadata:src/emc/tooldata/Submakefile:LinuxCNC tooldata build metadata defining mmap and NML backend source selection' \ linuxcnc-rs274-source-files.txt >/dev/null grep -F 'core:src/emc/tooldata/tooldata_common.cc:shared tool table parser/format logic linked with wasm-safe mmap backend' \ linuxcnc-rs274-wasm-source-files.txt >/dev/null grep -F 'blocked:src/emc/tooldata/tooldata_mmap.cc:native mmap implementation' \ linuxcnc-rs274-wasm-source-files.txt >/dev/null grep -F 'metadata:src/emc/tooldata/tooldata_db.cc:source basis for wasm-safe tooldata runtime export shim' \ linuxcnc-rs274-wasm-source-files.txt >/dev/null grep -F 'metadata:src/emc/tooldata/tooldata_nml.cc:source basis for wasm-safe tooldata runtime NML export shim' \ linuxcnc-rs274-wasm-source-files.txt >/dev/null grep -F 'LinuxCNC source basis: src/emc/tooldata/tooldata_mmap.cc provides these' \ core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null grep -F 'bool mmap_created = false;' \ core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null grep -F 'bool mmap_random_toolchanger = false;' \ core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null grep -F 'bool mmap_creator_called = false;' \ core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null grep -F 'int tool_mmap_creator(EMC_TOOL_STAT const *ptr, int random_toolchanger)' \ core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null grep -F 'Error: tool_mmap_creator already called BYE' \ core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null grep -F 'mmap_created = true;' \ core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null grep -F 'mmap_random_toolchanger = random_toolchanger;' \ core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null grep -F 'last_index = 0;' \ core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null grep -F 'int tool_mmap_user(void)' \ core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null grep -F 'tool_mmap_user(): tool mmap not available' \ core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null grep -F 'void tool_mmap_close(void)' \ core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null grep -F 'mmap_created = false;' \ core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null grep -F 'bool tool_mmap_is_random_toolchanger(void)' \ core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null grep -F 'return mmap_random_toolchanger;' \ core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null grep -F 'int tooldata_last_index_get(void)' \ core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null grep -F 'return -1;' \ core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null grep -F 'LinuxCNC source basis: src/emc/tooldata/tooldata_nml.cc and tooldata_db.cc' \ core/wasm_shims/tooldata/tooldata_runtime_stubs.cc >/dev/null grep -F 'constexpr int MAX_DB_PROGRAM_ARGS = 10;' \ core/wasm_shims/tooldata/tooldata_runtime_stubs.cc >/dev/null grep -F 'strtok_r(progname_plus_args, " ", &saveptr)' \ core/wasm_shims/tooldata/tooldata_runtime_stubs.cc >/dev/null grep -F 'argc exceeds MAX_DB_PROGRAM_ARGS=%d' \ core/wasm_shims/tooldata/tooldata_runtime_stubs.cc >/dev/null grep -F 'access(program, X_OK) != 0' \ core/wasm_shims/tooldata/tooldata_runtime_stubs.cc >/dev/null grep -F 'returns 0 when' core/wasm_shims/tooldata/tooldata_runtime_stubs.cc >/dev/null grep -F 'LinuxCNC source basis: src/emc/tooldata/tooldata_common.cc provides' \ core/wasm_shims/tooldata/tooldata_common_probe_main.cc >/dev/null grep -F 'LinuxCNC source basis: src/emc/tooldata/tooldata_mmap.cc, tooldata_nml.cc,' \ core/wasm_shims/tooldata/tooldata_probe_main.cc >/dev/null grep -F 'tooldata_db_notify() returns 0 without side effects while db_live is' \ core/wasm_shims/tooldata/tooldata_probe_main.cc >/dev/null grep -F 'tooldata_db_init(missing_db_program_with_arg.data(), 1) != -1' \ core/wasm_shims/tooldata/tooldata_probe_main.cc >/dev/null grep -F 'missing_db_program_with_arg[separator] != 0' \ core/wasm_shims/tooldata/tooldata_probe_main.cc >/dev/null grep -F 'tooldata_db_init(too_many_db_args, 1) != -1' \ core/wasm_shims/tooldata/tooldata_probe_main.cc >/dev/null grep -F 'tooldata_db_init(non_db_program, 1) != -1' \ core/wasm_shims/tooldata/tooldata_probe_main.cc >/dev/null grep -F 'tool_mmap_creator(nullptr, 1);' \ core/wasm_shims/tooldata/tooldata_probe_main.cc >/dev/null grep -F 'WEXITSTATUS(recreate_status) == 0' \ core/wasm_shims/tooldata/tooldata_probe_main.cc >/dev/null grep -F 'core/wasm_shims/tooldata/tooldata_mmap_backend.cc' docs/linuxcnc-wasm-shims-source-map.md >/dev/null grep -F 'core/wasm_shims/tooldata/tooldata_runtime_stubs.cc' docs/linuxcnc-wasm-shims-source-map.md >/dev/null grep -F 'LinuxCNC source basis: this link probe runs the wasm-safe tooldata shim' \ test-linuxcnc-wasm-tooldata-link.sh >/dev/null grep -F 'LinuxCNC source basis: this link probe runs tooldata_common through the' \ test-linuxcnc-wasm-tooldata-common-link.sh >/dev/null grep -F 'LinuxCNC source basis: this symbol probe compares the wasm mmap backend shim' \ test-linuxcnc-wasm-tooldata-mmap-symbols.sh >/dev/null grep -F 'LinuxCNC source basis: this probe compares wasm-safe tooldata runtime stubs' \ test-linuxcnc-wasm-tooldata-runtime-symbols.sh >/dev/null grep -F '!!!!db_init: argc exceeds MAX_DB_PROGRAM_ARGS=10' \ test-linuxcnc-wasm-tooldata-link.sh >/dev/null grep -F 'not executable' test-linuxcnc-wasm-tooldata-link.sh >/dev/null grep -F 'Error: tool_mmap_creator already called BYE' \ test-linuxcnc-wasm-tooldata-link.sh >/dev/null grep -F 'run_parallel_wasm_probe "LinuxCNC wasm tooldata shim link probe" ./test-linuxcnc-wasm-tooldata-link.sh' \ build-wasm.sh >/dev/null grep -F 'run_parallel_wasm_probe "LinuxCNC wasm tooldata_common link probe" ./test-linuxcnc-wasm-tooldata-common-link.sh' \ build-wasm.sh >/dev/null grep -F 'run_parallel_wasm_probe "LinuxCNC wasm tooldata_mmap symbol probe" ./test-linuxcnc-wasm-tooldata-mmap-symbols.sh' \ build-wasm.sh >/dev/null grep -F 'run_parallel_wasm_probe "LinuxCNC wasm tooldata runtime symbol probe" ./test-linuxcnc-wasm-tooldata-runtime-symbols.sh' \ build-wasm.sh >/dev/null python3 - <<'PY' from pathlib import Path import subprocess doc = Path("docs/linuxcnc-tooldata-source-map.md").read_text(encoding="utf-8") build_wasm_lines = Path("build-wasm.sh").read_text(encoding="utf-8").splitlines() native_tooldata = subprocess.check_output( ["./list-linuxcnc-source-manifest-sources.sh", "linuxcnc-rs274-source-files.txt", "tooldata"], text=True, ).splitlines() expected_native = [ "src/emc/tooldata/tooldata_common.cc", "src/emc/tooldata/tooldata_mmap.cc", "src/emc/tooldata/tooldata_db.cc", "src/emc/tooldata/tooldata_nml.cc", "src/emc/tooldata/tool_mmap_read.cc", "src/emc/tooldata/tool_watch.cc", ] for source in expected_native: if source not in native_tooldata: raise SystemExit(f"native RS274 manifest missing LinuxCNC tooldata source: {source}") kin_tooldata = subprocess.check_output( ["./list-linuxcnc-kinematics-manifest-sources.sh", "linuxcnc-kinematics-source-files.txt", "tooldata"], text=True, ).splitlines() if len([source for source in kin_tooldata if source.endswith(".tbl")]) != 10: raise SystemExit("kinematics manifest must track exactly 10 LinuxCNC TOOL_TABLE sources") positions = {} for index, line in enumerate(build_wasm_lines, start=1): if 'run_parallel_wasm_probe "LinuxCNC wasm tooldata shim link probe"' in line: positions["tooldata_link"] = index if 'run_parallel_wasm_probe "LinuxCNC wasm tooldata_common link probe"' in line: positions["tooldata_common"] = index if 'run_parallel_wasm_probe "LinuxCNC wasm tooldata_mmap symbol probe"' in line: positions["tooldata_mmap"] = index if 'run_parallel_wasm_probe "LinuxCNC wasm tooldata runtime symbol probe"' in line: positions["tooldata_runtime"] = index if "cp build/wasm/cnc_sim.js web/public/cnc_sim.js" in line: positions["copy_js"] = index if "cp build/wasm/cnc_sim.wasm web/public/cnc_sim.wasm" in line: positions["copy_wasm"] = index missing = [ name for name in ( "tooldata_link", "tooldata_common", "tooldata_mmap", "tooldata_runtime", "copy_js", "copy_wasm", ) if name not in positions ] if missing: raise SystemExit(f"build-wasm.sh missing tooldata/copy ordering anchor: {', '.join(missing)}") for probe in ("tooldata_link", "tooldata_common", "tooldata_mmap", "tooldata_runtime"): if not (positions[probe] < positions["copy_js"] and positions[probe] < positions["copy_wasm"]): raise SystemExit("build-wasm.sh must run all tooldata link/symbol probes before copying browser artifacts") for path in [ "core/wasm_shims/tooldata/tooldata_mmap_backend.cc", "core/wasm_shims/tooldata/tooldata_runtime_stubs.cc", "test-linuxcnc-wasm-tooldata-link.sh", "test-linuxcnc-wasm-tooldata-common-link.sh", "test-linuxcnc-wasm-tooldata-mmap-symbols.sh", "test-linuxcnc-wasm-tooldata-runtime-symbols.sh", ]: if path not in doc: raise SystemExit(f"docs/linuxcnc-tooldata-source-map.md missing coverage path: {path}") for phrase in [ "must not add CNC behavior", "must not expand the temporary smoke parser", "source coverage only", "must not become browser API fields", "WASM-safe tooldata shims must preserve the LinuxCNC export surface", "must keep external DB services unavailable", "must not fork, exec, poll", "MAX_DB_PROGRAM_ARGS=10", "`strtok_r()` argument splitting", "access(..., X_OK)", "mmap tool table reader utility", "NML/mmap tool table watcher utility", "mmap creator/user/close lifecycle", "random-toolchanger flag", "last-index unavailable return", "in-memory replacement for native mmap", "inactive-DB return values", "./check-linuxcnc-tooldata-source-map.sh", "does not interpret G-code", ]: if phrase not in doc: raise SystemExit(f"docs/linuxcnc-tooldata-source-map.md 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-tooldata-source-map.sh" not in text: raise SystemExit(f"{entry} must run ./check-linuxcnc-tooldata-source-map.sh") PY