提示词:快速推进,避免纯 source-map-only,不自动推送。 结论:基于 LinuxCNC rs274ngc_pre.cc save_parameters() 的 filename + .new rename 语义,补强浏览器 OPFS moveFile 在新 WASM 实例中持久删除源文件并恢复目标 mirror 的覆盖;./test-native.sh 和 ./test-linuxcnc-source-link.sh 通过。
183 lines
11 KiB
Bash
Executable File
183 lines
11 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
cd "$(dirname "$0")"
|
|
|
|
# LinuxCNC source basis: this guard ties browser OPFS parameter persistence to
|
|
# interp_internal.hh defaults and rs274ngc_pre.cc restore_parameters() /
|
|
# save_parameters() file semantics. It validates bridge 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 '#define RS274NGC_PARAMETER_FILE_NAME_DEFAULT "rs274ngc.var"' \
|
|
"$linuxcnc_root/src/emc/rs274ngc/interp_internal.hh" >/dev/null
|
|
grep -F '#define RS274NGC_PARAMETER_FILE_BACKUP_SUFFIX ".bak"' \
|
|
"$linuxcnc_root/src/emc/rs274ngc/interp_internal.hh" >/dev/null
|
|
grep -F 'int Interp::restore_parameters(const char *filename)' \
|
|
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
|
|
grep -F 'int Interp::open(const char *filename)' \
|
|
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
|
|
grep -F '_setup.file_pointer = fopen(filename, "r");' \
|
|
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
|
|
grep -F 'read_text(command, _setup.file_pointer, _setup.linetext,' \
|
|
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
|
|
grep -F 'int Interp::save_parameters(const char *filename,' \
|
|
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.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 'RS274NGC_PARAMETER_FILE_NAME_DEFAULT` as `rs274ngc.var`' \
|
|
docs/linuxcnc-opfs-source-map.md >/dev/null
|
|
grep -F 'RS274NGC_PARAMETER_FILE_BACKUP_SUFFIX` as `.bak`' \
|
|
docs/linuxcnc-opfs-source-map.md >/dev/null
|
|
grep -F '`Interp::restore_parameters()` for parameter-file reads' \
|
|
docs/linuxcnc-opfs-source-map.md >/dev/null
|
|
grep -F '`Interp::open()` by opening' \
|
|
docs/linuxcnc-opfs-source-map.md >/dev/null
|
|
grep -F '`fopen(filename, "r")`, then `Interp::_read()`' \
|
|
docs/linuxcnc-opfs-source-map.md >/dev/null
|
|
grep -F '`read_text()`' docs/linuxcnc-opfs-source-map.md >/dev/null
|
|
grep -F '`Interp::save_parameters()` for `filename + ".new"`' \
|
|
docs/linuxcnc-opfs-source-map.md >/dev/null
|
|
grep -F 'unlinks `filename + ".bak"`, links the' \
|
|
docs/linuxcnc-opfs-source-map.md >/dev/null
|
|
grep -F 'must not add independent filesystem behavior or expand the temporary smoke parser' \
|
|
docs/linuxcnc-opfs-source-map.md >/dev/null
|
|
grep -F 'parseFileWithParameterFile()' docs/linuxcnc-opfs-source-map.md >/dev/null
|
|
grep -F 'OPFS Coverage' docs/linuxcnc-opfs-source-map.md >/dev/null
|
|
grep -F '| `web/src/wasm-core.js` |' docs/linuxcnc-opfs-source-map.md >/dev/null
|
|
grep -F '| `web/src/wasm-core.d.ts` |' docs/linuxcnc-opfs-source-map.md >/dev/null
|
|
grep -F '| `web/src/app.js` |' docs/linuxcnc-opfs-source-map.md >/dev/null
|
|
grep -F '| `web/test-browser-wasm-smoke-opfs-basic-sections.js` |' docs/linuxcnc-opfs-source-map.md >/dev/null
|
|
grep -F '| `web/test-browser-wasm-smoke-opfs-parameter-sections.js` |' docs/linuxcnc-opfs-source-map.md >/dev/null
|
|
grep -F '| `web/test-browser-wasm-smoke-opfs-workspace-sections.js` |' docs/linuxcnc-opfs-source-map.md >/dev/null
|
|
grep -F '| `web/test-browser-wasm-smoke-opfs-mirror-sections.js` |' docs/linuxcnc-opfs-source-map.md >/dev/null
|
|
grep -F '| `web/test-browser-wasm-smoke-opfs-directory-sections.js` |' docs/linuxcnc-opfs-source-map.md >/dev/null
|
|
grep -F '| `web/test-browser-wasm-smoke-opfs-policy-sections.js` |' docs/linuxcnc-opfs-source-map.md >/dev/null
|
|
grep -F '| `web/test-browser-wasm-smoke-app-sections.js` |' docs/linuxcnc-opfs-source-map.md >/dev/null
|
|
|
|
grep -F 'const LINUXCNC_DEFAULT_PARAMETER_FILE = "rs274ngc.var";' web/src/wasm-core.js >/dev/null
|
|
grep -F 'async parseFile(path, dialect = "linuxcnc", options = {})' web/src/wasm-core.js >/dev/null
|
|
grep -F 'OPFS workspace is not available for program file parsing' web/src/wasm-core.js >/dev/null
|
|
grep -F 'const program = new TextDecoder().decode(await opfs.readFile(path));' web/src/wasm-core.js >/dev/null
|
|
grep -F 'async parseFileWithParameterFile(path, parameterPath, dialect = "linuxcnc", options = {})' \
|
|
web/src/wasm-core.js >/dev/null
|
|
grep -F 'OPFS workspace is not available for program and parameter file parsing' web/src/wasm-core.js >/dev/null
|
|
grep -F '`${LINUXCNC_DEFAULT_PARAMETER_FILE}.new`' web/src/wasm-core.js >/dev/null
|
|
grep -F '`${LINUXCNC_DEFAULT_PARAMETER_FILE}.bak`' web/src/wasm-core.js >/dev/null
|
|
grep -F 'LinuxCNC source basis: rs274ngc_pre.cc restore_parameters() reads the' web/src/wasm-core.js >/dev/null
|
|
grep -F 'before replacing the main file and managing filename + ".bak".' web/src/wasm-core.js >/dev/null
|
|
grep -F 'app frame did not clean up LinuxCNC temporary parameter file in OPFS' \
|
|
web/test-browser-wasm-smoke-app-sections.js >/dev/null
|
|
grep -F 'OPFS cannot be disabled in browser contexts with OPFS support' web/src/wasm-core.js >/dev/null
|
|
grep -F 'OPFS path must stay inside the CNC workspace' web/src/wasm-core.js >/dev/null
|
|
grep -F 'OPFS mount point must stay inside the Emscripten filesystem' web/src/wasm-core.js >/dev/null
|
|
grep -F 'async persistDirectory(path)' web/src/wasm-core.js >/dev/null
|
|
grep -F 'async readDirectory(path)' web/src/wasm-core.js >/dev/null
|
|
grep -F 'export type WasmOpfsWorkspace = {' web/src/wasm-core.d.ts >/dev/null
|
|
grep -F 'readDirectory(path: string): Promise<string[]>;' web/src/wasm-core.d.ts >/dev/null
|
|
grep -F 'persistDirectory(path: string): Promise<string[]>;' web/src/wasm-core.d.ts >/dev/null
|
|
grep -F 'loadParameterFile(path: string): Promise<Uint8Array | null>;' web/src/wasm-core.d.ts >/dev/null
|
|
grep -F 'persistParameterFile(path: string): Promise<Uint8Array>;' web/src/wasm-core.d.ts >/dev/null
|
|
grep -F 'opfs?: false | {' web/src/wasm-core.d.ts >/dev/null
|
|
grep -F 'mountPoint?: string;' web/src/wasm-core.d.ts >/dev/null
|
|
grep -F 'workspacePath?: string;' web/src/wasm-core.d.ts >/dev/null
|
|
grep -F 'const events = await simulator.parseFileWithParameterFile(programPath, parameterPath, "linuxcnc", parseOptions);' \
|
|
web/src/app.js >/dev/null
|
|
grep -F 'OPFS workspace is required for browser program parsing' web/src/app.js >/dev/null
|
|
grep -F 'runBrowserOpfsBasicSections(context, simulator)' \
|
|
web/test-browser-wasm-smoke-opfs-workspace-sections.js >/dev/null
|
|
grep -F 'runBrowserOpfsParameterSections(context, simulator)' \
|
|
web/test-browser-wasm-smoke-opfs-workspace-sections.js >/dev/null
|
|
grep -F 'runBrowserOpfsMirrorSections(context, simulator)' \
|
|
web/test-browser-wasm-smoke-opfs-workspace-sections.js >/dev/null
|
|
grep -F 'runBrowserOpfsDirectorySections(context, simulator)' \
|
|
web/test-browser-wasm-smoke-opfs-workspace-sections.js >/dev/null
|
|
grep -F 'runBrowserOpfsPolicySections(context)' web/test-browser-wasm-smoke-sections.js >/dev/null
|
|
grep -F 'runSection("opfs directory persist and load"' \
|
|
web/test-browser-wasm-smoke-opfs-directory-sections.js >/dev/null
|
|
grep -F 'runSection("opfs metadata queries avoid wasm mirror pollution"' \
|
|
web/test-browser-wasm-smoke-opfs-directory-sections.js >/dev/null
|
|
grep -F 'runSection("opfs copy and move mirror files"' \
|
|
web/test-browser-wasm-smoke-opfs-mirror-sections.js >/dev/null
|
|
grep -F 'runSection("browser opfs policy"' \
|
|
web/test-browser-wasm-smoke-opfs-policy-sections.js >/dev/null
|
|
grep -F 'runSection("opfs parameter restore in new instance"' test-web-wasm-browser-smoke.sh >/dev/null
|
|
grep -F 'runSection("opfs invalid parameter restore failures"' test-web-wasm-browser-smoke.sh >/dev/null
|
|
grep -F 'runSection("opfs stale parameter state cleanup"' test-web-wasm-browser-smoke.sh >/dev/null
|
|
grep -F 'runSection("opfs program parse and restore"' test-web-wasm-browser-smoke.sh >/dev/null
|
|
grep -F 'runSection("browser app opfs restore"' test-web-wasm-browser-smoke.sh >/dev/null
|
|
|
|
if grep -R -n -E 'localStorage|sessionStorage|indexedDB|showOpenFilePicker|showSaveFilePicker|showDirectoryPicker|webkitRequestFileSystem|FileReader' web/src; then
|
|
echo "browser app/wasm code must not add filesystem persistence outside OPFS" >&2
|
|
exit 1
|
|
fi
|
|
if grep -n -E 'localStorage|sessionStorage|indexedDB|showOpenFilePicker|showSaveFilePicker|showDirectoryPicker|webkitRequestFileSystem|FileReader' \
|
|
web/test-browser-wasm-smoke-*.js \
|
|
web/test-browser-wasm-smoke.html \
|
|
test-web-wasm-browser-smoke.sh; then
|
|
echo "browser OPFS smoke harness must not add filesystem persistence outside OPFS" >&2
|
|
exit 1
|
|
fi
|
|
|
|
python3 - <<'PY'
|
|
from pathlib import Path
|
|
|
|
source_map = Path("docs/linuxcnc-opfs-source-map.md").read_text(encoding="utf-8")
|
|
coverage_entries = [
|
|
"web/src/wasm-core.js",
|
|
"web/src/wasm-core.d.ts",
|
|
"web/src/app.js",
|
|
"web/test-browser-wasm-smoke-opfs-basic-sections.js",
|
|
"web/test-browser-wasm-smoke-opfs-parameter-sections.js",
|
|
"web/test-browser-wasm-smoke-opfs-workspace-sections.js",
|
|
"web/test-browser-wasm-smoke-opfs-mirror-sections.js",
|
|
"web/test-browser-wasm-smoke-opfs-directory-sections.js",
|
|
"web/test-browser-wasm-smoke-opfs-policy-sections.js",
|
|
"web/test-browser-wasm-smoke-app-sections.js",
|
|
]
|
|
for entry in coverage_entries:
|
|
if f"| `{entry}` |" not in source_map:
|
|
raise SystemExit(f"docs/linuxcnc-opfs-source-map.md missing OPFS coverage entry: {entry}")
|
|
|
|
required_phrases = [
|
|
"programs/current.ngc",
|
|
"parameters/rs274ngc.var",
|
|
'fopen(filename, "r")',
|
|
"LinuxCNC `read_text()`",
|
|
"program reads and parameter restore/save bridge files under the OPFS workspace",
|
|
"`.new`, `.bak`, failure, and stale-state semantics",
|
|
'unlinks `filename + ".bak"`',
|
|
"OPFS/WASM mirror persistence",
|
|
"OPFS directory persistence and WASM mirror refresh",
|
|
"OPFS workspace and mount policy",
|
|
"Browser OPFS smoke harness files must also stay free of Local Storage",
|
|
"they may only verify OPFS-backed browser persistence",
|
|
"OPFS-backed workspace, directory, parameter-file, and mount/workspace option types",
|
|
"app-level OPFS program, parameter, backup, and reload behavior",
|
|
"./check-linuxcnc-opfs-source-map.sh",
|
|
"does not interpret G-code",
|
|
]
|
|
for phrase in required_phrases:
|
|
if phrase not in source_map:
|
|
raise SystemExit(f"docs/linuxcnc-opfs-source-map.md missing OPFS coverage 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-opfs-source-map.sh" not in text:
|
|
raise SystemExit(f"{entry} must run ./check-linuxcnc-opfs-source-map.sh")
|
|
PY
|