From 0abf801856c16bb5604be2cb49af55597e2572b2 Mon Sep 17 00:00:00 2001 From: cnc Date: Tue, 2 Jun 2026 14:40:52 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8C=89=E6=8F=90=E7=A4=BA=E8=AF=8D=E7=BB=93?= =?UTF-8?q?=E8=AE=BA=E8=A1=A5=20switchkins=20=E5=AE=8C=E6=95=B4=E8=A6=86?= =?UTF-8?q?=E7=9B=96=E9=94=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- check-linuxcnc-switchkins-remap-table.sh | 142 ++++++++++++++++++ test-all-native.sh | 2 + test-linuxcnc-wasm-cmake-safe-probe.sh | 5 +- ...inuxcnc-wasm-rs274ngc-pre-link-blockers.sh | 7 +- test-native.sh | 5 + test-web-wasm-browser-smoke.sh | 3 + test-web-wasm-node-smoke.sh | 3 + 7 files changed, 164 insertions(+), 3 deletions(-) diff --git a/check-linuxcnc-switchkins-remap-table.sh b/check-linuxcnc-switchkins-remap-table.sh index 8a026b4..aa62d1c 100755 --- a/check-linuxcnc-switchkins-remap-table.sh +++ b/check-linuxcnc-switchkins-remap-table.sh @@ -36,6 +36,148 @@ if [[ ! -f "$manifest" ]]; then exit 1 fi +validate_manifest_switchkins_complete() { + local manifest_file=$1 + if command -v python3 >/dev/null 2>&1; then + if ! python3 - "$linuxcnc_root" "$manifest_file" <<'PY' +import os +import re +import sys + +linuxcnc_root, manifest_file = sys.argv[1:] +sim_root = os.path.join(linuxcnc_root, "configs", "sim") +remap_ini_re = re.compile(r"REMAP\s*=\s*M(428|429|430)(?:\D|$)", re.I) +switchkins_remap_names = {"428remap.ngc", "429remap.ngc", "430remap.ngc"} +manifest_config_paths = set() +manifest_remap_paths = set() +with open(manifest_file, encoding="utf-8") as manifest: + for line in manifest: + parts = line.rstrip("\n").split(":", 2) + if len(parts) < 2: + continue + group, source_path = parts[:2] + if group == "config" and source_path.endswith(".ini"): + with open(os.path.join(linuxcnc_root, source_path), encoding="utf-8") as handle: + if remap_ini_re.search(handle.read()): + manifest_config_paths.add(source_path) + elif group == "remap": + manifest_remap_paths.add(source_path) + +linuxcnc_config_paths = set() +linuxcnc_remap_paths = set() +for current_root, _dirs, files in os.walk(sim_root): + for filename in files: + source = os.path.join(current_root, filename) + source_path = os.path.relpath(source, linuxcnc_root) + if filename.endswith(".ini"): + with open(source, encoding="utf-8", errors="ignore") as handle: + if remap_ini_re.search(handle.read()): + linuxcnc_config_paths.add(source_path) + elif filename in switchkins_remap_names: + linuxcnc_remap_paths.add(source_path) + +missing_configs = sorted(linuxcnc_config_paths - manifest_config_paths) +extra_configs = sorted(manifest_config_paths - linuxcnc_config_paths) +missing_remaps = sorted(linuxcnc_remap_paths - manifest_remap_paths) +extra_remaps = sorted(manifest_remap_paths - linuxcnc_remap_paths) +for source_path in missing_configs: + print(f"missing LinuxCNC switchkins REMAP INI in manifest: {source_path}", file=sys.stderr) +for source_path in extra_configs: + print(f"unexpected LinuxCNC switchkins REMAP INI in manifest: {source_path}", file=sys.stderr) +for source_path in missing_remaps: + print(f"missing LinuxCNC switchkins remap source in manifest: {source_path}", file=sys.stderr) +for source_path in extra_remaps: + print(f"unexpected LinuxCNC switchkins remap source in manifest: {source_path}", file=sys.stderr) +if missing_configs or extra_configs or missing_remaps or extra_remaps: + raise SystemExit(1) +PY + then + exit 1 + fi + elif command -v node >/dev/null 2>&1; then + if ! node - "$linuxcnc_root" "$manifest_file" <<'JS' +const fs = require("fs"); +const path = require("path"); +const [linuxcncRoot, manifestFile] = process.argv.slice(2); +const simRoot = path.join(linuxcncRoot, "configs", "sim"); +const remapIniRe = /REMAP\s*=\s*M(428|429|430)(?:\D|$)/i; +const switchkinsRemapNames = new Set(["428remap.ngc", "429remap.ngc", "430remap.ngc"]); +const manifestConfigPaths = new Set(); +const manifestRemapPaths = new Set(); +for (const line of fs.readFileSync(manifestFile, "utf8").split(/\r?\n/)) { + const parts = line.split(":", 3); + if (parts.length < 2) { + continue; + } + const [group, sourcePath] = parts; + if (group === "config" && sourcePath.endsWith(".ini")) { + const text = fs.readFileSync(path.join(linuxcncRoot, sourcePath), "utf8"); + if (remapIniRe.test(text)) { + manifestConfigPaths.add(sourcePath); + } + } else if (group === "remap") { + manifestRemapPaths.add(sourcePath); + } +} + +function walkFiles(root) { + const files = []; + for (const entry of fs.readdirSync(root, { withFileTypes: true })) { + const fullPath = path.join(root, entry.name); + if (entry.isDirectory()) { + files.push(...walkFiles(fullPath)); + } else if (entry.isFile()) { + files.push(fullPath); + } + } + return files; +} + +const linuxcncConfigPaths = new Set(); +const linuxcncRemapPaths = new Set(); +for (const source of walkFiles(simRoot)) { + const sourcePath = path.relative(linuxcncRoot, source); + const basename = path.basename(source); + if (basename.endsWith(".ini")) { + const text = fs.readFileSync(source, "utf8"); + if (remapIniRe.test(text)) { + linuxcncConfigPaths.add(sourcePath); + } + } else if (switchkinsRemapNames.has(basename)) { + linuxcncRemapPaths.add(sourcePath); + } +} + +const missingConfigs = [...linuxcncConfigPaths].filter((sourcePath) => !manifestConfigPaths.has(sourcePath)).sort(); +const extraConfigs = [...manifestConfigPaths].filter((sourcePath) => !linuxcncConfigPaths.has(sourcePath)).sort(); +const missingRemaps = [...linuxcncRemapPaths].filter((sourcePath) => !manifestRemapPaths.has(sourcePath)).sort(); +const extraRemaps = [...manifestRemapPaths].filter((sourcePath) => !linuxcncRemapPaths.has(sourcePath)).sort(); +for (const sourcePath of missingConfigs) { + console.error(`missing LinuxCNC switchkins REMAP INI in manifest: ${sourcePath}`); +} +for (const sourcePath of extraConfigs) { + console.error(`unexpected LinuxCNC switchkins REMAP INI in manifest: ${sourcePath}`); +} +for (const sourcePath of missingRemaps) { + console.error(`missing LinuxCNC switchkins remap source in manifest: ${sourcePath}`); +} +for (const sourcePath of extraRemaps) { + console.error(`unexpected LinuxCNC switchkins remap source in manifest: ${sourcePath}`); +} +if (missingConfigs.length || extraConfigs.length || missingRemaps.length || extraRemaps.length) { + process.exit(1); +} +JS + then + exit 1 + fi + else + echo "missing python3 or node for switchkins remap manifest completeness validation" >&2 + exit 1 + fi +} +validate_manifest_switchkins_complete "$manifest" + validate_manifest_remap_kinstypes() { local manifest_file=$1 if command -v python3 >/dev/null 2>&1; then diff --git a/test-all-native.sh b/test-all-native.sh index f1f6b8e..2755d8c 100755 --- a/test-all-native.sh +++ b/test-all-native.sh @@ -77,6 +77,8 @@ grep -F 'LinuxCNC source basis: the wasm build preflights and compiles the' buil grep -F 'LinuxCNC source basis: manifest validation checks LinuxCNC-root relative' check-linuxcnc-inputs.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 'missing LinuxCNC switchkins REMAP INI in manifest:' check-linuxcnc-switchkins-remap-table.sh >/dev/null +grep -F 'missing LinuxCNC switchkins remap source in manifest:' check-linuxcnc-switchkins-remap-table.sh >/dev/null grep -F 'LinuxCNC source basis: cached switchkins/remap validation hashes the LinuxCNC' check-linuxcnc-switchkins-remap-table-cached.sh >/dev/null grep -F 'LinuxCNC source basis: generated switchkins/remap tables are extracted from' generate-linuxcnc-switchkins-remap-table.sh >/dev/null grep -F 'LinuxCNC source basis: Puma560 DH parameter output is generated from' generate-linuxcnc-puma560-dh-parameters.sh >/dev/null diff --git a/test-linuxcnc-wasm-cmake-safe-probe.sh b/test-linuxcnc-wasm-cmake-safe-probe.sh index dda7f39..b1a482c 100755 --- a/test-linuxcnc-wasm-cmake-safe-probe.sh +++ b/test-linuxcnc-wasm-cmake-safe-probe.sh @@ -536,6 +536,7 @@ if grep -F '} > "$report_file" || true' test-linuxcnc-wasm-rs274ngc-pre-link-blo exit 1 fi grep -Fx 'keep_report_file=0' test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh >/dev/null +grep -F 'LinuxCNC source basis: this blocker scan inspects unresolved symbols from' test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh >/dev/null grep -Fx 'trap cleanup_rs274ngc_pre_blocker_temps EXIT' test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh >/dev/null grep -Fx 'keep_report_file=1' test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh >/dev/null if ! awk ' @@ -557,7 +558,9 @@ if ! awk ' fi grep -F 'CNC_SIM_WASM_RS274NGC_PRE_LINK_BLOCKERS_CACHE_DIR' test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh >/dev/null grep -F 'rs274ngc-pre-link-blockers.lock' test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh >/dev/null -grep -F "printf 'FILTER_RULE_VERSION=1\n'" test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh >/dev/null +grep -F "printf 'FILTER_RULE_VERSION=2\n'" test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh >/dev/null +grep -F 'setenv|setlocale' test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh >/dev/null +grep -F 'unlink|unsetenv|uselocale' test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh >/dev/null grep -F "stat -c 'OBJECT=%n:%s:%y'" test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh >/dev/null grep -F 'cmp -s "$signature" "$next_signature"' test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh >/dev/null for build_dir_probe_script in \ diff --git a/test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh b/test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh index 5f1ec8e..2833742 100755 --- a/test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh +++ b/test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh @@ -3,6 +3,9 @@ set -euo pipefail cd "$(dirname "$0")" +# LinuxCNC source basis: this blocker scan inspects unresolved symbols from +# manifest-selected wasm rs274ngc_pre objects built from +# src/emc/rs274ngc/rs274ngc_pre.cc and its RS274 dependencies. linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} work_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_rs274ngc_pre_link_blockers.XXXXXX") manifest=${1:-linuxcnc-rs274-wasm-source-files.txt} @@ -72,7 +75,7 @@ next_report_file="$cache_dir/rs274ngc-pre-link-blockers.txt.next" printf 'PWD=%s\n' "$PWD" printf 'LINUXCNC_ROOT=%s\n' "$linuxcnc_root" printf 'MANIFEST=%s\n' "$manifest" - printf 'FILTER_RULE_VERSION=1\n' + printf 'FILTER_RULE_VERSION=2\n' printf 'OBJECTS:\n' printf '%s\n' "${objects[@]}" printf 'OBJECT_STATS:\n' @@ -110,7 +113,7 @@ nm --defined-only "${objects[@]}" \ | awk ' $0 ~ /^(_GLOBAL_OFFSET_TABLE_|_Unwind_Resume)$/ { next } $0 ~ /^(__assert_fail|__cxa_|__divdc3|__dso_handle|__errno_location|__gxx_personality_v0|__isoc23_|__muldc3|__stack_chk_fail)/ { next } - $0 ~ /^(abort|access|acos|asin|atan2|atof|atoi|basename|cabs|calloc|ceil|close|closedir|copysign|cos|cosl|exit|exp|fclose|fdatasync|fdopen|feof|fflush|fgetc|fgets|fileno|floor|fmax|fmod|fopen|fprintf|fputs|free|freelocale|fseek|fstat|ftell|fwrite|getcwd|getenv|getpid|gettimeofday|hypot|isalnum|isalpha|islower|isprint|isspace|isupper|isxdigit|link|localtime|log|lstat|malloc|memchr|memcmp|memcpy|memmove|memset|mkstemp|nearbyint|newlocale|open|opendir|perror|pow|printf|pthread_mutex_lock|pthread_mutex_unlock|putc|puts|read|readdir|realpath|realloc|remove|rename|setlocale|sin|sinl|snprintf|sprintf|sqrt|sscanf|stat|stderr|stdout|strcasecmp|strcat|strchr|strcmp|strcpy|strdup|strerror|strlen|strncasecmp|strncat|strncmp|strncpy|strrchr|strspn|strstr|strtod|strtok_r|strtol|strtoul|tan|tanl|tolower|toupper|towlower|unlink|uselocale|vfprintf|vsnprintf|wordexp|wordfree)$/ { next } + $0 ~ /^(abort|access|acos|asin|atan2|atof|atoi|basename|cabs|calloc|ceil|close|closedir|copysign|cos|cosl|exit|exp|fclose|fdatasync|fdopen|feof|fflush|fgetc|fgets|fileno|floor|fmax|fmod|fopen|fprintf|fputs|free|freelocale|fseek|fstat|ftell|fwrite|getcwd|getenv|getpid|gettimeofday|hypot|isalnum|isalpha|islower|isprint|isspace|isupper|isxdigit|link|localtime|log|lstat|malloc|memchr|memcmp|memcpy|memmove|memset|mkstemp|nearbyint|newlocale|open|opendir|perror|pow|printf|pthread_mutex_lock|pthread_mutex_unlock|putc|puts|read|readdir|realpath|realloc|remove|rename|setenv|setlocale|sin|sinl|snprintf|sprintf|sqrt|sscanf|stat|stderr|stdout|strcasecmp|strcat|strchr|strcmp|strcpy|strdup|strerror|strlen|strncasecmp|strncat|strncmp|strncpy|strrchr|strspn|strstr|strtod|strtok_r|strtol|strtoul|tan|tanl|tolower|toupper|towlower|unlink|unsetenv|uselocale|vfprintf|vsnprintf|wordexp|wordfree)$/ { next } $0 ~ /^(_ZN3fmt|_ZSt|_ZNSt|_ZNKSt|_ZNKRSt|_ZNS|_ZTI|_ZTS|_ZTV|_Zdl|_Znwm|_Znam|_Zda|_ZdaPv|_ZdaPvm|_ZdlPvm|_ZTv|_ZTh)/ { next } { print } ' diff --git a/test-native.sh b/test-native.sh index 492e11b..5a3071c 100755 --- a/test-native.sh +++ b/test-native.sh @@ -55,6 +55,8 @@ grep -F 'LinuxCNC source basis: the wasm build preflights and compiles the' buil grep -F 'LinuxCNC source basis: manifest validation checks LinuxCNC-root relative' check-linuxcnc-inputs.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 'missing LinuxCNC switchkins REMAP INI in manifest:' check-linuxcnc-switchkins-remap-table.sh >/dev/null +grep -F 'missing LinuxCNC switchkins remap source in manifest:' check-linuxcnc-switchkins-remap-table.sh >/dev/null grep -F 'LinuxCNC source basis: cached switchkins/remap validation hashes the LinuxCNC' check-linuxcnc-switchkins-remap-table-cached.sh >/dev/null grep -F 'LinuxCNC source basis: generated switchkins/remap tables are extracted from' generate-linuxcnc-switchkins-remap-table.sh >/dev/null grep -F 'LinuxCNC source basis: Puma560 DH parameter output is generated from' generate-linuxcnc-puma560-dh-parameters.sh >/dev/null @@ -110,6 +112,7 @@ grep -F 'LinuxCNC source basis: the wasm syntax probe compiles the' test-linuxcn 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 blocker scan inspects unresolved symbols from' test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh >/dev/null grep -F 'header:src/emc/ini/inifile.h:LinuxCNC C INI reader declarations exported by ini Submakefile' linuxcnc-rs274-wasm-source-files.txt >/dev/null grep -F 'header:src/emc/ini/inifile.hh:LinuxCNC C++ INI reader declarations used by inifile.cc' linuxcnc-rs274-wasm-source-files.txt >/dev/null grep -F 'metadata:src/emc/ini/Submakefile:LinuxCNC ini build metadata defining liblinuxcncini source and exported headers' linuxcnc-rs274-wasm-source-files.txt >/dev/null @@ -309,6 +312,7 @@ grep -F 'createWasmSimulator' test-web-wasm-node-smoke.cjs >/dev/null grep -F 'locateFile: (file) => path.join(wasmDir, file)' test-web-wasm-node-smoke.cjs >/dev/null grep -F 'printErr: () => {}' test-web-wasm-node-smoke.cjs >/dev/null grep -F 'expected web wasm-core options to pass LinuxCNC xyzac-trt RTCP config into WASM' test-web-wasm-node-smoke.cjs >/dev/null +grep -F 'LinuxCNC source basis: Node smoke only verifies the wasm/API bridge around' test-web-wasm-node-smoke.sh >/dev/null grep -F 'node test-web-wasm-node-smoke.cjs' test-web-wasm-node-smoke.sh >/dev/null grep -F 'WASM_NODE_SMOKE_MIN_STEPS:-50' test-web-wasm-node-smoke.sh >/dev/null grep -F 'web wasm node smoke passed ([0-9][0-9]* steps)' test-web-wasm-node-smoke.sh >/dev/null @@ -360,6 +364,7 @@ grep -F 'LinuxCNC out-of-range parameter file remained in WASM FS after restore grep -F 'browser wasm smoke passed' web/test-browser-wasm-smoke-helpers.js >/dev/null grep -F 'browser wasm smoke passed' web/test-browser-wasm-smoke.html >/dev/null grep -F 'chromium chromium-browser google-chrome' test-web-wasm-browser-smoke.sh >/dev/null +grep -F 'LinuxCNC source basis: browser smoke verifies OPFS-backed wasm/API/app flows' test-web-wasm-browser-smoke.sh >/dev/null grep -F 'browser wasm smoke passed' test-web-wasm-browser-smoke.sh >/dev/null grep -F 'BROWSER_SMOKE_MIN_SECTIONS:-50' test-web-wasm-browser-smoke.sh >/dev/null grep -F 'browser wasm smoke passed ([0-9][0-9]* sections)' test-web-wasm-browser-smoke.sh >/dev/null diff --git a/test-web-wasm-browser-smoke.sh b/test-web-wasm-browser-smoke.sh index ec887ac..31d3477 100755 --- a/test-web-wasm-browser-smoke.sh +++ b/test-web-wasm-browser-smoke.sh @@ -3,6 +3,9 @@ set -euo pipefail cd "$(dirname "$0")" +# LinuxCNC source basis: browser smoke verifies OPFS-backed wasm/API/app flows +# against source-backed LinuxCNC RS274 parameter persistence and generated +# switchkins/remap fixtures without bypassing OPFS. chromium_bin=${CHROMIUM:-} if [[ -z "$chromium_bin" ]]; then for candidate in chromium chromium-browser google-chrome; do diff --git a/test-web-wasm-node-smoke.sh b/test-web-wasm-node-smoke.sh index 9a901ad..1b11e61 100755 --- a/test-web-wasm-node-smoke.sh +++ b/test-web-wasm-node-smoke.sh @@ -3,6 +3,9 @@ set -euo pipefail cd "$(dirname "$0")" +# LinuxCNC source basis: Node smoke only verifies the wasm/API bridge around +# source-backed LinuxCNC RS274 and switchkins/remap fixtures; it must not grow +# independent G-code or kinematics behavior. missing=() minimum_steps=${WASM_NODE_SMOKE_MIN_STEPS:-50} if ! [[ "$minimum_steps" =~ ^[1-9][0-9]*$ ]]; then