结论:依据 LinuxCNC RS274 Python/Boost.Python 绑定源 canonmodule.cc、gcodemodule.cc、interpmodule.cc 与 py*.cc,blocker 分析新增 python-compile-covered 分组,锁定这些 blocker 已由 inert Python C API symbol probe 编译覆盖;Python/remap 执行仍保持禁用,未扩展 smoke。 检查:./test-linuxcnc-wasm-python-c-api-symbols.sh;./test-linuxcnc-wasm-blockers.sh linuxcnc-rs274-wasm-source-files.txt;CNC_SIM_WASM_BLOCKERS_SELF_CHECKS=1 ./test-linuxcnc-wasm-blockers.sh linuxcnc-rs274-wasm-source-files.txt;./check-linuxcnc-wasm-blockers-source-map.sh;./test-native.sh;./test-linuxcnc-source-link.sh。
195 lines
9.2 KiB
Bash
Executable File
195 lines
9.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
cd "$(dirname "$0")"
|
|
|
|
# LinuxCNC source basis: this guard ties the documented WASM blocker map to
|
|
# LinuxCNC RS274/Python/remap/tooldata sources, the rs274 WASM manifest, and
|
|
# wasm-safe shim replacement sources. It validates source coverage only; it
|
|
# does not add CNC behavior, expand smoke parsing, or interpret G-code.
|
|
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: this blocker scan is constrained to the RS274/WASM' \
|
|
analyze-linuxcnc-wasm-blockers.sh >/dev/null
|
|
grep -F 'blocked replacement is not in wasm shim build set' \
|
|
analyze-linuxcnc-wasm-blockers.sh >/dev/null
|
|
grep -F 'shim_source_output=$(./list-linuxcnc-wasm-safe-shims.sh)' \
|
|
analyze-linuxcnc-wasm-blockers.sh >/dev/null
|
|
grep -F 'LinuxCNC source basis: this guardrail locks the blocker categories reported' \
|
|
test-linuxcnc-wasm-blockers.sh >/dev/null
|
|
grep -F 'Cache wasm blocker analyzer output' docs/linuxcnc-source-policy.md >/dev/null
|
|
|
|
grep -F '#include <Python.h>' "$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null
|
|
grep -F 'PyInit_gcode' "$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null
|
|
grep -F '#include <boost/python/module.hpp>' "$linuxcnc_root/src/emc/rs274ngc/interpmodule.cc" >/dev/null
|
|
grep -F '#include "pythonplugin/python_plugin.hh"' "$linuxcnc_root/src/emc/rs274ngc/interp_python.cc" >/dev/null
|
|
grep -F 'dlopen' "$linuxcnc_root/src/emc/rs274ngc/interp_base.cc" >/dev/null
|
|
grep -F 'opendir(direct)' "$linuxcnc_root/src/emc/rs274ngc/interp_o_word.cc" >/dev/null
|
|
grep -F 'readdir(aDir)' "$linuxcnc_root/src/emc/rs274ngc/interp_o_word.cc" >/dev/null
|
|
grep -F 'std::string tempfile = std::string(filename) + ".new";' \
|
|
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
|
|
grep -F '+ RS274NGC_PARAMETER_FILE_BACKUP_SUFFIX;' \
|
|
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
|
|
grep -F 'unlink(bakfile.c_str());' \
|
|
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
|
|
grep -F 'if(link(filename, bakfile.c_str()) < 0)' \
|
|
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
|
|
grep -F 'if(rename(tempfile.c_str(), filename) < 0)' \
|
|
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.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_mmap_creator(' "$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
|
|
|
|
python3 - "$linuxcnc_root" "$manifest" <<'PY'
|
|
from pathlib import Path
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
|
|
linuxcnc_root = Path(sys.argv[1])
|
|
manifest = Path(sys.argv[2])
|
|
source_map = Path("docs/linuxcnc-wasm-blockers-source-map.md").read_text(encoding="utf-8")
|
|
manifest_lines = [
|
|
line for line in manifest.read_text(encoding="utf-8").splitlines()
|
|
if line and not line.startswith("#")
|
|
]
|
|
manifest_counts = {"core": 0, "blocked": 0, "header": 0, "blocked-header": 0, "metadata": 0}
|
|
for line in manifest_lines:
|
|
group = line.split(":", 1)[0]
|
|
if group not in manifest_counts:
|
|
raise SystemExit(f"unexpected WASM manifest group in blocker source map guard: {group}")
|
|
manifest_counts[group] += 1
|
|
for group, count in manifest_counts.items():
|
|
if f"| `{group}` | {count} |" not in source_map:
|
|
raise SystemExit(f"docs/linuxcnc-wasm-blockers-source-map.md count mismatch for {group}")
|
|
|
|
output = subprocess.check_output(
|
|
["./analyze-linuxcnc-wasm-blockers.sh", str(manifest)],
|
|
text=True,
|
|
env={"LINUXCNC_ROOT": str(linuxcnc_root)},
|
|
)
|
|
sections = {}
|
|
current = None
|
|
for line in output.splitlines():
|
|
if line.startswith("[") and line.endswith("]"):
|
|
current = line[1:-1]
|
|
sections[current] = []
|
|
elif current and line:
|
|
sections[current].append(line)
|
|
|
|
expected_counts = {
|
|
"manifest-core": "core=26",
|
|
"manifest-blocked": "blocked=9",
|
|
"python": 8,
|
|
"python-compile-covered": 8,
|
|
"tooldata": 1,
|
|
"native-backend": 1,
|
|
"native-fs": 1,
|
|
"blocked-replacements": 9,
|
|
"core-python-shimmed": 6,
|
|
"core-dlopen-shimmed": 1,
|
|
"core-tooldata-shimmed": 1,
|
|
"core-tooldata-users-shimmed": 2,
|
|
"core-native-fs-shimmed": 16,
|
|
}
|
|
manifest_section = sections.get("manifest", [])
|
|
if expected_counts["manifest-core"] not in manifest_section:
|
|
raise SystemExit("WASM blocker analyzer core count changed")
|
|
if expected_counts["manifest-blocked"] not in manifest_section:
|
|
raise SystemExit("WASM blocker analyzer blocked count changed")
|
|
for section, count in expected_counts.items():
|
|
if section.startswith("manifest-"):
|
|
continue
|
|
entries = [entry for entry in sections.get(section, []) if entry != "(none)"]
|
|
if len(entries) != count:
|
|
raise SystemExit(f"WASM blocker analyzer section {section} count changed: {len(entries)}")
|
|
if f"| `{section}` | {count} |" not in source_map:
|
|
raise SystemExit(f"docs/linuxcnc-wasm-blockers-source-map.md analyzer count mismatch for {section}")
|
|
if sections.get("dlopen") != ["(none)"]:
|
|
raise SystemExit("blocked dlopen section is no longer empty")
|
|
if sections.get("tooldata-users") != ["(none)"]:
|
|
raise SystemExit("blocked tooldata-users section is no longer empty")
|
|
if sections.get("python") != sections.get("python-compile-covered"):
|
|
raise SystemExit("blocked Python sources are no longer fully compile-covered by the inert Python C API probe")
|
|
|
|
shim_paths = set(Path(line).as_posix() for line in subprocess.check_output(
|
|
["./list-linuxcnc-wasm-safe-shims.sh"],
|
|
text=True,
|
|
).splitlines() if line)
|
|
required_tracked_entries = [
|
|
"src/emc/rs274ngc/interp_base.cc -> core/wasm_shims/dlfcn.cc",
|
|
"src/emc/rs274ngc/gcodemodule.cc",
|
|
"src/emc/rs274ngc/interpmodule.cc",
|
|
"src/emc/tooldata/tooldata_mmap.cc",
|
|
"src/emc/rs274ngc/interp_base.cc",
|
|
"src/emc/rs274ngc/interp_o_word.cc",
|
|
"src/emc/rs274ngc/rs274ngc_pre.cc",
|
|
]
|
|
required_replacements = [
|
|
"src/emc/rs274ngc/canonmodule.cc -> core/wasm_shims/linuxcnc_runtime_shim.cc",
|
|
"src/emc/rs274ngc/gcodemodule.cc -> core/wasm_shims/linuxcnc_runtime_shim.cc",
|
|
"src/emc/rs274ngc/interpmodule.cc -> core/wasm_shims/linuxcnc_runtime_shim.cc",
|
|
"src/emc/rs274ngc/pyarrays.cc -> core/wasm_shims/python_c_api_shim.cc",
|
|
"src/emc/rs274ngc/pyblock.cc -> core/wasm_shims/python_c_api_shim.cc",
|
|
"src/emc/rs274ngc/pyemctypes.cc -> core/wasm_shims/python_c_api_shim.cc",
|
|
"src/emc/rs274ngc/pyinterp1.cc -> core/wasm_shims/python_c_api_shim.cc",
|
|
"src/emc/rs274ngc/pyparamclass.cc -> core/wasm_shims/python_c_api_shim.cc",
|
|
"src/emc/tooldata/tooldata_mmap.cc -> core/wasm_shims/tooldata/tooldata_mmap_backend.cc",
|
|
]
|
|
for entry in required_tracked_entries:
|
|
if " -> " in entry:
|
|
source, shim = entry.split(" -> ", 1)
|
|
if source not in sections.get("core-dlopen-shimmed", []):
|
|
raise SystemExit(f"WASM blocker analyzer output missing core dlopen shimmed source: {source}")
|
|
if shim not in shim_paths:
|
|
raise SystemExit(f"WASM blocker analyzer core dlopen shim is absent from wasm-safe shim set: {shim}")
|
|
elif entry not in output:
|
|
raise SystemExit(f"WASM blocker analyzer output missing required entry: {entry}")
|
|
if entry not in source_map:
|
|
raise SystemExit(f"docs/linuxcnc-wasm-blockers-source-map.md missing required entry: {entry}")
|
|
replacement_entries = [entry for entry in sections.get("blocked-replacements", []) if entry != "(none)"]
|
|
if replacement_entries != required_replacements:
|
|
raise SystemExit(f"WASM blocker replacement set changed: {replacement_entries}")
|
|
if "Required replacement set:" not in source_map:
|
|
raise SystemExit("docs/linuxcnc-wasm-blockers-source-map.md must document the complete replacement set")
|
|
for entry in required_replacements:
|
|
if entry not in source_map:
|
|
raise SystemExit(f"docs/linuxcnc-wasm-blockers-source-map.md missing required replacement: {entry}")
|
|
|
|
for replacement in re.findall(r"-> (core/wasm_shims/[^\s]+)", output):
|
|
if replacement not in shim_paths:
|
|
raise SystemExit(f"blocked replacement is absent from wasm-safe shim set: {replacement}")
|
|
for phrase in [
|
|
"does not add CNC behavior",
|
|
"must not become smoke parser behavior",
|
|
"blocked `dlopen` and blocked tooldata-users remain empty",
|
|
'parameter-file replacement through `filename + ".new"`',
|
|
"O-word lookup and parameter-file replacement",
|
|
"Python/Boost.Python remap paths remain tracked blockers",
|
|
"Python/Boost.Python blockers are compile-covered by the inert Python C API probe",
|
|
"./check-linuxcnc-wasm-blockers-source-map.sh",
|
|
"does not interpret G-code",
|
|
]:
|
|
if phrase not in source_map:
|
|
raise SystemExit(f"docs/linuxcnc-wasm-blockers-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-wasm-blockers-source-map.sh" not in text:
|
|
raise SystemExit(f"{entry} must run ./check-linuxcnc-wasm-blockers-source-map.sh")
|
|
for entry in [
|
|
"test-native.sh",
|
|
"test-linuxcnc-source-link.sh",
|
|
]:
|
|
text = Path(entry).read_text(encoding="utf-8")
|
|
if "CNC_SIM_WASM_BLOCKERS_SELF_CHECKS=1" not in text:
|
|
raise SystemExit(f"{entry} must run wasm blocker analyzer self-checks")
|
|
PY
|