diff --git a/check-linuxcnc-wasm-shims-source-map.sh b/check-linuxcnc-wasm-shims-source-map.sh new file mode 100755 index 0000000..67ad9a1 --- /dev/null +++ b/check-linuxcnc-wasm-shims-source-map.sh @@ -0,0 +1,136 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd "$(dirname "$0")" + +# LinuxCNC source basis: this guard ties the documented wasm-safe shim map to +# the linuxcnc_wasm_safe_probe_shims CMake set, the RS274/WASM source manifest, +# and the LinuxCNC sources each shim cites. It validates source coverage only; +# it does not add CNC behavior or expand smoke parsing. +linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} +manifest=${1:-linuxcnc-rs274-wasm-source-files.txt} + +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh "$manifest" +linuxcnc_root=$(cd "$linuxcnc_root" && pwd) + +grep -F 'LinuxCNC source basis: wasm-safe shim probe sources are limited to the' \ + list-linuxcnc-wasm-safe-shims.sh >/dev/null +grep -F 'set(linuxcnc_wasm_safe_probe_shims' core/CMakeLists.txt >/dev/null +grep -F 'LinuxCNC source basis: this guard ties the documented WASM blocker map' \ + check-linuxcnc-wasm-blockers-source-map.sh >/dev/null + +grep -F 'dlopen' "$linuxcnc_root/src/emc/rs274ngc/interp_base.cc" >/dev/null +grep -F '#define _(s) gettext(s)' "$linuxcnc_root/src/emc/rs274ngc/interp_internal.hh" >/dev/null +grep -F 'int _task = 0;' "$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null +grep -F 'int _task = 1;' "$linuxcnc_root/src/emc/task/emctaskmain.cc" >/dev/null +grep -F 'struct _inittab builtin_modules[]' "$linuxcnc_root/src/emc/task/taskclass.cc" >/dev/null +grep -F 'int Interp::fetch_hal_param(' "$linuxcnc_root/src/emc/rs274ngc/interp_namedparams.cc" >/dev/null +grep -F 'wordexp(basename, &exp_result, 0);' "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null +grep -F 'EMC_STAT *emcStatus = new EMC_STAT;' "$linuxcnc_root/src/emc/sai/dummyemcstat.cc" >/dev/null +grep -F 'NMLmsg::NMLmsg' "$linuxcnc_root/src/libnml/nml/nmlmsg.cc" >/dev/null +grep -F 'RCS_STAT_MSG::RCS_STAT_MSG' "$linuxcnc_root/src/libnml/nml/stat_msg.cc" >/dev/null +grep -F 'print_rcs_error_new' "$linuxcnc_root/src/libnml/rcs/rcs_print.cc" >/dev/null +grep -F 'rtapi_snprintf' "$linuxcnc_root/src/rtapi/uspace_common.h" >/dev/null +grep -F 'class PythonPlugin' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.hh" >/dev/null +grep -F 'PythonPlugin::instantiate' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null +grep -F '#define TOOL_MMAP_FILENAME ".tool.mmap"' "$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 + +python3 - "$manifest" <<'PY' +from pathlib import Path +import re +import subprocess +import sys + +manifest = Path(sys.argv[1]) +source_map = Path("docs/linuxcnc-wasm-shims-source-map.md").read_text(encoding="utf-8") +manifest_text = manifest.read_text(encoding="utf-8") +shim_paths = [ + line for line in subprocess.check_output(["./list-linuxcnc-wasm-safe-shims.sh"], text=True).splitlines() + if line +] +if len(shim_paths) != 11: + raise SystemExit(f"expected 11 wasm-safe CMake shim sources, found {len(shim_paths)}") +if f"| CMake `linuxcnc_wasm_safe_probe_shims` sources | {len(shim_paths)} |" not in source_map: + raise SystemExit("docs/linuxcnc-wasm-shims-source-map.md shim count mismatch") + +expected_shims = { + "core/wasm_shims/dlfcn.cc": ["src/emc/rs274ngc/interp_base.cc"], + "core/wasm_shims/emc_status_shim.cc": ["src/emc/sai/dummyemcstat.cc"], + "core/wasm_shims/gettext_shim.cc": ["src/emc/rs274ngc/interp_internal.hh"], + "core/wasm_shims/linuxcnc_runtime_shim.cc": [ + "src/emc/rs274ngc/gcodemodule.cc", + "src/emc/task/emctaskmain.cc", + "src/emc/task/taskclass.cc", + "src/emc/rs274ngc/interp_namedparams.cc", + "src/hal/hal_lib.c", + "src/emc/rs274ngc/rs274ngc_pre.cc", + ], + "core/wasm_shims/nml_status_shim.cc": [ + "src/libnml/nml/nmlmsg.cc", + "src/libnml/nml/stat_msg.cc", + ], + "core/wasm_shims/python_c_api_shim.cc": [ + "src/emc/rs274ngc/interp_python.cc", + "src/emc/rs274ngc/interp_namedparams.cc", + "src/emc/rs274ngc/gcodemodule.cc", + "src/emc/pythonplugin/python_plugin.cc", + ], + "core/wasm_shims/rcs_print_shim.cc": ["src/libnml/rcs/rcs_print.cc"], + "core/wasm_shims/rtapi_compat.cc": [ + "src/rtapi/uspace_common.h", + "src/rtapi/rtapi.h", + ], + "core/wasm_shims/tooldata/tooldata_mmap_backend.cc": [ + "src/emc/tooldata/tooldata_mmap.cc", + ], + "core/wasm_shims/tooldata/tooldata_runtime_stubs.cc": [ + "src/emc/tooldata/tooldata_nml.cc", + "src/emc/tooldata/tooldata_db.cc", + ], + "core/wasm_shims/pythonplugin/python_plugin.cc": [ + "src/emc/pythonplugin/python_plugin.cc", + "src/emc/pythonplugin/python_plugin.hh", + ], +} +if shim_paths != list(expected_shims): + raise SystemExit("wasm-safe shim CMake order no longer matches docs/linuxcnc-wasm-shims-source-map.md") + +for shim_path, linuxcnc_sources in expected_shims.items(): + shim_text = Path(shim_path).read_text(encoding="utf-8") + if "LinuxCNC source basis:" not in shim_text: + raise SystemExit(f"{shim_path} is missing a LinuxCNC source basis comment") + if f"| `{shim_path}` |" not in source_map: + raise SystemExit(f"docs/linuxcnc-wasm-shims-source-map.md missing shim row: {shim_path}") + for source in linuxcnc_sources: + if source not in source_map: + raise SystemExit(f"docs/linuxcnc-wasm-shims-source-map.md missing source {source} for {shim_path}") + if source not in manifest_text: + raise SystemExit(f"{source} is not tracked in {manifest}") + +for phrase in [ + "must not add CNC behavior", + "must not expand the temporary smoke parser", + "CMake `linuxcnc_wasm_safe_probe_shims` set", + "RS274/WASM manifest", + "browser-safe LinuxCNC adaptations", +]: + if phrase not in source_map: + raise SystemExit(f"docs/linuxcnc-wasm-shims-source-map.md missing boundary phrase: {phrase}") + +cmake_text = Path("core/CMakeLists.txt").read_text(encoding="utf-8") +for shim_path in shim_paths: + cmake_suffix = shim_path.removeprefix("core/") + if cmake_suffix not in cmake_text: + raise SystemExit(f"{shim_path} is not listed in linuxcnc_wasm_safe_probe_shims") + +blocker_map = Path("docs/linuxcnc-wasm-blockers-source-map.md").read_text(encoding="utf-8") +for shim_path in [ + "core/wasm_shims/linuxcnc_runtime_shim.cc", + "core/wasm_shims/python_c_api_shim.cc", + "core/wasm_shims/tooldata/tooldata_mmap_backend.cc", +]: + if shim_path not in blocker_map: + raise SystemExit(f"docs/linuxcnc-wasm-blockers-source-map.md no longer references {shim_path}") +PY diff --git a/docs/linuxcnc-wasm-shims-source-map.md b/docs/linuxcnc-wasm-shims-source-map.md new file mode 100644 index 0000000..9378008 --- /dev/null +++ b/docs/linuxcnc-wasm-shims-source-map.md @@ -0,0 +1,49 @@ +# LinuxCNC WASM shims source map + +This map records the source basis for the C++ sources in the +CMake `linuxcnc_wasm_safe_probe_shims` set. It is a build/source-link guard +only; it must not add CNC behavior and must not expand the temporary smoke parser. + +## Generated Coverage + +| Output | Count | Source coverage role | +| --- | ---: | --- | +| CMake `linuxcnc_wasm_safe_probe_shims` sources | 11 | Browser-safe LinuxCNC adaptation shims linked into WASM source probes. | + +## Shim Map + +| Shim | LinuxCNC source basis | Boundary | +| --- | --- | --- | +| `core/wasm_shims/dlfcn.cc` | `src/emc/rs274ngc/interp_base.cc` | Satisfies native `dlopen` symbols for browser-safe probes. | +| `core/wasm_shims/emc_status_shim.cc` | `src/emc/sai/dummyemcstat.cc` | Provides the source-backed `emcStatus` singleton shape without task/NML startup. | +| `core/wasm_shims/gettext_shim.cc` | `src/emc/rs274ngc/interp_internal.hh` | Preserves LinuxCNC diagnostic call sites without browser gettext catalogs. | +| `core/wasm_shims/linuxcnc_runtime_shim.cc` | `src/emc/rs274ngc/gcodemodule.cc`, `src/emc/task/emctaskmain.cc`, `src/emc/task/taskclass.cc`, `src/emc/rs274ngc/interp_namedparams.cc`, `src/hal/hal_lib.c`, `src/emc/rs274ngc/rs274ngc_pre.cc` | Keeps preview/task, builtin Python module, HAL lookup, and `wordexp()` edges compile-safe. | +| `core/wasm_shims/nml_status_shim.cc` | `src/libnml/nml/nmlmsg.cc`, `src/libnml/nml/stat_msg.cc` | Provides NML status constructors needed by source-linked EMC status objects. | +| `core/wasm_shims/python_c_api_shim.cc` | `src/emc/rs274ngc/interp_python.cc`, `src/emc/rs274ngc/interp_namedparams.cc`, `src/emc/rs274ngc/gcodemodule.cc`, `src/emc/pythonplugin/python_plugin.cc` | Satisfies Python C API symbols while Python/Boost.Python remap paths remain tracked blockers. | +| `core/wasm_shims/rcs_print_shim.cc` | `src/libnml/rcs/rcs_print.cc` | Provides LinuxCNC RCS print diagnostics used by NML validation. | +| `core/wasm_shims/rtapi_compat.cc` | `src/rtapi/uspace_common.h`, `src/rtapi/rtapi.h` | Provides userspace RTAPI formatting declarations used by RS274/NML code. | +| `core/wasm_shims/tooldata/tooldata_mmap_backend.cc` | `src/emc/tooldata/tooldata_mmap.cc` | Replaces native mmap storage with an in-memory WASM-safe backend for probes. | +| `core/wasm_shims/tooldata/tooldata_runtime_stubs.cc` | `src/emc/tooldata/tooldata_nml.cc`, `src/emc/tooldata/tooldata_db.cc` | Satisfies NML and external tool database entry points without native services. | +| `core/wasm_shims/pythonplugin/python_plugin.cc` | `src/emc/pythonplugin/python_plugin.cc`, `src/emc/pythonplugin/python_plugin.hh` | Keeps LinuxCNC PythonPlugin API available while Python execution is disabled for browser-safe probes. | + +## Boundaries + +- The CMake `linuxcnc_wasm_safe_probe_shims` set and `list-linuxcnc-wasm-safe-shims.sh` must stay synchronized. +- Each shim source must keep a nearby `LinuxCNC source basis:` comment naming the LinuxCNC source it adapts. +- Every LinuxCNC source named above must remain tracked in the RS274/WASM manifest as `core`, `header`, or `metadata`. +- These shims are browser-safe LinuxCNC adaptations for source-link probes; they are not new interpreter behavior. + +## Checks + +Run: + +```bash +./check-linuxcnc-wasm-shims-source-map.sh +./test-linuxcnc-wasm-cmake-safe-probe.sh +./test-native.sh +./test-linuxcnc-source-link.sh +``` + +`check-linuxcnc-wasm-shims-source-map.sh` keeps this document, the CMake shim +set, `list-linuxcnc-wasm-safe-shims.sh`, LinuxCNC source anchors, and the +RS274/WASM manifest synchronized. diff --git a/test-all-native.sh b/test-all-native.sh index c3b4786..af49a0c 100755 --- a/test-all-native.sh +++ b/test-all-native.sh @@ -151,6 +151,7 @@ grep -F 'LinuxCNC source basis: this guard ties generated switchkins/remap alias grep -F './check-linuxcnc-switchkins-remap-source-map.sh' test-native.sh >/dev/null grep -F './check-linuxcnc-switchkins-remap-source-map.sh' test-linuxcnc-source-link.sh >/dev/null grep -F 'LinuxCNC source basis: this guard ties the documented WASM blocker map' check-linuxcnc-wasm-blockers-source-map.sh >/dev/null +grep -F 'LinuxCNC source basis: this guard ties the documented wasm-safe shim map' check-linuxcnc-wasm-shims-source-map.sh >/dev/null grep -F 'LinuxCNC source basis: the cached input checker keys check-linuxcnc-inputs.sh' check-linuxcnc-inputs-cached.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 'LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh "$manifest" --require-kinematics-complete' check-linuxcnc-switchkins-remap-table.sh >/dev/null @@ -226,6 +227,9 @@ grep -F './check-linuxcnc-switchkins-remap-source-map.sh' test-linuxcnc-source-l grep -F 'LinuxCNC WASM blockers source map' docs/linuxcnc-wasm-blockers-source-map.md >/dev/null grep -F '| `blocked-replacements` | 9 | Blocked LinuxCNC sources mapped to wasm-safe shim files. |' docs/linuxcnc-wasm-blockers-source-map.md >/dev/null grep -F 'Python/Boost.Python remap paths remain tracked blockers' docs/linuxcnc-wasm-blockers-source-map.md >/dev/null +grep -F 'LinuxCNC WASM shims source map' docs/linuxcnc-wasm-shims-source-map.md >/dev/null +grep -F '| CMake `linuxcnc_wasm_safe_probe_shims` sources | 11 | Browser-safe LinuxCNC adaptation shims linked into WASM source probes. |' docs/linuxcnc-wasm-shims-source-map.md >/dev/null +grep -F 'must not expand the temporary smoke parser' docs/linuxcnc-wasm-shims-source-map.md >/dev/null grep -F 'check-linuxcnc-switchkins-remap-table-cached.sh --cache-dir "$preflight_cache_dir" linuxcnc-kinematics-source-files.txt' test-native.sh >/dev/null grep -F 'check-linuxcnc-switchkins-remap-table-cached.sh --cache-dir "$preflight_cache_dir" linuxcnc-kinematics-source-files.txt' test-linuxcnc-source-link.sh >/dev/null grep -F 'check-linuxcnc-switchkins-remap-table-cached.sh --cache-dir "$preflight_cache_dir" linuxcnc-kinematics-source-files.txt' build-wasm.sh >/dev/null @@ -2002,6 +2006,7 @@ if ! grep -F "missing manifest source: src/emc/rs274ngc/does_not_exist.cc" "$mis fi ./check-linuxcnc-wasm-blockers-source-map.sh linuxcnc-rs274-wasm-source-files.txt +./check-linuxcnc-wasm-shims-source-map.sh linuxcnc-rs274-wasm-source-files.txt if [[ "$guardrails_only" == "1" ]]; then echo "all native guardrail checks passed"