参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke

结论:按 LinuxCNC source policy 将 TOOL_TABLE 恢复为 source coverage only,撤回生成 API/browser switchkins cases,并增加 checker/native 守护;表校验、native、source-link 均通过。
This commit is contained in:
cnc
2026-06-03 15:13:01 +08:00
parent aff6f85d8a
commit 457557dee8
9 changed files with 163 additions and 370 deletions

View File

@@ -2556,6 +2556,144 @@ JS
fi
}
validate_tool_table_source_coverage_only() {
local table_file=$1
local cpp_file=$2
local json_file=$3
local api_file=$4
local wasm_core_file=$5
local types_file=$6
local label=$7
if command -v python3 >/dev/null 2>&1; then
if ! python3 - "$table_file" "$cpp_file" "$json_file" "$api_file" "$wasm_core_file" "$types_file" "$label" <<'PY'
import json
import re
import sys
table_file, cpp_file, json_file, api_file, wasm_core_file, types_file, label = sys.argv[1:]
table_row_re = re.compile(
r'^\s*\{("(?:(?:\\.)|[^"\\])*"),\s*'
r'(-?\d+),\s*(-?\d+),\s*(-?\d+)\},?\s*$'
)
case_row_re = re.compile(
r'^\s*\{("(?:(?:\\.)|[^"\\])*"),\s*'
r'("(?:(?:\\.)|[^"\\])*"),\s*'
r'(-?\d+),\s*(-?\d+),\s*(-?\d+)\},?\s*$'
)
bad_aliases = []
with open(table_file, encoding="utf-8") as handle:
for line_number, line in enumerate(handle, 1):
match = table_row_re.match(line)
if match:
alias = json.loads(match.group(1)).lower()
if alias.endswith(".tbl") or "/tool" in alias or "tool_table" in alias or "tooltable" in alias:
bad_aliases.append((line_number, alias))
bad_cases = []
with open(cpp_file, encoding="utf-8") as handle:
for line_number, line in enumerate(handle, 1):
match = case_row_re.match(line)
if match:
field = json.loads(match.group(1)).lower()
value = json.loads(match.group(2)).lower()
if "tool" in field or value.endswith(".tbl"):
bad_cases.append((line_number, field, value))
with open(json_file, encoding="utf-8") as handle:
for index, entry in enumerate(json.load(handle)):
field = entry["field"].lower()
value = entry["value"].lower()
if "tool" in field or value.endswith(".tbl"):
bad_cases.append((index, field, value))
with open(api_file, encoding="utf-8") as handle:
api_text = handle.read()
with open(wasm_core_file, encoding="utf-8") as handle:
wasm_text = handle.read()
with open(types_file, encoding="utf-8") as handle:
types_text = handle.read()
bad_bridge_tokens = []
for token, text in {
"api": api_text,
"wasm": wasm_text,
"types": types_text,
}.items():
if re.search(r'\btoolTable\b|\btooltable\b|\btool_table\b|\bTOOL_TABLE\b', text):
bad_bridge_tokens.append(token)
if bad_aliases:
raise SystemExit(f"{label} generated TOOL_TABLE alias(es), but TOOL_TABLE is source coverage only: {bad_aliases}")
if bad_cases:
raise SystemExit(f"{label} generated TOOL_TABLE case(s), but TOOL_TABLE is source coverage only: {bad_cases}")
if bad_bridge_tokens:
raise SystemExit(f"{label} exposes TOOL_TABLE through switchkins API/web bridge: {bad_bridge_tokens}")
PY
then
exit 1
fi
elif command -v node >/dev/null 2>&1; then
if ! node - "$table_file" "$cpp_file" "$json_file" "$api_file" "$wasm_core_file" "$types_file" "$label" <<'JS'
const fs = require("fs");
const [tableFile, cppFile, jsonFile, apiFile, wasmCoreFile, typesFile, label] = process.argv.slice(2);
const tableRowRe = /^\s*\{("(?:(?:\\.)|[^"\\])*"),\s*(-?\d+),\s*(-?\d+),\s*(-?\d+)\},?\s*$/;
const caseRowRe = /^\s*\{("(?:(?:\\.)|[^"\\])*"),\s*("(?:(?:\\.)|[^"\\])*"),\s*(-?\d+),\s*(-?\d+),\s*(-?\d+)\},?\s*$/;
const badAliases = [];
fs.readFileSync(tableFile, "utf8").split(/\r?\n/).forEach((line, index) => {
const match = tableRowRe.exec(line);
if (!match) {
return;
}
const alias = JSON.parse(match[1]).toLowerCase();
if (alias.endsWith(".tbl") || alias.includes("/tool") || alias.includes("tool_table") || alias.includes("tooltable")) {
badAliases.push([index + 1, alias]);
}
});
const badCases = [];
fs.readFileSync(cppFile, "utf8").split(/\r?\n/).forEach((line, index) => {
const match = caseRowRe.exec(line);
if (!match) {
return;
}
const field = JSON.parse(match[1]).toLowerCase();
const value = JSON.parse(match[2]).toLowerCase();
if (field.includes("tool") || value.endsWith(".tbl")) {
badCases.push([index + 1, field, value]);
}
});
JSON.parse(fs.readFileSync(jsonFile, "utf8")).forEach((entry, index) => {
const field = entry.field.toLowerCase();
const value = entry.value.toLowerCase();
if (field.includes("tool") || value.endsWith(".tbl")) {
badCases.push([index, field, value]);
}
});
const bridgeTokenRe = /\btoolTable\b|\btooltable\b|\btool_table\b|\bTOOL_TABLE\b/;
const badBridgeTokens = [
["api", fs.readFileSync(apiFile, "utf8")],
["wasm", fs.readFileSync(wasmCoreFile, "utf8")],
["types", fs.readFileSync(typesFile, "utf8")],
].filter(([_name, text]) => bridgeTokenRe.test(text)).map(([name]) => name);
if (badAliases.length > 0) {
throw new Error(`${label} generated TOOL_TABLE alias(es), but TOOL_TABLE is source coverage only: ${JSON.stringify(badAliases)}`);
}
if (badCases.length > 0) {
throw new Error(`${label} generated TOOL_TABLE case(s), but TOOL_TABLE is source coverage only: ${JSON.stringify(badCases)}`);
}
if (badBridgeTokens.length > 0) {
throw new Error(`${label} exposes TOOL_TABLE through switchkins API/web bridge: ${badBridgeTokens.join(",")}`);
}
JS
then
exit 1
fi
else
echo "missing python3 or node for switchkins TOOL_TABLE source-only validation" >&2
exit 1
fi
}
for generated_file in \
linuxcnc_switchkins_remap_table.inc \
linuxcnc_switchkins_remap_config_cases.inc \
@@ -2576,6 +2714,14 @@ validate_web_switchkins_case_fields \
web/src/wasm-core.js \
web/src/index.ts \
"generated"
validate_tool_table_source_coverage_only \
"$generated_dir/linuxcnc_switchkins_remap_table.inc" \
"$generated_dir/linuxcnc_switchkins_remap_config_cases.inc" \
"$generated_dir/linuxcnc_switchkins_remap_config_cases.json" \
core/src/cnc_sim_api.cpp \
web/src/wasm-core.js \
web/src/index.ts \
"generated"
validate_generated_header \
"$generated_dir/linuxcnc_switchkins_remap_table.inc" \
@@ -2596,22 +2742,22 @@ validate_generated_header \
validate_generated_source_notes \
"$generated_dir/linuxcnc_switchkins_remap_table.inc" \
"table" \
"// Source: LinuxCNC INI MACHINE/KINEMATICS/HALFILE/POSTGUI_HALFILE/TOOL_TABLE/SUBROUTINE_PATH/REMAP entries and" \
"// Source: LinuxCNC INI MACHINE/KINEMATICS/HALFILE/POSTGUI_HALFILE/SUBROUTINE_PATH/REMAP entries and" \
"// adjacent remap_subs/{428,429,430}remap.ngc #<kinstype> assignments."
validate_generated_source_notes \
core/src/linuxcnc_switchkins_remap_table.inc \
"tracked table" \
"// Source: LinuxCNC INI MACHINE/KINEMATICS/HALFILE/POSTGUI_HALFILE/TOOL_TABLE/SUBROUTINE_PATH/REMAP entries and" \
"// Source: LinuxCNC INI MACHINE/KINEMATICS/HALFILE/POSTGUI_HALFILE/SUBROUTINE_PATH/REMAP entries and" \
"// adjacent remap_subs/{428,429,430}remap.ngc #<kinstype> assignments."
validate_generated_source_notes \
"$generated_dir/linuxcnc_switchkins_remap_config_cases.inc" \
"config cases" \
"// Source: LinuxCNC INI config path, MACHINE, KINEMATICS, non-LIB HALFILE/POSTGUI_HALFILE, TOOL_TABLE," \
"// Source: LinuxCNC INI config path, MACHINE, KINEMATICS, non-LIB HALFILE/POSTGUI_HALFILE," \
"// SUBROUTINE_PATH-resolved remap_subs entries, and adjacent M428/M429/M430 #<kinstype> assignments."
validate_generated_source_notes \
core/tests/linuxcnc_switchkins_remap_config_cases.inc \
"tracked config cases" \
"// Source: LinuxCNC INI config path, MACHINE, KINEMATICS, non-LIB HALFILE/POSTGUI_HALFILE, TOOL_TABLE," \
"// Source: LinuxCNC INI config path, MACHINE, KINEMATICS, non-LIB HALFILE/POSTGUI_HALFILE," \
"// SUBROUTINE_PATH-resolved remap_subs entries, and adjacent M428/M429/M430 #<kinstype> assignments."
validate_json "$generated_dir/linuxcnc_switchkins_remap_config_cases.json" "generated linuxcnc_switchkins_remap_config_cases.json"