Compare commits
322 Commits
0a0083b2d3
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c81f9d0111 | ||
|
|
c75eec1057 | ||
|
|
23e67bb6cd | ||
|
|
42e5b0784d | ||
|
|
64f336d6f2 | ||
|
|
fc56b11d8f | ||
|
|
fb9561ccc7 | ||
|
|
4e246738ac | ||
|
|
92562db29f | ||
|
|
de5f28f7ee | ||
|
|
82f222ba44 | ||
|
|
cdc30dda7e | ||
|
|
1d96aa8a55 | ||
|
|
25cb6885bf | ||
|
|
ccd51ff373 | ||
|
|
10bf0a5897 | ||
|
|
ced3e5de0e | ||
|
|
7ba3a22441 | ||
|
|
5aa328b413 | ||
|
|
586c28656d | ||
|
|
6efb77b8e4 | ||
|
|
ebb29f8cb6 | ||
|
|
8544872198 | ||
|
|
e320d6d420 | ||
|
|
ba2d680500 | ||
|
|
9af3443d95 | ||
|
|
4adcd82b74 | ||
|
|
66ea34a76d | ||
|
|
5bc568eaa9 | ||
|
|
23a4679c28 | ||
|
|
3c05202097 | ||
|
|
ba0d973f4f | ||
|
|
f69e10bedf | ||
|
|
a8f6a9d907 | ||
|
|
0850954ca8 | ||
|
|
6bd1c6a429 | ||
|
|
e928b32e69 | ||
|
|
36763c6bcd | ||
|
|
03080afe24 | ||
|
|
120a294f62 | ||
|
|
a3daa829fc | ||
|
|
71c61fca84 | ||
|
|
ff2980569c | ||
|
|
695186ddfc | ||
|
|
8394042d18 | ||
|
|
7e18c6cd40 | ||
|
|
09e2259669 | ||
|
|
867bf09c5d | ||
|
|
d35766e387 | ||
|
|
9c73577ade | ||
|
|
55149b0a0a | ||
|
|
6a886be52d | ||
|
|
acbf5b4686 | ||
|
|
77c6674905 | ||
|
|
eb91ced792 | ||
|
|
1b60bc2b2e | ||
|
|
37879f2a67 | ||
|
|
a7cd889b3a | ||
|
|
4887b2f64e | ||
|
|
cb315ce1cd | ||
|
|
9a4e0e3712 | ||
|
|
98755528e4 | ||
|
|
504b9b6d23 | ||
|
|
284afa1c64 | ||
|
|
428fc46f4e | ||
|
|
d5c6b7555a | ||
|
|
b773946c45 | ||
|
|
7a2a80ea71 | ||
|
|
41db47f440 | ||
|
|
27ac0bfb25 | ||
|
|
cd71eff8a5 | ||
|
|
eb1d31bfee | ||
|
|
2892f2ca9d | ||
|
|
a301965452 | ||
|
|
0737d5ea65 | ||
|
|
3a478ca335 | ||
|
|
a144576e90 | ||
|
|
60745adeef | ||
|
|
6506d23dc2 | ||
|
|
440a61be32 | ||
|
|
9b9e942dd6 | ||
|
|
3c72356c92 | ||
|
|
a89c107676 | ||
|
|
035b25862a | ||
|
|
167703b87a | ||
|
|
b600d6a516 | ||
|
|
0a2d37fae8 | ||
|
|
111cb5f9b9 | ||
|
|
218ac523b8 | ||
|
|
abaebe7940 | ||
|
|
77e92b0116 | ||
|
|
cf3154b043 | ||
|
|
9da6ffcd6a | ||
|
|
c71d2964ee | ||
|
|
c1a130f5fb | ||
|
|
baf8429736 | ||
|
|
2e2fb34cc3 | ||
|
|
810bd92ddc | ||
|
|
6d99ca67a9 | ||
|
|
6a2f63c894 | ||
|
|
bc80d74d1f | ||
|
|
71676ac1be | ||
|
|
89af73298e | ||
|
|
dc5cdeacc2 | ||
|
|
b170ccd95a | ||
|
|
29197b25cc | ||
|
|
5ce3c18103 | ||
|
|
79077fa695 | ||
|
|
f64db24060 | ||
|
|
fb2b5c4920 | ||
|
|
9c52e426f7 | ||
|
|
d80405ccb3 | ||
|
|
a48c02f5cc | ||
|
|
f56468b664 | ||
|
|
ba2d576f8b | ||
|
|
7f45abe93c | ||
|
|
ae7de73a7c | ||
|
|
7471444781 | ||
|
|
f36e2a96c4 | ||
|
|
aaa6dce99a | ||
|
|
b89d6311ff | ||
|
|
a12c0166b5 | ||
|
|
9fd23192dd | ||
|
|
a46910b6c0 | ||
|
|
0b387db8ca | ||
|
|
22c1c311ff | ||
|
|
3079656cfb | ||
|
|
7502684780 | ||
|
|
340966b6f7 | ||
|
|
1f314b7e9e | ||
|
|
1bfb7426d4 | ||
|
|
abd96dfdde | ||
|
|
7b171c605b | ||
|
|
cb22ecd8ef | ||
|
|
a0f6228908 | ||
|
|
8fe468ba3c | ||
|
|
b3b1117ec9 | ||
|
|
4eb694164f | ||
|
|
59b8e2a832 | ||
|
|
f9ac38251b | ||
|
|
002c8a2d53 | ||
|
|
decc008609 | ||
|
|
3c2df90872 | ||
|
|
81c7b061a8 | ||
|
|
2223e6d601 | ||
|
|
708b2bee17 | ||
|
|
0a270c73a0 | ||
|
|
52d7a49861 | ||
|
|
802e85cd63 | ||
|
|
0de612651e | ||
|
|
6f21d22912 | ||
|
|
f8902b3f8d | ||
|
|
3d368b2564 | ||
|
|
8b89b5b4c6 | ||
|
|
21c242c9a4 | ||
|
|
945e839e9d | ||
|
|
feb04102e7 | ||
|
|
aaa2573bba | ||
|
|
d7b9006ba5 | ||
|
|
51b415a701 | ||
|
|
856b526acb | ||
|
|
bc54b5f8f8 | ||
|
|
8151829643 | ||
|
|
5fb041f1a5 | ||
|
|
bba306ccfe | ||
|
|
4a3d87045e | ||
|
|
951dcc7980 | ||
|
|
aa6e79ac64 | ||
|
|
39f6f9133c | ||
|
|
bc7b947d14 | ||
|
|
1ce460d1d5 | ||
|
|
96b5973e1b | ||
|
|
c8b834e36b | ||
|
|
32b4a96a9f | ||
|
|
ee0a3130bf | ||
|
|
c031bc0a2c | ||
|
|
dcf246c0a0 | ||
|
|
1f4044efe2 | ||
|
|
3cc7590121 | ||
|
|
180c0f6dbe | ||
|
|
702f94cc52 | ||
|
|
b366d220f5 | ||
|
|
3090b9a79a | ||
|
|
c851476007 | ||
|
|
ddc20eeb98 | ||
|
|
1ef3f2c7df | ||
|
|
eef30e1831 | ||
|
|
1433435121 | ||
|
|
46807f05a5 | ||
|
|
77f9754833 | ||
|
|
457557dee8 | ||
|
|
aff6f85d8a | ||
|
|
4086445900 | ||
|
|
18f14ca111 | ||
|
|
7d28d0a025 | ||
|
|
0d4e248811 | ||
|
|
5481ab3bff | ||
|
|
8ff22065dd | ||
|
|
5c2d5cfa05 | ||
|
|
f98a715ed7 | ||
|
|
1928ff3b6a | ||
|
|
6cf7c73392 | ||
|
|
028825cd9c | ||
|
|
e778a37fdb | ||
|
|
6cc07c2950 | ||
|
|
c5a1d655b0 | ||
|
|
0895c04ab1 | ||
|
|
c932e10801 | ||
|
|
d1e0152eb8 | ||
|
|
a134dcef7d | ||
|
|
bbd5739e7a | ||
|
|
611e6417b4 | ||
|
|
fa4296d730 | ||
|
|
2dd9847a7b | ||
|
|
9bf9206869 | ||
|
|
090a5fb89c | ||
|
|
2f7a46758b | ||
|
|
1b16d3e0b5 | ||
|
|
d0a7a9fcec | ||
|
|
dbf27a4e6b | ||
|
|
e71b9146e1 | ||
|
|
59287b9920 | ||
|
|
33e1ee5a06 | ||
|
|
b26620dc32 | ||
|
|
74cb5f826c | ||
|
|
2ba8cd7fb8 | ||
|
|
e3d47e8267 | ||
|
|
3df61e8da5 | ||
|
|
28aa1956b7 | ||
|
|
4e3df07b8a | ||
|
|
b7610d1b96 | ||
|
|
55c75ffe80 | ||
|
|
ad3720713e | ||
|
|
ba526a6042 | ||
|
|
93f2a903b5 | ||
|
|
c52ecc09eb | ||
|
|
cc05532528 | ||
|
|
ecec27aa64 | ||
|
|
c25b0eff22 | ||
|
|
777d29651c | ||
|
|
138fe862ae | ||
|
|
d985b13a8e | ||
|
|
030074b8c5 | ||
|
|
84fdc8a1c5 | ||
|
|
9c03f91fa8 | ||
|
|
868f20382a | ||
|
|
cde0bdaba6 | ||
|
|
3a60624108 | ||
|
|
4d0cf788b1 | ||
|
|
e875496fe0 | ||
|
|
7e15a99d28 | ||
|
|
3435c3556a | ||
|
|
318220b766 | ||
|
|
43a3e66106 | ||
|
|
4f1042c548 | ||
|
|
cc708b1ccc | ||
|
|
25519b5bc4 | ||
|
|
a44199eb48 | ||
|
|
9a9b931982 | ||
|
|
7bd1dc640f | ||
|
|
b83bc47c45 | ||
|
|
6ffcada028 | ||
|
|
b2945cf57f | ||
|
|
05ece027c4 | ||
|
|
b4d0bfae46 | ||
|
|
3f5c3faf25 | ||
|
|
dd4c62fce6 | ||
|
|
4861287138 | ||
|
|
7319e18810 | ||
|
|
df5c459b1d | ||
|
|
23284e528f | ||
|
|
952d3ecfd2 | ||
|
|
6e0a5627f0 | ||
|
|
6266887fec | ||
|
|
b8ad3a0346 | ||
|
|
0abf801856 | ||
|
|
31e1e4499e | ||
|
|
4780dd8621 | ||
|
|
303b6bfa36 | ||
|
|
854cc682b3 | ||
|
|
dda5b4973e | ||
|
|
ce0605d20d | ||
|
|
67154a70fa | ||
|
|
5c7093ba39 | ||
|
|
0e1e22a864 | ||
|
|
cee1bf3a3f | ||
|
|
d807464cf7 | ||
|
|
303b7f7a72 | ||
|
|
154fc7578a | ||
|
|
08978b82de | ||
|
|
dfd41bb7fc | ||
|
|
1f37c979e9 | ||
|
|
23f88a619a | ||
|
|
34aa884a22 | ||
|
|
56f78b3054 | ||
|
|
c3ec85196f | ||
|
|
c2e5e8f2c8 | ||
|
|
10c1b70ed8 | ||
|
|
68b6c19392 | ||
|
|
e1fe284078 | ||
|
|
082495d3ad | ||
|
|
e117ac8a5b | ||
|
|
518628bfea | ||
|
|
89f9930ec4 | ||
|
|
7bf4300c84 | ||
|
|
16ac5cbfef | ||
|
|
820ac7e72b | ||
|
|
5f4e918663 | ||
|
|
de13d8efeb | ||
|
|
8826f2ee73 | ||
|
|
e1708c96ee | ||
|
|
cc7faef3a7 | ||
|
|
d8c2364816 | ||
|
|
93e83d52bf | ||
|
|
c4e174f011 | ||
|
|
00d62bd61a | ||
|
|
c9c5db8e9c | ||
|
|
26da1e6d9d | ||
|
|
d5c99f99c0 | ||
|
|
158fd343c6 | ||
|
|
35d341de70 | ||
|
|
d6cae8bf56 |
@@ -3,6 +3,13 @@ set -euo pipefail
|
|||||||
|
|
||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
# LinuxCNC source basis: this blocker scan is constrained to the RS274/WASM
|
||||||
|
# manifest. Python and Boost.Python blockers come from
|
||||||
|
# src/emc/rs274ngc/gcodemodule.cc, interp_namedparams.cc, interp_o_word.cc,
|
||||||
|
# and the py*.cc module bindings; tooldata/native-backend blockers come from
|
||||||
|
# src/emc/tooldata/tooldata_mmap.cc and tooldata_db.cc; native filesystem
|
||||||
|
# coverage comes from LinuxCNC RS274 sources such as interp_o_word.cc
|
||||||
|
# directory traversal and rs274ngc_pre.cc parameter-file replacement.
|
||||||
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
|
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
|
||||||
manifest=${1:-linuxcnc-rs274-wasm-source-files.txt}
|
manifest=${1:-linuxcnc-rs274-wasm-source-files.txt}
|
||||||
|
|
||||||
@@ -10,6 +17,7 @@ LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh "$manifest"
|
|||||||
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
|
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
|
||||||
|
|
||||||
python_blockers=()
|
python_blockers=()
|
||||||
|
python_compile_covered=()
|
||||||
core_python_sources=()
|
core_python_sources=()
|
||||||
dlopen_blockers=()
|
dlopen_blockers=()
|
||||||
core_dlopen_sources=()
|
core_dlopen_sources=()
|
||||||
@@ -54,13 +62,27 @@ wasm_safe_shim_in_build() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
python_compile_probe_covers() {
|
||||||
|
grep -F "$1" test-linuxcnc-wasm-python-c-api-symbols.sh >/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
core_sources=()
|
core_sources=()
|
||||||
blocked_sources=()
|
blocked_sources=()
|
||||||
while IFS=: read -r group path note extra; do
|
manifest_line_number=0
|
||||||
case "$group" in
|
while IFS= read -r manifest_line || [[ -n "$manifest_line" ]]; do
|
||||||
|
manifest_line_number=$((manifest_line_number + 1))
|
||||||
|
case "$manifest_line" in
|
||||||
""|\#*)
|
""|\#*)
|
||||||
continue
|
continue
|
||||||
;;
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
IFS=: read -r group path note extra <<<"$manifest_line"
|
||||||
|
if [[ -z "$group" || -z "$path" || -z "$note" || -n "${extra:-}" ]]; then
|
||||||
|
echo "bad manifest line $manifest_line_number: $manifest_line" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
case "$group" in
|
||||||
core)
|
core)
|
||||||
core_sources+=("$path")
|
core_sources+=("$path")
|
||||||
;;
|
;;
|
||||||
@@ -117,6 +139,9 @@ scan_source() {
|
|||||||
if grep -Eq 'Python\.h|boost/python|pythonplugin|PyObject|PyInit_' "$full_path"; then
|
if grep -Eq 'Python\.h|boost/python|pythonplugin|PyObject|PyInit_' "$full_path"; then
|
||||||
if [[ "$group" == "blocked" ]]; then
|
if [[ "$group" == "blocked" ]]; then
|
||||||
python_blockers+=("$path")
|
python_blockers+=("$path")
|
||||||
|
if python_compile_probe_covers "$path"; then
|
||||||
|
python_compile_covered+=("$path")
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
core_python_sources+=("$path")
|
core_python_sources+=("$path")
|
||||||
fi
|
fi
|
||||||
@@ -175,6 +200,7 @@ echo "blocked=$blocked_count"
|
|||||||
echo
|
echo
|
||||||
|
|
||||||
print_group "python" "${python_blockers[@]}"
|
print_group "python" "${python_blockers[@]}"
|
||||||
|
print_group "python-compile-covered" "${python_compile_covered[@]}"
|
||||||
print_group "dlopen" "${dlopen_blockers[@]}"
|
print_group "dlopen" "${dlopen_blockers[@]}"
|
||||||
print_group "tooldata" "${tooldata_blockers[@]}"
|
print_group "tooldata" "${tooldata_blockers[@]}"
|
||||||
print_group "tooldata-users" "${shimmed_tooldata_users[@]}"
|
print_group "tooldata-users" "${shimmed_tooldata_users[@]}"
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ set -euo pipefail
|
|||||||
|
|
||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
# LinuxCNC source basis: native bridge object caches compile only project
|
||||||
|
# canon/kinematics bridge sources that wrap LinuxCNC src/emc/nml_intf/canon.hh
|
||||||
|
# and src/emc/kinematics implementations; G-code behavior remains in
|
||||||
|
# LinuxCNC RS274 objects linked by the caller.
|
||||||
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
|
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
|
||||||
cxx=${CXX:-g++}
|
cxx=${CXX:-g++}
|
||||||
if [[ -z ${CXX:-} ]] && command -v ccache >/dev/null 2>&1; then
|
if [[ -z ${CXX:-} ]] && command -v ccache >/dev/null 2>&1; then
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ set -euo pipefail
|
|||||||
|
|
||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
# LinuxCNC source basis: link probe modes reuse wasm-safe objects built from
|
||||||
|
# linuxcnc-rs274-wasm-source-files.txt and add only a narrow probe main that
|
||||||
|
# asserts LinuxCNC rs274ngc_pre/runtime symbols stay linked.
|
||||||
usage() {
|
usage() {
|
||||||
echo "usage: $0 --mode rs274ngc-pre|runtime [manifest]" >&2
|
echo "usage: $0 --mode rs274ngc-pre|runtime [manifest]" >&2
|
||||||
}
|
}
|
||||||
@@ -82,11 +85,15 @@ target_name=rs274ngc_pre_probe
|
|||||||
common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh --profile core20-sections)
|
common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh --profile core20-sections)
|
||||||
mapfile -t common_flags <<<"$common_flag_output"
|
mapfile -t common_flags <<<"$common_flag_output"
|
||||||
case "$mode" in
|
case "$mode" in
|
||||||
|
rs274ngc-pre)
|
||||||
|
grep -F 'LinuxCNC source basis: src/emc/rs274ngc/rs274ngc_pre.cc provides' "$main_source" >/dev/null
|
||||||
|
;;
|
||||||
runtime)
|
runtime)
|
||||||
object_mode=runtime
|
object_mode=runtime
|
||||||
main_source=core/wasm_shims/cnc_sim_wasm_runtime_probe_main.cc
|
main_source=core/wasm_shims/cnc_sim_wasm_runtime_probe_main.cc
|
||||||
target_name=cnc_sim_wasm_runtime_probe
|
target_name=cnc_sim_wasm_runtime_probe
|
||||||
common_flags+=(-DCNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND)
|
common_flags+=(-DCNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND)
|
||||||
|
grep -F 'LinuxCNC source basis: src/emc/rs274ngc/rs274ngc_pre.cc provides read(),' "$main_source" >/dev/null
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,12 @@ set -euo pipefail
|
|||||||
|
|
||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
# LinuxCNC source basis: object probes compile the core entries selected from
|
||||||
|
# linuxcnc-rs274-wasm-source-files.txt, including src/emc/rs274ngc,
|
||||||
|
# src/emc/tooldata/tooldata_common.cc, src/emc/ini/inifile.cc, and
|
||||||
|
# src/emc/nml_intf/emcops.cc, together with CMake-listed wasm-safe project
|
||||||
|
# bridge sources and shims that replace the manifest's blocked Python, dlopen,
|
||||||
|
# tooldata mmap, HAL, and runtime edges.
|
||||||
usage() {
|
usage() {
|
||||||
echo "usage: $0 --mode source-objects|rs274ngc-pre|runtime [manifest]" >&2
|
echo "usage: $0 --mode source-objects|rs274ngc-pre|runtime [manifest]" >&2
|
||||||
}
|
}
|
||||||
@@ -94,6 +100,9 @@ case "$mode" in
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
grep -F 'LinuxCNC source basis: wasm-safe project probe sources are the thin bridge' list-linuxcnc-wasm-safe-project-sources.sh >/dev/null
|
||||||
|
grep -F 'LinuxCNC source basis: wasm-safe shim probe sources are limited to the' list-linuxcnc-wasm-safe-shims.sh >/dev/null
|
||||||
|
|
||||||
sources=()
|
sources=()
|
||||||
shim_output=$(./list-linuxcnc-wasm-safe-shims.sh)
|
shim_output=$(./list-linuxcnc-wasm-safe-shims.sh)
|
||||||
mapfile -t shim_sources <<<"$shim_output"
|
mapfile -t shim_sources <<<"$shim_output"
|
||||||
@@ -101,21 +110,13 @@ sources+=("${shim_sources[@]}")
|
|||||||
case "$mode" in
|
case "$mode" in
|
||||||
source-objects)
|
source-objects)
|
||||||
;;
|
;;
|
||||||
rs274ngc-pre)
|
rs274ngc-pre|runtime)
|
||||||
project_source_output=$(./list-linuxcnc-wasm-safe-project-sources.sh)
|
project_source_output=$(./list-linuxcnc-wasm-safe-project-sources.sh)
|
||||||
if [[ -n "$project_source_output" ]]; then
|
if [[ -n "$project_source_output" ]]; then
|
||||||
mapfile -t project_sources <<<"$project_source_output"
|
mapfile -t project_sources <<<"$project_source_output"
|
||||||
sources+=("${project_sources[@]}")
|
sources+=("${project_sources[@]}")
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
runtime)
|
|
||||||
core_source_output=$(./list-cmake-set-sources.sh cnc_sim_core_sources core/)
|
|
||||||
mapfile -t core_sources <<<"$core_source_output"
|
|
||||||
sources+=("${core_sources[@]}")
|
|
||||||
backend_source_output=$(./list-cmake-set-sources.sh cnc_sim_linuxcnc_rs274_backend_sources core/)
|
|
||||||
mapfile -t backend_sources <<<"$backend_source_output"
|
|
||||||
sources+=("${backend_sources[@]}")
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
source_output=$(CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core full)
|
source_output=$(CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core full)
|
||||||
mapfile -t linuxcnc_sources <<<"$source_output"
|
mapfile -t linuxcnc_sources <<<"$source_output"
|
||||||
|
|||||||
@@ -3,8 +3,11 @@ set -euo pipefail
|
|||||||
|
|
||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
# LinuxCNC source basis: standalone probe modes either compile LinuxCNC
|
||||||
|
# rs274/tooldata sources directly or link wasm-safe shims whose source basis is
|
||||||
|
# checked against LinuxCNC source comments before building.
|
||||||
usage() {
|
usage() {
|
||||||
echo "usage: $0 --mode tooldata-link|tooldata-common-link|interp-base-link|python-plugin-link|rs274ngc-pre-object" >&2
|
echo "usage: $0 --mode tooldata-link|tooldata-common-link|interp-base-link|python-plugin-link|python-plugin-inittab-link|rs274ngc-pre-object" >&2
|
||||||
}
|
}
|
||||||
|
|
||||||
mode=
|
mode=
|
||||||
@@ -30,7 +33,7 @@ while [[ "$#" -gt 0 ]]; do
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
case "$mode" in
|
case "$mode" in
|
||||||
tooldata-link|tooldata-common-link|interp-base-link|python-plugin-link|rs274ngc-pre-object)
|
tooldata-link|tooldata-common-link|interp-base-link|python-plugin-link|python-plugin-inittab-link|rs274ngc-pre-object)
|
||||||
;;
|
;;
|
||||||
"")
|
"")
|
||||||
usage
|
usage
|
||||||
@@ -85,6 +88,8 @@ case "$mode" in
|
|||||||
tooldata_mmap_backend_shim=$(./list-linuxcnc-wasm-safe-shims.sh tooldata_mmap_backend.cc)
|
tooldata_mmap_backend_shim=$(./list-linuxcnc-wasm-safe-shims.sh tooldata_mmap_backend.cc)
|
||||||
tooldata_runtime_stubs_shim=$(./list-linuxcnc-wasm-safe-shims.sh tooldata_runtime_stubs.cc)
|
tooldata_runtime_stubs_shim=$(./list-linuxcnc-wasm-safe-shims.sh tooldata_runtime_stubs.cc)
|
||||||
tooldata_common_source=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-files.sh src/emc/tooldata/tooldata_common.cc)
|
tooldata_common_source=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-files.sh src/emc/tooldata/tooldata_common.cc)
|
||||||
|
grep -F 'LinuxCNC source basis: src/emc/tooldata/tooldata_common.cc provides' core/wasm_shims/tooldata/tooldata_common_probe_main.cc >/dev/null
|
||||||
|
grep -F 'LinuxCNC source basis: src/emc/tooldata/tooldata_mmap.cc, tooldata_nml.cc,' core/wasm_shims/tooldata/tooldata_probe_main.cc >/dev/null
|
||||||
sources+=(
|
sources+=(
|
||||||
"$tooldata_common_source"
|
"$tooldata_common_source"
|
||||||
"$tooldata_mmap_backend_shim"
|
"$tooldata_mmap_backend_shim"
|
||||||
@@ -101,6 +106,7 @@ case "$mode" in
|
|||||||
interp-base-link)
|
interp-base-link)
|
||||||
dlfcn_shim=$(./list-linuxcnc-wasm-safe-shims.sh dlfcn.cc)
|
dlfcn_shim=$(./list-linuxcnc-wasm-safe-shims.sh dlfcn.cc)
|
||||||
interp_base_source=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-files.sh src/emc/rs274ngc/interp_base.cc)
|
interp_base_source=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-files.sh src/emc/rs274ngc/interp_base.cc)
|
||||||
|
grep -F 'LinuxCNC source basis: src/emc/rs274ngc/interp_base.cc provides' core/wasm_shims/interp_base_probe_main.cc >/dev/null
|
||||||
sources+=(
|
sources+=(
|
||||||
"$dlfcn_shim"
|
"$dlfcn_shim"
|
||||||
"$interp_base_source"
|
"$interp_base_source"
|
||||||
@@ -108,16 +114,25 @@ case "$mode" in
|
|||||||
)
|
)
|
||||||
target_name=interp_base_probe
|
target_name=interp_base_probe
|
||||||
;;
|
;;
|
||||||
python-plugin-link)
|
python-plugin-link|python-plugin-inittab-link)
|
||||||
python_plugin_shim=$(./list-linuxcnc-wasm-safe-shims.sh python_plugin.cc)
|
python_plugin_shim=$(./list-linuxcnc-wasm-safe-shims.sh python_plugin.cc)
|
||||||
|
if [[ "$mode" == "python-plugin-link" ]]; then
|
||||||
|
probe_source=core/wasm_shims/pythonplugin/python_plugin_probe_main.cc
|
||||||
|
target_name=python_plugin_probe
|
||||||
|
grep -F 'LinuxCNC source basis: src/emc/pythonplugin/python_plugin.cc and' "$probe_source" >/dev/null
|
||||||
|
else
|
||||||
|
probe_source=core/wasm_shims/pythonplugin/python_plugin_inittab_probe_main.cc
|
||||||
|
target_name=python_plugin_inittab_probe
|
||||||
|
grep -F 'LinuxCNC source basis: src/emc/pythonplugin/python_plugin.cc' "$probe_source" >/dev/null
|
||||||
|
fi
|
||||||
sources+=(
|
sources+=(
|
||||||
"$python_plugin_shim"
|
"$python_plugin_shim"
|
||||||
core/wasm_shims/pythonplugin/python_plugin_probe_main.cc
|
"$probe_source"
|
||||||
)
|
)
|
||||||
target_name=python_plugin_probe
|
|
||||||
;;
|
;;
|
||||||
rs274ngc-pre-object)
|
rs274ngc-pre-object)
|
||||||
rs274ngc_pre_source=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-files.sh src/emc/rs274ngc/rs274ngc_pre.cc)
|
rs274ngc_pre_source=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-files.sh src/emc/rs274ngc/rs274ngc_pre.cc)
|
||||||
|
grep -F 'core:src/emc/rs274ngc/rs274ngc_pre.cc:Interp implementation covered by rs274ngc_pre link probe' linuxcnc-rs274-wasm-source-files.txt >/dev/null
|
||||||
sources+=("$rs274ngc_pre_source")
|
sources+=("$rs274ngc_pre_source")
|
||||||
target_name=rs274ngc_pre.o
|
target_name=rs274ngc_pre.o
|
||||||
;;
|
;;
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ set -euo pipefail
|
|||||||
|
|
||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
# LinuxCNC source basis: the wasm build preflights and compiles the
|
||||||
|
# linuxcnc-rs274-wasm-source-files.txt manifest entries from
|
||||||
|
# src/emc/rs274ngc/tooldata plus source-basis-checked shims before copying the
|
||||||
|
# browser artifacts; it must not add independent interpreter behavior.
|
||||||
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
|
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
|
||||||
manifest=${1:-linuxcnc-rs274-wasm-source-files.txt}
|
manifest=${1:-linuxcnc-rs274-wasm-source-files.txt}
|
||||||
build_jobs=${CNC_SIM_BUILD_JOBS:-8}
|
build_jobs=${CNC_SIM_BUILD_JOBS:-8}
|
||||||
@@ -22,6 +26,18 @@ fi
|
|||||||
|
|
||||||
preflight_cache_dir=build/wasm-preflight-cache
|
preflight_cache_dir=build/wasm-preflight-cache
|
||||||
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" "$manifest"
|
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" "$manifest"
|
||||||
|
./check-linuxcnc-rs274-source-map.sh
|
||||||
|
./check-linuxcnc-kinematics-source-map.sh
|
||||||
|
./check-linuxcnc-opfs-source-map.sh
|
||||||
|
./check-linuxcnc-switchkins-remap-source-map.sh
|
||||||
|
./check-linuxcnc-wasm-smoke-source-map.sh
|
||||||
|
./check-linuxcnc-build-source-map.sh
|
||||||
|
./check-linuxcnc-browser-app-source-map.sh
|
||||||
|
./check-linuxcnc-workstreams-source-map.sh
|
||||||
|
./check-linuxcnc-tooldata-source-map.sh
|
||||||
|
./check-linuxcnc-wasm-shims-source-map.sh
|
||||||
|
./check-linuxcnc-wasm-blockers-source-map.sh
|
||||||
|
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-switchkins-remap-table-cached.sh --cache-dir "$preflight_cache_dir" linuxcnc-kinematics-source-files.txt
|
||||||
manifest=$(cd "$(dirname "$manifest")" && pwd)/$(basename "$manifest")
|
manifest=$(cd "$(dirname "$manifest")" && pwd)/$(basename "$manifest")
|
||||||
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
|
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
|
||||||
default_manifest="$PWD/linuxcnc-rs274-wasm-source-files.txt"
|
default_manifest="$PWD/linuxcnc-rs274-wasm-source-files.txt"
|
||||||
@@ -128,6 +144,7 @@ run_parallel_wasm_probe "LinuxCNC wasm interp_base link probe" ./test-linuxcnc-w
|
|||||||
run_parallel_wasm_probe "LinuxCNC wasm interp_find link probe" ./test-linuxcnc-wasm-interp-find-link.sh
|
run_parallel_wasm_probe "LinuxCNC wasm interp_find link probe" ./test-linuxcnc-wasm-interp-find-link.sh
|
||||||
run_parallel_wasm_probe "LinuxCNC wasm python_plugin link probe" ./test-linuxcnc-wasm-python-plugin-link.sh
|
run_parallel_wasm_probe "LinuxCNC wasm python_plugin link probe" ./test-linuxcnc-wasm-python-plugin-link.sh
|
||||||
run_parallel_wasm_probe "LinuxCNC wasm Python C API symbol probe" ./test-linuxcnc-wasm-python-c-api-symbols.sh
|
run_parallel_wasm_probe "LinuxCNC wasm Python C API symbol probe" ./test-linuxcnc-wasm-python-c-api-symbols.sh
|
||||||
|
run_parallel_wasm_probe "LinuxCNC wasm Python binding link probe" ./test-linuxcnc-wasm-python-binding-link.sh
|
||||||
run_parallel_wasm_probe "LinuxCNC wasm rs274ngc_pre object probe" ./test-linuxcnc-wasm-rs274ngc-pre-object.sh
|
run_parallel_wasm_probe "LinuxCNC wasm rs274ngc_pre object probe" ./test-linuxcnc-wasm-rs274ngc-pre-object.sh
|
||||||
wait_parallel_wasm_probes
|
wait_parallel_wasm_probes
|
||||||
|
|
||||||
@@ -164,7 +181,7 @@ wasm_build_next_signature=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_wasm_build.signature
|
|||||||
printf 'COMPILER_LAUNCHER=%s\n' "$cmake_compiler_launcher_mode"
|
printf 'COMPILER_LAUNCHER=%s\n' "$cmake_compiler_launcher_mode"
|
||||||
printf 'CMAKE_LISTS='
|
printf 'CMAKE_LISTS='
|
||||||
sha256sum core/CMakeLists.txt
|
sha256sum core/CMakeLists.txt
|
||||||
printf 'CONFIGURE_RULE_VERSION=1\n'
|
printf 'CONFIGURE_RULE_VERSION=2\n'
|
||||||
} > "$wasm_build_next_signature"
|
} > "$wasm_build_next_signature"
|
||||||
configure_wasm=1
|
configure_wasm=1
|
||||||
if [[ ! -f "$wasm_build_signature" ]] || ! cmp -s "$wasm_build_signature" "$wasm_build_next_signature"; then
|
if [[ ! -f "$wasm_build_signature" ]] || ! cmp -s "$wasm_build_signature" "$wasm_build_next_signature"; then
|
||||||
@@ -179,6 +196,7 @@ emcmake cmake -S core -B build/wasm \
|
|||||||
-DCNC_SIM_LINUXCNC_ROOT="$linuxcnc_root" \
|
-DCNC_SIM_LINUXCNC_ROOT="$linuxcnc_root" \
|
||||||
-DCNC_SIM_LINUXCNC_WASM_SOURCE_MANIFEST="$manifest" \
|
-DCNC_SIM_LINUXCNC_WASM_SOURCE_MANIFEST="$manifest" \
|
||||||
-DCNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE=ON \
|
-DCNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE=ON \
|
||||||
|
-DCNC_SIM_ENABLE_SMOKE_BACKEND=OFF \
|
||||||
"${cmake_compiler_launcher_args[@]}"
|
"${cmake_compiler_launcher_args[@]}"
|
||||||
fi
|
fi
|
||||||
mv "$wasm_build_next_signature" "$wasm_build_signature"
|
mv "$wasm_build_next_signature" "$wasm_build_signature"
|
||||||
|
|||||||
130
check-linuxcnc-browser-app-source-map.sh
Executable file
130
check-linuxcnc-browser-app-source-map.sh
Executable file
@@ -0,0 +1,130 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
# LinuxCNC source basis: this guard ties the browser app entry point to
|
||||||
|
# LinuxCNC RS274 parameter-file defaults, rs274ngc_pre.cc restore/save/read
|
||||||
|
# file semantics, and the existing OPFS-backed wasm-core bridge. It validates
|
||||||
|
# app integration policy only; it does not add CNC behavior or 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::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 'read_text(command, _setup.file_pointer, _setup.linetext,' \
|
||||||
|
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
|
||||||
|
|
||||||
|
grep -F 'LinuxCNC browser app source map' docs/linuxcnc-browser-app-source-map.md >/dev/null
|
||||||
|
grep -F 'must not add CNC behavior' docs/linuxcnc-browser-app-source-map.md >/dev/null
|
||||||
|
grep -F 'must not expand the temporary smoke parser' docs/linuxcnc-browser-app-source-map.md >/dev/null
|
||||||
|
grep -F '`RS274NGC_PARAMETER_FILE_NAME_DEFAULT` as `rs274ngc.var`' \
|
||||||
|
docs/linuxcnc-browser-app-source-map.md >/dev/null
|
||||||
|
grep -F '`parseFileWithParameterFile(programPath, parameterPath, "linuxcnc", ...)`' \
|
||||||
|
docs/linuxcnc-browser-app-source-map.md >/dev/null
|
||||||
|
grep -F 'orders parameter saves as backup unlink' \
|
||||||
|
docs/linuxcnc-browser-app-source-map.md >/dev/null
|
||||||
|
|
||||||
|
grep -F '<script type="module" src="/src/app.js"></script>' web/index.html >/dev/null
|
||||||
|
grep -F '<option value="linuxcnc-rs274">LINUXCNC</option>' web/index.html >/dev/null
|
||||||
|
if [[ "$(grep -Fc '<option value=' web/index.html)" -ne 1 ]]; then
|
||||||
|
echo "browser app backend selector must expose only the LinuxCNC RS274 backend" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
grep -F 'import { createWasmSimulator } from "./wasm-core.js";' web/src/app.js >/dev/null
|
||||||
|
grep -F 'const programPath = "programs/current.ngc";' web/src/app.js >/dev/null
|
||||||
|
grep -F 'const linuxCncParameterFileName = "rs274ngc.var";' web/src/app.js >/dev/null
|
||||||
|
grep -F 'const parameterPath = `parameters/${linuxCncParameterFileName}`;' web/src/app.js >/dev/null
|
||||||
|
grep -F 'const linuxCncBackend = "linuxcnc-rs274";' web/src/app.js >/dev/null
|
||||||
|
grep -F 'const opfsOptions = { required: true, mountPoint: "/cnc", workspacePath: "cnc-simulator" };' \
|
||||||
|
web/src/app.js >/dev/null
|
||||||
|
grep -F 'simulatorPromise = createWasmSimulator({' web/src/app.js >/dev/null
|
||||||
|
grep -F 'opfs: opfsOptions,' web/src/app.js >/dev/null
|
||||||
|
grep -F 'OPFS workspace is required for browser program parsing' web/src/app.js >/dev/null
|
||||||
|
grep -F 'await simulator.fs.opfs.writeFile(programPath, elements.input.value);' web/src/app.js >/dev/null
|
||||||
|
grep -F 'const events = await simulator.parseFileWithParameterFile(programPath, parameterPath, "linuxcnc", parseOptions);' \
|
||||||
|
web/src/app.js >/dev/null
|
||||||
|
grep -F 'elements.backendSelect.value = linuxCncBackend;' web/src/app.js >/dev/null
|
||||||
|
grep -F 'globalThis.__cncSimulatorForTest = simulatorPromise;' web/src/app.js >/dev/null
|
||||||
|
|
||||||
|
grep -F 'app frame did not persist current program into OPFS' \
|
||||||
|
web/test-browser-wasm-smoke-app-sections.js >/dev/null
|
||||||
|
grep -F 'app frame did not persist LinuxCNC parameter state into OPFS' \
|
||||||
|
web/test-browser-wasm-smoke-app-sections.js >/dev/null
|
||||||
|
grep -F 'app frame did not persist LinuxCNC parameter backup into OPFS' \
|
||||||
|
web/test-browser-wasm-smoke-app-sections.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 PROGRAM RESTORED' web/test-browser-wasm-smoke-app-sections.js >/dev/null
|
||||||
|
grep -F 'app frame did not reload LinuxCNC parameter state from OPFS after reload' \
|
||||||
|
web/test-browser-wasm-smoke-app-sections.js >/dev/null
|
||||||
|
grep -F 'runSection("browser app opfs save"' 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
|
||||||
|
|
||||||
|
python3 - <<'PY'
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
source_map = Path("docs/linuxcnc-browser-app-source-map.md").read_text(encoding="utf-8")
|
||||||
|
for entry in [
|
||||||
|
"web/index.html",
|
||||||
|
"web/src/app.js",
|
||||||
|
"web/test-browser-wasm-smoke-app-sections.js",
|
||||||
|
]:
|
||||||
|
if f"| `{entry}` |" not in source_map:
|
||||||
|
raise SystemExit(f"docs/linuxcnc-browser-app-source-map.md missing app entry: {entry}")
|
||||||
|
|
||||||
|
for phrase in [
|
||||||
|
"must not add CNC behavior",
|
||||||
|
"must not expand the temporary smoke parser",
|
||||||
|
"existing `createWasmSimulator()` OPFS API",
|
||||||
|
"programs/current.ngc",
|
||||||
|
"parameters/rs274ngc.var",
|
||||||
|
"visible backend selector must stay limited",
|
||||||
|
"./check-linuxcnc-browser-app-source-map.sh",
|
||||||
|
"does not interpret G-code",
|
||||||
|
]:
|
||||||
|
if phrase not in source_map:
|
||||||
|
raise SystemExit(f"docs/linuxcnc-browser-app-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-browser-app-source-map.sh" not in text:
|
||||||
|
raise SystemExit(f"{entry} must run ./check-linuxcnc-browser-app-source-map.sh")
|
||||||
|
|
||||||
|
app = Path("web/src/app.js").read_text(encoding="utf-8")
|
||||||
|
if "parse(" in app.replace("parseProgram", ""):
|
||||||
|
raise SystemExit("browser app should route program execution through parseFileWithParameterFile, not direct parse")
|
||||||
|
if app.count("elements.backendSelect.value = linuxCncBackend;") != 2:
|
||||||
|
raise SystemExit("browser app backend selector must only be forced to the LinuxCNC backend")
|
||||||
|
if "backendSelect.value" in app.replace("elements.backendSelect.value = linuxCncBackend;", ""):
|
||||||
|
raise SystemExit("browser app must not read the backend selector as a dynamic backend source")
|
||||||
|
if 'backend: linuxCncBackend' not in app:
|
||||||
|
raise SystemExit("browser app parse options must force the LinuxCNC backend")
|
||||||
|
PY
|
||||||
202
check-linuxcnc-build-source-map.sh
Executable file
202
check-linuxcnc-build-source-map.sh
Executable file
@@ -0,0 +1,202 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
# LinuxCNC source basis: this guard ties native/source-link/WASM build entry
|
||||||
|
# points to LinuxCNC Submakefile source ownership, source manifests, persistent
|
||||||
|
# build signatures, and source-backed WASM artifact ordering. It validates
|
||||||
|
# build 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 'LIBRS274SRCS := $(addprefix emc/rs274ngc/,' \
|
||||||
|
"$linuxcnc_root/src/emc/rs274ngc/Submakefile" >/dev/null
|
||||||
|
grep -F 'rs274ngc_pre.cc \' "$linuxcnc_root/src/emc/rs274ngc/Submakefile" >/dev/null
|
||||||
|
grep -F '../lib/libtooldata.so.0' "$linuxcnc_root/src/emc/rs274ngc/Submakefile" >/dev/null
|
||||||
|
grep -F 'LIBTOOLDATA_SRCS += emc/tooldata/tooldata_common.cc' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/Submakefile" >/dev/null
|
||||||
|
grep -F 'LIBTOOLDATA_SRCS += emc/tooldata/tooldata_mmap.cc' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/Submakefile" >/dev/null
|
||||||
|
grep -F 'LIBTOOLDATA_SRCS += emc/tooldata/tooldata_db.cc' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/Submakefile" >/dev/null
|
||||||
|
grep -F 'LIBLCNCINISRCS := emc/ini/inifile.cc' \
|
||||||
|
"$linuxcnc_root/src/emc/ini/Submakefile" >/dev/null
|
||||||
|
grep -F 'LIBPPSRCS := $(addprefix emc/pythonplugin/,' \
|
||||||
|
"$linuxcnc_root/src/emc/pythonplugin/Submakefile" >/dev/null
|
||||||
|
grep -F 'GENSERKINSSRCS := emc/kinematics/ugenserkins.c' \
|
||||||
|
"$linuxcnc_root/src/emc/kinematics/Submakefile" >/dev/null
|
||||||
|
grep -F 'extern int kinematicsInverse(const struct EmcPose * world,' \
|
||||||
|
"$linuxcnc_root/src/emc/kinematics/kinematics.h" >/dev/null
|
||||||
|
|
||||||
|
grep -F 'LinuxCNC build source map' docs/linuxcnc-build-source-map.md >/dev/null
|
||||||
|
grep -F 'must not add CNC' docs/linuxcnc-build-source-map.md >/dev/null
|
||||||
|
grep -F 'must not expand the temporary smoke parser' docs/linuxcnc-build-source-map.md >/dev/null
|
||||||
|
grep -F '`src/emc/rs274ngc/Submakefile` defines `LIBRS274SRCS`' \
|
||||||
|
docs/linuxcnc-build-source-map.md >/dev/null
|
||||||
|
grep -F '`src/emc/tooldata/Submakefile` defines the LinuxCNC tooldata' \
|
||||||
|
docs/linuxcnc-build-source-map.md >/dev/null
|
||||||
|
grep -F 'Default WASM manifest partition must remain 26 wasm-safe `core` sources and' \
|
||||||
|
docs/linuxcnc-build-source-map.md >/dev/null
|
||||||
|
grep -F '`build-wasm.sh` must configure with `CNC_SIM_ENABLE_SMOKE_BACKEND=OFF`' \
|
||||||
|
docs/linuxcnc-build-source-map.md >/dev/null
|
||||||
|
grep -F '`test-native.sh`, `test-linuxcnc-source-link.sh`, and `build-wasm.sh` must' \
|
||||||
|
docs/linuxcnc-build-source-map.md >/dev/null
|
||||||
|
|
||||||
|
grep -F 'LinuxCNC source basis: the wasm build preflights and compiles the' build-wasm.sh >/dev/null
|
||||||
|
source_map_guards=(
|
||||||
|
'./check-linuxcnc-rs274-source-map.sh'
|
||||||
|
'./check-linuxcnc-kinematics-source-map.sh'
|
||||||
|
'./check-linuxcnc-opfs-source-map.sh'
|
||||||
|
'./check-linuxcnc-switchkins-remap-source-map.sh'
|
||||||
|
'./check-linuxcnc-wasm-smoke-source-map.sh'
|
||||||
|
'./check-linuxcnc-build-source-map.sh'
|
||||||
|
'./check-linuxcnc-browser-app-source-map.sh'
|
||||||
|
'./check-linuxcnc-workstreams-source-map.sh'
|
||||||
|
'./check-linuxcnc-tooldata-source-map.sh'
|
||||||
|
'./check-linuxcnc-wasm-shims-source-map.sh'
|
||||||
|
'./check-linuxcnc-wasm-blockers-source-map.sh'
|
||||||
|
)
|
||||||
|
for guarded_entry in test-native.sh test-linuxcnc-source-link.sh build-wasm.sh; do
|
||||||
|
for source_map_guard in "${source_map_guards[@]}"; do
|
||||||
|
grep -F "$source_map_guard" "$guarded_entry" >/dev/null
|
||||||
|
done
|
||||||
|
done
|
||||||
|
grep -F 'manifest_core_count=$(count_nonempty_lines "$manifest_core_output")' build-wasm.sh >/dev/null
|
||||||
|
grep -F 'manifest_blocked_count=$(count_nonempty_lines "$manifest_blocked_output")' build-wasm.sh >/dev/null
|
||||||
|
grep -F 'manifest_core_count" -ne 26 || "$manifest_blocked_count" -ne 9' build-wasm.sh >/dev/null
|
||||||
|
grep -F 'run_parallel_wasm_probe "LinuxCNC wasm tooldata shim link probe" ./test-linuxcnc-wasm-tooldata-link.sh' build-wasm.sh >/dev/null
|
||||||
|
grep -F 'run_parallel_wasm_probe "LinuxCNC wasm rs274ngc_pre object probe" ./test-linuxcnc-wasm-rs274ngc-pre-object.sh' build-wasm.sh >/dev/null
|
||||||
|
grep -F 'wait_parallel_wasm_probes' build-wasm.sh >/dev/null
|
||||||
|
grep -F 'for required_tool in cmake emcmake emcc node; do' build-wasm.sh >/dev/null
|
||||||
|
grep -F 'CONFIGURE_RULE_VERSION=2' build-wasm.sh >/dev/null
|
||||||
|
grep -F -- '-DCNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE=ON' build-wasm.sh >/dev/null
|
||||||
|
grep -F -- '-DCNC_SIM_ENABLE_SMOKE_BACKEND=OFF' build-wasm.sh >/dev/null
|
||||||
|
grep -F 'cmake --build build/wasm --target cnc_sim_wasm_runtime_probe cnc_sim_wasm --parallel "$build_jobs"' build-wasm.sh >/dev/null
|
||||||
|
grep -F 'cmp -s build/wasm/cnc_sim.js web/public/cnc_sim.js' build-wasm.sh >/dev/null
|
||||||
|
grep -F 'cmp -s build/wasm/cnc_sim.wasm web/public/cnc_sim.wasm' build-wasm.sh >/dev/null
|
||||||
|
grep -F './test-web-wasm-node-smoke.sh' build-wasm.sh >/dev/null
|
||||||
|
grep -F './test-web-wasm-browser-smoke.sh' build-wasm.sh >/dev/null
|
||||||
|
|
||||||
|
grep -F 'LinuxCNC source basis: this source-link test compiles the manifest-selected' \
|
||||||
|
test-linuxcnc-source-link.sh >/dev/null
|
||||||
|
grep -F 'BUILD_RULE_VERSION=1' test-linuxcnc-source-link.sh >/dev/null
|
||||||
|
grep -F 'make --output-sync=target -j"$build_jobs" -f "$source_link_makefile"' \
|
||||||
|
test-linuxcnc-source-link.sh >/dev/null
|
||||||
|
grep -F 'LinuxCNC source basis: this syntax probe checks the native RS274 manifest' \
|
||||||
|
test-linuxcnc-source-syntax.sh >/dev/null
|
||||||
|
grep -F 'LinuxCNC source basis: the wasm syntax probe compiles the' \
|
||||||
|
test-linuxcnc-wasm-source-syntax.sh >/dev/null
|
||||||
|
grep -F "printf 'CONFIGURE_RULE_VERSION=1\n'" \
|
||||||
|
test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
|
||||||
|
grep -F 'cmake --build "$build_dir" --target linuxcnc_rs274_wasm_safe_probe cnc_sim_wasm_runtime_probe --parallel "$build_jobs"' \
|
||||||
|
test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
|
||||||
|
|
||||||
|
if grep -F 'printf '\''BUILD_JOBS=%s\n'\'' "$build_jobs"' build-wasm.sh >/dev/null; then
|
||||||
|
echo "build-wasm configure signature must not depend on CNC_SIM_BUILD_JOBS" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
for hot_signature_script in \
|
||||||
|
build-linuxcnc-wasm-link-probe.sh \
|
||||||
|
build-linuxcnc-wasm-probe-objects.sh \
|
||||||
|
build-linuxcnc-wasm-standalone-probe.sh \
|
||||||
|
build-linuxcnc-native-bridge-objects.sh \
|
||||||
|
test-linuxcnc-api-native.sh \
|
||||||
|
test-linuxcnc-bridge-native.sh \
|
||||||
|
test-linuxcnc-rs274-native.sh \
|
||||||
|
test-linuxcnc-source-link.sh \
|
||||||
|
test-linuxcnc-source-objects.sh \
|
||||||
|
test-linuxcnc-source-syntax.sh \
|
||||||
|
test-native.sh; do
|
||||||
|
if grep -F "sha256sum $hot_signature_script" "$hot_signature_script" >/dev/null; then
|
||||||
|
echo "$hot_signature_script hot object signature must use BUILD_RULE_VERSION instead of the whole script hash" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
python3 - <<'PY'
|
||||||
|
from pathlib import Path
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
source_map = Path("docs/linuxcnc-build-source-map.md").read_text(encoding="utf-8")
|
||||||
|
wasm_manifest = Path("linuxcnc-rs274-wasm-source-files.txt")
|
||||||
|
core_count = len(subprocess.check_output(
|
||||||
|
["./list-linuxcnc-wasm-manifest-sources.sh", str(wasm_manifest), "core"],
|
||||||
|
text=True,
|
||||||
|
).splitlines())
|
||||||
|
blocked_count = len(subprocess.check_output(
|
||||||
|
["./list-linuxcnc-wasm-manifest-sources.sh", str(wasm_manifest), "blocked"],
|
||||||
|
text=True,
|
||||||
|
).splitlines())
|
||||||
|
if core_count != 26 or blocked_count != 9:
|
||||||
|
raise SystemExit(f"unexpected default WASM manifest partition: core={core_count} blocked={blocked_count}")
|
||||||
|
|
||||||
|
for entry in [
|
||||||
|
"test-native.sh",
|
||||||
|
"test-linuxcnc-source-link.sh",
|
||||||
|
"test-linuxcnc-source-syntax.sh",
|
||||||
|
"test-linuxcnc-wasm-cmake-safe-probe.sh",
|
||||||
|
"build-wasm.sh",
|
||||||
|
]:
|
||||||
|
if f"| `{entry}` |" not in source_map:
|
||||||
|
raise SystemExit(f"docs/linuxcnc-build-source-map.md missing build entry: {entry}")
|
||||||
|
|
||||||
|
for phrase in [
|
||||||
|
"must not add CNC",
|
||||||
|
"must not expand the temporary smoke parser",
|
||||||
|
"CNC_SIM_BUILD_JOBS",
|
||||||
|
"BUILD_RULE_VERSION",
|
||||||
|
"CONFIGURE_RULE_VERSION",
|
||||||
|
"CNC_SIM_ENABLE_SMOKE_BACKEND=OFF",
|
||||||
|
"Node smoke before browser smoke",
|
||||||
|
"does not interpret",
|
||||||
|
"G-code",
|
||||||
|
]:
|
||||||
|
if phrase not in source_map:
|
||||||
|
raise SystemExit(f"docs/linuxcnc-build-source-map.md missing boundary phrase: {phrase}")
|
||||||
|
|
||||||
|
build_wasm = Path("build-wasm.sh").read_text(encoding="utf-8").splitlines()
|
||||||
|
positions = {}
|
||||||
|
for index, line in enumerate(build_wasm, start=1):
|
||||||
|
if 'run_parallel_wasm_probe "LinuxCNC wasm tooldata shim link probe"' in line:
|
||||||
|
positions["parallel_start"] = index
|
||||||
|
if "wait_parallel_wasm_probes" in line and "parallel_start" in positions and "wait" not in positions:
|
||||||
|
positions["wait"] = index
|
||||||
|
if "LinuxCNC wasm rs274ngc_pre link blocker scan" in line:
|
||||||
|
positions["pre_link"] = index
|
||||||
|
if "for required_tool in cmake emcmake emcc node; do" in line:
|
||||||
|
positions["tools"] = index
|
||||||
|
if "emcmake cmake -S core -B build/wasm" in line:
|
||||||
|
positions["configure"] = index
|
||||||
|
if "cmake --build build/wasm --target cnc_sim_wasm_runtime_probe cnc_sim_wasm" in line:
|
||||||
|
positions["build"] = index
|
||||||
|
if "cp build/wasm/cnc_sim.js web/public/cnc_sim.js" in line:
|
||||||
|
positions["cp_js"] = index
|
||||||
|
if "cp build/wasm/cnc_sim.wasm web/public/cnc_sim.wasm" in line:
|
||||||
|
positions["cp_wasm"] = index
|
||||||
|
if "cmp -s build/wasm/cnc_sim.js web/public/cnc_sim.js" in line:
|
||||||
|
positions["cmp_js"] = index
|
||||||
|
if "cmp -s build/wasm/cnc_sim.wasm web/public/cnc_sim.wasm" in line:
|
||||||
|
positions["cmp_wasm"] = index
|
||||||
|
if "./test-web-wasm-node-smoke.sh" in line:
|
||||||
|
positions["node"] = index
|
||||||
|
if "./test-web-wasm-browser-smoke.sh" in line:
|
||||||
|
positions["browser"] = index
|
||||||
|
|
||||||
|
required = [
|
||||||
|
"parallel_start", "wait", "pre_link", "tools", "configure", "build",
|
||||||
|
"cp_js", "cp_wasm", "cmp_js", "cmp_wasm", "node", "browser",
|
||||||
|
]
|
||||||
|
missing = [name for name in required if name not in positions]
|
||||||
|
if missing:
|
||||||
|
raise SystemExit(f"build-wasm.sh missing ordered build anchors: {', '.join(missing)}")
|
||||||
|
if not (positions["parallel_start"] < positions["wait"] < positions["pre_link"]):
|
||||||
|
raise SystemExit("build-wasm.sh must wait for parallel probes before rs274ngc_pre link checks")
|
||||||
|
if not (positions["tools"] < positions["configure"] < positions["build"]):
|
||||||
|
raise SystemExit("build-wasm.sh must check tools, then configure, then build")
|
||||||
|
if not (positions["cp_js"] < positions["cmp_js"] < positions["node"] < positions["browser"]):
|
||||||
|
raise SystemExit("build-wasm.js artifact JS copy/compare or smoke ordering changed")
|
||||||
|
if not (positions["cp_wasm"] < positions["cmp_wasm"] < positions["node"] < positions["browser"]):
|
||||||
|
raise SystemExit("build-wasm.wasm artifact copy/compare or smoke ordering changed")
|
||||||
|
PY
|
||||||
@@ -3,6 +3,9 @@ set -euo pipefail
|
|||||||
|
|
||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
# LinuxCNC source basis: the cached input checker keys check-linuxcnc-inputs.sh
|
||||||
|
# results by manifest contents and LinuxCNC source mtimes so source-backed
|
||||||
|
# rs274/kinematics coverage is not silently reused after upstream changes.
|
||||||
cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-}
|
cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-}
|
||||||
check_args=("$@")
|
check_args=("$@")
|
||||||
if [[ "${1:-}" == "--cache-dir" ]]; then
|
if [[ "${1:-}" == "--cache-dir" ]]; then
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ set -euo pipefail
|
|||||||
|
|
||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
# LinuxCNC source basis: manifest validation checks LinuxCNC-root relative
|
||||||
|
# sources and completeness against src/emc/rs274ngc, src/emc/kinematics,
|
||||||
|
# src/libnml/posemath, and configs/sim without interpreting CNC behavior.
|
||||||
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
|
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
|
||||||
manifest=${1:-}
|
manifest=${1:-}
|
||||||
require_complete=${2:-}
|
require_complete=${2:-}
|
||||||
@@ -458,6 +461,34 @@ if [[ -n "$manifest" && "$require_complete" == "--require-kinematics-complete" ]
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
generated_component_manpage_sources=()
|
||||||
|
for source in "${manifest_generated_sources[@]}"; do
|
||||||
|
case "$source" in
|
||||||
|
src/objects/hal/components/*.c)
|
||||||
|
component_name=$(basename "$source" .c)
|
||||||
|
manpage_source="src/objects/man/man9/$component_name.9.adoc"
|
||||||
|
if [[ -f "$linuxcnc_root/$manpage_source" ]]; then
|
||||||
|
generated_component_manpage_sources+=("$manpage_source")
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
mapfile -t generated_component_manpage_sources < <(
|
||||||
|
printf '%s\n' "${generated_component_manpage_sources[@]}" | sed '/^$/d' | sort -u
|
||||||
|
)
|
||||||
|
|
||||||
|
missing_generated_component_manpage_sources=()
|
||||||
|
for source in "${generated_component_manpage_sources[@]}"; do
|
||||||
|
if ! printf '%s\n' "${manifest_metadata_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
|
||||||
|
missing_generated_component_manpage_sources+=("$source")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if ((${#missing_generated_component_manpage_sources[@]} > 0)); then
|
||||||
|
echo "manifest does not cover all LinuxCNC generated component manpage sources adjacent to generated kinematics sources: $manifest" >&2
|
||||||
|
printf 'missing generated component manpage source: %s\n' "${missing_generated_component_manpage_sources[@]}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
missing_m428_m430_ini_remap_sources=()
|
missing_m428_m430_ini_remap_sources=()
|
||||||
unresolved_m428_m430_ini_remaps=()
|
unresolved_m428_m430_ini_remaps=()
|
||||||
for source in "${manifest_m428_m430_ini_sources[@]}"; do
|
for source in "${manifest_m428_m430_ini_sources[@]}"; do
|
||||||
@@ -868,6 +899,57 @@ if [[ -n "$manifest" && "$require_complete" == "--require-kinematics-complete" ]
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extract_waited_loadusr_programs() {
|
||||||
|
# Source basis: LinuxCNC src/hal/utils/halcmd_commands.cc
|
||||||
|
# do_loadusr_cmd() parses loadusr options with getopt("+wWin:"),
|
||||||
|
# and its help text plus src/hal/utils/halcmd_completion.c list both
|
||||||
|
# "-W" and "-Wn" wait forms.
|
||||||
|
awk '
|
||||||
|
function emit_waited_program(fields, field_count, start_index, i, token, waiting) {
|
||||||
|
waiting = 0
|
||||||
|
for (i = start_index + 1; i <= field_count; i++) {
|
||||||
|
token = fields[i]
|
||||||
|
if (token == "-W") {
|
||||||
|
waiting = 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (token == "-Wn") {
|
||||||
|
waiting = 1
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (token == "-n") {
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (token ~ /^-Wn.+/) {
|
||||||
|
waiting = 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (token ~ /^-n.+/) continue
|
||||||
|
if (token ~ /^-[wWi]+$/) {
|
||||||
|
if (token ~ /W/) waiting = 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (token ~ /^-/) continue
|
||||||
|
if (waiting && token != "") print token
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
line = $0
|
||||||
|
sub(/[[:space:]]*[#;].*$/, "", line)
|
||||||
|
field_count = split(line, fields, /[[:space:]]+/)
|
||||||
|
for (i = 1; i <= field_count; i++) {
|
||||||
|
if (fields[i] == "loadusr") {
|
||||||
|
emit_waited_program(fields, field_count, i)
|
||||||
|
next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
' "$1"
|
||||||
|
}
|
||||||
|
|
||||||
m428_m430_vismach_loadusr_sources=()
|
m428_m430_vismach_loadusr_sources=()
|
||||||
for source in "${manifest_m428_m430_ini_sources[@]}"; do
|
for source in "${manifest_m428_m430_ini_sources[@]}"; do
|
||||||
while IFS= read -r command_name; do
|
while IFS= read -r command_name; do
|
||||||
@@ -875,20 +957,7 @@ if [[ -n "$manifest" && "$require_complete" == "--require-kinematics-complete" ]
|
|||||||
while IFS= read -r vismach_source; do
|
while IFS= read -r vismach_source; do
|
||||||
[[ -n "$vismach_source" ]] && m428_m430_vismach_loadusr_sources+=("$vismach_source")
|
[[ -n "$vismach_source" ]] && m428_m430_vismach_loadusr_sources+=("$vismach_source")
|
||||||
done < <(resolve_vismach_loadusr_source "$command_name")
|
done < <(resolve_vismach_loadusr_source "$command_name")
|
||||||
done < <(
|
done < <(extract_waited_loadusr_programs "$linuxcnc_root/$source")
|
||||||
awk '
|
|
||||||
BEGIN { IGNORECASE = 1 }
|
|
||||||
/^[[:space:]]*HALCMD[[:space:]]*=/ && /loadusr[[:space:]]/ {
|
|
||||||
for (i = 1; i <= NF; i++) {
|
|
||||||
if ($i == "-W" && (i + 1) <= NF) {
|
|
||||||
value = $(i + 1)
|
|
||||||
sub(/[[:space:]#;].*/, "", value)
|
|
||||||
if (value != "") print value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
' "$linuxcnc_root/$source"
|
|
||||||
)
|
|
||||||
done
|
done
|
||||||
for source in "${m428_m430_config_hal_sources[@]}"; do
|
for source in "${m428_m430_config_hal_sources[@]}"; do
|
||||||
while IFS= read -r command_name; do
|
while IFS= read -r command_name; do
|
||||||
@@ -896,19 +965,7 @@ if [[ -n "$manifest" && "$require_complete" == "--require-kinematics-complete" ]
|
|||||||
while IFS= read -r vismach_source; do
|
while IFS= read -r vismach_source; do
|
||||||
[[ -n "$vismach_source" ]] && m428_m430_vismach_loadusr_sources+=("$vismach_source")
|
[[ -n "$vismach_source" ]] && m428_m430_vismach_loadusr_sources+=("$vismach_source")
|
||||||
done < <(resolve_vismach_loadusr_source "$command_name")
|
done < <(resolve_vismach_loadusr_source "$command_name")
|
||||||
done < <(
|
done < <(extract_waited_loadusr_programs "$linuxcnc_root/$source")
|
||||||
awk '
|
|
||||||
/^[[:space:]]*loadusr[[:space:]]/ {
|
|
||||||
for (i = 1; i <= NF; i++) {
|
|
||||||
if ($i == "-W" && (i + 1) <= NF) {
|
|
||||||
value = $(i + 1)
|
|
||||||
sub(/[[:space:]#;].*/, "", value)
|
|
||||||
if (value != "") print value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
' "$linuxcnc_root/$source"
|
|
||||||
)
|
|
||||||
done
|
done
|
||||||
mapfile -t m428_m430_vismach_loadusr_sources < <(
|
mapfile -t m428_m430_vismach_loadusr_sources < <(
|
||||||
printf '%s\n' "${m428_m430_vismach_loadusr_sources[@]}" | sed '/^$/d' | sort -u
|
printf '%s\n' "${m428_m430_vismach_loadusr_sources[@]}" | sed '/^$/d' | sort -u
|
||||||
@@ -1131,6 +1188,37 @@ if [[ -n "$manifest" && "$require_complete" == "--require-kinematics-complete" ]
|
|||||||
printf '%s\n' "${unresolved_m428_m430_config_tooldata_sources[@]}" | sed '/^$/d' | sort -u
|
printf '%s\n' "${unresolved_m428_m430_config_tooldata_sources[@]}" | sed '/^$/d' | sort -u
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Source basis: these LinuxCNC M428/M429/M430 INI files name TOOL_TABLE
|
||||||
|
# entries that are absent from the current LinuxCNC source tree. Keep the
|
||||||
|
# unresolved set explicit so new missing tool tables do not pass silently.
|
||||||
|
expected_unresolved_m428_m430_config_tooldata_sources=(
|
||||||
|
"configs/sim/axis/vismach/hexapod-sim/hexapod.ini TOOL_TABLE=hexapod.tbl"
|
||||||
|
"configs/sim/axis/vismach/scara/scara.ini TOOL_TABLE=scara.tbl"
|
||||||
|
"configs/sim/qtvcp_screens/non-trivial/scara/scara.ini TOOL_TABLE=scara.tbl"
|
||||||
|
)
|
||||||
|
unexpected_unresolved_m428_m430_config_tooldata_sources=()
|
||||||
|
for source in "${unresolved_m428_m430_config_tooldata_sources[@]}"; do
|
||||||
|
if ! printf '%s\n' "${expected_unresolved_m428_m430_config_tooldata_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
|
||||||
|
unexpected_unresolved_m428_m430_config_tooldata_sources+=("$source")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
missing_expected_unresolved_m428_m430_config_tooldata_sources=()
|
||||||
|
for source in "${expected_unresolved_m428_m430_config_tooldata_sources[@]}"; do
|
||||||
|
if ! printf '%s\n' "${unresolved_m428_m430_config_tooldata_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
|
||||||
|
missing_expected_unresolved_m428_m430_config_tooldata_sources+=("$source")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if ((${#unexpected_unresolved_m428_m430_config_tooldata_sources[@]} > 0)); then
|
||||||
|
echo "LinuxCNC M428/M429/M430 INI TOOL_TABLE files have new unresolved sources: $manifest" >&2
|
||||||
|
printf 'unexpected unresolved M428/M429/M430 tool table source: %s\n' "${unexpected_unresolved_m428_m430_config_tooldata_sources[@]}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ((${#missing_expected_unresolved_m428_m430_config_tooldata_sources[@]} > 0)); then
|
||||||
|
echo "LinuxCNC M428/M429/M430 INI TOOL_TABLE unresolved source set changed: $manifest" >&2
|
||||||
|
printf 'missing expected unresolved M428/M429/M430 tool table source: %s\n' "${missing_expected_unresolved_m428_m430_config_tooldata_sources[@]}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
missing_m428_m430_config_tooldata_sources=()
|
missing_m428_m430_config_tooldata_sources=()
|
||||||
for source in "${m428_m430_config_tooldata_sources[@]}"; do
|
for source in "${m428_m430_config_tooldata_sources[@]}"; do
|
||||||
if ! printf '%s\n' "${manifest_tooldata_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
|
if ! printf '%s\n' "${manifest_tooldata_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
|
||||||
|
|||||||
97
check-linuxcnc-kinematics-source-map.sh
Executable file
97
check-linuxcnc-kinematics-source-map.sh
Executable file
@@ -0,0 +1,97 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
# LinuxCNC source basis: this guard keeps the kinematics source map aligned
|
||||||
|
# with linuxcnc-kinematics-source-files.txt, which enumerates LinuxCNC
|
||||||
|
# kinematics, posemath, switchkins INI/HAL/remap/tooldata/assets, and adjacent
|
||||||
|
# build metadata. It validates source coverage documentation 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 'GENSERKINSSRCS := emc/kinematics/ugenserkins.c' \
|
||||||
|
"$linuxcnc_root/src/emc/kinematics/Submakefile" >/dev/null
|
||||||
|
grep -F 'extern int kinematicsInverse(const struct EmcPose * world,' \
|
||||||
|
"$linuxcnc_root/src/emc/kinematics/kinematics.h" >/dev/null
|
||||||
|
grep -F 'extern int kinematicsSwitch(int switchkins_type);' \
|
||||||
|
"$linuxcnc_root/src/emc/kinematics/kinematics.h" >/dev/null
|
||||||
|
grep -F 'motion.switchkins-type' \
|
||||||
|
"$linuxcnc_root/configs/sim/axis/vismach/5axis/bridgemill/5axis.ini" >/dev/null
|
||||||
|
grep -F '#<kinstype>' \
|
||||||
|
"$linuxcnc_root/configs/sim/axis/vismach/5axis/bridgemill/remap_subs/428remap.ngc" >/dev/null
|
||||||
|
|
||||||
|
python3 - <<'PY'
|
||||||
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
|
||||||
|
manifest = Path("linuxcnc-kinematics-source-files.txt").read_text(encoding="utf-8").splitlines()
|
||||||
|
source_map = Path("docs/linuxcnc-kinematics-source-map.md").read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
expected_groups = [
|
||||||
|
"core",
|
||||||
|
"config",
|
||||||
|
"remap",
|
||||||
|
"component",
|
||||||
|
"generated",
|
||||||
|
"header",
|
||||||
|
"metadata",
|
||||||
|
"asset",
|
||||||
|
"tooldata",
|
||||||
|
]
|
||||||
|
counts = {group: 0 for group in expected_groups}
|
||||||
|
for line in manifest:
|
||||||
|
if not line or line.startswith("#"):
|
||||||
|
continue
|
||||||
|
group = line.split(":", 1)[0]
|
||||||
|
if group not in counts:
|
||||||
|
raise SystemExit(f"unexpected kinematics manifest group in source map guard: {group}")
|
||||||
|
counts[group] += 1
|
||||||
|
|
||||||
|
for group, count in counts.items():
|
||||||
|
pattern = rf"\| `{re.escape(group)}` \| {count} \|"
|
||||||
|
if not re.search(pattern, source_map):
|
||||||
|
raise SystemExit(
|
||||||
|
f"docs/linuxcnc-kinematics-source-map.md {group} count no longer matches linuxcnc-kinematics-source-files.txt"
|
||||||
|
)
|
||||||
|
|
||||||
|
for group in expected_groups:
|
||||||
|
if f"`{group}`" not in source_map:
|
||||||
|
raise SystemExit(
|
||||||
|
f"docs/linuxcnc-kinematics-source-map.md is missing kinematics manifest group {group}"
|
||||||
|
)
|
||||||
|
|
||||||
|
required_phrases = [
|
||||||
|
"LinuxCNC Source Anchors",
|
||||||
|
"must not add CNC behavior",
|
||||||
|
"must not",
|
||||||
|
"expand the temporary smoke parser",
|
||||||
|
"`src/emc/kinematics/Submakefile`",
|
||||||
|
"`src/emc/kinematics/kinematics.h`",
|
||||||
|
"motion.switchkins-type",
|
||||||
|
"`#<kinstype>` remap assignment",
|
||||||
|
"not browser API surface",
|
||||||
|
"OPFS-only",
|
||||||
|
"derive M428/M429/M430 cases from",
|
||||||
|
"RTCP and five-axis coverage",
|
||||||
|
"./check-linuxcnc-kinematics-source-map.sh",
|
||||||
|
"./check-linuxcnc-inputs.sh linuxcnc-kinematics-source-files.txt --require-kinematics-complete",
|
||||||
|
"./check-linuxcnc-switchkins-remap-table.sh linuxcnc-kinematics-source-files.txt",
|
||||||
|
"does not interpret G-code",
|
||||||
|
]
|
||||||
|
for phrase in required_phrases:
|
||||||
|
if phrase not in source_map:
|
||||||
|
raise SystemExit(
|
||||||
|
f"docs/linuxcnc-kinematics-source-map.md is missing source-coverage 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-kinematics-source-map.sh" not in text:
|
||||||
|
raise SystemExit(f"{entry} must run ./check-linuxcnc-kinematics-source-map.sh")
|
||||||
|
PY
|
||||||
182
check-linuxcnc-opfs-source-map.sh
Executable file
182
check-linuxcnc-opfs-source-map.sh
Executable file
@@ -0,0 +1,182 @@
|
|||||||
|
#!/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
|
||||||
102
check-linuxcnc-rs274-source-map.sh
Executable file
102
check-linuxcnc-rs274-source-map.sh
Executable file
@@ -0,0 +1,102 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
# LinuxCNC source basis: this guard keeps the local source map aligned with the
|
||||||
|
# LinuxCNC-root relative rs274 manifests that drive native and WASM source-link
|
||||||
|
# probes. It validates documentation/manifest drift only; it does not add CNC
|
||||||
|
# behavior, expand smoke parsing, or interpret G-code.
|
||||||
|
python3 - <<'PY'
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
source_map = Path("docs/linuxcnc-rs274-source-map.md").read_text(encoding="utf-8")
|
||||||
|
native_manifest = Path("linuxcnc-rs274-source-files.txt").read_text(encoding="utf-8").splitlines()
|
||||||
|
wasm_manifest = Path("linuxcnc-rs274-wasm-source-files.txt").read_text(encoding="utf-8").splitlines()
|
||||||
|
|
||||||
|
expected = {
|
||||||
|
"### Interpreter core": [
|
||||||
|
Path(line.split(":", 2)[1]).name
|
||||||
|
for line in native_manifest
|
||||||
|
if line.startswith("core:")
|
||||||
|
],
|
||||||
|
"### Python binding modules": [
|
||||||
|
Path(line.split(":", 2)[1]).name
|
||||||
|
for line in native_manifest
|
||||||
|
if line.startswith("binding:")
|
||||||
|
],
|
||||||
|
"### WASM-safe source core": [
|
||||||
|
line.split(":", 2)[1]
|
||||||
|
for line in wasm_manifest
|
||||||
|
if line.startswith("core:")
|
||||||
|
],
|
||||||
|
"### WASM-blocked sources": [
|
||||||
|
line.split(":", 2)[1]
|
||||||
|
for line in wasm_manifest
|
||||||
|
if line.startswith("blocked:")
|
||||||
|
],
|
||||||
|
"### WASM-tracked headers": [
|
||||||
|
line.split(":", 2)[1]
|
||||||
|
for line in wasm_manifest
|
||||||
|
if line.startswith("header:")
|
||||||
|
],
|
||||||
|
"### WASM-blocked headers": [
|
||||||
|
line.split(":", 2)[1]
|
||||||
|
for line in wasm_manifest
|
||||||
|
if line.startswith("blocked-header:")
|
||||||
|
],
|
||||||
|
"### WASM metadata sources": [
|
||||||
|
line.split(":", 2)[1]
|
||||||
|
for line in wasm_manifest
|
||||||
|
if line.startswith("metadata:")
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
actual = {section: [] for section in expected}
|
||||||
|
active_section = None
|
||||||
|
for line in source_map.splitlines():
|
||||||
|
if line in expected:
|
||||||
|
active_section = line
|
||||||
|
continue
|
||||||
|
if active_section and line.startswith("### "):
|
||||||
|
active_section = None
|
||||||
|
if active_section and line.startswith("- `"):
|
||||||
|
actual[active_section].append(line.split("`", 2)[1])
|
||||||
|
|
||||||
|
labels = {
|
||||||
|
"### Interpreter core": "interpreter core",
|
||||||
|
"### Python binding modules": "Python binding module",
|
||||||
|
"### WASM-safe source core": "WASM-safe source core",
|
||||||
|
"### WASM-blocked sources": "WASM-blocked source",
|
||||||
|
"### WASM-tracked headers": "WASM-tracked header",
|
||||||
|
"### WASM-blocked headers": "WASM-blocked header",
|
||||||
|
"### WASM metadata sources": "WASM metadata source",
|
||||||
|
}
|
||||||
|
for section, expected_entries in expected.items():
|
||||||
|
if actual[section] != expected_entries:
|
||||||
|
raise SystemExit(
|
||||||
|
f"docs/linuxcnc-rs274-source-map.md {labels[section]} list no longer matches source manifest"
|
||||||
|
)
|
||||||
|
|
||||||
|
for phrase in [
|
||||||
|
"must not add CNC behavior",
|
||||||
|
"must not expand the temporary smoke parser",
|
||||||
|
"source coverage only",
|
||||||
|
"must not become browser API fields",
|
||||||
|
"temporary smoke parser fallback behavior",
|
||||||
|
"Browser-hostile dependencies must remain tracked blockers",
|
||||||
|
"./check-linuxcnc-rs274-source-map.sh",
|
||||||
|
"does not interpret G-code",
|
||||||
|
]:
|
||||||
|
if phrase not in source_map:
|
||||||
|
raise SystemExit(f"docs/linuxcnc-rs274-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-rs274-source-map.sh" not in text:
|
||||||
|
raise SystemExit(f"{entry} must run ./check-linuxcnc-rs274-source-map.sh")
|
||||||
|
PY
|
||||||
107
check-linuxcnc-switchkins-remap-source-map.sh
Executable file
107
check-linuxcnc-switchkins-remap-source-map.sh
Executable file
@@ -0,0 +1,107 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
# LinuxCNC source basis: this guard ties generated switchkins/remap aliases and
|
||||||
|
# config cases to LinuxCNC configs/sim INI REMAP entries, remap_subs
|
||||||
|
# #<kinstype> assignments, and halcmd loadusr parsing sources. It validates
|
||||||
|
# generator/table 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 'LinuxCNC source basis: generated switchkins/remap tables are extracted from' \
|
||||||
|
generate-linuxcnc-switchkins-remap-table.sh >/dev/null
|
||||||
|
grep -F 'src/hal/utils/halcmd_commands.cc do_loadusr_cmd()' \
|
||||||
|
generate-linuxcnc-switchkins-remap-table.sh >/dev/null
|
||||||
|
grep -F 'getopt("+wWin:")' generate-linuxcnc-switchkins-remap-table.sh >/dev/null
|
||||||
|
grep -F 'src/hal/utils/halcmd_completion.c lists the common -W/-Wn/-w/-iw forms' \
|
||||||
|
generate-linuxcnc-switchkins-remap-table.sh >/dev/null
|
||||||
|
grep -F 'LinuxCNC TOOL_TABLE source is not listed as tooldata' \
|
||||||
|
generate-linuxcnc-switchkins-remap-table.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 'validate_tool_table_source_coverage_only' \
|
||||||
|
check-linuxcnc-switchkins-remap-table.sh >/dev/null
|
||||||
|
grep -F 'exposes TOOL_TABLE through switchkins API/web bridge' \
|
||||||
|
check-linuxcnc-switchkins-remap-table.sh >/dev/null
|
||||||
|
|
||||||
|
grep -F 'int do_loadusr_cmd(const char *args[])' "$linuxcnc_root/src/hal/utils/halcmd_commands.cc" >/dev/null
|
||||||
|
grep -F 'getopt(argc,' "$linuxcnc_root/src/hal/utils/halcmd_commands.cc" >/dev/null
|
||||||
|
grep -F '"+wWin:"' "$linuxcnc_root/src/hal/utils/halcmd_commands.cc" >/dev/null
|
||||||
|
grep -F -- '-Wn' "$linuxcnc_root/src/hal/utils/halcmd_completion.c" >/dev/null
|
||||||
|
|
||||||
|
python3 - "$linuxcnc_root" <<'PY'
|
||||||
|
from pathlib import Path
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
linuxcnc_root = Path(sys.argv[1])
|
||||||
|
source_map = Path("docs/linuxcnc-switchkins-remap-source-map.md").read_text(encoding="utf-8")
|
||||||
|
table = Path("core/src/linuxcnc_switchkins_remap_table.inc").read_text(encoding="utf-8")
|
||||||
|
cpp_cases = Path("core/tests/linuxcnc_switchkins_remap_config_cases.inc").read_text(encoding="utf-8")
|
||||||
|
json_cases = json.loads(Path("web/public/linuxcnc_switchkins_remap_config_cases.json").read_text(encoding="utf-8"))
|
||||||
|
|
||||||
|
counts = {
|
||||||
|
"core/src/linuxcnc_switchkins_remap_table.inc": len(re.findall(r'^\s*\{"', table, re.M)),
|
||||||
|
"core/tests/linuxcnc_switchkins_remap_config_cases.inc": len(re.findall(r'^\s*\{"', cpp_cases, re.M)),
|
||||||
|
"web/public/linuxcnc_switchkins_remap_config_cases.json": len(json_cases),
|
||||||
|
}
|
||||||
|
for path, count in counts.items():
|
||||||
|
if f"| `{path}` | {count} |" not in source_map:
|
||||||
|
raise SystemExit(f"docs/linuxcnc-switchkins-remap-source-map.md count mismatch for {path}")
|
||||||
|
|
||||||
|
remap_sources = [
|
||||||
|
path
|
||||||
|
for path in (linuxcnc_root / "configs/sim").rglob("*remap.ngc")
|
||||||
|
if path.name in {"428remap.ngc", "429remap.ngc", "430remap.ngc"}
|
||||||
|
]
|
||||||
|
remap_ini_re = re.compile(r"REMAP\s*=\s*M(428|429|430)(?:\D|$)", re.I)
|
||||||
|
remap_inis = [
|
||||||
|
path
|
||||||
|
for path in (linuxcnc_root / "configs/sim").rglob("*.ini")
|
||||||
|
if remap_ini_re.search(path.read_text(encoding="utf-8", errors="ignore"))
|
||||||
|
]
|
||||||
|
if f"| LinuxCNC M428/M429/M430 remap sources | {len(remap_sources)} |" not in source_map:
|
||||||
|
raise SystemExit("docs/linuxcnc-switchkins-remap-source-map.md remap source count mismatch")
|
||||||
|
if f"| LinuxCNC switchkins REMAP INI sources | {len(remap_inis)} |" not in source_map:
|
||||||
|
raise SystemExit("docs/linuxcnc-switchkins-remap-source-map.md REMAP INI source count mismatch")
|
||||||
|
|
||||||
|
required_phrases = [
|
||||||
|
"must not add CNC behavior",
|
||||||
|
"must not expand the temporary smoke parser",
|
||||||
|
"`#<kinstype>` assignments",
|
||||||
|
"[EMCIO] TOOL_TABLE` entries are source coverage only",
|
||||||
|
"must not become generated switchkins aliases",
|
||||||
|
"./check-linuxcnc-switchkins-remap-source-map.sh",
|
||||||
|
"./check-linuxcnc-switchkins-remap-table.sh linuxcnc-kinematics-source-files.txt",
|
||||||
|
"does not interpret G-code",
|
||||||
|
]
|
||||||
|
for phrase in required_phrases:
|
||||||
|
if phrase not in source_map:
|
||||||
|
raise SystemExit(
|
||||||
|
f"docs/linuxcnc-switchkins-remap-source-map.md is 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-switchkins-remap-source-map.sh" not in text:
|
||||||
|
raise SystemExit(f"{entry} must run ./check-linuxcnc-switchkins-remap-source-map.sh")
|
||||||
|
|
||||||
|
case_fields = {case["field"] for case in json_cases}
|
||||||
|
source_only_field_re = re.compile(r"asset|metadata|tool|tooldata|tool_table|TOOL_TABLE", re.I)
|
||||||
|
bad_fields = [field for field in case_fields if source_only_field_re.search(field)]
|
||||||
|
if bad_fields:
|
||||||
|
raise SystemExit(f"generated switchkins config cases expose source-only fields: {bad_fields}")
|
||||||
|
api_surface_re = re.compile(r'\b(?:asset|metadata|toolData|tooldata|toolTable|tooltable|tool_table|TOOL_TABLE)\b')
|
||||||
|
for api_path in ["web/src/wasm-core.js", "web/src/index.ts", "core/include/cnc_sim_api.h"]:
|
||||||
|
if api_surface_re.search(Path(api_path).read_text(encoding="utf-8")):
|
||||||
|
raise SystemExit(f"{api_path} exposes source-only switchkins inputs through API surface")
|
||||||
|
PY
|
||||||
@@ -3,6 +3,9 @@ set -euo pipefail
|
|||||||
|
|
||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
# LinuxCNC source basis: cached switchkins/remap validation hashes the LinuxCNC
|
||||||
|
# kinematics manifest, referenced configs/remaps, generator script, and bridge
|
||||||
|
# outputs before reusing table-check results.
|
||||||
cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-}
|
cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-}
|
||||||
check_args=("$@")
|
check_args=("$@")
|
||||||
if [[ "${1:-}" == "--cache-dir" ]]; then
|
if [[ "${1:-}" == "--cache-dir" ]]; then
|
||||||
@@ -89,6 +92,10 @@ if ! {
|
|||||||
sha256sum check-linuxcnc-switchkins-remap-table-cached.sh
|
sha256sum check-linuxcnc-switchkins-remap-table-cached.sh
|
||||||
printf 'CHECK_SCRIPT='
|
printf 'CHECK_SCRIPT='
|
||||||
sha256sum check-linuxcnc-switchkins-remap-table.sh
|
sha256sum check-linuxcnc-switchkins-remap-table.sh
|
||||||
|
printf 'INPUT_CACHED_SCRIPT='
|
||||||
|
sha256sum check-linuxcnc-inputs-cached.sh
|
||||||
|
printf 'INPUT_CHECK_SCRIPT='
|
||||||
|
sha256sum check-linuxcnc-inputs.sh
|
||||||
printf 'GENERATOR_SCRIPT='
|
printf 'GENERATOR_SCRIPT='
|
||||||
sha256sum generate-linuxcnc-switchkins-remap-table.sh
|
sha256sum generate-linuxcnc-switchkins-remap-table.sh
|
||||||
printf 'LINUXCNC_ROOT=%s\n' "$linuxcnc_root"
|
printf 'LINUXCNC_ROOT=%s\n' "$linuxcnc_root"
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ set -euo pipefail
|
|||||||
|
|
||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
# LinuxCNC source basis: switchkins/remap table validation derives M428/M429/M430
|
||||||
|
# cases from LinuxCNC configs/sim INI/HAL files, remap_subs/*.ngc #<kinstype>
|
||||||
|
# assignments, and src/emc/kinematics switchkins sources.
|
||||||
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
|
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
|
||||||
manifest=${1:-linuxcnc-kinematics-source-files.txt}
|
manifest=${1:-linuxcnc-kinematics-source-files.txt}
|
||||||
|
|
||||||
@@ -32,6 +35,149 @@ if [[ ! -f "$manifest" ]]; then
|
|||||||
echo "missing kinematics manifest: $manifest" >&2
|
echo "missing kinematics manifest: $manifest" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh "$manifest" --require-kinematics-complete
|
||||||
|
|
||||||
|
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() {
|
validate_manifest_remap_kinstypes() {
|
||||||
local manifest_file=$1
|
local manifest_file=$1
|
||||||
@@ -210,28 +356,64 @@ validate_generator_cli_contract() {
|
|||||||
local cli_dir
|
local cli_dir
|
||||||
local cli_manifest
|
local cli_manifest
|
||||||
local cli_manifest_with_config_ngc
|
local cli_manifest_with_config_ngc
|
||||||
|
local cli_manifest_with_config_tbl
|
||||||
local cli_manifest_with_duplicate_ini
|
local cli_manifest_with_duplicate_ini
|
||||||
local cli_manifest_with_duplicate_remap
|
local cli_manifest_with_duplicate_remap
|
||||||
|
local cli_manifest_with_duplicate_tooldata
|
||||||
local cli_manifest_with_unused_hal
|
local cli_manifest_with_unused_hal
|
||||||
local cli_manifest_with_unused_postgui
|
local cli_manifest_with_unused_postgui
|
||||||
local cli_manifest_with_unused_remap
|
local cli_manifest_with_unused_remap
|
||||||
|
local cli_manifest_with_unused_tooldata
|
||||||
|
local cli_manifest_without_halfile_loadusr_asset
|
||||||
|
local cli_melfa_manifest
|
||||||
|
local cli_melfa_without_halcmd_loadusr_asset
|
||||||
|
local cli_trsrn_manifest
|
||||||
|
local cli_trsrn_without_g531_asset
|
||||||
|
local cli_trsrn_without_g69_asset
|
||||||
|
local cli_trsrn_without_halcmd_asset
|
||||||
|
local cli_trsrn_without_halcmd_import_asset
|
||||||
|
local cli_trsrn_without_on_abort_asset
|
||||||
|
local cli_trsrn_without_python_import_asset
|
||||||
|
local cli_trsrn_without_python_toplevel_asset
|
||||||
local cli_manifest_without_ini
|
local cli_manifest_without_ini
|
||||||
local cli_manifest_without_hal
|
local cli_manifest_without_hal
|
||||||
local cli_manifest_without_postgui
|
local cli_manifest_without_postgui
|
||||||
local cli_manifest_without_remap
|
local cli_manifest_without_remap
|
||||||
|
local cli_manifest_without_tooldata
|
||||||
|
local cli_output_root_log
|
||||||
|
local cli_output_file_log
|
||||||
|
local cli_output_whitespace_log
|
||||||
cli_dir=$(mktemp -d "${TMPDIR:-/tmp}/linuxcnc_switchkins_remap_cli.XXXXXX")
|
cli_dir=$(mktemp -d "${TMPDIR:-/tmp}/linuxcnc_switchkins_remap_cli.XXXXXX")
|
||||||
trap 'rm -rf "$cli_dir"' EXIT
|
trap 'rm -rf "$cli_dir"' EXIT
|
||||||
cli_manifest="$cli_dir/bridgemill-manifest.txt"
|
cli_manifest="$cli_dir/bridgemill-manifest.txt"
|
||||||
cli_manifest_with_config_ngc="$cli_dir/bridgemill-manifest-with-config-ngc.txt"
|
cli_manifest_with_config_ngc="$cli_dir/bridgemill-manifest-with-config-ngc.txt"
|
||||||
|
cli_manifest_with_config_tbl="$cli_dir/bridgemill-manifest-with-config-tbl.txt"
|
||||||
cli_manifest_with_duplicate_ini="$cli_dir/bridgemill-manifest-with-duplicate-ini.txt"
|
cli_manifest_with_duplicate_ini="$cli_dir/bridgemill-manifest-with-duplicate-ini.txt"
|
||||||
cli_manifest_with_duplicate_remap="$cli_dir/bridgemill-manifest-with-duplicate-remap.txt"
|
cli_manifest_with_duplicate_remap="$cli_dir/bridgemill-manifest-with-duplicate-remap.txt"
|
||||||
|
cli_manifest_with_duplicate_tooldata="$cli_dir/bridgemill-manifest-with-duplicate-tooldata.txt"
|
||||||
cli_manifest_with_unused_hal="$cli_dir/bridgemill-manifest-with-unused-hal.txt"
|
cli_manifest_with_unused_hal="$cli_dir/bridgemill-manifest-with-unused-hal.txt"
|
||||||
cli_manifest_with_unused_postgui="$cli_dir/bridgemill-manifest-with-unused-postgui.txt"
|
cli_manifest_with_unused_postgui="$cli_dir/bridgemill-manifest-with-unused-postgui.txt"
|
||||||
cli_manifest_with_unused_remap="$cli_dir/bridgemill-manifest-with-unused-remap.txt"
|
cli_manifest_with_unused_remap="$cli_dir/bridgemill-manifest-with-unused-remap.txt"
|
||||||
|
cli_manifest_with_unused_tooldata="$cli_dir/bridgemill-manifest-with-unused-tooldata.txt"
|
||||||
|
cli_manifest_without_halfile_loadusr_asset="$cli_dir/bridgemill-manifest-without-halfile-loadusr-asset.txt"
|
||||||
|
cli_melfa_manifest="$cli_dir/melfa-manifest.txt"
|
||||||
|
cli_melfa_without_halcmd_loadusr_asset="$cli_dir/melfa-manifest-without-halcmd-loadusr-asset.txt"
|
||||||
|
cli_trsrn_manifest="$cli_dir/trsrn-manifest.txt"
|
||||||
|
cli_trsrn_without_g531_asset="$cli_dir/trsrn-manifest-without-g531-asset.txt"
|
||||||
|
cli_trsrn_without_g69_asset="$cli_dir/trsrn-manifest-without-g69-asset.txt"
|
||||||
|
cli_trsrn_without_halcmd_asset="$cli_dir/trsrn-manifest-without-halcmd-asset.txt"
|
||||||
|
cli_trsrn_without_halcmd_import_asset="$cli_dir/trsrn-manifest-without-halcmd-import-asset.txt"
|
||||||
|
cli_trsrn_without_on_abort_asset="$cli_dir/trsrn-manifest-without-on-abort-asset.txt"
|
||||||
|
cli_trsrn_without_python_import_asset="$cli_dir/trsrn-manifest-without-python-import-asset.txt"
|
||||||
|
cli_trsrn_without_python_toplevel_asset="$cli_dir/trsrn-manifest-without-python-toplevel-asset.txt"
|
||||||
cli_manifest_without_ini="$cli_dir/bridgemill-manifest-without-ini.txt"
|
cli_manifest_without_ini="$cli_dir/bridgemill-manifest-without-ini.txt"
|
||||||
cli_manifest_without_hal="$cli_dir/bridgemill-manifest-without-hal.txt"
|
cli_manifest_without_hal="$cli_dir/bridgemill-manifest-without-hal.txt"
|
||||||
cli_manifest_without_postgui="$cli_dir/bridgemill-manifest-without-postgui.txt"
|
cli_manifest_without_postgui="$cli_dir/bridgemill-manifest-without-postgui.txt"
|
||||||
cli_manifest_without_remap="$cli_dir/bridgemill-manifest-without-remap.txt"
|
cli_manifest_without_remap="$cli_dir/bridgemill-manifest-without-remap.txt"
|
||||||
|
cli_manifest_without_tooldata="$cli_dir/bridgemill-manifest-without-tooldata.txt"
|
||||||
|
cli_output_root_log="$cli_dir/output-root.log"
|
||||||
|
cli_output_file_log="$cli_dir/output-file.log"
|
||||||
|
cli_output_whitespace_log="$cli_dir/output-whitespace.log"
|
||||||
|
|
||||||
if ! awk -F: -v manifest="$manifest_file" '
|
if ! awk -F: -v manifest="$manifest_file" '
|
||||||
function require(key) {
|
function require(key) {
|
||||||
@@ -245,6 +427,8 @@ validate_generator_cli_contract() {
|
|||||||
require("remap:configs/sim/axis/vismach/5axis/bridgemill/remap_subs/428remap.ngc")
|
require("remap:configs/sim/axis/vismach/5axis/bridgemill/remap_subs/428remap.ngc")
|
||||||
require("remap:configs/sim/axis/vismach/5axis/bridgemill/remap_subs/429remap.ngc")
|
require("remap:configs/sim/axis/vismach/5axis/bridgemill/remap_subs/429remap.ngc")
|
||||||
require("remap:configs/sim/axis/vismach/5axis/bridgemill/remap_subs/430remap.ngc")
|
require("remap:configs/sim/axis/vismach/5axis/bridgemill/remap_subs/430remap.ngc")
|
||||||
|
require("tooldata:configs/sim/axis/vismach/5axis/bridgemill/5axis.tbl")
|
||||||
|
require("asset:src/hal/user_comps/vismach/5axisgui.py")
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
key = $1 ":" $2
|
key = $1 ":" $2
|
||||||
@@ -282,6 +466,15 @@ validate_generator_cli_contract() {
|
|||||||
echo "switchkins remap generator accepted LinuxCNC remap source listed as config" >&2
|
echo "switchkins remap generator accepted LinuxCNC remap source listed as config" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
cp "$cli_manifest" "$cli_manifest_with_config_tbl"
|
||||||
|
grep -F \
|
||||||
|
'tooldata:configs/sim/axis/vismach/5axis/bridgemill/5axis.tbl:' \
|
||||||
|
"$cli_manifest" | sed 's/^tooldata:/config:/' >>"$cli_manifest_with_config_tbl"
|
||||||
|
if LINUXCNC_ROOT="$linuxcnc_root" ./generate-linuxcnc-switchkins-remap-table.sh \
|
||||||
|
"$cli_manifest_with_config_tbl" >/dev/null 2>&1; then
|
||||||
|
echo "switchkins remap generator accepted LinuxCNC TOOL_TABLE source listed as config" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
cp "$cli_manifest" "$cli_manifest_with_duplicate_ini"
|
cp "$cli_manifest" "$cli_manifest_with_duplicate_ini"
|
||||||
grep -F \
|
grep -F \
|
||||||
'config:configs/sim/axis/vismach/5axis/bridgemill/5axis.ini:' \
|
'config:configs/sim/axis/vismach/5axis/bridgemill/5axis.ini:' \
|
||||||
@@ -300,6 +493,15 @@ validate_generator_cli_contract() {
|
|||||||
echo "switchkins remap generator accepted duplicate LinuxCNC M428 remap source" >&2
|
echo "switchkins remap generator accepted duplicate LinuxCNC M428 remap source" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
cp "$cli_manifest" "$cli_manifest_with_duplicate_tooldata"
|
||||||
|
grep -F \
|
||||||
|
'tooldata:configs/sim/axis/vismach/5axis/bridgemill/5axis.tbl:' \
|
||||||
|
"$cli_manifest" >>"$cli_manifest_with_duplicate_tooldata"
|
||||||
|
if LINUXCNC_ROOT="$linuxcnc_root" ./generate-linuxcnc-switchkins-remap-table.sh \
|
||||||
|
"$cli_manifest_with_duplicate_tooldata" >/dev/null 2>&1; then
|
||||||
|
echo "switchkins remap generator accepted duplicate LinuxCNC TOOL_TABLE source" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
grep -Fv \
|
grep -Fv \
|
||||||
'config:configs/sim/axis/vismach/5axis/bridgemill/5axis.ini:' \
|
'config:configs/sim/axis/vismach/5axis/bridgemill/5axis.ini:' \
|
||||||
"$cli_manifest" >"$cli_manifest_without_ini"
|
"$cli_manifest" >"$cli_manifest_without_ini"
|
||||||
@@ -332,6 +534,72 @@ validate_generator_cli_contract() {
|
|||||||
echo "switchkins remap generator accepted manifest missing LinuxCNC M430 remap source" >&2
|
echo "switchkins remap generator accepted manifest missing LinuxCNC M430 remap source" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
grep -Fv \
|
||||||
|
'tooldata:configs/sim/axis/vismach/5axis/bridgemill/5axis.tbl:' \
|
||||||
|
"$cli_manifest" >"$cli_manifest_without_tooldata"
|
||||||
|
if LINUXCNC_ROOT="$linuxcnc_root" ./generate-linuxcnc-switchkins-remap-table.sh \
|
||||||
|
"$cli_manifest_without_tooldata" >/dev/null 2>&1; then
|
||||||
|
echo "switchkins remap generator accepted manifest missing LinuxCNC TOOL_TABLE source" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
grep -Fv \
|
||||||
|
'asset:src/hal/user_comps/vismach/5axisgui.py:' \
|
||||||
|
"$cli_manifest" >"$cli_manifest_without_halfile_loadusr_asset"
|
||||||
|
if LINUXCNC_ROOT="$linuxcnc_root" ./generate-linuxcnc-switchkins-remap-table.sh \
|
||||||
|
"$cli_manifest_without_halfile_loadusr_asset" >/dev/null 2>&1; then
|
||||||
|
echo "switchkins remap generator accepted manifest missing LinuxCNC HALFILE loadusr asset source" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! awk -F: -v manifest="$manifest_file" '
|
||||||
|
function require(key) {
|
||||||
|
expected[++expected_count] = key
|
||||||
|
expected_keys[key] = 1
|
||||||
|
}
|
||||||
|
BEGIN {
|
||||||
|
require("config:configs/sim/axis/vismach/melfa-sim/melfa.ini")
|
||||||
|
require("config:configs/sim/axis/vismach/melfa-sim/melfa_dh.hal")
|
||||||
|
require("config:configs/sim/axis/vismach/melfa-sim/melfa-postgui.hal")
|
||||||
|
require("remap:configs/sim/axis/vismach/melfa-sim/remap_subs/428remap.ngc")
|
||||||
|
require("remap:configs/sim/axis/vismach/melfa-sim/remap_subs/429remap.ngc")
|
||||||
|
require("remap:configs/sim/axis/vismach/melfa-sim/remap_subs/430remap.ngc")
|
||||||
|
require("tooldata:configs/sim/axis/vismach/melfa-sim/melfa.tbl")
|
||||||
|
require("asset:src/hal/user_comps/vismach/melfagui.py")
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key = $1 ":" $2
|
||||||
|
if (key in expected_keys) {
|
||||||
|
if (key in manifest_line) {
|
||||||
|
print "duplicate melfa switchkins source in " manifest ": " key > "/dev/stderr"
|
||||||
|
duplicate = 1
|
||||||
|
}
|
||||||
|
manifest_line[key] = $0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
END {
|
||||||
|
missing = 0
|
||||||
|
for (expected_index = 1; expected_index <= expected_count; expected_index++) {
|
||||||
|
key = expected[expected_index]
|
||||||
|
if (!(key in manifest_line)) {
|
||||||
|
print "melfa switchkins source is not listed in " manifest ": " key > "/dev/stderr"
|
||||||
|
missing = 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
print manifest_line[key]
|
||||||
|
}
|
||||||
|
exit missing || duplicate
|
||||||
|
}
|
||||||
|
' "$manifest_file" >"$cli_melfa_manifest"; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
grep -Fv \
|
||||||
|
'asset:src/hal/user_comps/vismach/melfagui.py:' \
|
||||||
|
"$cli_melfa_manifest" >"$cli_melfa_without_halcmd_loadusr_asset"
|
||||||
|
if LINUXCNC_ROOT="$linuxcnc_root" ./generate-linuxcnc-switchkins-remap-table.sh \
|
||||||
|
"$cli_melfa_without_halcmd_loadusr_asset" >/dev/null 2>&1; then
|
||||||
|
echo "switchkins remap generator accepted manifest missing LinuxCNC HALCMD loadusr command asset source" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
cp "$cli_manifest" "$cli_manifest_with_unused_hal"
|
cp "$cli_manifest" "$cli_manifest_with_unused_hal"
|
||||||
grep -F \
|
grep -F \
|
||||||
'config:configs/sim/axis/vismach/melfa-sim/melfa_dh.hal:' \
|
'config:configs/sim/axis/vismach/melfa-sim/melfa_dh.hal:' \
|
||||||
@@ -359,6 +627,162 @@ validate_generator_cli_contract() {
|
|||||||
echo "switchkins remap generator accepted unused LinuxCNC M428 remap source" >&2
|
echo "switchkins remap generator accepted unused LinuxCNC M428 remap source" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
cp "$cli_manifest" "$cli_manifest_with_unused_tooldata"
|
||||||
|
grep -F \
|
||||||
|
'tooldata:configs/sim/axis/vismach/melfa-sim/melfa.tbl:' \
|
||||||
|
"$manifest_file" >>"$cli_manifest_with_unused_tooldata"
|
||||||
|
if LINUXCNC_ROOT="$linuxcnc_root" ./generate-linuxcnc-switchkins-remap-table.sh \
|
||||||
|
"$cli_manifest_with_unused_tooldata" >/dev/null 2>&1; then
|
||||||
|
echo "switchkins remap generator accepted unused LinuxCNC TOOL_TABLE source" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! awk -F: -v manifest="$manifest_file" '
|
||||||
|
function require(key) {
|
||||||
|
expected[++expected_count] = key
|
||||||
|
expected_keys[key] = 1
|
||||||
|
}
|
||||||
|
BEGIN {
|
||||||
|
require("config:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini")
|
||||||
|
require("config:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn_postgui.hal")
|
||||||
|
require("config:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn.ini")
|
||||||
|
require("config:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn_postgui.hal")
|
||||||
|
require("remap:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/428remap.ngc")
|
||||||
|
require("remap:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/429remap.ngc")
|
||||||
|
require("remap:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/430remap.ngc")
|
||||||
|
require("tooldata:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.tbl")
|
||||||
|
require("tooldata:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn.tbl")
|
||||||
|
require("asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/python/remap.py")
|
||||||
|
require("asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/python/toplevel.py")
|
||||||
|
require("asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/python/twp-helper-comp.py")
|
||||||
|
require("asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/python/util.py")
|
||||||
|
require("asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/g531remap.ngc")
|
||||||
|
require("asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/g533remap.ngc")
|
||||||
|
require("asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/g536remap.ngc")
|
||||||
|
require("asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/g69remap.ngc")
|
||||||
|
require("asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/on_abort_no_twp_reset.ngc")
|
||||||
|
require("asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/on_abort_with_twp_reset.ngc")
|
||||||
|
require("asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/vismach/twp_vismach.py")
|
||||||
|
require("asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/vismach/xyzacb-trsrn-gui.py")
|
||||||
|
require("asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/vismach/xyzbca-trsrn-gui.py")
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key = $1 ":" $2
|
||||||
|
if (key in expected_keys) {
|
||||||
|
if (key in manifest_line) {
|
||||||
|
print "duplicate TRSRN switchkins source in " manifest ": " key > "/dev/stderr"
|
||||||
|
duplicate = 1
|
||||||
|
}
|
||||||
|
manifest_line[key] = $0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
END {
|
||||||
|
missing = 0
|
||||||
|
for (expected_index = 1; expected_index <= expected_count; expected_index++) {
|
||||||
|
key = expected[expected_index]
|
||||||
|
if (!(key in manifest_line)) {
|
||||||
|
print "TRSRN switchkins source is not listed in " manifest ": " key > "/dev/stderr"
|
||||||
|
missing = 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
print manifest_line[key]
|
||||||
|
}
|
||||||
|
exit missing || duplicate
|
||||||
|
}
|
||||||
|
' "$manifest_file" >"$cli_trsrn_manifest"; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
grep -Fv \
|
||||||
|
'asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/python/toplevel.py:' \
|
||||||
|
"$cli_trsrn_manifest" >"$cli_trsrn_without_python_toplevel_asset"
|
||||||
|
if LINUXCNC_ROOT="$linuxcnc_root" ./generate-linuxcnc-switchkins-remap-table.sh \
|
||||||
|
"$cli_trsrn_without_python_toplevel_asset" >/dev/null 2>&1; then
|
||||||
|
echo "switchkins remap generator accepted manifest missing LinuxCNC PYTHON TOPLEVEL asset source" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
grep -Fv \
|
||||||
|
'asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/python/util.py:' \
|
||||||
|
"$cli_trsrn_manifest" >"$cli_trsrn_without_python_import_asset"
|
||||||
|
if LINUXCNC_ROOT="$linuxcnc_root" ./generate-linuxcnc-switchkins-remap-table.sh \
|
||||||
|
"$cli_trsrn_without_python_import_asset" >/dev/null 2>&1; then
|
||||||
|
echo "switchkins remap generator accepted manifest missing LinuxCNC Python import asset source" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
grep -Fv \
|
||||||
|
'asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/python/twp-helper-comp.py:' \
|
||||||
|
"$cli_trsrn_manifest" >"$cli_trsrn_without_halcmd_asset"
|
||||||
|
if LINUXCNC_ROOT="$linuxcnc_root" ./generate-linuxcnc-switchkins-remap-table.sh \
|
||||||
|
"$cli_trsrn_without_halcmd_asset" >/dev/null 2>&1; then
|
||||||
|
echo "switchkins remap generator accepted manifest missing LinuxCNC HALCMD loadusr asset source" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
grep -Fv \
|
||||||
|
'asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/vismach/twp_vismach.py:' \
|
||||||
|
"$cli_trsrn_manifest" >"$cli_trsrn_without_halcmd_import_asset"
|
||||||
|
if LINUXCNC_ROOT="$linuxcnc_root" ./generate-linuxcnc-switchkins-remap-table.sh \
|
||||||
|
"$cli_trsrn_without_halcmd_import_asset" >/dev/null 2>&1; then
|
||||||
|
echo "switchkins remap generator accepted manifest missing LinuxCNC HALCMD Python import asset source" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
grep -Fv \
|
||||||
|
'asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/g531remap.ngc:' \
|
||||||
|
"$cli_trsrn_manifest" >"$cli_trsrn_without_g531_asset"
|
||||||
|
if LINUXCNC_ROOT="$linuxcnc_root" ./generate-linuxcnc-switchkins-remap-table.sh \
|
||||||
|
"$cli_trsrn_without_g531_asset" >/dev/null 2>&1; then
|
||||||
|
echo "switchkins remap generator accepted manifest missing LinuxCNC G53.1 remap asset source" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
grep -Fv \
|
||||||
|
'asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/g69remap.ngc:' \
|
||||||
|
"$cli_trsrn_manifest" >"$cli_trsrn_without_g69_asset"
|
||||||
|
if LINUXCNC_ROOT="$linuxcnc_root" ./generate-linuxcnc-switchkins-remap-table.sh \
|
||||||
|
"$cli_trsrn_without_g69_asset" >/dev/null 2>&1; then
|
||||||
|
echo "switchkins remap generator accepted manifest missing LinuxCNC G69 remap asset source" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
grep -Fv \
|
||||||
|
'asset:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/on_abort_no_twp_reset.ngc:' \
|
||||||
|
"$cli_trsrn_manifest" >"$cli_trsrn_without_on_abort_asset"
|
||||||
|
if LINUXCNC_ROOT="$linuxcnc_root" ./generate-linuxcnc-switchkins-remap-table.sh \
|
||||||
|
"$cli_trsrn_without_on_abort_asset" >/dev/null 2>&1; then
|
||||||
|
echo "switchkins remap generator accepted manifest missing LinuxCNC ON_ABORT_COMMAND asset source" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if LINUXCNC_ROOT="$linuxcnc_root" ./generate-linuxcnc-switchkins-remap-table.sh \
|
||||||
|
--all-output-dir "$cli_dir/out with space" "$cli_manifest" >"$cli_output_whitespace_log" 2>&1; then
|
||||||
|
echo "switchkins remap generator accepted an output directory with whitespace" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! grep -F "switchkins remap output directory must not contain whitespace: $cli_dir/out with space" \
|
||||||
|
"$cli_output_whitespace_log" >/dev/null; then
|
||||||
|
echo "switchkins remap generator did not report whitespace output directories clearly" >&2
|
||||||
|
sed -n '1,20p' "$cli_output_whitespace_log" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if LINUXCNC_ROOT="$linuxcnc_root" ./generate-linuxcnc-switchkins-remap-table.sh \
|
||||||
|
--all-output-dir / "$cli_manifest" >"$cli_output_root_log" 2>&1; then
|
||||||
|
echo "switchkins remap generator accepted the filesystem root as output directory" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! grep -F "switchkins remap output directory must not be the filesystem root" \
|
||||||
|
"$cli_output_root_log" >/dev/null; then
|
||||||
|
echo "switchkins remap generator did not report filesystem-root output directories clearly" >&2
|
||||||
|
sed -n '1,20p' "$cli_output_root_log" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
printf 'not a directory\n' >"$cli_dir/output-file"
|
||||||
|
if LINUXCNC_ROOT="$linuxcnc_root" ./generate-linuxcnc-switchkins-remap-table.sh \
|
||||||
|
--all-output-dir "$cli_dir/output-file" "$cli_manifest" >"$cli_output_file_log" 2>&1; then
|
||||||
|
echo "switchkins remap generator accepted a file as output directory" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! grep -F "switchkins remap output path is not a directory: $cli_dir/output-file" \
|
||||||
|
"$cli_output_file_log" >/dev/null; then
|
||||||
|
echo "switchkins remap generator did not report file output paths clearly" >&2
|
||||||
|
sed -n '1,20p' "$cli_output_file_log" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
LINUXCNC_ROOT="$linuxcnc_root" ./generate-linuxcnc-switchkins-remap-table.sh --all-output-dir "$cli_dir/out" "$cli_manifest"
|
LINUXCNC_ROOT="$linuxcnc_root" ./generate-linuxcnc-switchkins-remap-table.sh --all-output-dir "$cli_dir/out" "$cli_manifest"
|
||||||
LINUXCNC_ROOT="$linuxcnc_root" ./generate-linuxcnc-switchkins-remap-table.sh "$cli_manifest" \
|
LINUXCNC_ROOT="$linuxcnc_root" ./generate-linuxcnc-switchkins-remap-table.sh "$cli_manifest" \
|
||||||
@@ -2367,6 +2791,144 @@ JS
|
|||||||
fi
|
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 \
|
for generated_file in \
|
||||||
linuxcnc_switchkins_remap_table.inc \
|
linuxcnc_switchkins_remap_table.inc \
|
||||||
linuxcnc_switchkins_remap_config_cases.inc \
|
linuxcnc_switchkins_remap_config_cases.inc \
|
||||||
@@ -2387,6 +2949,22 @@ validate_web_switchkins_case_fields \
|
|||||||
web/src/wasm-core.js \
|
web/src/wasm-core.js \
|
||||||
web/src/index.ts \
|
web/src/index.ts \
|
||||||
"generated"
|
"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_tool_table_source_coverage_only \
|
||||||
|
core/src/linuxcnc_switchkins_remap_table.inc \
|
||||||
|
core/tests/linuxcnc_switchkins_remap_config_cases.inc \
|
||||||
|
web/public/linuxcnc_switchkins_remap_config_cases.json \
|
||||||
|
core/src/cnc_sim_api.cpp \
|
||||||
|
web/src/wasm-core.js \
|
||||||
|
web/src/index.ts \
|
||||||
|
"tracked"
|
||||||
|
|
||||||
validate_generated_header \
|
validate_generated_header \
|
||||||
"$generated_dir/linuxcnc_switchkins_remap_table.inc" \
|
"$generated_dir/linuxcnc_switchkins_remap_table.inc" \
|
||||||
|
|||||||
333
check-linuxcnc-tooldata-source-map.sh
Executable file
333
check-linuxcnc-tooldata-source-map.sh
Executable file
@@ -0,0 +1,333 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
# LinuxCNC source basis: this guard ties LinuxCNC tooldata/Submakefile,
|
||||||
|
# src/emc/tooldata sources, native/WASM manifests, and wasm-safe tooldata shims
|
||||||
|
# to the tooldata source map. It validates source coverage 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 'LIBTOOLDATA_SRCS += emc/tooldata/tooldata_nml.cc' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/Submakefile" >/dev/null
|
||||||
|
grep -F 'LIBTOOLDATA_SRCS += emc/tooldata/tooldata_mmap.cc' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/Submakefile" >/dev/null
|
||||||
|
grep -F 'LIBTOOLDATA_SRCS += emc/tooldata/tooldata_common.cc' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/Submakefile" >/dev/null
|
||||||
|
grep -F 'LIBTOOLDATA_SRCS += emc/tooldata/tooldata_db.cc' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/Submakefile" >/dev/null
|
||||||
|
grep -F 'TOOL_MMAP_READ_SRCS = emc/tooldata/tool_mmap_read.cc' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/Submakefile" >/dev/null
|
||||||
|
grep -F 'TOOL_WATCH_SRCS = emc/tooldata/tool_watch.cc' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/Submakefile" >/dev/null
|
||||||
|
grep -F 'int main (int argc,char**argv)' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tool_mmap_read.cc" >/dev/null
|
||||||
|
grep -F 'if (tool_mmap_user())' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tool_mmap_read.cc" >/dev/null
|
||||||
|
grep -F 'idx_last = tooldata_last_index_get();' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tool_mmap_read.cc" >/dev/null
|
||||||
|
grep -F 'if (tooldata_get(&tdata,idx) != IDX_OK)' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tool_mmap_read.cc" >/dev/null
|
||||||
|
grep -F 'RCS_STAT_CHANNEL *stat = 0;' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tool_watch.cc" >/dev/null
|
||||||
|
grep -F 'stat = new RCS_STAT_CHANNEL(emcFormat, "emcStatus", "xemc", nmlfile);' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tool_watch.cc" >/dev/null
|
||||||
|
grep -F 'EMC_STAT *emcStatus = static_cast<EMC_STAT*>(stat->get_address());' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tool_watch.cc" >/dev/null
|
||||||
|
grep -F 'emcStatus->io.tool.pocketPrepped' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tool_watch.cc" >/dev/null
|
||||||
|
grep -F '#define TOOL_MMAP_FILENAME ".tool.mmap"' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
|
||||||
|
grep -F 'bool tool_mmap_is_random_toolchanger(void)' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
|
||||||
|
grep -F 'ans = hptr->is_random_toolchanger;' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
|
||||||
|
grep -F 'int tool_mmap_creator(EMC_TOOL_STAT const * ptr,int random_toolchanger)' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
|
||||||
|
grep -F 'static int inited=0;' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
|
||||||
|
grep -F 'if (inited) {' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
|
||||||
|
grep -F 'inited = 1;' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
|
||||||
|
grep -F 'hptr->is_random_toolchanger = random_toolchanger;' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
|
||||||
|
grep -F 'hptr->last_index = 0;' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
|
||||||
|
grep -F 'int tool_mmap_user()' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
|
||||||
|
grep -F 'fprintf(stderr,"tool_mmap_user(): tool mmap not available\n");' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
|
||||||
|
grep -F 'return(-1);' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
|
||||||
|
grep -F 'void tool_mmap_close()' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
|
||||||
|
grep -F 'if (!tool_mmap_base) { return; }' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
|
||||||
|
grep -F 'int tooldata_last_index_get(void)' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
|
||||||
|
grep -F 'tool_mmap_mutex_give(); return -1;' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
|
||||||
|
grep -F 'int tooldata_find_index_for_tool(int toolno)' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
|
||||||
|
grep -F 'if (toolno == -1) {tool_mmap_mutex_give(); return -1;}' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
|
||||||
|
grep -F 'if (!hptr->is_random_toolchanger && toolno == 0)' \
|
||||||
|
"$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
|
||||||
|
grep -F '#define MAX_DB_PROGRAM_ARGS 10' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tooldata_db.cc" >/dev/null
|
||||||
|
grep -F 'char* token = strtok_r(progname_plus_args, " ", &saveptr);' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tooldata_db.cc" >/dev/null
|
||||||
|
grep -F 'if (child_argc >= MAX_DB_PROGRAM_ARGS)' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tooldata_db.cc" >/dev/null
|
||||||
|
grep -F 'if (access(child_argv[0],X_OK))' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tooldata_db.cc" >/dev/null
|
||||||
|
grep -F 'int tooldata_db_getall() {' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tooldata_db.cc" >/dev/null
|
||||||
|
grep -F 'if (!db_live) {return -1;}' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tooldata_db.cc" >/dev/null
|
||||||
|
grep -F 'int tooldata_db_notify(tool_notify_t ntype,' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tooldata_db.cc" >/dev/null
|
||||||
|
grep -F 'if (!db_live) return 0;' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tooldata_db.cc" >/dev/null
|
||||||
|
grep -F 'int tooldata_read_entry(const char *input_line)' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/tooldata_common.cc" >/dev/null
|
||||||
|
|
||||||
|
grep -F 'LinuxCNC tooldata source map' docs/linuxcnc-tooldata-source-map.md >/dev/null
|
||||||
|
grep -F 'must not add CNC behavior' docs/linuxcnc-tooldata-source-map.md >/dev/null
|
||||||
|
grep -F 'must not expand the temporary smoke parser' docs/linuxcnc-tooldata-source-map.md >/dev/null
|
||||||
|
grep -F '`src/emc/tooldata/Submakefile` selects `tooldata_mmap.cc`' \
|
||||||
|
docs/linuxcnc-tooldata-source-map.md >/dev/null
|
||||||
|
grep -F 'Tool table files referenced by switchkins INI files are source coverage only' \
|
||||||
|
docs/linuxcnc-tooldata-source-map.md >/dev/null
|
||||||
|
grep -F '`src/emc/tooldata/tool_mmap_read.cc` is the mmap tool table reader utility' \
|
||||||
|
docs/linuxcnc-tooldata-source-map.md >/dev/null
|
||||||
|
grep -F '`tool_mmap_user()`, `tooldata_last_index_get()`, and `tooldata_get()`' \
|
||||||
|
docs/linuxcnc-tooldata-source-map.md >/dev/null
|
||||||
|
grep -F "single-creator process boundary from" docs/linuxcnc-tooldata-source-map.md >/dev/null
|
||||||
|
grep -F "including after" docs/linuxcnc-tooldata-source-map.md >/dev/null
|
||||||
|
grep -F '`src/emc/tooldata/tool_watch.cc` is the NML/mmap tool table watcher utility' \
|
||||||
|
docs/linuxcnc-tooldata-source-map.md >/dev/null
|
||||||
|
grep -F '`EMC_STAT::io.tool` fields through `RCS_STAT_CHANNEL`' \
|
||||||
|
docs/linuxcnc-tooldata-source-map.md >/dev/null
|
||||||
|
|
||||||
|
grep -F 'tooldata:src/emc/tooldata/tooldata_common.cc:shared tool table parser/format logic' \
|
||||||
|
linuxcnc-rs274-source-files.txt >/dev/null
|
||||||
|
grep -F 'tooldata:src/emc/tooldata/tooldata_mmap.cc:native mmap implementation; replace for wasm' \
|
||||||
|
linuxcnc-rs274-source-files.txt >/dev/null
|
||||||
|
grep -F 'tooldata:src/emc/tooldata/tooldata_db.cc:external database integration; exclude from wasm core' \
|
||||||
|
linuxcnc-rs274-source-files.txt >/dev/null
|
||||||
|
grep -F 'tooldata:src/emc/tooldata/tooldata_nml.cc:NML tool table backend selected by LinuxCNC TOOL_NML_FLAG' \
|
||||||
|
linuxcnc-rs274-source-files.txt >/dev/null
|
||||||
|
grep -F 'header:src/emc/tooldata/tooldata.hh:LinuxCNC tooldata declarations used by RS274 and wasm-safe tooldata shims' \
|
||||||
|
linuxcnc-rs274-source-files.txt >/dev/null
|
||||||
|
grep -F 'metadata:src/emc/tooldata/Submakefile:LinuxCNC tooldata build metadata defining mmap and NML backend source selection' \
|
||||||
|
linuxcnc-rs274-source-files.txt >/dev/null
|
||||||
|
grep -F 'core:src/emc/tooldata/tooldata_common.cc:shared tool table parser/format logic linked with wasm-safe mmap backend' \
|
||||||
|
linuxcnc-rs274-wasm-source-files.txt >/dev/null
|
||||||
|
grep -F 'blocked:src/emc/tooldata/tooldata_mmap.cc:native mmap implementation' \
|
||||||
|
linuxcnc-rs274-wasm-source-files.txt >/dev/null
|
||||||
|
grep -F 'metadata:src/emc/tooldata/tooldata_db.cc:source basis for wasm-safe tooldata runtime export shim' \
|
||||||
|
linuxcnc-rs274-wasm-source-files.txt >/dev/null
|
||||||
|
grep -F 'metadata:src/emc/tooldata/tooldata_nml.cc:source basis for wasm-safe tooldata runtime NML export shim' \
|
||||||
|
linuxcnc-rs274-wasm-source-files.txt >/dev/null
|
||||||
|
|
||||||
|
grep -F 'LinuxCNC source basis: src/emc/tooldata/tooldata_mmap.cc provides these' \
|
||||||
|
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
|
||||||
|
grep -F 'bool mmap_created = false;' \
|
||||||
|
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
|
||||||
|
grep -F 'bool mmap_random_toolchanger = false;' \
|
||||||
|
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
|
||||||
|
grep -F 'bool mmap_creator_called = false;' \
|
||||||
|
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
|
||||||
|
grep -F 'int tool_mmap_creator(EMC_TOOL_STAT const *ptr, int random_toolchanger)' \
|
||||||
|
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
|
||||||
|
grep -F 'Error: tool_mmap_creator already called BYE' \
|
||||||
|
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
|
||||||
|
grep -F 'mmap_created = true;' \
|
||||||
|
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
|
||||||
|
grep -F 'mmap_random_toolchanger = random_toolchanger;' \
|
||||||
|
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
|
||||||
|
grep -F 'last_index = 0;' \
|
||||||
|
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
|
||||||
|
grep -F 'int tool_mmap_user(void)' \
|
||||||
|
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
|
||||||
|
grep -F 'tool_mmap_user(): tool mmap not available' \
|
||||||
|
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
|
||||||
|
grep -F 'void tool_mmap_close(void)' \
|
||||||
|
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
|
||||||
|
grep -F 'mmap_created = false;' \
|
||||||
|
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
|
||||||
|
grep -F 'bool tool_mmap_is_random_toolchanger(void)' \
|
||||||
|
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
|
||||||
|
grep -F 'return mmap_random_toolchanger;' \
|
||||||
|
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
|
||||||
|
grep -F 'int tooldata_last_index_get(void)' \
|
||||||
|
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
|
||||||
|
grep -F 'return -1;' \
|
||||||
|
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
|
||||||
|
grep -F 'LinuxCNC source basis: src/emc/tooldata/tooldata_nml.cc and tooldata_db.cc' \
|
||||||
|
core/wasm_shims/tooldata/tooldata_runtime_stubs.cc >/dev/null
|
||||||
|
grep -F 'constexpr int MAX_DB_PROGRAM_ARGS = 10;' \
|
||||||
|
core/wasm_shims/tooldata/tooldata_runtime_stubs.cc >/dev/null
|
||||||
|
grep -F 'strtok_r(progname_plus_args, " ", &saveptr)' \
|
||||||
|
core/wasm_shims/tooldata/tooldata_runtime_stubs.cc >/dev/null
|
||||||
|
grep -F 'argc exceeds MAX_DB_PROGRAM_ARGS=%d' \
|
||||||
|
core/wasm_shims/tooldata/tooldata_runtime_stubs.cc >/dev/null
|
||||||
|
grep -F 'access(program, X_OK) != 0' \
|
||||||
|
core/wasm_shims/tooldata/tooldata_runtime_stubs.cc >/dev/null
|
||||||
|
grep -F 'returns 0 when' core/wasm_shims/tooldata/tooldata_runtime_stubs.cc >/dev/null
|
||||||
|
grep -F 'LinuxCNC source basis: src/emc/tooldata/tooldata_common.cc provides' \
|
||||||
|
core/wasm_shims/tooldata/tooldata_common_probe_main.cc >/dev/null
|
||||||
|
grep -F 'LinuxCNC source basis: src/emc/tooldata/tooldata_mmap.cc, tooldata_nml.cc,' \
|
||||||
|
core/wasm_shims/tooldata/tooldata_probe_main.cc >/dev/null
|
||||||
|
grep -F 'tooldata_db_notify() returns 0 without side effects while db_live is' \
|
||||||
|
core/wasm_shims/tooldata/tooldata_probe_main.cc >/dev/null
|
||||||
|
grep -F 'tooldata_db_init(missing_db_program_with_arg.data(), 1) != -1' \
|
||||||
|
core/wasm_shims/tooldata/tooldata_probe_main.cc >/dev/null
|
||||||
|
grep -F 'missing_db_program_with_arg[separator] != 0' \
|
||||||
|
core/wasm_shims/tooldata/tooldata_probe_main.cc >/dev/null
|
||||||
|
grep -F 'tooldata_db_init(too_many_db_args, 1) != -1' \
|
||||||
|
core/wasm_shims/tooldata/tooldata_probe_main.cc >/dev/null
|
||||||
|
grep -F 'tooldata_db_init(non_db_program, 1) != -1' \
|
||||||
|
core/wasm_shims/tooldata/tooldata_probe_main.cc >/dev/null
|
||||||
|
grep -F 'tool_mmap_creator(nullptr, 1);' \
|
||||||
|
core/wasm_shims/tooldata/tooldata_probe_main.cc >/dev/null
|
||||||
|
grep -F 'WEXITSTATUS(recreate_status) == 0' \
|
||||||
|
core/wasm_shims/tooldata/tooldata_probe_main.cc >/dev/null
|
||||||
|
grep -F 'core/wasm_shims/tooldata/tooldata_mmap_backend.cc' docs/linuxcnc-wasm-shims-source-map.md >/dev/null
|
||||||
|
grep -F 'core/wasm_shims/tooldata/tooldata_runtime_stubs.cc' docs/linuxcnc-wasm-shims-source-map.md >/dev/null
|
||||||
|
|
||||||
|
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 probe compares wasm-safe tooldata runtime stubs' \
|
||||||
|
test-linuxcnc-wasm-tooldata-runtime-symbols.sh >/dev/null
|
||||||
|
grep -F '!!!!db_init: argc exceeds MAX_DB_PROGRAM_ARGS=10' \
|
||||||
|
test-linuxcnc-wasm-tooldata-link.sh >/dev/null
|
||||||
|
grep -F 'not executable' test-linuxcnc-wasm-tooldata-link.sh >/dev/null
|
||||||
|
grep -F 'Error: tool_mmap_creator already called BYE' \
|
||||||
|
test-linuxcnc-wasm-tooldata-link.sh >/dev/null
|
||||||
|
grep -F 'run_parallel_wasm_probe "LinuxCNC wasm tooldata shim link probe" ./test-linuxcnc-wasm-tooldata-link.sh' \
|
||||||
|
build-wasm.sh >/dev/null
|
||||||
|
grep -F 'run_parallel_wasm_probe "LinuxCNC wasm tooldata_common link probe" ./test-linuxcnc-wasm-tooldata-common-link.sh' \
|
||||||
|
build-wasm.sh >/dev/null
|
||||||
|
grep -F 'run_parallel_wasm_probe "LinuxCNC wasm tooldata_mmap symbol probe" ./test-linuxcnc-wasm-tooldata-mmap-symbols.sh' \
|
||||||
|
build-wasm.sh >/dev/null
|
||||||
|
grep -F 'run_parallel_wasm_probe "LinuxCNC wasm tooldata runtime symbol probe" ./test-linuxcnc-wasm-tooldata-runtime-symbols.sh' \
|
||||||
|
build-wasm.sh >/dev/null
|
||||||
|
|
||||||
|
python3 - <<'PY'
|
||||||
|
from pathlib import Path
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
doc = Path("docs/linuxcnc-tooldata-source-map.md").read_text(encoding="utf-8")
|
||||||
|
build_wasm_lines = Path("build-wasm.sh").read_text(encoding="utf-8").splitlines()
|
||||||
|
native_tooldata = subprocess.check_output(
|
||||||
|
["./list-linuxcnc-source-manifest-sources.sh", "linuxcnc-rs274-source-files.txt", "tooldata"],
|
||||||
|
text=True,
|
||||||
|
).splitlines()
|
||||||
|
expected_native = [
|
||||||
|
"src/emc/tooldata/tooldata_common.cc",
|
||||||
|
"src/emc/tooldata/tooldata_mmap.cc",
|
||||||
|
"src/emc/tooldata/tooldata_db.cc",
|
||||||
|
"src/emc/tooldata/tooldata_nml.cc",
|
||||||
|
"src/emc/tooldata/tool_mmap_read.cc",
|
||||||
|
"src/emc/tooldata/tool_watch.cc",
|
||||||
|
]
|
||||||
|
for source in expected_native:
|
||||||
|
if source not in native_tooldata:
|
||||||
|
raise SystemExit(f"native RS274 manifest missing LinuxCNC tooldata source: {source}")
|
||||||
|
|
||||||
|
kin_tooldata = subprocess.check_output(
|
||||||
|
["./list-linuxcnc-kinematics-manifest-sources.sh", "linuxcnc-kinematics-source-files.txt", "tooldata"],
|
||||||
|
text=True,
|
||||||
|
).splitlines()
|
||||||
|
if len([source for source in kin_tooldata if source.endswith(".tbl")]) != 10:
|
||||||
|
raise SystemExit("kinematics manifest must track exactly 10 LinuxCNC TOOL_TABLE sources")
|
||||||
|
|
||||||
|
positions = {}
|
||||||
|
for index, line in enumerate(build_wasm_lines, start=1):
|
||||||
|
if 'run_parallel_wasm_probe "LinuxCNC wasm tooldata shim link probe"' in line:
|
||||||
|
positions["tooldata_link"] = index
|
||||||
|
if 'run_parallel_wasm_probe "LinuxCNC wasm tooldata_common link probe"' in line:
|
||||||
|
positions["tooldata_common"] = index
|
||||||
|
if 'run_parallel_wasm_probe "LinuxCNC wasm tooldata_mmap symbol probe"' in line:
|
||||||
|
positions["tooldata_mmap"] = index
|
||||||
|
if 'run_parallel_wasm_probe "LinuxCNC wasm tooldata runtime symbol probe"' in line:
|
||||||
|
positions["tooldata_runtime"] = index
|
||||||
|
if "cp build/wasm/cnc_sim.js web/public/cnc_sim.js" in line:
|
||||||
|
positions["copy_js"] = index
|
||||||
|
if "cp build/wasm/cnc_sim.wasm web/public/cnc_sim.wasm" in line:
|
||||||
|
positions["copy_wasm"] = index
|
||||||
|
|
||||||
|
missing = [
|
||||||
|
name for name in (
|
||||||
|
"tooldata_link",
|
||||||
|
"tooldata_common",
|
||||||
|
"tooldata_mmap",
|
||||||
|
"tooldata_runtime",
|
||||||
|
"copy_js",
|
||||||
|
"copy_wasm",
|
||||||
|
)
|
||||||
|
if name not in positions
|
||||||
|
]
|
||||||
|
if missing:
|
||||||
|
raise SystemExit(f"build-wasm.sh missing tooldata/copy ordering anchor: {', '.join(missing)}")
|
||||||
|
for probe in ("tooldata_link", "tooldata_common", "tooldata_mmap", "tooldata_runtime"):
|
||||||
|
if not (positions[probe] < positions["copy_js"] and positions[probe] < positions["copy_wasm"]):
|
||||||
|
raise SystemExit("build-wasm.sh must run all tooldata link/symbol probes before copying browser artifacts")
|
||||||
|
|
||||||
|
for path in [
|
||||||
|
"core/wasm_shims/tooldata/tooldata_mmap_backend.cc",
|
||||||
|
"core/wasm_shims/tooldata/tooldata_runtime_stubs.cc",
|
||||||
|
"test-linuxcnc-wasm-tooldata-link.sh",
|
||||||
|
"test-linuxcnc-wasm-tooldata-common-link.sh",
|
||||||
|
"test-linuxcnc-wasm-tooldata-mmap-symbols.sh",
|
||||||
|
"test-linuxcnc-wasm-tooldata-runtime-symbols.sh",
|
||||||
|
]:
|
||||||
|
if path not in doc:
|
||||||
|
raise SystemExit(f"docs/linuxcnc-tooldata-source-map.md missing coverage path: {path}")
|
||||||
|
|
||||||
|
for phrase in [
|
||||||
|
"must not add CNC behavior",
|
||||||
|
"must not expand the temporary smoke parser",
|
||||||
|
"source coverage only",
|
||||||
|
"must not become browser API fields",
|
||||||
|
"WASM-safe tooldata shims must preserve the LinuxCNC export surface",
|
||||||
|
"must keep external DB services unavailable",
|
||||||
|
"must not fork, exec, poll",
|
||||||
|
"MAX_DB_PROGRAM_ARGS=10",
|
||||||
|
"`strtok_r()` argument splitting",
|
||||||
|
"access(..., X_OK)",
|
||||||
|
"mmap tool table reader utility",
|
||||||
|
"NML/mmap tool table watcher utility",
|
||||||
|
"mmap creator/user/close lifecycle",
|
||||||
|
"random-toolchanger flag",
|
||||||
|
"last-index unavailable return",
|
||||||
|
"in-memory replacement for native mmap",
|
||||||
|
"inactive-DB return values",
|
||||||
|
"./check-linuxcnc-tooldata-source-map.sh",
|
||||||
|
"does not interpret G-code",
|
||||||
|
]:
|
||||||
|
if phrase not in doc:
|
||||||
|
raise SystemExit(f"docs/linuxcnc-tooldata-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-tooldata-source-map.sh" not in text:
|
||||||
|
raise SystemExit(f"{entry} must run ./check-linuxcnc-tooldata-source-map.sh")
|
||||||
|
PY
|
||||||
194
check-linuxcnc-wasm-blockers-source-map.sh
Executable file
194
check-linuxcnc-wasm-blockers-source-map.sh
Executable file
@@ -0,0 +1,194 @@
|
|||||||
|
#!/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
|
||||||
499
check-linuxcnc-wasm-shims-source-map.sh
Executable file
499
check-linuxcnc-wasm-shims-source-map.sh
Executable file
@@ -0,0 +1,499 @@
|
|||||||
|
#!/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, 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: 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(NULL, RTLD_GLOBAL);' "$linuxcnc_root/src/emc/rs274ngc/interp_base.cc" >/dev/null
|
||||||
|
grep -F 'interp_lib = dlopen(interp_path, RTLD_NOW);' "$linuxcnc_root/src/emc/rs274ngc/interp_base.cc" >/dev/null
|
||||||
|
grep -F 'Constructor constructor = (Constructor)dlsym(interp_lib, "makeInterp");' \
|
||||||
|
"$linuxcnc_root/src/emc/rs274ngc/interp_base.cc" >/dev/null
|
||||||
|
grep -F 'dlerror()' "$linuxcnc_root/src/emc/rs274ngc/interp_base.cc" >/dev/null
|
||||||
|
grep -F '#include "libintl.h"' "$linuxcnc_root/src/emc/rs274ngc/interp_internal.hh" >/dev/null
|
||||||
|
grep -F '#define _(s) gettext(s)' "$linuxcnc_root/src/emc/rs274ngc/interp_internal.hh" >/dev/null
|
||||||
|
grep -F 'CHKS((!_readers[(int)' "$linuxcnc_root/src/emc/rs274ngc/interp_internal.cc" >/dev/null
|
||||||
|
grep -F 'Cannot use polar coordinate on a machine lacking X or Y axes' \
|
||||||
|
"$linuxcnc_root/src/emc/rs274ngc/interp_internal.cc" >/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 '{ "interpreter", PyInit_interpreter },' "$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null
|
||||||
|
grep -F '{ "emccanon", PyInit_emccanon },' "$linuxcnc_root/src/emc/rs274ngc/gcodemodule.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 'hal_get_pin_value_by_name(hal_name, &type, &ptr, &conn)' \
|
||||||
|
"$linuxcnc_root/src/emc/rs274ngc/interp_namedparams.cc" >/dev/null
|
||||||
|
grep -F 'hal_get_signal_value_by_name(hal_name, &type, &ptr, &conn)' \
|
||||||
|
"$linuxcnc_root/src/emc/rs274ngc/interp_namedparams.cc" >/dev/null
|
||||||
|
grep -F 'hal_get_param_value_by_name(hal_name, &type, &ptr)' \
|
||||||
|
"$linuxcnc_root/src/emc/rs274ngc/interp_namedparams.cc" >/dev/null
|
||||||
|
grep -F 'HAL_TYPE_UNINITIALIZED = 0,' "$linuxcnc_root/include/hal.h" >/dev/null
|
||||||
|
grep -F 'extern int hal_get_pin_value_by_name(' "$linuxcnc_root/include/hal.h" >/dev/null
|
||||||
|
grep -F 'extern int hal_get_signal_value_by_name(' "$linuxcnc_root/include/hal.h" >/dev/null
|
||||||
|
grep -F 'extern int hal_get_param_value_by_name(' "$linuxcnc_root/include/hal.h" >/dev/null
|
||||||
|
grep -F 'int hal_get_pin_value_by_name(' "$linuxcnc_root/src/hal/hal_lib.c" >/dev/null
|
||||||
|
grep -F 'int hal_get_signal_value_by_name(' "$linuxcnc_root/src/hal/hal_lib.c" >/dev/null
|
||||||
|
grep -F 'int hal_get_param_value_by_name(' "$linuxcnc_root/src/hal/hal_lib.c" >/dev/null
|
||||||
|
grep -F 'wordexp(basename, &exp_result, 0);' "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
|
||||||
|
grep -F 'wordexp(settings->program_prefix, &exp_result, 0);' \
|
||||||
|
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
|
||||||
|
grep -F 'wordexp(settings->subroutines[dct], &exp_result, 0);' \
|
||||||
|
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
|
||||||
|
grep -F 'wordfree(&exp_result);' "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
|
||||||
|
grep -F '#include "nml_intf/emc_nml.hh"' "$linuxcnc_root/src/emc/sai/dummyemcstat.cc" >/dev/null
|
||||||
|
grep -F 'EMC_STAT *emcStatus = new EMC_STAT;' "$linuxcnc_root/src/emc/sai/dummyemcstat.cc" >/dev/null
|
||||||
|
grep -F 'int NMLmsg::automatically_clear = 1;' \
|
||||||
|
"$linuxcnc_root/src/libnml/nml/nmlmsg.cc" >/dev/null
|
||||||
|
grep -F 'NMLmsg::NMLmsg' "$linuxcnc_root/src/libnml/nml/nmlmsg.cc" >/dev/null
|
||||||
|
grep -F 'void NMLmsg::clear()' "$linuxcnc_root/src/libnml/nml/nmlmsg.cc" >/dev/null
|
||||||
|
grep -F 'memset((void *) this, 0, size);' \
|
||||||
|
"$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 'command_type = -1;' "$linuxcnc_root/src/libnml/nml/stat_msg.cc" >/dev/null
|
||||||
|
grep -F 'echo_serial_number = -1;' "$linuxcnc_root/src/libnml/nml/stat_msg.cc" >/dev/null
|
||||||
|
grep -F 'status = RCS_STATUS::UNINITIALIZED;' "$linuxcnc_root/src/libnml/nml/stat_msg.cc" >/dev/null
|
||||||
|
grep -F '_state = -1;' "$linuxcnc_root/src/libnml/nml/stat_msg.cc" >/dev/null
|
||||||
|
grep -F 'extern int set_print_rcs_error_info(const char *file, int line);' \
|
||||||
|
"$linuxcnc_root/src/libnml/rcs/rcs_print.hh" >/dev/null
|
||||||
|
grep -F 'extern int print_rcs_error_new(const char *_fmt, ...)' \
|
||||||
|
"$linuxcnc_root/src/libnml/rcs/rcs_print.hh" >/dev/null
|
||||||
|
grep -F '#define rcs_print_error set_print_rcs_error_info( __FILE__, __LINE__); print_rcs_error_new' \
|
||||||
|
"$linuxcnc_root/src/libnml/rcs/rcs_print.hh" >/dev/null
|
||||||
|
grep -F 'int set_print_rcs_error_info(const char *file, int line)' \
|
||||||
|
"$linuxcnc_root/src/libnml/rcs/rcs_print.cc" >/dev/null
|
||||||
|
grep -F 'print_rcs_error_new' "$linuxcnc_root/src/libnml/rcs/rcs_print.cc" >/dev/null
|
||||||
|
grep -F 'retval = rcs_vprint(_fmt, args, 1);' \
|
||||||
|
"$linuxcnc_root/src/libnml/rcs/rcs_print.cc" >/dev/null
|
||||||
|
grep -F 'extern int rtapi_snprintf(char *buf, unsigned long int size,' \
|
||||||
|
"$linuxcnc_root/src/rtapi/rtapi.h" >/dev/null
|
||||||
|
grep -F 'extern int rtapi_vsnprintf(char *buf, unsigned long size,' \
|
||||||
|
"$linuxcnc_root/src/rtapi/rtapi.h" >/dev/null
|
||||||
|
grep -F 'int rtapi_snprintf(char *buffer, unsigned long int size, const char *msg, ...) {' \
|
||||||
|
"$linuxcnc_root/src/rtapi/uspace_common.h" >/dev/null
|
||||||
|
grep -F 'result = vsnprintf(buffer, size, msg, args);' \
|
||||||
|
"$linuxcnc_root/src/rtapi/uspace_common.h" >/dev/null
|
||||||
|
grep -F 'int rtapi_vsnprintf(char *buffer, unsigned long int size, const char *fmt,' \
|
||||||
|
"$linuxcnc_root/src/rtapi/uspace_common.h" >/dev/null
|
||||||
|
grep -F 'return vsnprintf(buffer, size, fmt, args);' \
|
||||||
|
"$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 'PLUGIN_PYTHON_NOT_INITIALIZED = -12,' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.hh" >/dev/null
|
||||||
|
grep -F 'PLUGIN_BAD_PATH = -5,' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.hh" >/dev/null
|
||||||
|
grep -F 'PLUGIN_INITTAB_FAILED = -7 ,' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.hh" >/dev/null
|
||||||
|
grep -F 'PLUGIN_EXCEPTION_DURING_PATH_PREPEND = -9,' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.hh" >/dev/null
|
||||||
|
grep -F 'PLUGIN_EXCEPTION_DURING_PATH_APPEND = -10,' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.hh" >/dev/null
|
||||||
|
grep -F 'PLUGIN_NO_CALLABLE = 1,' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.hh" >/dev/null
|
||||||
|
grep -F 'PLUGIN_EXCEPTION = 2' "$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 'for (int i = 0; inittab[i].name != NULL; i++) {' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null
|
||||||
|
grep -F 'if (module == NULL) {' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null
|
||||||
|
grep -F 'status = PLUGIN_INITTAB_FAILED;' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null
|
||||||
|
grep -F 'if (status < PLUGIN_OK)' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null
|
||||||
|
grep -F 'if (auto inistring = inifile.findString("TOPLEVEL", section)) {' \
|
||||||
|
"$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null
|
||||||
|
grep -F 'if (realpath(toplevel, real_path) == NULL) {' \
|
||||||
|
"$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null
|
||||||
|
grep -F 'status = PLUGIN_BAD_PATH;' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null
|
||||||
|
grep -F 'while (auto inistring = inifile.findString(n, "PATH_PREPEND", "PYTHON")) {' \
|
||||||
|
"$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null
|
||||||
|
grep -F 'while (auto inistring = inifile.findString(n, "PATH_APPEND", "PYTHON")) {' \
|
||||||
|
"$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null
|
||||||
|
grep -F 'status = PLUGIN_EXCEPTION_DURING_PATH_PREPEND;' \
|
||||||
|
"$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null
|
||||||
|
grep -F 'status = PLUGIN_EXCEPTION_DURING_PATH_APPEND;' \
|
||||||
|
"$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null
|
||||||
|
grep -F 'int IniFile::tildeExpand(const std::string &path, std::string &result)' \
|
||||||
|
"$linuxcnc_root/src/emc/ini/inifile.cc" >/dev/null
|
||||||
|
grep -F "if(!home)" "$linuxcnc_root/src/emc/ini/inifile.cc" >/dev/null
|
||||||
|
grep -F 'IniFile::trim(sect);' "$linuxcnc_root/src/emc/ini/inifile.cc" >/dev/null
|
||||||
|
grep -F 'Section header has trailing content, ignored' "$linuxcnc_root/src/emc/ini/inifile.cc" >/dev/null
|
||||||
|
grep -F 'retval = bp::exec_file(cmd, main_namespace, main_namespace);' \
|
||||||
|
"$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null
|
||||||
|
grep -F 'retval = bp::exec(cmd, main_namespace, main_namespace);' \
|
||||||
|
"$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null
|
||||||
|
grep -F 'PyObject *rv = PyObject_Call(function.ptr(), tupleargs.ptr(), kwargs.ptr());' \
|
||||||
|
"$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null
|
||||||
|
grep -F 'PyErr_Fetch(&exc,&val,&tb);' \
|
||||||
|
"$linuxcnc_root/src/emc/rs274ngc/interp_python.cc" >/dev/null
|
||||||
|
grep -F 'PyErr_NormalizeException(&exc,&val,&tb);' \
|
||||||
|
"$linuxcnc_root/src/emc/rs274ngc/interp_python.cc" >/dev/null
|
||||||
|
grep -F 'if (Py_IsInitialized()) {' \
|
||||||
|
"$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null
|
||||||
|
grep -F '#define callmethod(o, m, f, ...) PyObject_CallMethod((o), (char*)(m), (char*)(f), ## __VA_ARGS__)' \
|
||||||
|
"$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null
|
||||||
|
grep -F 'PyObject *result = PyObject_GetAttrString(callback, "parameter_file");' \
|
||||||
|
"$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null
|
||||||
|
grep -F 'PyErr_Format(PyExc_TypeError,' \
|
||||||
|
"$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null
|
||||||
|
grep -F 'return Py_BuildValue("[ddd][ddd][ddd][ddd]",' \
|
||||||
|
"$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null
|
||||||
|
grep -F 'PyObject *m = PyModule_Create(&gcode_moduledef);' \
|
||||||
|
"$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null
|
||||||
|
grep -F 'PyType_Ready(&LineCodeType);' \
|
||||||
|
"$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null
|
||||||
|
grep -F 'PyModule_AddObject(m, "linecode", (PyObject*)&LineCodeType);' \
|
||||||
|
"$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null
|
||||||
|
grep -F 'PyObject_SetAttrString(m, "MAX_ERROR", PyLong_FromLong(maxerror));' \
|
||||||
|
"$linuxcnc_root/src/emc/rs274ngc/gcodemodule.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
|
||||||
|
grep -F 'if (!db_live) {return -1;}' "$linuxcnc_root/src/emc/tooldata/tooldata_db.cc" >/dev/null
|
||||||
|
grep -F 'if (!db_live) return 0;' "$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",
|
||||||
|
"include/hal.h",
|
||||||
|
"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",
|
||||||
|
"src/libnml/rcs/rcs_print.hh",
|
||||||
|
],
|
||||||
|
"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",
|
||||||
|
"src/emc/ini/inifile.cc",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
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}")
|
||||||
|
|
||||||
|
runtime_shim = Path("core/wasm_shims/linuxcnc_runtime_shim.cc").read_text(encoding="utf-8")
|
||||||
|
runtime_probe = Path("core/wasm_shims/cnc_sim_wasm_runtime_probe_main.cc").read_text(encoding="utf-8")
|
||||||
|
dlfcn_shim = Path("core/wasm_shims/dlfcn.cc").read_text(encoding="utf-8")
|
||||||
|
dlfcn_header = Path("core/wasm_shims/dlfcn.h").read_text(encoding="utf-8")
|
||||||
|
emc_status_shim = Path("core/wasm_shims/emc_status_shim.cc").read_text(encoding="utf-8")
|
||||||
|
gettext_shim = Path("core/wasm_shims/gettext_shim.cc").read_text(encoding="utf-8")
|
||||||
|
for needle in [
|
||||||
|
"#define RTLD_GLOBAL 0x00100",
|
||||||
|
"#define RTLD_NOW 0x00002",
|
||||||
|
"void *dlopen(const char *filename, int flags);",
|
||||||
|
"void *dlsym(void *handle, const char *symbol);",
|
||||||
|
"char *dlerror(void);",
|
||||||
|
"int dlclose(void *handle);",
|
||||||
|
]:
|
||||||
|
if needle not in dlfcn_header:
|
||||||
|
raise SystemExit(f"dlfcn shim header missing LinuxCNC loader boundary: {needle}")
|
||||||
|
for needle in [
|
||||||
|
"dynamic loading is unavailable in wasm-safe probe",
|
||||||
|
"void *dlopen(const char *, int)",
|
||||||
|
"return nullptr;",
|
||||||
|
"void *dlsym(void *, const char *)",
|
||||||
|
"char *dlerror(void)",
|
||||||
|
"return last_error;",
|
||||||
|
"int dlclose(void *)",
|
||||||
|
"return 0;",
|
||||||
|
]:
|
||||||
|
if needle not in dlfcn_shim:
|
||||||
|
raise SystemExit(f"dlfcn shim missing unavailable-loader boundary: {needle}")
|
||||||
|
for needle in [
|
||||||
|
'#include "nml_intf/emc_nml.hh"',
|
||||||
|
"EMC_STAT *emcStatus = new EMC_STAT;",
|
||||||
|
]:
|
||||||
|
if needle not in emc_status_shim:
|
||||||
|
raise SystemExit(f"EMC status shim missing detached LinuxCNC status singleton: {needle}")
|
||||||
|
for needle in [
|
||||||
|
"extern \"C\" char *gettext(const char *msgid)",
|
||||||
|
"return const_cast<char *>(msgid ? msgid : \"\");",
|
||||||
|
]:
|
||||||
|
if needle not in gettext_shim:
|
||||||
|
raise SystemExit(f"gettext shim missing diagnostic passthrough boundary: {needle}")
|
||||||
|
nml_status_shim = Path("core/wasm_shims/nml_status_shim.cc").read_text(encoding="utf-8")
|
||||||
|
for needle in [
|
||||||
|
"int NMLmsg::automatically_clear = 1;",
|
||||||
|
"void NMLmsg::clear()",
|
||||||
|
"std::memset(static_cast<void *>(this), 0, static_cast<size_t>(size));",
|
||||||
|
"command_type = -1;",
|
||||||
|
"echo_serial_number = -1;",
|
||||||
|
"status = RCS_STATUS::UNINITIALIZED;",
|
||||||
|
"_state = -1;",
|
||||||
|
]:
|
||||||
|
if needle not in nml_status_shim:
|
||||||
|
raise SystemExit(f"NML status shim missing LinuxCNC constructor boundary: {needle}")
|
||||||
|
for needle in [
|
||||||
|
"int _task = 0;",
|
||||||
|
'extern "C" PyObject *PyInit_interpreter(void)',
|
||||||
|
'extern "C" PyObject *PyInit_emccanon(void)',
|
||||||
|
'extern "C" PyObject *PyInit_gcode(void)',
|
||||||
|
"return nullptr;",
|
||||||
|
'struct _inittab builtin_modules[] = {',
|
||||||
|
'{"interpreter", PyInit_interpreter}',
|
||||||
|
'{"emccanon", PyInit_emccanon}',
|
||||||
|
]:
|
||||||
|
if needle not in runtime_shim:
|
||||||
|
raise SystemExit(f"linuxcnc runtime shim missing preview/builtin module boundary: {needle}")
|
||||||
|
for needle in [
|
||||||
|
"std::string expand_home_directory(const char *words)",
|
||||||
|
"std::string expand_environment_variables(const std::string &words)",
|
||||||
|
"extern \"C\" int wordexp(const char *words, wordexp_t *pwordexp, int)",
|
||||||
|
"return WRDE_BADVAL;",
|
||||||
|
"return WRDE_NOSPACE;",
|
||||||
|
"pwordexp->we_wordc = 1;",
|
||||||
|
"pwordexp->we_offs = 0;",
|
||||||
|
"extern \"C\" void wordfree(wordexp_t *pwordexp)",
|
||||||
|
"pwordexp->we_wordc = 0;",
|
||||||
|
"pwordexp->we_wordv = nullptr;",
|
||||||
|
]:
|
||||||
|
if needle not in runtime_shim:
|
||||||
|
raise SystemExit(f"linuxcnc runtime shim missing limited wordexp boundary: {needle}")
|
||||||
|
for needle in [
|
||||||
|
'extern "C" int hal_get_pin_value_by_name',
|
||||||
|
'extern "C" int hal_get_signal_value_by_name',
|
||||||
|
'extern "C" int hal_get_param_value_by_name',
|
||||||
|
"*type = HAL_TYPE_UNINITIALIZED;",
|
||||||
|
"*data = nullptr;",
|
||||||
|
"return -1;",
|
||||||
|
]:
|
||||||
|
if needle not in runtime_shim:
|
||||||
|
raise SystemExit(f"linuxcnc runtime shim missing HAL unavailable boundary: {needle}")
|
||||||
|
for needle in [
|
||||||
|
"bool hal_lookup_is_unavailable()",
|
||||||
|
'hal_get_pin_value_by_name("cnc-sim.missing-pin", &type, &data, &connected) != -1',
|
||||||
|
'hal_get_signal_value_by_name("cnc-sim.missing-signal", &type, &data, &has_writers) != -1',
|
||||||
|
'hal_get_param_value_by_name("cnc-sim.missing-param", &type, &data) != -1',
|
||||||
|
"type != HAL_TYPE_UNINITIALIZED",
|
||||||
|
"data != nullptr",
|
||||||
|
]:
|
||||||
|
if needle not in runtime_probe:
|
||||||
|
raise SystemExit(f"runtime probe missing HAL unavailable assertion: {needle}")
|
||||||
|
|
||||||
|
python_plugin_shim = Path("core/wasm_shims/pythonplugin/python_plugin.cc").read_text(encoding="utf-8")
|
||||||
|
python_plugin_probe = Path("core/wasm_shims/pythonplugin/python_plugin_probe_main.cc").read_text(encoding="utf-8")
|
||||||
|
python_plugin_inittab_probe = Path("core/wasm_shims/pythonplugin/python_plugin_inittab_probe_main.cc").read_text(encoding="utf-8")
|
||||||
|
python_c_api_shim = Path("core/wasm_shims/python_c_api_shim.cc").read_text(encoding="utf-8")
|
||||||
|
for forbidden in [
|
||||||
|
"bp::exec(",
|
||||||
|
"bp::exec_file(",
|
||||||
|
"PyObject_Call(",
|
||||||
|
]:
|
||||||
|
if forbidden in python_plugin_shim:
|
||||||
|
raise SystemExit(f"PythonPlugin wasm shim must not execute Python: {forbidden}")
|
||||||
|
for needle in [
|
||||||
|
"python disabled in wasm-safe probe",
|
||||||
|
'find_ini_value(iniFilename, section, "TOPLEVEL", toplevel)',
|
||||||
|
"realpath(toplevel.c_str(), resolved_path) == nullptr",
|
||||||
|
"status = PLUGIN_BAD_PATH;",
|
||||||
|
"status = PLUGIN_INITTAB_FAILED;",
|
||||||
|
"failed to initialize built-in module",
|
||||||
|
"read_ini_section(text, section_name)",
|
||||||
|
"section_name = trim_ini_value(text.substr(1, close - 1));",
|
||||||
|
'check_python_path_entries(iniFilename,',
|
||||||
|
"PLUGIN_EXCEPTION_DURING_PATH_PREPEND",
|
||||||
|
"PLUGIN_EXCEPTION_DURING_PATH_APPEND",
|
||||||
|
"tilde_expand_path(entry, expanded)",
|
||||||
|
"status = PLUGIN_PYTHON_NOT_INITIALIZED;",
|
||||||
|
"return PLUGIN_NO_CALLABLE;",
|
||||||
|
"status = PLUGIN_EXCEPTION;",
|
||||||
|
"if (status < PLUGIN_OK)",
|
||||||
|
]:
|
||||||
|
if needle not in python_plugin_shim:
|
||||||
|
raise SystemExit(f"PythonPlugin wasm shim missing disabled-python boundary: {needle}")
|
||||||
|
for needle in [
|
||||||
|
"PythonPlugin::PythonPlugin(struct _inittab*) reports PLUGIN_INITTAB_FAILED",
|
||||||
|
"PythonPlugin *plugin = PythonPlugin::instantiate(bad_modules);",
|
||||||
|
"plugin != nullptr",
|
||||||
|
"python_plugin->plugin_status() != PLUGIN_INITTAB_FAILED",
|
||||||
|
"python_plugin->usable()",
|
||||||
|
'python_plugin->last_errmsg() != "failed to initialize built-in module"',
|
||||||
|
]:
|
||||||
|
if needle not in python_plugin_inittab_probe:
|
||||||
|
raise SystemExit(f"PythonPlugin inittab probe missing first-caller status assertion: {needle}")
|
||||||
|
for needle in [
|
||||||
|
"[ PYTHON ] ; section comment",
|
||||||
|
"PLUGIN_NO_SECTION",
|
||||||
|
"PLUGIN_BAD_INIFILE",
|
||||||
|
"PLUGIN_BAD_PATH",
|
||||||
|
"PLUGIN_EXCEPTION_DURING_PATH_PREPEND",
|
||||||
|
"PLUGIN_EXCEPTION_DURING_PATH_APPEND",
|
||||||
|
"PLUGIN_PYTHON_NOT_INITIALIZED",
|
||||||
|
"PLUGIN_NO_CALLABLE",
|
||||||
|
"PLUGIN_EXCEPTION",
|
||||||
|
"handle_pyerror().empty()",
|
||||||
|
]:
|
||||||
|
if needle not in python_plugin_probe:
|
||||||
|
raise SystemExit(f"PythonPlugin probe missing disabled-python status assertion: {needle}")
|
||||||
|
for needle in [
|
||||||
|
"static PyTypeObject none_type = {\"NoneType\"};",
|
||||||
|
"static PyObject none_object = {&none_type};",
|
||||||
|
"PyObject *_Py_NoneStructPtr = &none_object;",
|
||||||
|
"int Py_IsInitialized(void)",
|
||||||
|
"return 1;",
|
||||||
|
"PyObject *PyErr_Occurred(void)",
|
||||||
|
"return nullptr;",
|
||||||
|
"void PyErr_Fetch(PyObject **exc, PyObject **val, PyObject **tb)",
|
||||||
|
"*exc = nullptr;",
|
||||||
|
"*val = nullptr;",
|
||||||
|
"*tb = nullptr;",
|
||||||
|
"int PyCallable_Check(PyObject *)",
|
||||||
|
"return 0;",
|
||||||
|
"PyObject *PyObject_CallMethod(PyObject *, char *, char *, ...)",
|
||||||
|
"PyObject *PyObject_GetAttrString(PyObject *, const char *)",
|
||||||
|
"return &none_object;",
|
||||||
|
"PyObject *Py_BuildValue(const char *, ...)",
|
||||||
|
"PyObject *PyModule_Create(PyModuleDef *)",
|
||||||
|
"int PyModule_AddObject(PyObject *, const char *, PyObject *)",
|
||||||
|
"int PyType_Ready(PyTypeObject *)",
|
||||||
|
"void *PyObject_NewShim(std::size_t size, PyTypeObject *type)",
|
||||||
|
"std::calloc(1, size)",
|
||||||
|
]:
|
||||||
|
if needle not in python_c_api_shim:
|
||||||
|
raise SystemExit(f"Python C API shim missing symbol boundary: {needle}")
|
||||||
|
|
||||||
|
rcs_print_shim = Path("core/wasm_shims/rcs_print_shim.cc").read_text(encoding="utf-8")
|
||||||
|
for needle in [
|
||||||
|
"set_print_rcs_error_info",
|
||||||
|
"print_rcs_error_new",
|
||||||
|
"std::vfprintf(stderr, format, args)",
|
||||||
|
]:
|
||||||
|
if needle not in rcs_print_shim:
|
||||||
|
raise SystemExit(f"RCS print shim missing split print-error boundary: {needle}")
|
||||||
|
|
||||||
|
rtapi_compat_shim = Path("core/wasm_shims/rtapi_compat.cc").read_text(encoding="utf-8")
|
||||||
|
for needle in [
|
||||||
|
"rtapi_snprintf",
|
||||||
|
"std::vsnprintf(buf, static_cast<std::size_t>(size), fmt, ap)",
|
||||||
|
]:
|
||||||
|
if needle not in rtapi_compat_shim:
|
||||||
|
raise SystemExit(f"RTAPI compatibility shim missing userspace snprintf boundary: {needle}")
|
||||||
|
|
||||||
|
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",
|
||||||
|
"LinuxCNC `interp_base.cc` dynamic loader calls",
|
||||||
|
"unavailable-loader returns",
|
||||||
|
"detached LinuxCNC `EMC_STAT` singleton",
|
||||||
|
"LinuxCNC `_()` diagnostic call sites",
|
||||||
|
"browser gettext catalogs unavailable",
|
||||||
|
"LinuxCNC NML status constructors",
|
||||||
|
"`RCS_STAT_MSG` default status fields",
|
||||||
|
"dynamic loading unavailable",
|
||||||
|
"unavailable HAL lookup",
|
||||||
|
"must leave HAL values unavailable",
|
||||||
|
"preview `_task=0`",
|
||||||
|
"builtin module stubs",
|
||||||
|
"must not switch browser-safe probes into task mode",
|
||||||
|
"single-word `wordexp()` path expansion",
|
||||||
|
"`wordfree()` cleanup",
|
||||||
|
"must not become a shell expansion feature",
|
||||||
|
"must not become Python execution",
|
||||||
|
"Python C API shim must return inert objects",
|
||||||
|
"must not call into CPython runtime",
|
||||||
|
"keeping Python execution disabled",
|
||||||
|
"must not execute `bp::exec`, `bp::exec_file`, or",
|
||||||
|
"inactive-DB return values",
|
||||||
|
"negative-status short-circuit behavior",
|
||||||
|
"first-caller `_inittab` `PLUGIN_INITTAB_FAILED` edge",
|
||||||
|
"preserve the `TOPLEVEL` missing-path `PLUGIN_BAD_PATH` edge",
|
||||||
|
"preserve `PATH_PREPEND` and `PATH_APPEND` tilde-expansion failure statuses",
|
||||||
|
"preserve LinuxCNC INI section-name trimming and trailing section-comment handling",
|
||||||
|
"LinuxCNC `rcs_print_error` split entry points",
|
||||||
|
"without native print routing",
|
||||||
|
"LinuxCNC userspace `rtapi_snprintf()` formatting",
|
||||||
|
"`uspace_common.h` `vsnprintf()` implementation path",
|
||||||
|
"./check-linuxcnc-wasm-shims-source-map.sh",
|
||||||
|
"does not interpret G-code",
|
||||||
|
]:
|
||||||
|
if phrase not in source_map:
|
||||||
|
raise SystemExit(f"docs/linuxcnc-wasm-shims-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-shims-source-map.sh" not in text:
|
||||||
|
raise SystemExit(f"{entry} must run ./check-linuxcnc-wasm-shims-source-map.sh")
|
||||||
|
|
||||||
|
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
|
||||||
204
check-linuxcnc-wasm-smoke-source-map.sh
Executable file
204
check-linuxcnc-wasm-smoke-source-map.sh
Executable file
@@ -0,0 +1,204 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
# LinuxCNC source basis: this guard ties Node/browser WASM smoke coverage to
|
||||||
|
# LinuxCNC RS274 parameter/INI handling, M-code canon side effects, generated
|
||||||
|
# switchkins/remap config cases, kinematics sources, and OPFS-only browser
|
||||||
|
# persistence. It validates smoke-test source coverage 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 'int Interp::ini_load(const char *filename)' \
|
||||||
|
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/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::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 '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 'read_text(command, _setup.file_pointer, _setup.linetext,' \
|
||||||
|
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
|
||||||
|
grep -F 'int read_text(const char *command, FILE * inport, char *raw_line,' \
|
||||||
|
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_interp.hh" >/dev/null
|
||||||
|
|
||||||
|
grep -F 'int Interp::convert_m(block_pointer block' \
|
||||||
|
"$linuxcnc_root/src/emc/rs274ngc/interp_convert.cc" >/dev/null
|
||||||
|
grep -F 'int ret = WAIT(round_to_int(block->p_number), DIGITAL_INPUT, type, timeout);' \
|
||||||
|
"$linuxcnc_root/src/emc/rs274ngc/interp_convert.cc" >/dev/null
|
||||||
|
grep -F 'SET_AUX_OUTPUT_VALUE(round_to_int(block->e_number), block->q_number);' \
|
||||||
|
"$linuxcnc_root/src/emc/rs274ngc/interp_convert.cc" >/dev/null
|
||||||
|
grep -F 'extern int WAIT(int index' "$linuxcnc_root/src/emc/nml_intf/canon.hh" >/dev/null
|
||||||
|
grep -F 'extern void SET_AUX_OUTPUT_VALUE(int index, double value);' \
|
||||||
|
"$linuxcnc_root/src/emc/nml_intf/canon.hh" >/dev/null
|
||||||
|
|
||||||
|
grep -F 'kp->kinsname = "5axiskins";' "$linuxcnc_root/src/emc/kinematics/5axiskins.c" >/dev/null
|
||||||
|
grep -F 'int xyzbcKinematicsInverse(const EmcPose * pos' \
|
||||||
|
"$linuxcnc_root/src/emc/kinematics/trtfuncs.c" >/dev/null
|
||||||
|
grep -F 'int xyzacKinematicsInverse(const EmcPose * pos' \
|
||||||
|
"$linuxcnc_root/src/emc/kinematics/trtfuncs.c" >/dev/null
|
||||||
|
grep -F '*kinv1 = xyzacKinematicsInverse;' \
|
||||||
|
"$linuxcnc_root/src/emc/kinematics/xyzac-trt-kins.c" >/dev/null
|
||||||
|
grep -F '*kinv1 = xyzbcKinematicsInverse;' \
|
||||||
|
"$linuxcnc_root/src/emc/kinematics/xyzbc-trt-kins.c" >/dev/null
|
||||||
|
grep -F 'int userkKinematicsInverse(const EmcPose * pos' \
|
||||||
|
"$linuxcnc_root/src/emc/kinematics/userkfuncs.c" >/dev/null
|
||||||
|
|
||||||
|
grep -F 'LinuxCNC WASM smoke source map' docs/linuxcnc-wasm-smoke-source-map.md >/dev/null
|
||||||
|
grep -F 'must not expand the temporary smoke parser' docs/linuxcnc-wasm-smoke-source-map.md >/dev/null
|
||||||
|
grep -F 'OPFS-only' docs/linuxcnc-wasm-smoke-source-map.md >/dev/null
|
||||||
|
grep -F '`src/emc/rs274ngc/interp_convert.cc` provides `convert_m()` coverage' \
|
||||||
|
docs/linuxcnc-wasm-smoke-source-map.md >/dev/null
|
||||||
|
grep -F 'unlinking `filename + ".bak"`' docs/linuxcnc-wasm-smoke-source-map.md >/dev/null
|
||||||
|
grep -F '`src/emc/kinematics/5axiskins.c`, `src/emc/kinematics/trtfuncs.c`' \
|
||||||
|
docs/linuxcnc-wasm-smoke-source-map.md >/dev/null
|
||||||
|
grep -F 'Generated switchkins config smoke cases must continue to come from' \
|
||||||
|
docs/linuxcnc-wasm-smoke-source-map.md >/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 'minimum_steps=${WASM_NODE_SMOKE_MIN_STEPS:-50}' test-web-wasm-node-smoke.sh >/dev/null
|
||||||
|
grep -F 'WASM Node smoke covered $passed_steps steps, expected at least $minimum_steps' \
|
||||||
|
test-web-wasm-node-smoke.sh >/dev/null
|
||||||
|
grep -F 'expected Node wasm-core wrapper to leave OPFS disabled' test-web-wasm-node-smoke.cjs >/dev/null
|
||||||
|
grep -F 'expected Node wasm-core parseFile to require OPFS' test-web-wasm-node-smoke.cjs >/dev/null
|
||||||
|
grep -F 'linuxcnc_switchkins_remap_config_cases.json' test-web-wasm-node-smoke.cjs >/dev/null
|
||||||
|
grep -F 'expected LinuxCNC WASM M68 E3 Q${kinstype} remap side effect' \
|
||||||
|
test-web-wasm-node-smoke.cjs >/dev/null
|
||||||
|
grep -F 'expected LinuxCNC WASM M66 E0 L0 remap side effect' \
|
||||||
|
test-web-wasm-node-smoke.cjs >/dev/null
|
||||||
|
grep -F 'expected LinuxCNC WASM 5axiskins RTCP pivot from LinuxCNC 5axiskins inverse' \
|
||||||
|
test-web-wasm-node-smoke.cjs >/dev/null
|
||||||
|
grep -F 'expected LinuxCNC WASM configured xyzbc-trt RTCP pivot from LinuxCNC trtfuncs inverse' \
|
||||||
|
test-web-wasm-node-smoke.cjs >/dev/null
|
||||||
|
grep -F 'expected LinuxCNC WASM configured xyzac-trt RTCP pivot from LinuxCNC xyzab_tdr inverse' \
|
||||||
|
test-web-wasm-node-smoke.cjs >/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 'minimum_sections=${BROWSER_SMOKE_MIN_SECTIONS:-50}' test-web-wasm-browser-smoke.sh >/dev/null
|
||||||
|
grep -F 'BROWSER_SMOKE_MIN_SECTIONS must be a positive integer' test-web-wasm-browser-smoke.sh >/dev/null
|
||||||
|
grep -F 'browser WASM smoke covered $passed_sections sections, expected at least $minimum_sections' \
|
||||||
|
test-web-wasm-browser-smoke.sh >/dev/null
|
||||||
|
grep -F 'required_browser_sections = {' test-web-wasm-browser-smoke.sh >/dev/null
|
||||||
|
grep -F 'runSection("linuxcnc generated switchkins cases load"' test-web-wasm-browser-smoke.sh >/dev/null
|
||||||
|
grep -F 'runSection("opfs parameter restore in new instance"' test-web-wasm-browser-smoke.sh >/dev/null
|
||||||
|
grep -F 'runSection("opfs directory persist and load"' test-web-wasm-browser-smoke.sh >/dev/null
|
||||||
|
grep -F 'runSection("browser opfs policy"' test-web-wasm-browser-smoke.sh >/dev/null
|
||||||
|
grep -F 'runSection("browser app opfs restore"' test-web-wasm-browser-smoke.sh >/dev/null
|
||||||
|
grep -F 'runLinuxCncBrowserSections(context, simulator)' web/test-browser-wasm-smoke-sections.js >/dev/null
|
||||||
|
grep -F 'runBrowserOpfsWorkspaceSections(context, simulator)' web/test-browser-wasm-smoke-sections.js >/dev/null
|
||||||
|
grep -F 'runBrowserOpfsPolicySections(context)' web/test-browser-wasm-smoke-sections.js >/dev/null
|
||||||
|
grep -F 'runBrowserAppOpfsSections(context)' web/test-browser-wasm-smoke-sections.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 'linuxcnc_switchkins_remap_config_cases.json' \
|
||||||
|
web/test-browser-wasm-smoke-linuxcnc-sections.js >/dev/null
|
||||||
|
grep -F 'missing LinuxCNC switchkins config cases after INI_FILE_NAME remap-load filtering' \
|
||||||
|
web/test-browser-wasm-smoke-linuxcnc-sections.js >/dev/null
|
||||||
|
grep -F 'LinuxCNC source basis: rs274ngc_pre.cc restore_parameters()' \
|
||||||
|
web/test-browser-wasm-smoke-opfs-parameter-sections.js >/dev/null
|
||||||
|
grep -F 'LinuxCNC source basis: rs274ngc_pre.cc save_parameters()' \
|
||||||
|
web/test-browser-wasm-smoke-opfs-parameter-sections.js >/dev/null
|
||||||
|
grep -F 'LinuxCNC source basis: rs274ngc_pre.cc read_text() reads' \
|
||||||
|
web/test-browser-wasm-smoke-opfs-basic-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("browser opfs policy"' \
|
||||||
|
web/test-browser-wasm-smoke-opfs-policy-sections.js >/dev/null
|
||||||
|
grep -F 'app frame did not persist current program into OPFS' \
|
||||||
|
web/test-browser-wasm-smoke-app-sections.js >/dev/null
|
||||||
|
grep -F 'app frame did not persist LinuxCNC parameter backup into OPFS' \
|
||||||
|
web/test-browser-wasm-smoke-app-sections.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
|
||||||
|
|
||||||
|
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 WASM smoke harness must not add filesystem persistence outside OPFS" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
python3 - <<'PY'
|
||||||
|
from pathlib import Path
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
|
||||||
|
source_map = Path("docs/linuxcnc-wasm-smoke-source-map.md").read_text(encoding="utf-8")
|
||||||
|
json_cases = json.loads(Path("web/public/linuxcnc_switchkins_remap_config_cases.json").read_text(encoding="utf-8"))
|
||||||
|
if not isinstance(json_cases, list) or len(json_cases) == 0:
|
||||||
|
raise SystemExit("generated LinuxCNC switchkins browser config cases are missing")
|
||||||
|
|
||||||
|
node_wrapper = Path("test-web-wasm-node-smoke.sh").read_text(encoding="utf-8")
|
||||||
|
browser_wrapper = Path("test-web-wasm-browser-smoke.sh").read_text(encoding="utf-8")
|
||||||
|
node_min = re.search(r"minimum_steps=\$\{WASM_NODE_SMOKE_MIN_STEPS:-(\d+)\}", node_wrapper)
|
||||||
|
browser_min = re.search(r"minimum_sections=\$\{BROWSER_SMOKE_MIN_SECTIONS:-(\d+)\}", browser_wrapper)
|
||||||
|
if not node_min or int(node_min.group(1)) < 50:
|
||||||
|
raise SystemExit("Node WASM smoke minimum step threshold is missing or below 50")
|
||||||
|
if not browser_min or int(browser_min.group(1)) < 50:
|
||||||
|
raise SystemExit("browser WASM smoke minimum section threshold is missing or below 50")
|
||||||
|
|
||||||
|
for path in [
|
||||||
|
"test-web-wasm-node-smoke.sh",
|
||||||
|
"test-web-wasm-node-smoke.cjs",
|
||||||
|
"test-web-wasm-browser-smoke.sh",
|
||||||
|
"web/test-browser-wasm-smoke-linuxcnc-sections.js",
|
||||||
|
"web/test-browser-wasm-smoke-opfs-workspace-sections.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-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",
|
||||||
|
]:
|
||||||
|
if f"| `{path}` |" not in source_map:
|
||||||
|
raise SystemExit(f"docs/linuxcnc-wasm-smoke-source-map.md missing smoke entry: {path}")
|
||||||
|
|
||||||
|
required_phrases = [
|
||||||
|
"must not add CNC behavior",
|
||||||
|
"must not expand the temporary smoke parser",
|
||||||
|
"persistence OPFS-only",
|
||||||
|
"Node smoke must leave OPFS disabled",
|
||||||
|
"Browser smoke harness files must not use Local Storage",
|
||||||
|
"Generated switchkins config smoke cases",
|
||||||
|
"browser smoke shell guard also pins required section names",
|
||||||
|
"OPFS workspace/policy/directory handling",
|
||||||
|
'unlinking `filename + ".bak"`',
|
||||||
|
"./check-linuxcnc-wasm-smoke-source-map.sh",
|
||||||
|
"does not interpret G-code",
|
||||||
|
]
|
||||||
|
for phrase in required_phrases:
|
||||||
|
if phrase not in source_map:
|
||||||
|
raise SystemExit(f"docs/linuxcnc-wasm-smoke-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-smoke-source-map.sh" not in text:
|
||||||
|
raise SystemExit(f"{entry} must run ./check-linuxcnc-wasm-smoke-source-map.sh")
|
||||||
|
PY
|
||||||
132
check-linuxcnc-workstreams-source-map.sh
Executable file
132
check-linuxcnc-workstreams-source-map.sh
Executable file
@@ -0,0 +1,132 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
# LinuxCNC source basis: this guard ties the six active workstream contexts to
|
||||||
|
# their source-map guards and to LinuxCNC source/config anchors. It validates
|
||||||
|
# coordination policy only; it does not add CNC behavior or expand smoke
|
||||||
|
# parsing.
|
||||||
|
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
|
||||||
|
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
|
||||||
|
|
||||||
|
grep -F '1. `switchkins/remap` generator and tables' docs/architecture.md >/dev/null
|
||||||
|
grep -F '2. `linuxcnc-kinematics` source manifest' docs/architecture.md >/dev/null
|
||||||
|
grep -F '3. WASM filesystem and OPFS persistence' docs/architecture.md >/dev/null
|
||||||
|
grep -F '4. browser app integration' docs/architecture.md >/dev/null
|
||||||
|
grep -F '5. Node and browser smoke coverage' docs/architecture.md >/dev/null
|
||||||
|
grep -F '6. source-link, build, and documentation constraints' docs/architecture.md >/dev/null
|
||||||
|
grep -F 'The synchronized source-map guard for these contexts is' docs/architecture.md >/dev/null
|
||||||
|
grep -F '`docs/linuxcnc-workstreams-source-map.md`.' docs/architecture.md >/dev/null
|
||||||
|
|
||||||
|
grep -F 'REMAP = M428' "$linuxcnc_root/configs/sim/axis/vismach/5axis/bridgemill/5axis.ini" >/dev/null
|
||||||
|
grep -F '#<kinstype>' "$linuxcnc_root/configs/sim/axis/vismach/5axis/bridgemill/remap_subs/428remap.ngc" >/dev/null
|
||||||
|
grep -F 'GENSERKINSSRCS := emc/kinematics/ugenserkins.c' \
|
||||||
|
"$linuxcnc_root/src/emc/kinematics/Submakefile" >/dev/null
|
||||||
|
grep -F 'extern int kinematicsInverse(const struct EmcPose * world,' \
|
||||||
|
"$linuxcnc_root/src/emc/kinematics/kinematics.h" >/dev/null
|
||||||
|
grep -F '#define RS274NGC_PARAMETER_FILE_NAME_DEFAULT "rs274ngc.var"' \
|
||||||
|
"$linuxcnc_root/src/emc/rs274ngc/interp_internal.hh" >/dev/null
|
||||||
|
grep -F 'int Interp::save_parameters(const char *filename,' \
|
||||||
|
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
|
||||||
|
grep -F 'LIBRS274SRCS := $(addprefix emc/rs274ngc/,' \
|
||||||
|
"$linuxcnc_root/src/emc/rs274ngc/Submakefile" >/dev/null
|
||||||
|
grep -F 'LIBTOOLDATA_SRCS += emc/tooldata/tooldata_common.cc' \
|
||||||
|
"$linuxcnc_root/src/emc/tooldata/Submakefile" >/dev/null
|
||||||
|
grep -F 'LIBLCNCINISRCS := emc/ini/inifile.cc' \
|
||||||
|
"$linuxcnc_root/src/emc/ini/Submakefile" >/dev/null
|
||||||
|
grep -F 'LIBPPSRCS := $(addprefix emc/pythonplugin/,' \
|
||||||
|
"$linuxcnc_root/src/emc/pythonplugin/Submakefile" >/dev/null
|
||||||
|
|
||||||
|
grep -F 'LinuxCNC workstreams source map' docs/linuxcnc-workstreams-source-map.md >/dev/null
|
||||||
|
grep -F 'must not add CNC' docs/linuxcnc-workstreams-source-map.md >/dev/null
|
||||||
|
grep -F 'temporary smoke parser' docs/linuxcnc-workstreams-source-map.md >/dev/null
|
||||||
|
grep -F 'Every active workstream must have at least one source-map document and guard' \
|
||||||
|
docs/linuxcnc-workstreams-source-map.md >/dev/null
|
||||||
|
grep -F 'docs/linuxcnc-source-policy.md` must keep the same one-context workflow' \
|
||||||
|
docs/linuxcnc-workstreams-source-map.md >/dev/null
|
||||||
|
grep -F 'Browser filesystem persistence remains OPFS-only.' docs/linuxcnc-workstreams-source-map.md >/dev/null
|
||||||
|
grep -F 'Smoke coverage remains verification only' docs/linuxcnc-workstreams-source-map.md >/dev/null
|
||||||
|
grep -F 'Keep the change inside one active workstream context unless a dependency is' \
|
||||||
|
docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
grep -F 'Each pass should advance one reasonable, reviewable context.' \
|
||||||
|
docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
grep -F 'Keep the six active workstream contexts synchronized with' \
|
||||||
|
docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
grep -F '`docs/linuxcnc-workstreams-source-map.md` so source-map guards' \
|
||||||
|
docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
grep -F 'Smoke tests are verification guardrails. They must not be expanded with new' \
|
||||||
|
docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
grep -F 'Do not mix generator/table work, manifests, OPFS/browser work, and build' \
|
||||||
|
docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
grep -F 'Browser-side wasm filesystem behavior must use OPFS-backed storage.' \
|
||||||
|
docs/linuxcnc-source-policy.md >/dev/null
|
||||||
|
|
||||||
|
python3 - <<'PY'
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
doc = Path("docs/linuxcnc-workstreams-source-map.md").read_text(encoding="utf-8")
|
||||||
|
policy = Path("docs/linuxcnc-source-policy.md").read_text(encoding="utf-8")
|
||||||
|
expected = {
|
||||||
|
"switchkins/remap": [
|
||||||
|
"docs/linuxcnc-switchkins-remap-source-map.md",
|
||||||
|
"check-linuxcnc-switchkins-remap-source-map.sh",
|
||||||
|
],
|
||||||
|
"linuxcnc-kinematics": [
|
||||||
|
"docs/linuxcnc-kinematics-source-map.md",
|
||||||
|
"check-linuxcnc-kinematics-source-map.sh",
|
||||||
|
],
|
||||||
|
"WASM filesystem and OPFS persistence": [
|
||||||
|
"docs/linuxcnc-opfs-source-map.md",
|
||||||
|
"docs/linuxcnc-wasm-shims-source-map.md",
|
||||||
|
"docs/linuxcnc-wasm-blockers-source-map.md",
|
||||||
|
"docs/linuxcnc-tooldata-source-map.md",
|
||||||
|
"check-linuxcnc-opfs-source-map.sh",
|
||||||
|
"check-linuxcnc-wasm-shims-source-map.sh",
|
||||||
|
"check-linuxcnc-wasm-blockers-source-map.sh",
|
||||||
|
"check-linuxcnc-tooldata-source-map.sh",
|
||||||
|
],
|
||||||
|
"browser app integration": [
|
||||||
|
"docs/linuxcnc-browser-app-source-map.md",
|
||||||
|
"check-linuxcnc-browser-app-source-map.sh",
|
||||||
|
],
|
||||||
|
"Node and browser smoke coverage": [
|
||||||
|
"docs/linuxcnc-wasm-smoke-source-map.md",
|
||||||
|
"check-linuxcnc-wasm-smoke-source-map.sh",
|
||||||
|
],
|
||||||
|
"source-link, build, and documentation constraints": [
|
||||||
|
"docs/linuxcnc-rs274-source-map.md",
|
||||||
|
"docs/linuxcnc-build-source-map.md",
|
||||||
|
"docs/linuxcnc-tooldata-source-map.md",
|
||||||
|
"check-linuxcnc-rs274-source-map.sh",
|
||||||
|
"check-linuxcnc-build-source-map.sh",
|
||||||
|
"check-linuxcnc-tooldata-source-map.sh",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
for workstream, paths in expected.items():
|
||||||
|
if workstream not in doc:
|
||||||
|
raise SystemExit(f"missing workstream row in docs/linuxcnc-workstreams-source-map.md: {workstream}")
|
||||||
|
policy_terms = {
|
||||||
|
"switchkins/remap": ["switchkins/remap", "M428/M429/M430"],
|
||||||
|
"linuxcnc-kinematics": ["kinematics manifest", "RTCP coverage"],
|
||||||
|
"WASM filesystem and OPFS persistence": ["OPFS", "filesystem"],
|
||||||
|
"browser app integration": ["Browser app", "wasm bridge"],
|
||||||
|
"Node and browser smoke coverage": ["Smoke tests are verification guardrails", "temporary smoke parser"],
|
||||||
|
"source-link, build, and documentation constraints": ["source-link", "build-policy"],
|
||||||
|
}[workstream]
|
||||||
|
if not any(term in policy for term in policy_terms):
|
||||||
|
raise SystemExit(f"docs/linuxcnc-source-policy.md no longer references workstream boundary: {workstream}")
|
||||||
|
for path in paths:
|
||||||
|
if path not in doc:
|
||||||
|
raise SystemExit(f"missing source-map path in workstream index: {path}")
|
||||||
|
if not Path(path).exists():
|
||||||
|
raise SystemExit(f"source-map path listed in workstream index does not exist: {path}")
|
||||||
|
if path.startswith("check-") and not Path(path).stat().st_mode & 0o111:
|
||||||
|
raise SystemExit(f"source-map guard is not executable: {path}")
|
||||||
|
|
||||||
|
for test_entry in ["test-native.sh", "test-linuxcnc-source-link.sh", "build-wasm.sh"]:
|
||||||
|
text = Path(test_entry).read_text(encoding="utf-8")
|
||||||
|
for guard in sorted({path for paths in expected.values() for path in paths if path.startswith("check-")}):
|
||||||
|
if f"./{guard}" not in text:
|
||||||
|
raise SystemExit(f"{test_entry} does not run workstream source-map guard: {guard}")
|
||||||
|
PY
|
||||||
@@ -5,6 +5,9 @@ option(CNC_SIM_ENABLE_LINUXCNC_BRIDGE "Build the experimental LinuxCNC canon bri
|
|||||||
option(CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND "Build the native LinuxCNC librs274 backend" OFF)
|
option(CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND "Build the native LinuxCNC librs274 backend" OFF)
|
||||||
option(CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE "Compile the browser-safe subset of LinuxCNC rs274 sources" OFF)
|
option(CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE "Compile the browser-safe subset of LinuxCNC rs274 sources" OFF)
|
||||||
option(CNC_SIM_ENABLE_SMOKE_BACKEND "Build the legacy explicit smoke G-code backend test harness" OFF)
|
option(CNC_SIM_ENABLE_SMOKE_BACKEND "Build the legacy explicit smoke G-code backend test harness" OFF)
|
||||||
|
if(CNC_SIM_ENABLE_SMOKE_BACKEND AND CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE)
|
||||||
|
message(FATAL_ERROR "CNC_SIM_ENABLE_SMOKE_BACKEND cannot be combined with CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE")
|
||||||
|
endif()
|
||||||
set(CNC_SIM_LINUXCNC_ROOT "" CACHE PATH "LinuxCNC source root for the experimental bridge")
|
set(CNC_SIM_LINUXCNC_ROOT "" CACHE PATH "LinuxCNC source root for the experimental bridge")
|
||||||
set(CNC_SIM_LINUXCNC_WASM_SOURCE_MANIFEST
|
set(CNC_SIM_LINUXCNC_WASM_SOURCE_MANIFEST
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/../linuxcnc-rs274-wasm-source-files.txt"
|
"${CMAKE_CURRENT_SOURCE_DIR}/../linuxcnc-rs274-wasm-source-files.txt"
|
||||||
|
|||||||
@@ -234,6 +234,41 @@ void CanonEventSink::set_xy_rotation(double angle_degrees, int line) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CanonEventSink::use_length_units(double scale, int line) {
|
void CanonEventSink::use_length_units(double scale, int line) {
|
||||||
|
use_length_units(scale, line, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanonEventSink::use_length_units(double scale, int line, bool rescale_linear_state) {
|
||||||
|
if (rescale_linear_state && unit_scale_ != 0.0 && scale != unit_scale_) {
|
||||||
|
const double factor = unit_scale_ / scale;
|
||||||
|
// LinuxCNC src/emc/rs274ngc/interp_convert.cc convert_length_units()
|
||||||
|
// rescales linear current axes on G20/G21; A/B/C remain angular.
|
||||||
|
position_.x *= factor;
|
||||||
|
position_.y *= factor;
|
||||||
|
position_.z *= factor;
|
||||||
|
position_.u *= factor;
|
||||||
|
position_.v *= factor;
|
||||||
|
position_.w *= factor;
|
||||||
|
tool_length_offset_.x *= factor;
|
||||||
|
tool_length_offset_.y *= factor;
|
||||||
|
tool_length_offset_.z *= factor;
|
||||||
|
tool_length_offset_.u *= factor;
|
||||||
|
tool_length_offset_.v *= factor;
|
||||||
|
tool_length_offset_.w *= factor;
|
||||||
|
probe_position_.x *= factor;
|
||||||
|
probe_position_.y *= factor;
|
||||||
|
probe_position_.z *= factor;
|
||||||
|
probe_position_.u *= factor;
|
||||||
|
probe_position_.v *= factor;
|
||||||
|
probe_position_.w *= factor;
|
||||||
|
if (has_pending_motion_start_) {
|
||||||
|
pending_motion_start_.x *= factor;
|
||||||
|
pending_motion_start_.y *= factor;
|
||||||
|
pending_motion_start_.z *= factor;
|
||||||
|
pending_motion_start_.u *= factor;
|
||||||
|
pending_motion_start_.v *= factor;
|
||||||
|
pending_motion_start_.w *= factor;
|
||||||
|
}
|
||||||
|
}
|
||||||
unit_scale_ = scale;
|
unit_scale_ = scale;
|
||||||
CncSimEvent event = base_event(CNC_SIM_EVENT_SET_UNITS, line);
|
CncSimEvent event = base_event(CNC_SIM_EVENT_SET_UNITS, line);
|
||||||
event.feed = scale;
|
event.feed = scale;
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ public:
|
|||||||
void set_xy_rotation(double angle_degrees, int line);
|
void set_xy_rotation(double angle_degrees, int line);
|
||||||
|
|
||||||
void use_length_units(double scale, int line);
|
void use_length_units(double scale, int line);
|
||||||
|
void use_length_units(double scale, int line, bool rescale_linear_state);
|
||||||
void select_plane(int plane, int line);
|
void select_plane(int plane, int line);
|
||||||
void set_traverse_rate(double rate);
|
void set_traverse_rate(double rate);
|
||||||
void set_feed_rate(double feed, int line);
|
void set_feed_rate(double feed, int line);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
struct CncSimHandle {
|
struct CncSimHandle {
|
||||||
CncSimDialect dialect = CNC_SIM_DIALECT_LINUXCNC;
|
CncSimDialect dialect = CNC_SIM_DIALECT_LINUXCNC;
|
||||||
GcodeBackendKind backend = GcodeBackendKind::LinuxCncRs274;
|
GcodeBackendKind backend = GcodeBackendKind::LinuxCncRs274;
|
||||||
|
GcodeBackendOptions backend_options;
|
||||||
std::string last_error;
|
std::string last_error;
|
||||||
CanonEventSink sink;
|
CanonEventSink sink;
|
||||||
};
|
};
|
||||||
@@ -96,6 +97,72 @@ bool contains_string_value(const std::string &compact, const char *key, const ch
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool find_json_string_value(const std::string &json, const char *key, std::string *value) {
|
||||||
|
const std::string needle = std::string("\"") + key + "\"";
|
||||||
|
size_t pos = 0;
|
||||||
|
while ((pos = json.find(needle, pos)) != std::string::npos) {
|
||||||
|
pos += needle.size();
|
||||||
|
while (pos < json.size() && std::isspace(static_cast<unsigned char>(json[pos]))) {
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
if (pos >= json.size() || json[pos] != ':') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
++pos;
|
||||||
|
while (pos < json.size() && std::isspace(static_cast<unsigned char>(json[pos]))) {
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
if (pos >= json.size() || json[pos] != '"') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
++pos;
|
||||||
|
std::string parsed;
|
||||||
|
bool escaped = false;
|
||||||
|
for (; pos < json.size(); ++pos) {
|
||||||
|
const char ch = json[pos];
|
||||||
|
if (escaped) {
|
||||||
|
switch (ch) {
|
||||||
|
case '"':
|
||||||
|
case '\\':
|
||||||
|
case '/':
|
||||||
|
parsed.push_back(ch);
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
parsed.push_back('\n');
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
parsed.push_back('\r');
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
parsed.push_back('\t');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
parsed.push_back(ch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
escaped = false;
|
||||||
|
} else if (ch == '\\') {
|
||||||
|
escaped = true;
|
||||||
|
} else if (ch == '"') {
|
||||||
|
*value = parsed;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
parsed.push_back(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool find_json_string_any(const std::string &json, std::string *value, const char *first) {
|
||||||
|
return find_json_string_value(json, first, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Keys>
|
||||||
|
bool find_json_string_any(const std::string &json, std::string *value, const char *first, Keys... rest) {
|
||||||
|
return find_json_string_value(json, first, value) || find_json_string_any(json, value, rest...);
|
||||||
|
}
|
||||||
|
|
||||||
bool contains_switchkins_config_alias(const std::string &compact, const char *alias) {
|
bool contains_switchkins_config_alias(const std::string &compact, const char *alias) {
|
||||||
return contains_string_value(compact, "switchkins", alias) ||
|
return contains_string_value(compact, "switchkins", alias) ||
|
||||||
contains_string_value(compact, "remap", alias) ||
|
contains_string_value(compact, "remap", alias) ||
|
||||||
@@ -300,6 +367,20 @@ int apply_config(CncSimHandle *handle, const std::string &json) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string ini_file_name;
|
||||||
|
if (find_json_string_any(json,
|
||||||
|
&ini_file_name,
|
||||||
|
"ini",
|
||||||
|
"iniFile",
|
||||||
|
"inifile",
|
||||||
|
"iniFileName",
|
||||||
|
"inifilename",
|
||||||
|
"ini_file",
|
||||||
|
"ini_file_name",
|
||||||
|
"INI_FILE_NAME")) {
|
||||||
|
handle->backend_options.ini_file_name = ini_file_name;
|
||||||
|
}
|
||||||
|
|
||||||
bool rtcp_enabled = false;
|
bool rtcp_enabled = false;
|
||||||
double tool_length = 0.0;
|
double tool_length = 0.0;
|
||||||
const bool has_rtcp_enabled = contains_bool_value(compact, "rtcp", &rtcp_enabled) ||
|
const bool has_rtcp_enabled = contains_bool_value(compact, "rtcp", &rtcp_enabled) ||
|
||||||
@@ -383,7 +464,12 @@ int cnc_sim_parse_program(CncSimHandle *handle, const char *program, size_t prog
|
|||||||
}
|
}
|
||||||
|
|
||||||
handle->last_error.clear();
|
handle->last_error.clear();
|
||||||
const int rc = parse_gcode_with_backend(handle->backend, handle->sink, program, program_len, &handle->last_error);
|
const int rc = parse_gcode_with_backend(handle->backend,
|
||||||
|
handle->sink,
|
||||||
|
program,
|
||||||
|
program_len,
|
||||||
|
handle->backend_options,
|
||||||
|
&handle->last_error);
|
||||||
if (rc != 0 && handle->last_error.empty()) {
|
if (rc != 0 && handle->last_error.empty()) {
|
||||||
handle->last_error = "program parse failed";
|
handle->last_error = "program parse failed";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ int parse_gcode_with_backend(GcodeBackendKind backend,
|
|||||||
CanonEventSink &sink,
|
CanonEventSink &sink,
|
||||||
const char *program,
|
const char *program,
|
||||||
size_t program_len,
|
size_t program_len,
|
||||||
|
const GcodeBackendOptions &options,
|
||||||
std::string *error) {
|
std::string *error) {
|
||||||
switch (backend) {
|
switch (backend) {
|
||||||
case GcodeBackendKind::Smoke: {
|
case GcodeBackendKind::Smoke: {
|
||||||
@@ -37,7 +38,7 @@ int parse_gcode_with_backend(GcodeBackendKind backend,
|
|||||||
}
|
}
|
||||||
case GcodeBackendKind::LinuxCncRs274:
|
case GcodeBackendKind::LinuxCncRs274:
|
||||||
#ifdef CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND
|
#ifdef CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND
|
||||||
return parse_linuxcnc_rs274_backend(sink, program, program_len, error);
|
return parse_linuxcnc_rs274_backend(sink, program, program_len, options, error);
|
||||||
#else
|
#else
|
||||||
if (error) {
|
if (error) {
|
||||||
*error = "linuxcnc-rs274 backend is not compiled into this build";
|
*error = "linuxcnc-rs274 backend is not compiled into this build";
|
||||||
|
|||||||
@@ -10,11 +10,15 @@ enum class GcodeBackendKind {
|
|||||||
LinuxCncRs274,
|
LinuxCncRs274,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct GcodeBackendOptions {
|
||||||
|
std::string ini_file_name;
|
||||||
|
};
|
||||||
|
|
||||||
const char *gcode_backend_name(GcodeBackendKind backend);
|
const char *gcode_backend_name(GcodeBackendKind backend);
|
||||||
|
|
||||||
int parse_gcode_with_backend(GcodeBackendKind backend,
|
int parse_gcode_with_backend(GcodeBackendKind backend,
|
||||||
CanonEventSink &sink,
|
CanonEventSink &sink,
|
||||||
const char *program,
|
const char *program,
|
||||||
size_t program_len,
|
size_t program_len,
|
||||||
|
const GcodeBackendOptions &options,
|
||||||
std::string *error);
|
std::string *error);
|
||||||
|
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ void INIT_CANON() {
|
|||||||
void USE_LENGTH_UNITS(CANON_UNITS units) {
|
void USE_LENGTH_UNITS(CANON_UNITS units) {
|
||||||
trace_call("USE_LENGTH_UNITS");
|
trace_call("USE_LENGTH_UNITS");
|
||||||
if (active_sink) {
|
if (active_sink) {
|
||||||
active_sink->use_length_units(units_to_scale(units), event_line());
|
active_sink->use_length_units(units_to_scale(units), event_line(), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,10 +211,10 @@ void SET_FEED_RATE(double rate) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SET_SPINDLE_SPEED(int, double speed) {
|
void SET_SPINDLE_SPEED(int spindle, double speed) {
|
||||||
trace_call("SET_SPINDLE_SPEED");
|
trace_call("SET_SPINDLE_SPEED");
|
||||||
if (active_sink) {
|
if (active_sink) {
|
||||||
active_sink->set_spindle_speed(speed, event_line());
|
active_sink->set_spindle_speed(spindle, speed, event_line());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,6 +402,15 @@ void SET_MOTION_CONTROL_MODE(CANON_MOTION_MODE mode, double tolerance) {
|
|||||||
|
|
||||||
void SET_NAIVECAM_TOLERANCE(double tolerance) {
|
void SET_NAIVECAM_TOLERANCE(double tolerance) {
|
||||||
naivecam_tolerance = tolerance;
|
naivecam_tolerance = tolerance;
|
||||||
|
if (active_sink) {
|
||||||
|
CncSimEvent event{};
|
||||||
|
event.version = 1;
|
||||||
|
event.type = CNC_SIM_EVENT_COMMENT;
|
||||||
|
event.line = event_line();
|
||||||
|
event.feed = tolerance;
|
||||||
|
event.reserved = 641;
|
||||||
|
active_sink->emit_raw(event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SET_CUTTER_RADIUS_COMPENSATION(double) {
|
void SET_CUTTER_RADIUS_COMPENSATION(double) {
|
||||||
@@ -477,21 +486,21 @@ void SET_SPINDLE_MODE(int spindle, double mode) {
|
|||||||
void SPINDLE_RETRACT_TRAVERSE() {
|
void SPINDLE_RETRACT_TRAVERSE() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void START_SPINDLE_CLOCKWISE(int, int) {
|
void START_SPINDLE_CLOCKWISE(int spindle, int) {
|
||||||
if (active_sink) {
|
if (active_sink) {
|
||||||
active_sink->start_spindle(1, event_line());
|
active_sink->start_spindle(spindle, 1, event_line());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void START_SPINDLE_COUNTERCLOCKWISE(int, int) {
|
void START_SPINDLE_COUNTERCLOCKWISE(int spindle, int) {
|
||||||
if (active_sink) {
|
if (active_sink) {
|
||||||
active_sink->start_spindle(-1, event_line());
|
active_sink->start_spindle(spindle, -1, event_line());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void STOP_SPINDLE_TURNING(int) {
|
void STOP_SPINDLE_TURNING(int spindle) {
|
||||||
if (active_sink) {
|
if (active_sink) {
|
||||||
active_sink->stop_spindle(event_line());
|
active_sink->stop_spindle(spindle, event_line());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -773,11 +782,16 @@ int GET_EXTERNAL_FLOOD() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CANON_UNITS GET_EXTERNAL_LENGTH_UNIT_TYPE() {
|
CANON_UNITS GET_EXTERNAL_LENGTH_UNIT_TYPE() {
|
||||||
return active_sink && active_sink->unit_scale() == 25.4 ? CANON_UNITS_INCHES : CANON_UNITS_MM;
|
// LinuxCNC src/emc/task/emccanon.cc derives this from INI machine/user
|
||||||
|
// units, not from the active G20/G21 program units.
|
||||||
|
return CANON_UNITS_MM;
|
||||||
}
|
}
|
||||||
|
|
||||||
double GET_EXTERNAL_LENGTH_UNITS() {
|
double GET_EXTERNAL_LENGTH_UNITS() {
|
||||||
return active_sink ? active_sink->unit_scale() : 1.0;
|
// LinuxCNC src/emc/task/emccanon.cc returns traj.linearUnits here:
|
||||||
|
// external/user units per internal millimeter. The simulator has no INI
|
||||||
|
// machine-units bridge yet, so the source-linked default is metric.
|
||||||
|
return 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
double GET_EXTERNAL_ANGLE_UNITS() {
|
double GET_EXTERNAL_ANGLE_UNITS() {
|
||||||
|
|||||||
@@ -82,6 +82,37 @@ bool file_mode_enabled() {
|
|||||||
return std::getenv("CNC_SIM_RS274_FILE_MODE") != nullptr;
|
return std::getenv("CNC_SIM_RS274_FILE_MODE") != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ScopedEnvironmentVariable {
|
||||||
|
public:
|
||||||
|
ScopedEnvironmentVariable(const char *name, const std::string &value) : name_(name), active_(!value.empty()) {
|
||||||
|
if (!active_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (const char *old = std::getenv(name_)) {
|
||||||
|
had_old_ = true;
|
||||||
|
old_ = old;
|
||||||
|
}
|
||||||
|
setenv(name_, value.c_str(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
~ScopedEnvironmentVariable() {
|
||||||
|
if (!active_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (had_old_) {
|
||||||
|
setenv(name_, old_.c_str(), 1);
|
||||||
|
} else {
|
||||||
|
unsetenv(name_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const char *name_;
|
||||||
|
bool active_;
|
||||||
|
bool had_old_ = false;
|
||||||
|
std::string old_;
|
||||||
|
};
|
||||||
|
|
||||||
#ifndef CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE
|
#ifndef CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE
|
||||||
bool write_temp_program(const char *program,
|
bool write_temp_program(const char *program,
|
||||||
size_t program_len,
|
size_t program_len,
|
||||||
@@ -175,6 +206,7 @@ int execute_file_mode(InterpBase *interp,
|
|||||||
int parse_linuxcnc_rs274_backend(CanonEventSink &sink,
|
int parse_linuxcnc_rs274_backend(CanonEventSink &sink,
|
||||||
const char *program,
|
const char *program,
|
||||||
size_t program_len,
|
size_t program_len,
|
||||||
|
const GcodeBackendOptions &options,
|
||||||
std::string *error) {
|
std::string *error) {
|
||||||
if (!program && program_len != 0) {
|
if (!program && program_len != 0) {
|
||||||
if (error) {
|
if (error) {
|
||||||
@@ -183,14 +215,36 @@ int parse_linuxcnc_rs274_backend(CanonEventSink &sink,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LinuxCNC source basis: rs274ngc_pre.cc uses
|
||||||
|
// RS274NGC_PARAMETER_FILE_NAME_DEFAULT when the external parameter file
|
||||||
|
// name is empty. Reset the bridge every parse so a prior native env
|
||||||
|
// override does not leak into later default parses.
|
||||||
if (const char *parameter_file = std::getenv("CNC_SIM_RS274_VAR")) {
|
if (const char *parameter_file = std::getenv("CNC_SIM_RS274_VAR")) {
|
||||||
SET_PARAMETER_FILE_NAME(parameter_file);
|
SET_PARAMETER_FILE_NAME(parameter_file);
|
||||||
|
} else {
|
||||||
|
SET_PARAMETER_FILE_NAME("rs274ngc.var");
|
||||||
}
|
}
|
||||||
cnc_sim_init_minimal_linuxcnc_tooldata();
|
cnc_sim_init_minimal_linuxcnc_tooldata();
|
||||||
sink.clear_callback_status();
|
sink.clear_callback_status();
|
||||||
cnc_sim_linuxcnc_set_canon_sink(&sink);
|
cnc_sim_linuxcnc_set_canon_sink(&sink);
|
||||||
|
|
||||||
|
// LinuxCNC source basis: rs274ngc_pre.cc and interp_namedparams.cc read
|
||||||
|
// INI_FILE_NAME for startup and #<_ini[section]key> named parameters.
|
||||||
|
ScopedEnvironmentVariable ini_file_env("INI_FILE_NAME", options.ini_file_name);
|
||||||
InterpBase *interp = makeInterp();
|
InterpBase *interp = makeInterp();
|
||||||
|
if (!options.ini_file_name.empty()) {
|
||||||
|
// LinuxCNC source basis: emctask.cc calls Interp::ini_load() before
|
||||||
|
// Interp::init(), and rs274ngc_pre.cc ini_load() maps
|
||||||
|
// [RS274NGC] PARAMETER_FILE through SET_PARAMETER_FILE_NAME().
|
||||||
|
if (interp->ini_load(options.ini_file_name.c_str()) != INTERP_OK) {
|
||||||
|
if (error) {
|
||||||
|
*error = "ini_load failed: " + options.ini_file_name;
|
||||||
|
}
|
||||||
|
delete interp;
|
||||||
|
cnc_sim_linuxcnc_set_canon_sink(nullptr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
int status = interp->init();
|
int status = interp->init();
|
||||||
if (status != INTERP_OK) {
|
if (status != INTERP_OK) {
|
||||||
if (error) {
|
if (error) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "canon_event_sink.h"
|
#include "canon_event_sink.h"
|
||||||
|
#include "gcode_backend.h"
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -8,5 +9,5 @@
|
|||||||
int parse_linuxcnc_rs274_backend(CanonEventSink &sink,
|
int parse_linuxcnc_rs274_backend(CanonEventSink &sink,
|
||||||
const char *program,
|
const char *program,
|
||||||
size_t program_len,
|
size_t program_len,
|
||||||
|
const GcodeBackendOptions &options,
|
||||||
std::string *error);
|
std::string *error);
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,11 @@
|
|||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <system_error>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@@ -61,6 +64,34 @@ private:
|
|||||||
std::string old_;
|
std::string old_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ScopedTempDir {
|
||||||
|
public:
|
||||||
|
explicit ScopedTempDir(const char *prefix) {
|
||||||
|
const char *tmpdir = std::getenv("TMPDIR");
|
||||||
|
std::string tmpl = std::string(tmpdir ? tmpdir : "/tmp") + "/" + prefix + ".XXXXXX";
|
||||||
|
std::vector<char> buffer(tmpl.begin(), tmpl.end());
|
||||||
|
buffer.push_back('\0');
|
||||||
|
char *dir = mkdtemp(buffer.data());
|
||||||
|
if (!dir) {
|
||||||
|
std::perror("mkdtemp");
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
path_ = dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
~ScopedTempDir() {
|
||||||
|
std::error_code error;
|
||||||
|
std::filesystem::remove_all(path_, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string file(const char *name) const {
|
||||||
|
return path_ + "/" + name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string path_;
|
||||||
|
};
|
||||||
|
|
||||||
bool saw_comment_state(const std::vector<CncSimEvent> &events,
|
bool saw_comment_state(const std::vector<CncSimEvent> &events,
|
||||||
int line,
|
int line,
|
||||||
int reserved,
|
int reserved,
|
||||||
@@ -103,6 +134,17 @@ bool saw_kinematics_switch(const std::vector<CncSimEvent> &events, int line, int
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_ini_file_name_field(const std::string &field) {
|
||||||
|
return field == "ini" ||
|
||||||
|
field == "iniFile" ||
|
||||||
|
field == "inifile" ||
|
||||||
|
field == "iniFileName" ||
|
||||||
|
field == "inifilename" ||
|
||||||
|
field == "ini_file" ||
|
||||||
|
field == "ini_file_name" ||
|
||||||
|
field == "INI_FILE_NAME";
|
||||||
|
}
|
||||||
|
|
||||||
bool expect_switchkins_remap_config(const std::string &json,
|
bool expect_switchkins_remap_config(const std::string &json,
|
||||||
int m428_type,
|
int m428_type,
|
||||||
int m429_type,
|
int m429_type,
|
||||||
@@ -168,6 +210,8 @@ bool expect_switchkins_remap_config(const std::string &json,
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
|
ScopedTempDir temp_dir("cnc_sim_api_linuxcnc_rs274_smoke");
|
||||||
|
|
||||||
{
|
{
|
||||||
std::vector<CncSimEvent> default_events;
|
std::vector<CncSimEvent> default_events;
|
||||||
CncSimHandle *default_sim = cnc_sim_create();
|
CncSimHandle *default_sim = cnc_sim_create();
|
||||||
@@ -196,6 +240,108 @@ int main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const std::string ini_parameter_var_path = temp_dir.file("ini_parameter.var");
|
||||||
|
{
|
||||||
|
std::ofstream var_file(ini_parameter_var_path);
|
||||||
|
var_file << "5001\t44.000000\n";
|
||||||
|
}
|
||||||
|
const std::string rs274_ini_path = temp_dir.file("rs274_parameter.ini");
|
||||||
|
{
|
||||||
|
std::ofstream ini_file(rs274_ini_path);
|
||||||
|
ini_file << "[RS274NGC]\n";
|
||||||
|
ini_file << "PARAMETER_FILE = " << ini_parameter_var_path << "\n";
|
||||||
|
ini_file << "[SETUP]\n";
|
||||||
|
ini_file << "XPOS = 12\n";
|
||||||
|
}
|
||||||
|
std::vector<CncSimEvent> ini_events;
|
||||||
|
CncSimHandle *ini_sim = cnc_sim_create();
|
||||||
|
cnc_sim_set_event_callback(ini_sim, collect_event, &ini_events);
|
||||||
|
const std::string ini_config =
|
||||||
|
std::string("{\"backend\":\"linuxcnc-rs274\",\"iniFileName\":\"") +
|
||||||
|
rs274_ini_path +
|
||||||
|
"\"}";
|
||||||
|
bool ini_ok = true;
|
||||||
|
ini_ok &= expect(cnc_sim_load_config_json(ini_sim, ini_config.c_str(), ini_config.size()) == 0,
|
||||||
|
cnc_sim_last_error(ini_sim));
|
||||||
|
ScopedEnv file_mode_env("CNC_SIM_RS274_FILE_MODE", "1");
|
||||||
|
const char ini_parameter_check_program[] =
|
||||||
|
"G21 G90 G17\n"
|
||||||
|
"F100\n"
|
||||||
|
"G1 X#5001\n"
|
||||||
|
"G1 X#<_ini[setup]xpos>\n"
|
||||||
|
"M30\n";
|
||||||
|
const int ini_parse_rc = cnc_sim_parse_program(ini_sim,
|
||||||
|
ini_parameter_check_program,
|
||||||
|
sizeof(ini_parameter_check_program) - 1);
|
||||||
|
const std::string ini_parse_error = cnc_sim_last_error(ini_sim);
|
||||||
|
ini_ok &= expect(ini_parse_rc == 0, ini_parse_error.c_str());
|
||||||
|
bool saw_ini_parameter_restore = false;
|
||||||
|
bool saw_ini_named_parameter = false;
|
||||||
|
for (const auto &event : ini_events) {
|
||||||
|
saw_ini_parameter_restore = saw_ini_parameter_restore ||
|
||||||
|
(event.type == CNC_SIM_EVENT_LINEAR_FEED &&
|
||||||
|
event.line == 3 &&
|
||||||
|
near(event.end.x, 44.0));
|
||||||
|
saw_ini_named_parameter = saw_ini_named_parameter ||
|
||||||
|
(event.type == CNC_SIM_EVENT_LINEAR_FEED &&
|
||||||
|
event.line == 4 &&
|
||||||
|
near(event.end.x, 12.0));
|
||||||
|
}
|
||||||
|
// LinuxCNC source basis: emctask.cc calls interp.ini_load(), and
|
||||||
|
// rs274ngc_pre.cc ini_load() maps [RS274NGC] PARAMETER_FILE through
|
||||||
|
// SET_PARAMETER_FILE_NAME before rs274_ngc_init() restores parameters.
|
||||||
|
ini_ok &= expect(saw_ini_parameter_restore,
|
||||||
|
"expected LinuxCNC INI PARAMETER_FILE to restore numbered parameters");
|
||||||
|
// LinuxCNC source basis: rs274ngc_pre.cc and interp_namedparams.cc
|
||||||
|
// read INI_FILE_NAME for #<_ini[section]key> lookups.
|
||||||
|
ini_ok &= expect(saw_ini_named_parameter,
|
||||||
|
"expected LinuxCNC config INI_FILE_NAME to back #<_ini[]> named parameters");
|
||||||
|
cnc_sim_destroy(ini_sim);
|
||||||
|
if (!ini_ok) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::vector<CncSimEvent> percent_events;
|
||||||
|
CncSimHandle *percent_sim = cnc_sim_create();
|
||||||
|
cnc_sim_set_event_callback(percent_sim, collect_event, &percent_events);
|
||||||
|
ScopedEnv file_mode_env("CNC_SIM_RS274_FILE_MODE", "1");
|
||||||
|
const char percent_program[] =
|
||||||
|
"%\n"
|
||||||
|
"G21 G90\n"
|
||||||
|
"F100\n"
|
||||||
|
"G1 X1\n"
|
||||||
|
"%\n"
|
||||||
|
"G1 X99\n";
|
||||||
|
bool percent_ok = true;
|
||||||
|
percent_ok &= expect(cnc_sim_parse_program(percent_sim,
|
||||||
|
percent_program,
|
||||||
|
sizeof(percent_program) - 1) == 0,
|
||||||
|
cnc_sim_last_error(percent_sim));
|
||||||
|
std::vector<int> feed_lines;
|
||||||
|
std::vector<double> feed_x;
|
||||||
|
for (const auto &event : percent_events) {
|
||||||
|
if (event.type == CNC_SIM_EVENT_LINEAR_FEED) {
|
||||||
|
feed_lines.push_back(event.line);
|
||||||
|
feed_x.push_back(event.end.x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// LinuxCNC source basis: rs274ngc_pre.cc open() enables percent-file
|
||||||
|
// mode when the first nonblank line is '%'; interp_read.cc read_text()
|
||||||
|
// stops at the matching '%', so later program text is ignored.
|
||||||
|
percent_ok &= expect(feed_lines.size() == 1 &&
|
||||||
|
feed_lines[0] == 4 &&
|
||||||
|
feed_x.size() == 1 &&
|
||||||
|
near(feed_x[0], 1.0),
|
||||||
|
"expected LinuxCNC percent-file mode to ignore trailing text");
|
||||||
|
cnc_sim_destroy(percent_sim);
|
||||||
|
if (!percent_ok) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const char config[] =
|
const char config[] =
|
||||||
"{\"backend\":\"linuxcnc-rs274\",\"rtcp\":{\"enabled\":true,\"toolLength\":100,\"toolLengths\":{\"7\":125}}}";
|
"{\"backend\":\"linuxcnc-rs274\",\"rtcp\":{\"enabled\":true,\"toolLength\":100,\"toolLengths\":{\"7\":125}}}";
|
||||||
const char program[] =
|
const char program[] =
|
||||||
@@ -1688,6 +1834,13 @@ int main() {
|
|||||||
#include "linuxcnc_switchkins_remap_config_cases.inc"
|
#include "linuxcnc_switchkins_remap_config_cases.inc"
|
||||||
};
|
};
|
||||||
for (const SourceBackedConfigCase &source_case : source_cases) {
|
for (const SourceBackedConfigCase &source_case : source_cases) {
|
||||||
|
// LinuxCNC rs274ngc_pre.cc ini_load() loads REMAP NGC/Python
|
||||||
|
// dependencies for INI_FILE_NAME. The native API probe above
|
||||||
|
// covers real INI loading; generated switchkins INI aliases stay
|
||||||
|
// covered by the table checker and smoke bridge probes.
|
||||||
|
if (is_ini_file_name_field(source_case.field)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const std::string json = std::string("{\"backend\":\"linuxcnc-rs274\",\"") +
|
const std::string json = std::string("{\"backend\":\"linuxcnc-rs274\",\"") +
|
||||||
source_case.field +
|
source_case.field +
|
||||||
"\":\"" +
|
"\":\"" +
|
||||||
|
|||||||
@@ -6163,6 +6163,48 @@ int main() {
|
|||||||
"expected LinuxCNC rotary/UVW persistent numbered parameters to reload from parameter file");
|
"expected LinuxCNC rotary/UVW persistent numbered parameters to reload from parameter file");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::string parameter_env_leak_guard_var_path = temp_dir.file("parameter_env_leak_guard.var");
|
||||||
|
{
|
||||||
|
std::ofstream var_file(parameter_env_leak_guard_var_path);
|
||||||
|
var_file << "5001\t17.000000\n";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ScopedEnv var_env("CNC_SIM_RS274_VAR", parameter_env_leak_guard_var_path.c_str());
|
||||||
|
const char parameter_env_leak_guard_set_program[] =
|
||||||
|
"#5001 = 91\n"
|
||||||
|
"M30\n";
|
||||||
|
events.clear();
|
||||||
|
ok &= expect(cnc_sim_parse_program(sim,
|
||||||
|
parameter_env_leak_guard_set_program,
|
||||||
|
sizeof(parameter_env_leak_guard_set_program) - 1) == 0,
|
||||||
|
cnc_sim_last_error(sim));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const char parameter_env_default_check_program[] =
|
||||||
|
"F100\n"
|
||||||
|
"O10 if [#5001 EQ 91]\n"
|
||||||
|
"G1 X1\n"
|
||||||
|
"O10 endif\n";
|
||||||
|
events.clear();
|
||||||
|
ok &= expect(cnc_sim_parse_program(sim,
|
||||||
|
parameter_env_default_check_program,
|
||||||
|
sizeof(parameter_env_default_check_program) - 1) == 0,
|
||||||
|
cnc_sim_last_error(sim));
|
||||||
|
bool saw_stale_env_parameter = false;
|
||||||
|
for (const auto &event : events) {
|
||||||
|
saw_stale_env_parameter = saw_stale_env_parameter ||
|
||||||
|
(event.type == CNC_SIM_EVENT_LINEAR_FEED &&
|
||||||
|
event.line == 3 &&
|
||||||
|
near(event.end.x, 1.0));
|
||||||
|
}
|
||||||
|
// LinuxCNC source basis: rs274ngc_pre.cc rs274_ngc_init() falls back
|
||||||
|
// to RS274NGC_PARAMETER_FILE_NAME_DEFAULT when the external parameter
|
||||||
|
// file name is empty. The project env override must not persist after
|
||||||
|
// CNC_SIM_RS274_VAR is unset.
|
||||||
|
ok &= expect(!saw_stale_env_parameter,
|
||||||
|
"expected LinuxCNC parameter env override not to leak into default parses");
|
||||||
|
}
|
||||||
|
|
||||||
const char volatile_global_set_program[] =
|
const char volatile_global_set_program[] =
|
||||||
"#31 = 7\n"
|
"#31 = 7\n"
|
||||||
"M30\n";
|
"M30\n";
|
||||||
|
|||||||
@@ -160,6 +160,7 @@ int main() {
|
|||||||
bool saw_adaptive_feed_off = false;
|
bool saw_adaptive_feed_off = false;
|
||||||
bool saw_feed_hold_on = false;
|
bool saw_feed_hold_on = false;
|
||||||
bool saw_feed_hold_off = false;
|
bool saw_feed_hold_off = false;
|
||||||
|
bool saw_naivecam_tolerance = false;
|
||||||
bool saw_speed_feed_sync_start = false;
|
bool saw_speed_feed_sync_start = false;
|
||||||
bool saw_speed_feed_sync_stop = false;
|
bool saw_speed_feed_sync_stop = false;
|
||||||
bool saw_arc = false;
|
bool saw_arc = false;
|
||||||
@@ -287,6 +288,10 @@ int main() {
|
|||||||
event.line == 29 &&
|
event.line == 29 &&
|
||||||
event.reserved == 53 &&
|
event.reserved == 53 &&
|
||||||
event.feed == 0.0);
|
event.feed == 0.0);
|
||||||
|
saw_naivecam_tolerance = saw_naivecam_tolerance || (event.type == CNC_SIM_EVENT_COMMENT &&
|
||||||
|
event.line == 30 &&
|
||||||
|
event.reserved == 641 &&
|
||||||
|
event.feed == 0.025);
|
||||||
saw_speed_feed_sync_start = saw_speed_feed_sync_start || (event.type == CNC_SIM_EVENT_COMMENT &&
|
saw_speed_feed_sync_start = saw_speed_feed_sync_start || (event.type == CNC_SIM_EVENT_COMMENT &&
|
||||||
event.reserved == 3301 &&
|
event.reserved == 3301 &&
|
||||||
event.feed == 1.25 &&
|
event.feed == 1.25 &&
|
||||||
@@ -387,6 +392,7 @@ int main() {
|
|||||||
ok &= expect(external_motion_mode, "expected external motion-control mode query to reflect SET_MOTION_CONTROL_MODE");
|
ok &= expect(external_motion_mode, "expected external motion-control mode query to reflect SET_MOTION_CONTROL_MODE");
|
||||||
ok &= expect(external_motion_tolerance, "expected external motion-control tolerance query to reflect SET_MOTION_CONTROL_MODE");
|
ok &= expect(external_motion_tolerance, "expected external motion-control tolerance query to reflect SET_MOTION_CONTROL_MODE");
|
||||||
ok &= expect(external_naivecam_tolerance, "expected external naivecam tolerance query to reflect SET_NAIVECAM_TOLERANCE");
|
ok &= expect(external_naivecam_tolerance, "expected external naivecam tolerance query to reflect SET_NAIVECAM_TOLERANCE");
|
||||||
|
ok &= expect(saw_naivecam_tolerance, "expected naivecam tolerance state event");
|
||||||
ok &= expect(external_block_delete_on, "expected block-delete query to reflect enable");
|
ok &= expect(external_block_delete_on, "expected block-delete query to reflect enable");
|
||||||
ok &= expect(external_block_delete_off, "expected block-delete query to reflect disable");
|
ok &= expect(external_block_delete_off, "expected block-delete query to reflect disable");
|
||||||
ok &= expect(external_optional_stop_on, "expected optional-stop query to reflect enable");
|
ok &= expect(external_optional_stop_on, "expected optional-stop query to reflect enable");
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// LinuxCNC source basis: RS274 Python/remap sources include Python.h through
|
||||||
|
// Boost.Python and use the C API declarations provided by this wasm-safe shim.
|
||||||
#include "boost/python/object.hpp"
|
#include "boost/python/object.hpp"
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#ifndef CNC_SIM_WASM_SHIMS_BOOST_NONCOPYABLE_HPP
|
#ifndef CNC_SIM_WASM_SHIMS_BOOST_NONCOPYABLE_HPP
|
||||||
#define CNC_SIM_WASM_SHIMS_BOOST_NONCOPYABLE_HPP
|
#define CNC_SIM_WASM_SHIMS_BOOST_NONCOPYABLE_HPP
|
||||||
|
|
||||||
|
// LinuxCNC source basis: src/emc/rs274ngc/interp_base.hh derives InterpBase
|
||||||
|
// from boost::noncopyable.
|
||||||
namespace boost {
|
namespace boost {
|
||||||
|
|
||||||
class noncopyable {
|
class noncopyable {
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// LinuxCNC source basis: interpmodule.cc, pyblock.cc, pyemctypes.cc, and
|
||||||
|
// array1.hh include boost/python/class.hpp for Python binding classes.
|
||||||
#include <boost/call_traits.hpp>
|
#include <boost/call_traits.hpp>
|
||||||
#include <boost/config.hpp>
|
#include <boost/config.hpp>
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// LinuxCNC source basis: array1.hh includes boost/python/converter/registry.hpp
|
||||||
|
// for registered class object lookup.
|
||||||
#include "boost/python/object.hpp"
|
#include "boost/python/object.hpp"
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// LinuxCNC source basis: canonmodule.cc and interpmodule.cc include
|
||||||
|
// boost/python/def.hpp for Boost.Python module functions.
|
||||||
#include "boost/python/object.hpp"
|
#include "boost/python/object.hpp"
|
||||||
|
|
||||||
namespace boost {
|
namespace boost {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// LinuxCNC source basis: pyarrays.cc includes boost/python/detail/caller.hpp
|
||||||
|
// for return_internal_reference support.
|
||||||
#include "boost/python/object.hpp"
|
#include "boost/python/object.hpp"
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// LinuxCNC source basis: interp_o_word.cc, interp_namedparams.cc, and
|
||||||
|
// rs274ngc_pre.cc use boost::python::dict for Python call kwargs.
|
||||||
#include "boost/python/object.hpp"
|
#include "boost/python/object.hpp"
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// LinuxCNC source basis: canonmodule.cc, boost_pyenum_macros.hh, and array1.hh
|
||||||
|
// use boost::python::enum_ for Python enum bindings.
|
||||||
#include "boost/python/object.hpp"
|
#include "boost/python/object.hpp"
|
||||||
|
|
||||||
namespace boost {
|
namespace boost {
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// LinuxCNC source basis: interpmodule.cc uses
|
||||||
|
// boost::python::register_exception_translator.
|
||||||
#include "boost/python/object.hpp"
|
#include "boost/python/object.hpp"
|
||||||
|
|
||||||
namespace boost {
|
namespace boost {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// LinuxCNC source basis: interp_python.cc, interp_namedparams.cc,
|
||||||
|
// rs274ngc_pre.cc, and python_plugin.cc use boost::python::extract.
|
||||||
#include "boost/python/object.hpp"
|
#include "boost/python/object.hpp"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// LinuxCNC source basis: interp_python.cc, rs274ngc_pre.cc, and
|
||||||
|
// python_plugin.cc use boost::python::import.
|
||||||
#include "boost/python/object.hpp"
|
#include "boost/python/object.hpp"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// LinuxCNC source basis: interp_o_word.cc, interp_namedparams.cc, and
|
||||||
|
// rs274ngc_pre.cc use boost::python::list while preparing Python call args.
|
||||||
#include "boost/python/object.hpp"
|
#include "boost/python/object.hpp"
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// LinuxCNC source basis: canonmodule.cc and interpmodule.cc use
|
||||||
|
// BOOST_PYTHON_MODULE for embedded Python module declarations.
|
||||||
#include "boost/python/object.hpp"
|
#include "boost/python/object.hpp"
|
||||||
|
|
||||||
#define BOOST_PYTHON_MODULE(name) extern "C" void init_##name()
|
#define BOOST_PYTHON_MODULE(name) extern "C" void init_##name()
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// LinuxCNC source basis: src/emc/rs274ngc/interp_python.cc,
|
||||||
|
// interp_namedparams.cc, rs274ngc_pre.cc, and src/emc/pythonplugin/python_plugin.hh
|
||||||
|
// use Boost.Python object/handle/extract APIs in Python/remap paths.
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@@ -82,6 +85,18 @@ struct _inittab {
|
|||||||
PyObject *(*initfunc)(void);
|
PyObject *(*initfunc)(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PyStatus {
|
||||||
|
int _type;
|
||||||
|
const char *func;
|
||||||
|
const char *err_msg;
|
||||||
|
int exitcode;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PyConfig {
|
||||||
|
wchar_t *program_name;
|
||||||
|
int buffered_stdio;
|
||||||
|
};
|
||||||
|
|
||||||
using PyCFunction = PyObject *(*)(PyObject *, PyObject *);
|
using PyCFunction = PyObject *(*)(PyObject *, PyObject *);
|
||||||
using getter = PyObject *(*)(PyObject *, void *);
|
using getter = PyObject *(*)(PyObject *, void *);
|
||||||
using setter = int (*)(PyObject *, PyObject *, void *);
|
using setter = int (*)(PyObject *, PyObject *, void *);
|
||||||
@@ -143,6 +158,7 @@ void Py_DecRef(PyObject *);
|
|||||||
int PyArg_ParseTuple(PyObject *, const char *, ...);
|
int PyArg_ParseTuple(PyObject *, const char *, ...);
|
||||||
int PyArg_VaParse(PyObject *, const char *, va_list);
|
int PyArg_VaParse(PyObject *, const char *, va_list);
|
||||||
PyObject *PyObject_CallMethod(PyObject *, char *, char *, ...);
|
PyObject *PyObject_CallMethod(PyObject *, char *, char *, ...);
|
||||||
|
PyObject *PyObject_Call(PyObject *, PyObject *, PyObject *);
|
||||||
PyObject *PyObject_GetAttrString(PyObject *, const char *);
|
PyObject *PyObject_GetAttrString(PyObject *, const char *);
|
||||||
int PyObject_SetAttrString(PyObject *, const char *, PyObject *);
|
int PyObject_SetAttrString(PyObject *, const char *, PyObject *);
|
||||||
int PyObject_IsTrue(PyObject *);
|
int PyObject_IsTrue(PyObject *);
|
||||||
@@ -162,6 +178,17 @@ Py_ssize_t PySequence_Length(PyObject *);
|
|||||||
PyObject *Py_BuildValue(const char *, ...);
|
PyObject *Py_BuildValue(const char *, ...);
|
||||||
PyObject *PyModule_Create(PyModuleDef *);
|
PyObject *PyModule_Create(PyModuleDef *);
|
||||||
int PyModule_AddObject(PyObject *, const char *, PyObject *);
|
int PyModule_AddObject(PyObject *, const char *, PyObject *);
|
||||||
|
int PyImport_ExtendInittab(_inittab *);
|
||||||
|
PyObject *PyImport_GetModuleDict(void);
|
||||||
|
PyObject *PyImport_AddModule(const char *);
|
||||||
|
int PyDict_SetItemString(PyObject *, const char *, PyObject *);
|
||||||
|
void PyConfig_InitPythonConfig(PyConfig *);
|
||||||
|
PyStatus PyConfig_SetString(PyConfig *, wchar_t **, const wchar_t *);
|
||||||
|
void PyConfig_Clear(PyConfig *);
|
||||||
|
PyStatus Py_InitializeFromConfig(PyConfig *);
|
||||||
|
wchar_t *Py_DecodeLocale(const char *, std::size_t *);
|
||||||
|
int PyRun_SimpleString(const char *);
|
||||||
|
const char *Py_GetVersion(void);
|
||||||
int PyType_Ready(PyTypeObject *);
|
int PyType_Ready(PyTypeObject *);
|
||||||
PyObject *PyType_GenericNew(PyTypeObject *, PyObject *, PyObject *);
|
PyObject *PyType_GenericNew(PyTypeObject *, PyObject *, PyObject *);
|
||||||
void *PyObject_NewShim(std::size_t, PyTypeObject *);
|
void *PyObject_NewShim(std::size_t, PyTypeObject *);
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// LinuxCNC source basis: array1.hh includes boost/python/object/class_detail.hpp
|
||||||
|
// for registered_class_object().
|
||||||
#include "boost/python/object.hpp"
|
#include "boost/python/object.hpp"
|
||||||
|
|
||||||
namespace boost {
|
namespace boost {
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// LinuxCNC source basis: interp_internal.hh forward-declares Boost.Python
|
||||||
|
// object pointers through boost/python/object_fwd.hpp.
|
||||||
namespace boost {
|
namespace boost {
|
||||||
namespace python {
|
namespace python {
|
||||||
|
|
||||||
@@ -7,4 +9,3 @@ class object;
|
|||||||
|
|
||||||
} // namespace python
|
} // namespace python
|
||||||
} // namespace boost
|
} // namespace boost
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// LinuxCNC source basis: pyarrays.cc uses boost::python::return_internal_reference
|
||||||
|
// for array wrapper bindings.
|
||||||
#include "boost/python/object.hpp"
|
#include "boost/python/object.hpp"
|
||||||
|
|
||||||
namespace boost {
|
namespace boost {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// LinuxCNC source basis: rs274ngc_pre.cc uses boost::python::scope to publish
|
||||||
|
// interpreter.this for Python/remap handlers.
|
||||||
#include "boost/python/object.hpp"
|
#include "boost/python/object.hpp"
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// LinuxCNC source basis: interp_python.cc and python_plugin.cc use
|
||||||
|
// boost::python::str while formatting Python exceptions.
|
||||||
#include "boost/python/object.hpp"
|
#include "boost/python/object.hpp"
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// LinuxCNC source basis: interpmodule.cc, pyblock.cc, pyparamclass.cc, and
|
||||||
|
// pyinterp1.cc include map_indexing_suite for Boost.Python map wrappers.
|
||||||
namespace boost {
|
namespace boost {
|
||||||
namespace python {
|
namespace python {
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// LinuxCNC source basis: interp_o_word.cc, interp_namedparams.cc, and
|
||||||
|
// rs274ngc_pre.cc use boost::python::tuple for Python call arguments.
|
||||||
#include "boost/python/object.hpp"
|
#include "boost/python/object.hpp"
|
||||||
|
|||||||
@@ -1,12 +1,30 @@
|
|||||||
#include "cnc_sim_api.h"
|
#include "cnc_sim_api.h"
|
||||||
|
#include "boost/python/object.hpp"
|
||||||
|
#include "hal.h"
|
||||||
|
#include "libnml/nml/stat_msg.hh"
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <cstdarg>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <wordexp.h>
|
#include <wordexp.h>
|
||||||
|
|
||||||
|
extern int _task;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
extern PyObject *PyInit_interpreter(void);
|
||||||
|
extern PyObject *PyInit_emccanon(void);
|
||||||
|
extern struct _inittab builtin_modules[];
|
||||||
|
extern int rtapi_snprintf(char *buf, unsigned long size, const char *fmt, ...);
|
||||||
|
extern int rtapi_vsnprintf(char *buf, unsigned long size, const char *fmt, va_list ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
// LinuxCNC source basis: src/emc/rs274ngc/rs274ngc_pre.cc provides read(),
|
||||||
|
// execute(), and wordexp() paths; interp_convert.cc converts G0, G2, and M30.
|
||||||
|
// src/emc/rs274ngc/interp_namedparams.cc calls the HAL lookup functions
|
||||||
|
// declared in include/hal.h and implemented by src/hal/hal_lib.c.
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
int collect_event(const CncSimEvent *event, void *user_data) {
|
int collect_event(const CncSimEvent *event, void *user_data) {
|
||||||
@@ -55,6 +73,119 @@ bool wordexp_expands_environment_variables() {
|
|||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool wordexp_expands_braced_environment_variables() {
|
||||||
|
setenv("CNC_SIM_WORDEXP_BRACED_PROBE", "linuxcnc-braced-wordexp", 1);
|
||||||
|
|
||||||
|
wordexp_t expanded{};
|
||||||
|
if (wordexp("${CNC_SIM_WORDEXP_BRACED_PROBE}/subroutines", &expanded, 0) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool ok = expanded.we_wordc == 1 &&
|
||||||
|
expanded.we_wordv &&
|
||||||
|
expanded.we_wordv[0] &&
|
||||||
|
std::strcmp(expanded.we_wordv[0], "linuxcnc-braced-wordexp/subroutines") == 0;
|
||||||
|
wordfree(&expanded);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hal_lookup_is_unavailable() {
|
||||||
|
if (hal_init("cnc_sim_wasm_runtime_probe") != 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (hal_ready(1) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hal_data_u dummy{};
|
||||||
|
hal_data_u *data = &dummy;
|
||||||
|
hal_type_t type = HAL_FLOAT;
|
||||||
|
bool connected = true;
|
||||||
|
if (hal_get_pin_value_by_name("cnc-sim.missing-pin", &type, &data, &connected) != -1 ||
|
||||||
|
type != HAL_TYPE_UNINITIALIZED ||
|
||||||
|
data != nullptr ||
|
||||||
|
connected) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = &dummy;
|
||||||
|
type = HAL_FLOAT;
|
||||||
|
bool has_writers = true;
|
||||||
|
if (hal_get_signal_value_by_name("cnc-sim.missing-signal", &type, &data, &has_writers) != -1 ||
|
||||||
|
type != HAL_TYPE_UNINITIALIZED ||
|
||||||
|
data != nullptr ||
|
||||||
|
has_writers) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = &dummy;
|
||||||
|
type = HAL_FLOAT;
|
||||||
|
if (hal_get_param_value_by_name("cnc-sim.missing-param", &type, &data) != -1 ||
|
||||||
|
type != HAL_TYPE_UNINITIALIZED ||
|
||||||
|
data != nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nml_status_constructors_match_linuxcnc() {
|
||||||
|
// LinuxCNC source basis: src/libnml/nml/nmlmsg.cc and stat_msg.cc
|
||||||
|
// initialize RCS status messages before EMC status objects use them.
|
||||||
|
RCS_GENERIC_STATUS status;
|
||||||
|
return status._type == RCS_GENERIC_STATUS_TYPE &&
|
||||||
|
status.size == static_cast<long>(sizeof(RCS_GENERIC_STATUS)) &&
|
||||||
|
status.command_type == -1 &&
|
||||||
|
status.echo_serial_number == -1 &&
|
||||||
|
status.status == RCS_STATUS::UNINITIALIZED &&
|
||||||
|
status._state == -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool builtin_modules_match_linuxcnc_preview_runtime() {
|
||||||
|
// LinuxCNC source basis: src/emc/rs274ngc/gcodemodule.cc defines _task=0
|
||||||
|
// for preview and registers interpreter/emccanon builtin module entries;
|
||||||
|
// src/emc/task/taskclass.cc uses the same builtin module table shape.
|
||||||
|
return _task == 0 &&
|
||||||
|
builtin_modules[0].name &&
|
||||||
|
std::strcmp(builtin_modules[0].name, "interpreter") == 0 &&
|
||||||
|
builtin_modules[0].initfunc == PyInit_interpreter &&
|
||||||
|
builtin_modules[0].initfunc() == nullptr &&
|
||||||
|
builtin_modules[1].name &&
|
||||||
|
std::strcmp(builtin_modules[1].name, "emccanon") == 0 &&
|
||||||
|
builtin_modules[1].initfunc == PyInit_emccanon &&
|
||||||
|
builtin_modules[1].initfunc() == nullptr &&
|
||||||
|
builtin_modules[2].name == nullptr &&
|
||||||
|
builtin_modules[2].initfunc == nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int call_rtapi_vsnprintf(char *buffer, unsigned long size, const char *format, ...) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
const int result = rtapi_vsnprintf(buffer, size, format, args);
|
||||||
|
va_end(args);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rtapi_formatting_matches_linuxcnc_userspace() {
|
||||||
|
// LinuxCNC source basis: src/rtapi/uspace_common.h implements both
|
||||||
|
// rtapi_snprintf() and rtapi_vsnprintf() as userspace vsnprintf() wrappers.
|
||||||
|
char formatted[32]{};
|
||||||
|
if (rtapi_snprintf(formatted, sizeof(formatted), "HAL_%s_%d", "probe", 7) != 11 ||
|
||||||
|
std::strcmp(formatted, "HAL_probe_7") != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char via_va_list[32]{};
|
||||||
|
if (call_rtapi_vsnprintf(via_va_list, sizeof(via_va_list), "%s.%02d", "axis", 3) != 7 ||
|
||||||
|
std::strcmp(via_va_list, "axis.03") != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char truncated[5]{};
|
||||||
|
return rtapi_snprintf(truncated, sizeof(truncated), "abcdef") == 6 &&
|
||||||
|
std::strcmp(truncated, "abcd") == 0;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
@@ -64,6 +195,21 @@ int main() {
|
|||||||
if (!wordexp_expands_environment_variables()) {
|
if (!wordexp_expands_environment_variables()) {
|
||||||
return 6;
|
return 6;
|
||||||
}
|
}
|
||||||
|
if (!wordexp_expands_braced_environment_variables()) {
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
if (!hal_lookup_is_unavailable()) {
|
||||||
|
return 7;
|
||||||
|
}
|
||||||
|
if (!nml_status_constructors_match_linuxcnc()) {
|
||||||
|
return 9;
|
||||||
|
}
|
||||||
|
if (!builtin_modules_match_linuxcnc_preview_runtime()) {
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
if (!rtapi_formatting_matches_linuxcnc_userspace()) {
|
||||||
|
return 11;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<CncSimEvent> events;
|
std::vector<CncSimEvent> events;
|
||||||
CncSimHandle *sim = cnc_sim_create();
|
CncSimHandle *sim = cnc_sim_create();
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
// LinuxCNC source basis: src/emc/rs274ngc/interp_base.cc uses dlopen(),
|
||||||
|
// dlsym(), dlerror(), RTLD_GLOBAL, and RTLD_NOW for native interpreter loading.
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
thread_local char last_error[128] = "dynamic loading is unavailable in wasm-safe probe";
|
thread_local char last_error[128] = "dynamic loading is unavailable in wasm-safe probe";
|
||||||
@@ -27,4 +29,3 @@ int dlclose(void *) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* LinuxCNC source basis: src/emc/rs274ngc/interp_base.cc native loader flags. */
|
||||||
#define RTLD_GLOBAL 0x00100
|
#define RTLD_GLOBAL 0x00100
|
||||||
#define RTLD_NOW 0x00002
|
#define RTLD_NOW 0x00002
|
||||||
|
|
||||||
@@ -15,4 +16,3 @@ int dlclose(void *handle);
|
|||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#include "nml_intf/emc_nml.hh"
|
#include "nml_intf/emc_nml.hh"
|
||||||
|
|
||||||
// LinuxCNC src/emc/sai/dummyemcstat.cc uses the same source-backed status
|
// LinuxCNC source basis: src/emc/sai/dummyemcstat.cc defines the same
|
||||||
// object when the interpreter runs without the task controller.
|
// source-backed status object when the interpreter runs without task.
|
||||||
EMC_STAT *emcStatus = new EMC_STAT;
|
EMC_STAT *emcStatus = new EMC_STAT;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
|
||||||
|
// LinuxCNC source basis: src/emc/rs274ngc/interp_internal.hh maps _() to
|
||||||
|
// gettext(), and RS274 sources wrap interpreter diagnostics with _().
|
||||||
extern "C" char *gettext(const char *msgid) {
|
extern "C" char *gettext(const char *msgid) {
|
||||||
return const_cast<char *>(msgid ? msgid : "");
|
return const_cast<char *>(msgid ? msgid : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,38 @@
|
|||||||
|
#include "dlfcn.h"
|
||||||
#include "interp_base.hh"
|
#include "interp_base.hh"
|
||||||
|
|
||||||
int main() {
|
#include <cstring>
|
||||||
InterpBase *interp = interp_from_shlib("nonexistent-interpreter.so");
|
|
||||||
return interp == nullptr ? 0 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// LinuxCNC source basis: src/emc/rs274ngc/interp_base.cc provides
|
||||||
|
// interp_from_shlib(), with the declaration in interp_base.hh. Its native
|
||||||
|
// dlopen()/dlsym() loader edge is intentionally unavailable in WASM probes.
|
||||||
|
int main() {
|
||||||
|
if (dlopen(nullptr, RTLD_GLOBAL) != nullptr) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
if (dlopen("/tmp/cnc_sim_missing_interpreter.so", RTLD_NOW) != nullptr) {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
if (dlsym(nullptr, "makeInterp") != nullptr) {
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
const char *error = dlerror();
|
||||||
|
if (!error || !std::strstr(error, "dynamic loading is unavailable")) {
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
if (dlclose(nullptr) != 0) {
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
// LinuxCNC src/emc/rs274ngc/interp_base.cc uses absolute interpreter
|
||||||
|
// paths directly before calling dlopen().
|
||||||
|
if (interp_from_shlib("/tmp/cnc_sim_missing_absolute_interpreter.so") != nullptr) {
|
||||||
|
return 7;
|
||||||
|
}
|
||||||
|
// Relative interpreter names are resolved through EMC2_HOME/lib/linuxcnc
|
||||||
|
// before the same unavailable dynamic-loading edge is reached.
|
||||||
|
if (interp_from_shlib("nonexistent-interpreter.so") != nullptr) {
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#include "tooldata/tooldata.hh"
|
#include "tooldata/tooldata.hh"
|
||||||
#include "rs274ngc/rs274ngc_interp.hh"
|
#include "rs274ngc/rs274ngc_interp.hh"
|
||||||
|
|
||||||
|
// LinuxCNC source basis: src/emc/rs274ngc/interp_find.cc implements
|
||||||
|
// find_tool_index() and find_tool_pocket() against LinuxCNC tooldata.
|
||||||
int main() {
|
int main() {
|
||||||
if (tool_mmap_creator(nullptr, 1) != 0) {
|
if (tool_mmap_creator(nullptr, 1) != 0) {
|
||||||
return 7;
|
return 7;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
|
|
||||||
|
// LinuxCNC source basis: src/emc/rs274ngc/rs274ngc_interp.hh declares
|
||||||
|
// Interp::setError(), which interp_find.cc references in link probes.
|
||||||
void Interp::setError(const char *, ...) {
|
void Interp::setError(const char *, ...) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#ifndef CNC_SIM_WASM_SHIMS_LIBGEN_H
|
#ifndef CNC_SIM_WASM_SHIMS_LIBGEN_H
|
||||||
#define CNC_SIM_WASM_SHIMS_LIBGEN_H
|
#define CNC_SIM_WASM_SHIMS_LIBGEN_H
|
||||||
|
|
||||||
|
/* LinuxCNC source basis: src/emc/rs274ngc/interp_o_word.cc uses basename()
|
||||||
|
while resolving O-word subroutine names. */
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -7,8 +7,12 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <wordexp.h>
|
#include <wordexp.h>
|
||||||
|
|
||||||
|
// LinuxCNC source basis: src/emc/rs274ngc/gcodemodule.cc defines _task=0 for
|
||||||
|
// preview, while src/emc/task/emctaskmain.cc defines _task=1 for milltask.
|
||||||
int _task = 0;
|
int _task = 0;
|
||||||
|
|
||||||
|
// LinuxCNC source basis: src/emc/task/taskclass.cc and
|
||||||
|
// src/emc/rs274ngc/gcodemodule.cc register builtin interpreter/emccanon modules.
|
||||||
extern "C" PyObject *PyInit_interpreter(void) {
|
extern "C" PyObject *PyInit_interpreter(void) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -29,6 +33,9 @@ struct _inittab builtin_modules[] = {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LinuxCNC source basis: src/emc/rs274ngc/interp_namedparams.cc fetch_hal_param()
|
||||||
|
// uses these HAL entry points; include/hal.h declares them and src/hal/hal_lib.c
|
||||||
|
// provides the native backend.
|
||||||
extern "C" int hal_init(const char *) {
|
extern "C" int hal_init(const char *) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -159,6 +166,8 @@ std::string expand_path_word(const char *words) {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
// LinuxCNC source basis: src/emc/rs274ngc/rs274ngc_pre.cc uses wordexp() and
|
||||||
|
// wordfree() while resolving program/subroutine paths.
|
||||||
extern "C" int wordexp(const char *words, wordexp_t *pwordexp, int) {
|
extern "C" int wordexp(const char *words, wordexp_t *pwordexp, int) {
|
||||||
if (!words || !pwordexp) {
|
if (!words || !pwordexp) {
|
||||||
return WRDE_BADVAL;
|
return WRDE_BADVAL;
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
// LinuxCNC source basis: src/libnml/nml/nmlmsg.cc and stat_msg.cc provide
|
||||||
|
// these NML status constructors for source-linked EMC status objects.
|
||||||
int NMLmsg::automatically_clear = 1;
|
int NMLmsg::automatically_clear = 1;
|
||||||
|
|
||||||
NMLmsg::NMLmsg(NMLTYPE t, long s) {
|
NMLmsg::NMLmsg(NMLTYPE t, long s) {
|
||||||
@@ -69,3 +71,10 @@ RCS_STAT_MSG::RCS_STAT_MSG(NMLTYPE t, size_t sz) : NMLmsg(t, sz) {
|
|||||||
status = RCS_STATUS::UNINITIALIZED;
|
status = RCS_STATUS::UNINITIALIZED;
|
||||||
_state = -1;
|
_state = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RCS_GENERIC_STATUS::RCS_GENERIC_STATUS()
|
||||||
|
: RCS_STAT_MSG(RCS_GENERIC_STATUS_TYPE, sizeof(RCS_GENERIC_STATUS)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void RCS_GENERIC_STATUS::update(CMS *) {
|
||||||
|
}
|
||||||
|
|||||||
252
core/wasm_shims/python_c_api_probe_main.cc
Normal file
252
core/wasm_shims/python_c_api_probe_main.cc
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
#include "boost/python/class.hpp"
|
||||||
|
#include "boost/python/converter/registry.hpp"
|
||||||
|
#include "boost/python/def.hpp"
|
||||||
|
#include "boost/python/dict.hpp"
|
||||||
|
#include "boost/python/enum.hpp"
|
||||||
|
#include "boost/python/exception_translator.hpp"
|
||||||
|
#include "boost/python/extract.hpp"
|
||||||
|
#include "boost/python/import.hpp"
|
||||||
|
#include "boost/python/list.hpp"
|
||||||
|
#include "boost/python/module.hpp"
|
||||||
|
#include "boost/python/object.hpp"
|
||||||
|
#include "boost/python/object/class_detail.hpp"
|
||||||
|
#include "boost/python/return_internal_reference.hpp"
|
||||||
|
#include "boost/python/scope.hpp"
|
||||||
|
#include "boost/python/str.hpp"
|
||||||
|
#include "boost/python/suite/indexing/map_indexing_suite.hpp"
|
||||||
|
#include "boost/python/tuple.hpp"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// LinuxCNC source basis: src/emc/rs274ngc/gcodemodule.cc,
|
||||||
|
// interpmodule.cc, py*.cc, interp_python.cc, interp_namedparams.cc, and
|
||||||
|
// src/emc/pythonplugin/python_plugin.cc use these Python C API and
|
||||||
|
// Boost.Python entry points. The WASM shim keeps the API inert and link-safe
|
||||||
|
// while browser Python execution remains disabled.
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct ProbeObject {
|
||||||
|
PyObject_HEAD
|
||||||
|
int value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ProbeCppObject {
|
||||||
|
int value = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
PyObject *probe_getter(PyObject *, void *) {
|
||||||
|
return Py_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
int probe_setter(PyObject *, PyObject *, void *) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *probe_method(PyObject *, PyObject *) {
|
||||||
|
return Py_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
void translate_probe_exception(const std::runtime_error &) {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool boost_python_shims_are_inert() {
|
||||||
|
namespace bp = boost::python;
|
||||||
|
|
||||||
|
bp::object object;
|
||||||
|
if (object.ptr() != nullptr || object.attr("missing").operator bp::object().ptr() != nullptr ||
|
||||||
|
object["missing"].ptr() != nullptr || object[0].ptr() != nullptr ||
|
||||||
|
object().ptr() != nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bp::list list;
|
||||||
|
list.append(object);
|
||||||
|
list.extend(list);
|
||||||
|
bp::tuple tuple(list);
|
||||||
|
bp::dict dict;
|
||||||
|
if (bp::tuple().ptr() != nullptr || tuple.ptr() != nullptr ||
|
||||||
|
bp::make_tuple(1, 2.0, "three").ptr() != nullptr ||
|
||||||
|
dict.keys().ptr() != nullptr || bp::len(list) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((object % tuple).ptr() != nullptr || ("linuxcnc" % tuple).ptr() != nullptr ||
|
||||||
|
(object + object).ptr() != nullptr || (object + "suffix").ptr() != nullptr ||
|
||||||
|
("prefix" + object).ptr() != nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bp::scope scope(object);
|
||||||
|
scope.attr("this") = object;
|
||||||
|
if (scope.attr("this").operator bp::object().ptr() != nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bp::getattr(object, "__next__").ptr() != nullptr ||
|
||||||
|
bp::make_function(probe_method).ptr() != nullptr ||
|
||||||
|
bp::make_getter(probe_getter).ptr() != nullptr ||
|
||||||
|
bp::make_setter(probe_setter).ptr() != nullptr ||
|
||||||
|
bp::import("traceback").ptr() != nullptr ||
|
||||||
|
bp::str("\n").join(object).ptr() != nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (static_cast<int>(bp::extract<int>(object)) != 0 ||
|
||||||
|
static_cast<double>(bp::extract<double>(object)) != 0.0 ||
|
||||||
|
static_cast<std::string>(bp::extract<std::string>(object)) != std::string() ||
|
||||||
|
static_cast<char *>(bp::extract<char *>(object)) != nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bp::handle<> default_handle;
|
||||||
|
bp::handle<> null_handle(bp::allow_null(static_cast<PyObject *>(nullptr)));
|
||||||
|
bp::handle<> borrowed_handle(bp::borrowed(Py_None));
|
||||||
|
if (default_handle || default_handle.get() != nullptr ||
|
||||||
|
null_handle || null_handle.get() != nullptr ||
|
||||||
|
borrowed_handle || borrowed_handle.get() != nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bp::handle_exception();
|
||||||
|
|
||||||
|
bp::class_<ProbeCppObject>("ProbeCppObject")
|
||||||
|
.def("method", probe_method)
|
||||||
|
.def_readwrite("value", &ProbeCppObject::value)
|
||||||
|
.add_property("property", bp::make_getter(probe_getter), bp::make_setter(probe_setter));
|
||||||
|
bp::class_<ProbeCppObject>("ProbeCppObjectNoInit", bp::no_init);
|
||||||
|
bp::class_<ProbeCppObject>("ProbeCppObjectDoc", "doc");
|
||||||
|
bp::class_<ProbeCppObject>("ProbeCppObjectDocNoInit", "doc", bp::no_init);
|
||||||
|
|
||||||
|
bp::enum_<int>("ProbeEnum").value("one", 1).export_values();
|
||||||
|
bp::def("probe", probe_method);
|
||||||
|
bp::register_exception_translator<std::runtime_error>(translate_probe_exception);
|
||||||
|
bp::return_internal_reference<1, bp::default_call_policies> reference_policy;
|
||||||
|
(void)reference_policy;
|
||||||
|
bp::map_indexing_suite<std::map<int, int>> map_suite;
|
||||||
|
(void)map_suite;
|
||||||
|
if (bp::objects::registered_class_object(bp::type_id<ProbeCppObject>()) != nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
BOOST_PYTHON_MODULE(cnc_sim_python_c_api_probe_module) {
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
if (Py_IsInitialized() != 1) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (PyErr_Occurred() != nullptr) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
PyObject *exc = reinterpret_cast<PyObject *>(1);
|
||||||
|
PyObject *val = reinterpret_cast<PyObject *>(1);
|
||||||
|
PyObject *tb = reinterpret_cast<PyObject *>(1);
|
||||||
|
PyErr_Fetch(&exc, &val, &tb);
|
||||||
|
if (exc || val || tb) {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
if (!Py_None || !Py_TYPE(Py_None) || std::strcmp(Py_TYPE(Py_None)->tp_name, "NoneType") != 0) {
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
if (PyUnicode_FromString("linuxcnc") != Py_None) {
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
if (PyObject_Str(Py_None) != Py_None) {
|
||||||
|
return 16;
|
||||||
|
}
|
||||||
|
if (std::strcmp(PyUnicode_AsUTF8(Py_None), "") != 0) {
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
if (PyUnicode_Check(Py_None) != 0 || PyLong_Check(Py_None) != 0 || PyFloat_Check(Py_None) != 0 ||
|
||||||
|
PyGen_Check(Py_None) != 0 || PyErr_ExceptionMatches(PyExc_KeyError) != 0) {
|
||||||
|
return 17;
|
||||||
|
}
|
||||||
|
if (PyLong_FromLong(7) != Py_None || PyLong_AsLong(Py_None) != 0) {
|
||||||
|
return 7;
|
||||||
|
}
|
||||||
|
if (PyFloat_AsDouble(Py_None) != 0.0) {
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
if (PyTuple_New(2) != Py_None || PyTuple_SetItem(Py_None, 0, Py_None) != 0 || PyTuple_Size(Py_None) != 0) {
|
||||||
|
return 9;
|
||||||
|
}
|
||||||
|
if (PyList_New(2) != Py_None || PyList_SetItem(Py_None, 0, Py_None) != 0 || PyList_Size(Py_None) != 0) {
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
if (PyTuple_GetItem(Py_None, 0) != Py_None || PyList_GetItem(Py_None, 0) != Py_None ||
|
||||||
|
PySequence_GetItem(Py_None, 0) != Py_None || PySequence_Length(Py_None) != 0) {
|
||||||
|
return 18;
|
||||||
|
}
|
||||||
|
if (PyObject_IsTrue(Py_None) != 0 || PyCallable_Check(Py_None) != 0 || PyObject_IsInstance(Py_None, Py_None) != 0) {
|
||||||
|
return 11;
|
||||||
|
}
|
||||||
|
if (PyObject_CallMethod(Py_None, const_cast<char *>("noop"), const_cast<char *>("")) != Py_None ||
|
||||||
|
PyObject_Call(Py_None, Py_None, Py_None) != Py_None ||
|
||||||
|
PyObject_GetAttrString(Py_None, "noop") != Py_None ||
|
||||||
|
PyObject_SetAttrString(Py_None, "noop", Py_None) != 0) {
|
||||||
|
return 19;
|
||||||
|
}
|
||||||
|
if (PyArg_ParseTuple(Py_None, "") != 0 || Py_BuildValue("i", 1) != Py_None) {
|
||||||
|
return 20;
|
||||||
|
}
|
||||||
|
ProbeObject *probe = PyObject_New(ProbeObject, &PyList_Type);
|
||||||
|
if (!probe || Py_TYPE(reinterpret_cast<PyObject *>(probe)) != &PyList_Type) {
|
||||||
|
std::free(probe);
|
||||||
|
return 12;
|
||||||
|
}
|
||||||
|
std::free(probe);
|
||||||
|
if (PyModule_Create(nullptr) != Py_None || PyModule_AddObject(Py_None, "value", Py_None) != 0) {
|
||||||
|
return 13;
|
||||||
|
}
|
||||||
|
_inittab inittab[] = {
|
||||||
|
{"interpreter", nullptr},
|
||||||
|
{nullptr, nullptr},
|
||||||
|
};
|
||||||
|
if (PyImport_ExtendInittab(inittab) != 0 || PyImport_GetModuleDict() == Py_None ||
|
||||||
|
PyImport_AddModule("interpreter") == Py_None ||
|
||||||
|
PyDict_SetItemString(PyImport_GetModuleDict(), "interpreter", PyImport_AddModule("interpreter")) != 0) {
|
||||||
|
return 21;
|
||||||
|
}
|
||||||
|
PyConfig config{};
|
||||||
|
PyConfig_InitPythonConfig(&config);
|
||||||
|
if (config.program_name != nullptr || config.buffered_stdio != 1) {
|
||||||
|
return 22;
|
||||||
|
}
|
||||||
|
const wchar_t program_name[] = L"linuxcnc";
|
||||||
|
PyStatus config_status = PyConfig_SetString(&config, &config.program_name, program_name);
|
||||||
|
if (config_status._type != 0 || config.program_name != program_name) {
|
||||||
|
return 23;
|
||||||
|
}
|
||||||
|
PyStatus init_status = Py_InitializeFromConfig(&config);
|
||||||
|
if (init_status._type != 0) {
|
||||||
|
return 24;
|
||||||
|
}
|
||||||
|
std::size_t decoded_size = 99;
|
||||||
|
if (Py_DecodeLocale("linuxcnc", &decoded_size) != nullptr || decoded_size != 8) {
|
||||||
|
return 25;
|
||||||
|
}
|
||||||
|
if (PyRun_SimpleString("pass") != 0 || std::strcmp(Py_GetVersion(), "wasm-python-disabled") != 0) {
|
||||||
|
return 26;
|
||||||
|
}
|
||||||
|
PyConfig_Clear(&config);
|
||||||
|
if (config.program_name != nullptr || config.buffered_stdio != 0) {
|
||||||
|
return 27;
|
||||||
|
}
|
||||||
|
if (PyType_Ready(&PyList_Type) != 0 || PyType_GenericNew(&PyList_Type, Py_None, Py_None) != Py_None) {
|
||||||
|
return 14;
|
||||||
|
}
|
||||||
|
if (PyErr_Format(PyExc_TypeError, "linuxcnc") != nullptr) {
|
||||||
|
return 15;
|
||||||
|
}
|
||||||
|
if (!boost_python_shims_are_inert()) {
|
||||||
|
return 28;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -1,11 +1,19 @@
|
|||||||
#include "boost/python/object.hpp"
|
#include "boost/python/object.hpp"
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
// LinuxCNC source basis: src/emc/rs274ngc/interp_python.cc,
|
||||||
|
// interp_namedparams.cc, gcodemodule.cc, and src/emc/pythonplugin/python_plugin.cc
|
||||||
|
// use these Python C API symbols in RS274 Python/remap paths.
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
static PyTypeObject none_type = {"NoneType"};
|
static PyTypeObject none_type = {"NoneType"};
|
||||||
|
static PyTypeObject module_type = {"module"};
|
||||||
|
static PyTypeObject dict_type = {"dict"};
|
||||||
static PyObject none_object = {&none_type};
|
static PyObject none_object = {&none_type};
|
||||||
|
static PyObject module_object = {&module_type};
|
||||||
|
static PyObject dict_object = {&dict_type};
|
||||||
|
|
||||||
PyObject *PyExc_KeyError = reinterpret_cast<PyObject *>(1);
|
PyObject *PyExc_KeyError = reinterpret_cast<PyObject *>(1);
|
||||||
PyObject *PyExc_KeyboardInterrupt = reinterpret_cast<PyObject *>(2);
|
PyObject *PyExc_KeyboardInterrupt = reinterpret_cast<PyObject *>(2);
|
||||||
@@ -108,6 +116,10 @@ PyObject *PyObject_CallMethod(PyObject *, char *, char *, ...) {
|
|||||||
return &none_object;
|
return &none_object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyObject *PyObject_Call(PyObject *, PyObject *, PyObject *) {
|
||||||
|
return &none_object;
|
||||||
|
}
|
||||||
|
|
||||||
PyObject *PyObject_GetAttrString(PyObject *, const char *) {
|
PyObject *PyObject_GetAttrString(PyObject *, const char *) {
|
||||||
return &none_object;
|
return &none_object;
|
||||||
}
|
}
|
||||||
@@ -184,6 +196,64 @@ int PyModule_AddObject(PyObject *, const char *, PyObject *) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int PyImport_ExtendInittab(_inittab *) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *PyImport_GetModuleDict(void) {
|
||||||
|
return &dict_object;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *PyImport_AddModule(const char *) {
|
||||||
|
return &module_object;
|
||||||
|
}
|
||||||
|
|
||||||
|
int PyDict_SetItemString(PyObject *, const char *, PyObject *) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PyConfig_InitPythonConfig(PyConfig *config) {
|
||||||
|
if (!config) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
config->program_name = nullptr;
|
||||||
|
config->buffered_stdio = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyStatus PyConfig_SetString(PyConfig *, wchar_t **config_str, const wchar_t *value) {
|
||||||
|
if (config_str) {
|
||||||
|
*config_str = const_cast<wchar_t *>(value);
|
||||||
|
}
|
||||||
|
return PyStatus{0, nullptr, nullptr, 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
void PyConfig_Clear(PyConfig *config) {
|
||||||
|
if (!config) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
config->program_name = nullptr;
|
||||||
|
config->buffered_stdio = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyStatus Py_InitializeFromConfig(PyConfig *) {
|
||||||
|
return PyStatus{0, nullptr, nullptr, 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
wchar_t *Py_DecodeLocale(const char *arg, std::size_t *size) {
|
||||||
|
if (size) {
|
||||||
|
*size = arg ? std::strlen(arg) : 0;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int PyRun_SimpleString(const char *) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *Py_GetVersion(void) {
|
||||||
|
return "wasm-python-disabled";
|
||||||
|
}
|
||||||
|
|
||||||
int PyType_Ready(PyTypeObject *) {
|
int PyType_Ready(PyTypeObject *) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,155 @@
|
|||||||
#include "pythonplugin/python_plugin.hh"
|
#include "pythonplugin/python_plugin.hh"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
// LinuxCNC source basis: src/emc/pythonplugin/python_plugin.cc provides the
|
||||||
|
// native PythonPlugin implementation called by RS274 Python/remap paths.
|
||||||
__attribute__((weak)) std::string handle_pyerror() {
|
__attribute__((weak)) std::string handle_pyerror() {
|
||||||
return "python disabled in wasm-safe probe";
|
return "python disabled in wasm-safe probe";
|
||||||
}
|
}
|
||||||
|
|
||||||
PythonPlugin *python_plugin = nullptr;
|
PythonPlugin *python_plugin = nullptr;
|
||||||
|
|
||||||
PythonPlugin *PythonPlugin::instantiate(struct _inittab *) {
|
namespace {
|
||||||
|
|
||||||
|
std::string trim_ini_value(std::string value) {
|
||||||
|
const auto first = std::find_if_not(value.begin(), value.end(), [](unsigned char ch) {
|
||||||
|
return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n';
|
||||||
|
});
|
||||||
|
const auto last = std::find_if_not(value.rbegin(), value.rend(), [](unsigned char ch) {
|
||||||
|
return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n';
|
||||||
|
}).base();
|
||||||
|
if (first >= last) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return std::string(first, last);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool read_ini_section(const std::string &text, std::string §ion_name) {
|
||||||
|
if (text.empty() || text.front() != '[') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto close = text.find(']');
|
||||||
|
if (close == std::string::npos) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// LinuxCNC source basis: IniFile::processLine() trims the section name
|
||||||
|
// between '[' and ']' and ignores text after the closing bracket.
|
||||||
|
section_name = trim_ini_value(text.substr(1, close - 1));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> find_ini_values(const char *iniFilename,
|
||||||
|
const char *section,
|
||||||
|
const char *key) {
|
||||||
|
std::vector<std::string> values;
|
||||||
|
FILE *file = std::fopen(iniFilename, "r");
|
||||||
|
if (!file) {
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool in_section = false;
|
||||||
|
char line[PATH_MAX + 256];
|
||||||
|
while (std::fgets(line, sizeof(line), file)) {
|
||||||
|
std::string text = trim_ini_value(line);
|
||||||
|
if (text.empty() || text[0] == ';' || text[0] == '#') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::string section_name;
|
||||||
|
if (read_ini_section(text, section_name)) {
|
||||||
|
in_section = section_name == section;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!in_section) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto equals = text.find('=');
|
||||||
|
if (equals == std::string::npos) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (trim_ini_value(text.substr(0, equals)) == key) {
|
||||||
|
values.push_back(trim_ini_value(text.substr(equals + 1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::fclose(file);
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool find_ini_value(const char *iniFilename,
|
||||||
|
const char *section,
|
||||||
|
const char *key,
|
||||||
|
std::string &value) {
|
||||||
|
const std::vector<std::string> values = find_ini_values(iniFilename, section, key);
|
||||||
|
if (values.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
value = values.front();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tilde_expand_path(const std::string &path, std::string &expanded) {
|
||||||
|
// LinuxCNC source basis: IniFile::tildeExpand() copies non-"~/" paths and
|
||||||
|
// returns failure for "~/" paths when HOME is unavailable.
|
||||||
|
if (path.size() < 2 || path[0] != '~' || path[1] != '/') {
|
||||||
|
expanded = path;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const char *home = std::getenv("HOME");
|
||||||
|
if (!home) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
expanded = std::string(home) + path.substr(1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int check_python_path_entries(const char *iniFilename,
|
||||||
|
const char *key,
|
||||||
|
pp_status failure_status) {
|
||||||
|
for (const std::string &entry : find_ini_values(iniFilename, "PYTHON", key)) {
|
||||||
|
std::string expanded;
|
||||||
|
// LinuxCNC source basis: python_plugin.cc configure() runs
|
||||||
|
// TildeExpansion on [PYTHON] PATH_PREPEND/PATH_APPEND before
|
||||||
|
// PyRun_SimpleString(); browser-safe probes preserve the expansion
|
||||||
|
// failure status without executing Python path mutation.
|
||||||
|
if (!tilde_expand_path(entry, expanded)) {
|
||||||
|
return failure_status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return PLUGIN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
PythonPlugin *PythonPlugin::instantiate(struct _inittab *inittab) {
|
||||||
static PythonPlugin plugin;
|
static PythonPlugin plugin;
|
||||||
|
if (!python_plugin) {
|
||||||
python_plugin = &plugin;
|
python_plugin = &plugin;
|
||||||
python_plugin->status = PLUGIN_OK;
|
python_plugin->status = PLUGIN_OK;
|
||||||
|
if (inittab) {
|
||||||
|
for (int i = 0; inittab[i].name != nullptr; ++i) {
|
||||||
|
// LinuxCNC source basis: python_plugin.cc reports
|
||||||
|
// PLUGIN_INITTAB_FAILED when a built-in module from the
|
||||||
|
// _inittab cannot be initialized.
|
||||||
|
if (!inittab[i].initfunc || inittab[i].initfunc() == nullptr) {
|
||||||
|
python_plugin->error_msg = "failed to initialize built-in module";
|
||||||
|
python_plugin->status = PLUGIN_INITTAB_FAILED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!python_plugin->usable()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
return python_plugin;
|
return python_plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,8 +177,35 @@ int PythonPlugin::configure(const char *iniFilename, const char *section) {
|
|||||||
}
|
}
|
||||||
std::fclose(file);
|
std::fclose(file);
|
||||||
|
|
||||||
status = PLUGIN_OK;
|
std::string toplevel;
|
||||||
|
if (find_ini_value(iniFilename, section, "TOPLEVEL", toplevel)) {
|
||||||
|
char resolved_path[PATH_MAX];
|
||||||
|
// LinuxCNC source basis: python_plugin.cc configure() expands and
|
||||||
|
// resolves [PYTHON] TOPLEVEL before initialize(); a missing path
|
||||||
|
// returns PLUGIN_BAD_PATH before Python runtime status is considered.
|
||||||
|
if (realpath(toplevel.c_str(), resolved_path) == nullptr) {
|
||||||
|
error_msg = "bad path";
|
||||||
|
status = PLUGIN_BAD_PATH;
|
||||||
return status;
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
status = check_python_path_entries(iniFilename,
|
||||||
|
"PATH_PREPEND",
|
||||||
|
PLUGIN_EXCEPTION_DURING_PATH_PREPEND);
|
||||||
|
if (status != PLUGIN_OK) {
|
||||||
|
error_msg = "bad path prepend";
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
status = check_python_path_entries(iniFilename,
|
||||||
|
"PATH_APPEND",
|
||||||
|
PLUGIN_EXCEPTION_DURING_PATH_APPEND);
|
||||||
|
if (status != PLUGIN_OK) {
|
||||||
|
error_msg = "bad path append";
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
return initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PythonPlugin::is_callable(const char *, const char *) {
|
bool PythonPlugin::is_callable(const char *, const char *) {
|
||||||
@@ -56,6 +220,9 @@ int PythonPlugin::call(const char *,
|
|||||||
if (!callable) {
|
if (!callable) {
|
||||||
return PLUGIN_NO_CALLABLE;
|
return PLUGIN_NO_CALLABLE;
|
||||||
}
|
}
|
||||||
|
if (status < PLUGIN_OK) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
retval = boost::python::object();
|
retval = boost::python::object();
|
||||||
exception_msg = handle_pyerror();
|
exception_msg = handle_pyerror();
|
||||||
@@ -70,12 +237,21 @@ int PythonPlugin::run_string(const char *, boost::python::object &retval, bool)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int PythonPlugin::call_method(boost::python::object, boost::python::object &retval) {
|
int PythonPlugin::call_method(boost::python::object, boost::python::object &retval) {
|
||||||
|
// LinuxCNC source basis: src/emc/pythonplugin/python_plugin.cc returns the
|
||||||
|
// existing negative plugin status before invoking a Python callable.
|
||||||
|
if (status < PLUGIN_OK) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
retval = boost::python::object();
|
retval = boost::python::object();
|
||||||
status = PLUGIN_OK;
|
status = PLUGIN_OK;
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PythonPlugin::initialize() {
|
int PythonPlugin::initialize() {
|
||||||
status = PLUGIN_OK;
|
// LinuxCNC source basis: src/emc/pythonplugin/python_plugin.cc sets
|
||||||
|
// PLUGIN_PYTHON_NOT_INITIALIZED when initialize() runs without an active
|
||||||
|
// Python runtime. Browser-safe probes keep Python execution disabled.
|
||||||
|
error_msg = "python disabled in wasm-safe probe";
|
||||||
|
status = PLUGIN_PYTHON_NOT_INITIALIZED;
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
|
|
||||||
extern std::string handle_pyerror();
|
extern std::string handle_pyerror();
|
||||||
|
|
||||||
|
// LinuxCNC source basis: src/emc/pythonplugin/python_plugin.hh declares this
|
||||||
|
// status/API surface used by interp_python.cc, interp_namedparams.cc, and
|
||||||
|
// rs274ngc_pre.cc.
|
||||||
enum pp_status {
|
enum pp_status {
|
||||||
PLUGIN_NO_SECTION = -1,
|
PLUGIN_NO_SECTION = -1,
|
||||||
PLUGIN_NO_INIFILE = -2,
|
PLUGIN_NO_INIFILE = -2,
|
||||||
@@ -57,4 +60,3 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
extern PythonPlugin *python_plugin;
|
extern PythonPlugin *python_plugin;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
#include "pythonplugin/python_plugin.hh"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
PyObject *init_bad_module() {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// LinuxCNC source basis: src/emc/pythonplugin/python_plugin.cc
|
||||||
|
// PythonPlugin::PythonPlugin(struct _inittab*) reports PLUGIN_INITTAB_FAILED
|
||||||
|
// when a built-in Python module from the first caller's inittab cannot be
|
||||||
|
// initialized; instantiate() then returns nullptr for the unusable plugin.
|
||||||
|
int main() {
|
||||||
|
_inittab bad_modules[] = {
|
||||||
|
{"cnc_sim_bad_module", init_bad_module},
|
||||||
|
{nullptr, nullptr},
|
||||||
|
};
|
||||||
|
|
||||||
|
PythonPlugin *plugin = PythonPlugin::instantiate(bad_modules);
|
||||||
|
if (plugin != nullptr) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (python_plugin == nullptr) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
if (python_plugin->plugin_status() != PLUGIN_INITTAB_FAILED) {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
if (python_plugin->usable()) {
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
if (python_plugin->last_errmsg() != "failed to initialize built-in module") {
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -1,5 +1,11 @@
|
|||||||
#include "pythonplugin/python_plugin.hh"
|
#include "pythonplugin/python_plugin.hh"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
// LinuxCNC source basis: src/emc/pythonplugin/python_plugin.cc and
|
||||||
|
// python_plugin.hh define the PythonPlugin API exercised by this link probe.
|
||||||
int main() {
|
int main() {
|
||||||
auto *plugin = PythonPlugin::instantiate(nullptr);
|
auto *plugin = PythonPlugin::instantiate(nullptr);
|
||||||
if (!plugin) {
|
if (!plugin) {
|
||||||
@@ -11,13 +17,271 @@ int main() {
|
|||||||
if (plugin->configure(nullptr, nullptr) != PLUGIN_NO_SECTION) {
|
if (plugin->configure(nullptr, nullptr) != PLUGIN_NO_SECTION) {
|
||||||
return 6;
|
return 6;
|
||||||
}
|
}
|
||||||
|
if (plugin->usable()) {
|
||||||
|
return 26;
|
||||||
|
}
|
||||||
|
if (plugin->last_errmsg() != "no section") {
|
||||||
|
return 23;
|
||||||
|
}
|
||||||
if (plugin->configure("/tmp/cnc_sim_missing_python_plugin_probe.ini", "PYTHON") != PLUGIN_BAD_INIFILE) {
|
if (plugin->configure("/tmp/cnc_sim_missing_python_plugin_probe.ini", "PYTHON") != PLUGIN_BAD_INIFILE) {
|
||||||
return 7;
|
return 7;
|
||||||
}
|
}
|
||||||
|
if (plugin->usable()) {
|
||||||
|
return 27;
|
||||||
|
}
|
||||||
boost::python::object retval;
|
boost::python::object retval;
|
||||||
|
if (plugin->call(nullptr, "noop", boost::python::object(), boost::python::object(), retval) != PLUGIN_BAD_INIFILE) {
|
||||||
|
return 14;
|
||||||
|
}
|
||||||
|
if (plugin->is_callable(nullptr, "noop")) {
|
||||||
|
return 28;
|
||||||
|
}
|
||||||
|
if (plugin->call_method(boost::python::object(), retval) != PLUGIN_BAD_INIFILE) {
|
||||||
|
return 15;
|
||||||
|
}
|
||||||
if (plugin->run_string("noop", retval, false) != PLUGIN_OK) {
|
if (plugin->run_string("noop", retval, false) != PLUGIN_OK) {
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
if (!plugin->usable()) {
|
||||||
|
return 29;
|
||||||
|
}
|
||||||
|
unsetenv("INI_FILE_NAME");
|
||||||
|
if (plugin->configure(nullptr, "PYTHON") != PLUGIN_NO_INIFILE) {
|
||||||
|
return 21;
|
||||||
|
}
|
||||||
|
if (plugin->last_errmsg() != "no inifile") {
|
||||||
|
return 24;
|
||||||
|
}
|
||||||
|
const char *ini = "/tmp/cnc_sim_python_plugin_probe.ini";
|
||||||
|
{
|
||||||
|
FILE *file = std::fopen(ini, "w");
|
||||||
|
if (!file) {
|
||||||
|
return 16;
|
||||||
|
}
|
||||||
|
std::fputs("[PYTHON]\n", file);
|
||||||
|
std::fclose(file);
|
||||||
|
}
|
||||||
|
const char *bad_path_ini = "/tmp/cnc_sim_python_plugin_probe_bad_path.ini";
|
||||||
|
{
|
||||||
|
FILE *file = std::fopen(bad_path_ini, "w");
|
||||||
|
if (!file) {
|
||||||
|
std::remove(ini);
|
||||||
|
return 33;
|
||||||
|
}
|
||||||
|
std::fputs("[PYTHON]\nTOPLEVEL = /tmp/cnc_sim_missing_python_toplevel.py\n", file);
|
||||||
|
std::fclose(file);
|
||||||
|
}
|
||||||
|
if (plugin->configure(bad_path_ini, "PYTHON") != PLUGIN_BAD_PATH) {
|
||||||
|
std::remove(ini);
|
||||||
|
std::remove(bad_path_ini);
|
||||||
|
return 34;
|
||||||
|
}
|
||||||
|
if (plugin->usable()) {
|
||||||
|
std::remove(ini);
|
||||||
|
std::remove(bad_path_ini);
|
||||||
|
return 35;
|
||||||
|
}
|
||||||
|
if (plugin->last_errmsg() != "bad path") {
|
||||||
|
std::remove(ini);
|
||||||
|
std::remove(bad_path_ini);
|
||||||
|
return 36;
|
||||||
|
}
|
||||||
|
std::remove(bad_path_ini);
|
||||||
|
const char *path_prepend_ini = "/tmp/cnc_sim_python_plugin_probe_path_prepend.ini";
|
||||||
|
{
|
||||||
|
FILE *file = std::fopen(path_prepend_ini, "w");
|
||||||
|
if (!file) {
|
||||||
|
std::remove(ini);
|
||||||
|
return 37;
|
||||||
|
}
|
||||||
|
std::fputs("[PYTHON]\nPATH_PREPEND = ~/linuxcnc-python\n", file);
|
||||||
|
std::fclose(file);
|
||||||
|
}
|
||||||
|
const char *saved_home = std::getenv("HOME");
|
||||||
|
const std::string saved_home_value = saved_home ? saved_home : "";
|
||||||
|
unsetenv("HOME");
|
||||||
|
if (plugin->configure(path_prepend_ini, "PYTHON") != PLUGIN_EXCEPTION_DURING_PATH_PREPEND) {
|
||||||
|
if (saved_home) {
|
||||||
|
setenv("HOME", saved_home_value.c_str(), 1);
|
||||||
|
}
|
||||||
|
std::remove(ini);
|
||||||
|
std::remove(path_prepend_ini);
|
||||||
|
return 38;
|
||||||
|
}
|
||||||
|
if (plugin->usable()) {
|
||||||
|
if (saved_home) {
|
||||||
|
setenv("HOME", saved_home_value.c_str(), 1);
|
||||||
|
}
|
||||||
|
std::remove(ini);
|
||||||
|
std::remove(path_prepend_ini);
|
||||||
|
return 39;
|
||||||
|
}
|
||||||
|
if (plugin->last_errmsg() != "bad path prepend") {
|
||||||
|
if (saved_home) {
|
||||||
|
setenv("HOME", saved_home_value.c_str(), 1);
|
||||||
|
}
|
||||||
|
std::remove(ini);
|
||||||
|
std::remove(path_prepend_ini);
|
||||||
|
return 40;
|
||||||
|
}
|
||||||
|
if (saved_home) {
|
||||||
|
setenv("HOME", saved_home_value.c_str(), 1);
|
||||||
|
}
|
||||||
|
std::remove(path_prepend_ini);
|
||||||
|
const char *spaced_section_ini = "/tmp/cnc_sim_python_plugin_probe_spaced_section.ini";
|
||||||
|
{
|
||||||
|
FILE *file = std::fopen(spaced_section_ini, "w");
|
||||||
|
if (!file) {
|
||||||
|
std::remove(ini);
|
||||||
|
return 47;
|
||||||
|
}
|
||||||
|
std::fputs("[ PYTHON ] ; section comment\nPATH_PREPEND = ~/linuxcnc-python\n", file);
|
||||||
|
std::fclose(file);
|
||||||
|
}
|
||||||
|
unsetenv("HOME");
|
||||||
|
if (plugin->configure(spaced_section_ini, "PYTHON") != PLUGIN_EXCEPTION_DURING_PATH_PREPEND) {
|
||||||
|
if (saved_home) {
|
||||||
|
setenv("HOME", saved_home_value.c_str(), 1);
|
||||||
|
}
|
||||||
|
std::remove(ini);
|
||||||
|
std::remove(spaced_section_ini);
|
||||||
|
return 48;
|
||||||
|
}
|
||||||
|
if (plugin->last_errmsg() != "bad path prepend") {
|
||||||
|
if (saved_home) {
|
||||||
|
setenv("HOME", saved_home_value.c_str(), 1);
|
||||||
|
}
|
||||||
|
std::remove(ini);
|
||||||
|
std::remove(spaced_section_ini);
|
||||||
|
return 49;
|
||||||
|
}
|
||||||
|
if (saved_home) {
|
||||||
|
setenv("HOME", saved_home_value.c_str(), 1);
|
||||||
|
}
|
||||||
|
std::remove(spaced_section_ini);
|
||||||
|
const char *path_append_ini = "/tmp/cnc_sim_python_plugin_probe_path_append.ini";
|
||||||
|
{
|
||||||
|
FILE *file = std::fopen(path_append_ini, "w");
|
||||||
|
if (!file) {
|
||||||
|
std::remove(ini);
|
||||||
|
return 41;
|
||||||
|
}
|
||||||
|
std::fputs("[PYTHON]\nPATH_APPEND = ~/linuxcnc-python\n", file);
|
||||||
|
std::fclose(file);
|
||||||
|
}
|
||||||
|
unsetenv("HOME");
|
||||||
|
if (plugin->configure(path_append_ini, "PYTHON") != PLUGIN_EXCEPTION_DURING_PATH_APPEND) {
|
||||||
|
if (saved_home) {
|
||||||
|
setenv("HOME", saved_home_value.c_str(), 1);
|
||||||
|
}
|
||||||
|
std::remove(ini);
|
||||||
|
std::remove(path_append_ini);
|
||||||
|
return 42;
|
||||||
|
}
|
||||||
|
if (plugin->usable()) {
|
||||||
|
if (saved_home) {
|
||||||
|
setenv("HOME", saved_home_value.c_str(), 1);
|
||||||
|
}
|
||||||
|
std::remove(ini);
|
||||||
|
std::remove(path_append_ini);
|
||||||
|
return 43;
|
||||||
|
}
|
||||||
|
if (plugin->last_errmsg() != "bad path append") {
|
||||||
|
if (saved_home) {
|
||||||
|
setenv("HOME", saved_home_value.c_str(), 1);
|
||||||
|
}
|
||||||
|
std::remove(ini);
|
||||||
|
std::remove(path_append_ini);
|
||||||
|
return 44;
|
||||||
|
}
|
||||||
|
if (saved_home) {
|
||||||
|
setenv("HOME", saved_home_value.c_str(), 1);
|
||||||
|
}
|
||||||
|
std::remove(path_append_ini);
|
||||||
|
const char *section_path_append_ini = "/tmp/cnc_sim_python_plugin_probe_section_path_append.ini";
|
||||||
|
{
|
||||||
|
FILE *file = std::fopen(section_path_append_ini, "w");
|
||||||
|
if (!file) {
|
||||||
|
std::remove(ini);
|
||||||
|
return 50;
|
||||||
|
}
|
||||||
|
std::fputs("[REMAP]\n"
|
||||||
|
"[PYTHON]\nTOPLEVEL = /tmp/cnc_sim_missing_python_section_toplevel.py\n"
|
||||||
|
"PATH_APPEND = ~/linuxcnc-python\n",
|
||||||
|
file);
|
||||||
|
std::fclose(file);
|
||||||
|
}
|
||||||
|
unsetenv("HOME");
|
||||||
|
// LinuxCNC source basis: python_plugin.cc configure() looks up TOPLEVEL in
|
||||||
|
// the caller-provided section, but PATH_APPEND entries are read from the
|
||||||
|
// fixed PYTHON section before initialize(); a PYTHON TOPLEVEL is ignored
|
||||||
|
// when the caller configures a different section.
|
||||||
|
if (plugin->configure(section_path_append_ini, "REMAP") != PLUGIN_EXCEPTION_DURING_PATH_APPEND) {
|
||||||
|
if (saved_home) {
|
||||||
|
setenv("HOME", saved_home_value.c_str(), 1);
|
||||||
|
}
|
||||||
|
std::remove(ini);
|
||||||
|
std::remove(section_path_append_ini);
|
||||||
|
return 51;
|
||||||
|
}
|
||||||
|
if (plugin->last_errmsg() != "bad path append") {
|
||||||
|
if (saved_home) {
|
||||||
|
setenv("HOME", saved_home_value.c_str(), 1);
|
||||||
|
}
|
||||||
|
std::remove(ini);
|
||||||
|
std::remove(section_path_append_ini);
|
||||||
|
return 52;
|
||||||
|
}
|
||||||
|
if (saved_home) {
|
||||||
|
setenv("HOME", saved_home_value.c_str(), 1);
|
||||||
|
}
|
||||||
|
std::remove(section_path_append_ini);
|
||||||
|
const char *plain_path_ini = "/tmp/cnc_sim_python_plugin_probe_plain_path.ini";
|
||||||
|
{
|
||||||
|
FILE *file = std::fopen(plain_path_ini, "w");
|
||||||
|
if (!file) {
|
||||||
|
std::remove(ini);
|
||||||
|
return 45;
|
||||||
|
}
|
||||||
|
std::fputs("[PYTHON]\nPATH_PREPEND = /tmp\nPATH_APPEND = /tmp\n", file);
|
||||||
|
std::fclose(file);
|
||||||
|
}
|
||||||
|
if (plugin->configure(plain_path_ini, "PYTHON") != PLUGIN_PYTHON_NOT_INITIALIZED) {
|
||||||
|
std::remove(ini);
|
||||||
|
std::remove(plain_path_ini);
|
||||||
|
return 46;
|
||||||
|
}
|
||||||
|
std::remove(plain_path_ini);
|
||||||
|
setenv("INI_FILE_NAME", ini, 1);
|
||||||
|
if (plugin->configure(nullptr, "PYTHON") != PLUGIN_PYTHON_NOT_INITIALIZED) {
|
||||||
|
std::remove(ini);
|
||||||
|
return 22;
|
||||||
|
}
|
||||||
|
if (plugin->usable()) {
|
||||||
|
std::remove(ini);
|
||||||
|
return 30;
|
||||||
|
}
|
||||||
|
unsetenv("INI_FILE_NAME");
|
||||||
|
if (plugin->configure(ini, "PYTHON") != PLUGIN_PYTHON_NOT_INITIALIZED) {
|
||||||
|
std::remove(ini);
|
||||||
|
return 17;
|
||||||
|
}
|
||||||
|
std::remove(ini);
|
||||||
|
if (plugin->call(nullptr, "noop", boost::python::object(), boost::python::object(), retval) != PLUGIN_PYTHON_NOT_INITIALIZED) {
|
||||||
|
return 18;
|
||||||
|
}
|
||||||
|
if (plugin->call_method(boost::python::object(), retval) != PLUGIN_PYTHON_NOT_INITIALIZED) {
|
||||||
|
return 19;
|
||||||
|
}
|
||||||
|
if (plugin->is_callable(nullptr, "noop")) {
|
||||||
|
return 31;
|
||||||
|
}
|
||||||
|
if (plugin->run_string("noop", retval, false) != PLUGIN_OK) {
|
||||||
|
return 20;
|
||||||
|
}
|
||||||
|
if (plugin->is_callable(nullptr, nullptr)) {
|
||||||
|
return 32;
|
||||||
|
}
|
||||||
if (plugin->call(nullptr, nullptr, boost::python::object(), boost::python::object(), retval) != PLUGIN_NO_CALLABLE) {
|
if (plugin->call(nullptr, nullptr, boost::python::object(), boost::python::object(), retval) != PLUGIN_NO_CALLABLE) {
|
||||||
return 8;
|
return 8;
|
||||||
}
|
}
|
||||||
@@ -33,6 +297,9 @@ int main() {
|
|||||||
if (plugin->last_exception().empty()) {
|
if (plugin->last_exception().empty()) {
|
||||||
return 12;
|
return 12;
|
||||||
}
|
}
|
||||||
|
if (plugin->last_exception() != handle_pyerror()) {
|
||||||
|
return 25;
|
||||||
|
}
|
||||||
if (plugin->run_string("noop", retval, false) != PLUGIN_OK) {
|
if (plugin->run_string("noop", retval, false) != PLUGIN_OK) {
|
||||||
return 13;
|
return 13;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
|
// LinuxCNC source basis: src/libnml/rcs/rcs_print.cc provides these RCS
|
||||||
|
// print-error entry points used by NML message validation.
|
||||||
extern "C" int set_print_rcs_error_info(const char *, int) {
|
extern "C" int set_print_rcs_error_info(const char *, int) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
|
// LinuxCNC source basis: src/emc/rs274ngc/rs274ngc_pre.cc provides
|
||||||
|
// Interp::init(), read(), execute(), and sequence_number().
|
||||||
int main() {
|
int main() {
|
||||||
cnc_sim_init_minimal_linuxcnc_tooldata();
|
cnc_sim_init_minimal_linuxcnc_tooldata();
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
|
// LinuxCNC source basis: src/rtapi/uspace_common.h provides rtapi_snprintf()
|
||||||
|
// and rtapi_vsnprintf() for userspace RTAPI formatting; src/rtapi/rtapi.h
|
||||||
|
// declares both APIs.
|
||||||
extern "C" int rtapi_snprintf(char *buf, unsigned long size, const char *fmt, ...) {
|
extern "C" int rtapi_snprintf(char *buf, unsigned long size, const char *fmt, ...) {
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
@@ -9,3 +12,6 @@ extern "C" int rtapi_snprintf(char *buf, unsigned long size, const char *fmt, ..
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" int rtapi_vsnprintf(char *buf, unsigned long size, const char *fmt, va_list ap) {
|
||||||
|
return std::vsnprintf(buf, static_cast<std::size_t>(size), fmt, ap);
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
#include "boost/python/object.hpp"
|
#include "boost/python/object.hpp"
|
||||||
|
|
||||||
|
// LinuxCNC source basis: src/emc/rs274ngc/gcodemodule.cc includes
|
||||||
|
// structmember.h for PyMemberDef entries using T_INT, T_DOUBLE, and READONLY.
|
||||||
#define T_INT 1
|
#define T_INT 1
|
||||||
#define T_DOUBLE 2
|
#define T_DOUBLE 2
|
||||||
#define READONLY 1
|
#define READONLY 1
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
#include "nml_intf/canon.hh"
|
#include "nml_intf/canon.hh"
|
||||||
#include "nml_intf/emc_nml.hh"
|
#include "nml_intf/emc_nml.hh"
|
||||||
|
|
||||||
|
// LinuxCNC source basis: src/emc/tooldata/tooldata.hh declares the tooldata
|
||||||
|
// API used by taskclass.cc, emccanon.cc, and RS274 tool table paths.
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
// LinuxCNC source basis: src/emc/tooldata/tooldata_common.cc provides
|
||||||
|
// tool table parsing, formatting, load/save, and DB mode entry points.
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
std::string make_temp_template(const char *name) {
|
std::string make_temp_template(const char *name) {
|
||||||
@@ -87,6 +89,83 @@ int main() {
|
|||||||
return 9;
|
return 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tooldata_init(false);
|
||||||
|
std::string nonrandom_path_template = make_temp_template("cnc_tooldata_common_nonrandom_probe_XXXXXX");
|
||||||
|
std::vector<char> nonrandom_path(nonrandom_path_template.begin(), nonrandom_path_template.end());
|
||||||
|
nonrandom_path.push_back('\0');
|
||||||
|
const int nonrandom_fd = mkstemp(nonrandom_path.data());
|
||||||
|
if (nonrandom_fd < 0) {
|
||||||
|
return 16;
|
||||||
|
}
|
||||||
|
FILE *nonrandom_fp = fdopen(nonrandom_fd, "w");
|
||||||
|
if (!nonrandom_fp) {
|
||||||
|
close(nonrandom_fd);
|
||||||
|
std::remove(nonrandom_path.data());
|
||||||
|
return 17;
|
||||||
|
}
|
||||||
|
std::fputs("T-1 P5 Z4.25\n", nonrandom_fp);
|
||||||
|
std::fclose(nonrandom_fp);
|
||||||
|
|
||||||
|
if (tooldata_load(nonrandom_path.data()) != 0) {
|
||||||
|
std::remove(nonrandom_path.data());
|
||||||
|
return 18;
|
||||||
|
}
|
||||||
|
std::remove(nonrandom_path.data());
|
||||||
|
|
||||||
|
// LinuxCNC source basis: src/emc/tooldata/tooldata_common.cc
|
||||||
|
// tooldata_load() mirrors a matching nonrandom table entry into spindle
|
||||||
|
// pocket 0 after tooldata_read_entry() assigns fake pocket indices.
|
||||||
|
CANON_TOOL_TABLE nonrandom_spindle = tooldata_entry_init();
|
||||||
|
if (tooldata_get(&nonrandom_spindle, 0) != IDX_OK) {
|
||||||
|
return 19;
|
||||||
|
}
|
||||||
|
if (nonrandom_spindle.toolno != -1 || nonrandom_spindle.pocketno != 5 ||
|
||||||
|
nonrandom_spindle.offset.tran.z != 4.25) {
|
||||||
|
return 20;
|
||||||
|
}
|
||||||
|
CANON_TOOL_TABLE spindle_only = tooldata_entry_init();
|
||||||
|
spindle_only.toolno = 99;
|
||||||
|
spindle_only.pocketno = 0;
|
||||||
|
spindle_only.offset.tran.z = 9.0;
|
||||||
|
if (tooldata_put(spindle_only, 0) != IDX_OK) {
|
||||||
|
return 21;
|
||||||
|
}
|
||||||
|
CANON_TOOL_TABLE saved_nonspindle = tooldata_entry_init();
|
||||||
|
saved_nonspindle.toolno = 7;
|
||||||
|
saved_nonspindle.pocketno = 5;
|
||||||
|
saved_nonspindle.offset.tran.z = 4.25;
|
||||||
|
if (tooldata_put(saved_nonspindle, 1) == IDX_FAIL) {
|
||||||
|
return 26;
|
||||||
|
}
|
||||||
|
std::string nonrandom_save_template = make_temp_template("cnc_tooldata_common_nonrandom_save_XXXXXX");
|
||||||
|
std::vector<char> nonrandom_save_path(nonrandom_save_template.begin(), nonrandom_save_template.end());
|
||||||
|
nonrandom_save_path.push_back('\0');
|
||||||
|
const int nonrandom_save_fd = mkstemp(nonrandom_save_path.data());
|
||||||
|
if (nonrandom_save_fd < 0) {
|
||||||
|
return 22;
|
||||||
|
}
|
||||||
|
close(nonrandom_save_fd);
|
||||||
|
// LinuxCNC source basis: src/emc/tooldata/tooldata_common.cc saves
|
||||||
|
// nonrandom tool tables starting at pocket 1, so spindle pocket 0 is not
|
||||||
|
// emitted unless DB_ACTIVE selects the one-entry spindle save path.
|
||||||
|
if (tooldata_save(nonrandom_save_path.data()) != 0) {
|
||||||
|
std::remove(nonrandom_save_path.data());
|
||||||
|
return 23;
|
||||||
|
}
|
||||||
|
FILE *nonrandom_save_fp = std::fopen(nonrandom_save_path.data(), "r");
|
||||||
|
if (!nonrandom_save_fp) {
|
||||||
|
std::remove(nonrandom_save_path.data());
|
||||||
|
return 24;
|
||||||
|
}
|
||||||
|
char nonrandom_saved[CANON_TOOL_ENTRY_LEN * 2] = {};
|
||||||
|
std::fread(nonrandom_saved, 1, sizeof(nonrandom_saved) - 1, nonrandom_save_fp);
|
||||||
|
std::fclose(nonrandom_save_fp);
|
||||||
|
std::remove(nonrandom_save_path.data());
|
||||||
|
if (std::strstr(nonrandom_saved, "T99") || !std::strstr(nonrandom_saved, "T7")) {
|
||||||
|
return 25;
|
||||||
|
}
|
||||||
|
|
||||||
|
tooldata_init(true);
|
||||||
tooldata_set_db(DB_ACTIVE);
|
tooldata_set_db(DB_ACTIVE);
|
||||||
if (tooldata_load(path.data()) != -1) {
|
if (tooldata_load(path.data()) != -1) {
|
||||||
return 10;
|
return 10;
|
||||||
|
|||||||
@@ -4,6 +4,9 @@
|
|||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
// LinuxCNC source basis: src/emc/tooldata/tooldata_mmap.cc provides these
|
||||||
|
// tool_mmap and indexed tool table entry points; this shim keeps them
|
||||||
|
// available for WASM without native mmap.
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
CANON_TOOL_TABLE tool_table[CANON_POCKETS_MAX];
|
CANON_TOOL_TABLE tool_table[CANON_POCKETS_MAX];
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
// LinuxCNC source basis: src/emc/tooldata/tooldata_mmap.cc, tooldata_nml.cc,
|
||||||
|
// tooldata_db.cc, and taskclass.cc cover these tooldata runtime edge cases.
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
std::string make_temp_path(const char *name) {
|
std::string make_temp_path(const char *name) {
|
||||||
@@ -82,6 +84,71 @@ int main() {
|
|||||||
return 45;
|
return 45;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const pid_t nonrandom_t0_child = fork();
|
||||||
|
if (nonrandom_t0_child < 0) {
|
||||||
|
return 64;
|
||||||
|
}
|
||||||
|
if (nonrandom_t0_child == 0) {
|
||||||
|
if (tool_mmap_creator(nullptr, 0) != 0) {
|
||||||
|
_exit(65);
|
||||||
|
}
|
||||||
|
CANON_TOOL_TABLE nonrandom_t0 = tooldata_entry_init();
|
||||||
|
nonrandom_t0.toolno = 0;
|
||||||
|
nonrandom_t0.pocketno = 12;
|
||||||
|
if (tooldata_put(nonrandom_t0, 12) == IDX_FAIL) {
|
||||||
|
_exit(66);
|
||||||
|
}
|
||||||
|
// LinuxCNC src/emc/tooldata/tooldata_mmap.cc returns pocket 0 for T0
|
||||||
|
// before scanning table entries when the toolchanger is not random.
|
||||||
|
if (tooldata_find_index_for_tool(0) != 0) {
|
||||||
|
_exit(67);
|
||||||
|
}
|
||||||
|
_exit(0);
|
||||||
|
}
|
||||||
|
int nonrandom_t0_status = 0;
|
||||||
|
if (waitpid(nonrandom_t0_child, &nonrandom_t0_status, 0) != nonrandom_t0_child) {
|
||||||
|
return 68;
|
||||||
|
}
|
||||||
|
if (!WIFEXITED(nonrandom_t0_status) || WEXITSTATUS(nonrandom_t0_status) != 0) {
|
||||||
|
return 69;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pid_t random_t0_child = fork();
|
||||||
|
if (random_t0_child < 0) {
|
||||||
|
return 70;
|
||||||
|
}
|
||||||
|
if (random_t0_child == 0) {
|
||||||
|
if (tool_mmap_creator(nullptr, 1) != 0) {
|
||||||
|
_exit(71);
|
||||||
|
}
|
||||||
|
tooldata_reset();
|
||||||
|
CANON_TOOL_TABLE spindle_t0 = tooldata_entry_init();
|
||||||
|
spindle_t0.toolno = 0;
|
||||||
|
spindle_t0.pocketno = 0;
|
||||||
|
if (tooldata_put(spindle_t0, 0) == IDX_FAIL) {
|
||||||
|
_exit(72);
|
||||||
|
}
|
||||||
|
CANON_TOOL_TABLE random_t0 = tooldata_entry_init();
|
||||||
|
random_t0.toolno = 0;
|
||||||
|
random_t0.pocketno = 15;
|
||||||
|
if (tooldata_put(random_t0, 15) == IDX_FAIL) {
|
||||||
|
_exit(73);
|
||||||
|
}
|
||||||
|
// LinuxCNC src/emc/tooldata/tooldata_mmap.cc continues past pocket 0
|
||||||
|
// while scanning random toolchanger entries, so a nonzero T0 pocket wins.
|
||||||
|
if (tooldata_find_index_for_tool(0) != 15) {
|
||||||
|
_exit(74);
|
||||||
|
}
|
||||||
|
_exit(0);
|
||||||
|
}
|
||||||
|
int random_t0_status = 0;
|
||||||
|
if (waitpid(random_t0_child, &random_t0_status, 0) != random_t0_child) {
|
||||||
|
return 75;
|
||||||
|
}
|
||||||
|
if (!WIFEXITED(random_t0_status) || WEXITSTATUS(random_t0_status) != 0) {
|
||||||
|
return 76;
|
||||||
|
}
|
||||||
|
|
||||||
if (tool_mmap_user() != -1) {
|
if (tool_mmap_user() != -1) {
|
||||||
return 25;
|
return 25;
|
||||||
}
|
}
|
||||||
@@ -111,6 +178,18 @@ int main() {
|
|||||||
if (tooldata_db_getall() != -1) {
|
if (tooldata_db_getall() != -1) {
|
||||||
return 28;
|
return 28;
|
||||||
}
|
}
|
||||||
|
// LinuxCNC source basis: src/emc/tooldata/tooldata_db.cc
|
||||||
|
// tooldata_db_notify() returns 0 without side effects while db_live is
|
||||||
|
// false, which is the browser-safe shim state for external DB services.
|
||||||
|
if (tooldata_db_notify(SPINDLE_LOAD, 7, 12, uncreated_tool) != 0) {
|
||||||
|
return 57;
|
||||||
|
}
|
||||||
|
if (tooldata_db_notify(SPINDLE_UNLOAD, 7, 12, uncreated_tool) != 0) {
|
||||||
|
return 58;
|
||||||
|
}
|
||||||
|
if (tooldata_db_notify(TOOL_OFFSET, 7, 12, uncreated_tool) != 0) {
|
||||||
|
return 59;
|
||||||
|
}
|
||||||
std::string missing_db_program_with_arg_string =
|
std::string missing_db_program_with_arg_string =
|
||||||
make_temp_path("cnc_tooldata_probe_missing_db_program arg");
|
make_temp_path("cnc_tooldata_probe_missing_db_program arg");
|
||||||
std::vector<char> missing_db_program_with_arg(
|
std::vector<char> missing_db_program_with_arg(
|
||||||
@@ -124,6 +203,19 @@ int main() {
|
|||||||
if (separator == std::string::npos || missing_db_program_with_arg[separator] != 0) {
|
if (separator == std::string::npos || missing_db_program_with_arg[separator] != 0) {
|
||||||
return 56;
|
return 56;
|
||||||
}
|
}
|
||||||
|
char missing_db_program_with_args[] = "/tmp/cnc_tooldata_probe_missing_db_program alpha beta";
|
||||||
|
if (tooldata_db_init(missing_db_program_with_args, 0) != -1) {
|
||||||
|
return 83;
|
||||||
|
}
|
||||||
|
// LinuxCNC src/emc/tooldata/tooldata_db.cc tokenizes progname_plus_args
|
||||||
|
// in place with strtok_r() before checking whether argv[0] is executable.
|
||||||
|
if (missing_db_program_with_args[42] != 0 ||
|
||||||
|
missing_db_program_with_args[48] != 0 ||
|
||||||
|
std::strcmp(missing_db_program_with_args, "/tmp/cnc_tooldata_probe_missing_db_program") != 0 ||
|
||||||
|
std::strcmp(missing_db_program_with_args + 43, "alpha") != 0 ||
|
||||||
|
std::strcmp(missing_db_program_with_args + 49, "beta") != 0) {
|
||||||
|
return 84;
|
||||||
|
}
|
||||||
std::string missing_db_program_string = make_temp_path("cnc_tooldata_probe_missing_db_program");
|
std::string missing_db_program_string = make_temp_path("cnc_tooldata_probe_missing_db_program");
|
||||||
std::vector<char> missing_db_program(
|
std::vector<char> missing_db_program(
|
||||||
missing_db_program_string.begin(),
|
missing_db_program_string.begin(),
|
||||||
@@ -145,6 +237,12 @@ int main() {
|
|||||||
}
|
}
|
||||||
alignas(EMC_TOOL_STAT) unsigned char toolstat_storage[sizeof(EMC_TOOL_STAT)] = {};
|
alignas(EMC_TOOL_STAT) unsigned char toolstat_storage[sizeof(EMC_TOOL_STAT)] = {};
|
||||||
auto *toolstat = reinterpret_cast<EMC_TOOL_STAT *>(toolstat_storage);
|
auto *toolstat = reinterpret_cast<EMC_TOOL_STAT *>(toolstat_storage);
|
||||||
|
// LinuxCNC src/emc/tooldata/tooldata_nml.cc accepts a non-null table
|
||||||
|
// pointer before NML-backed tooldata operations can use it.
|
||||||
|
CANON_TOOL_TABLE nml_tool_table[CANON_POCKETS_MAX] = {};
|
||||||
|
if (tool_nml_register(nml_tool_table) != 0) {
|
||||||
|
return 77;
|
||||||
|
}
|
||||||
if (tool_mmap_creator(toolstat, 1) != 0) {
|
if (tool_mmap_creator(toolstat, 1) != 0) {
|
||||||
return 15;
|
return 15;
|
||||||
}
|
}
|
||||||
@@ -187,6 +285,23 @@ int main() {
|
|||||||
if (tooldata_put(tool, 12) != IDX_NEW) {
|
if (tooldata_put(tool, 12) != IDX_NEW) {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
CANON_TOOL_TABLE lower_index_tool = tooldata_entry_init();
|
||||||
|
lower_index_tool.toolno = 3;
|
||||||
|
lower_index_tool.pocketno = 5;
|
||||||
|
// LinuxCNC src/emc/tooldata/tooldata_mmap.cc returns IDX_OK for writes at
|
||||||
|
// or below the current last_index and leaves last_index unchanged.
|
||||||
|
if (tooldata_put(lower_index_tool, 5) != IDX_OK) {
|
||||||
|
return 80;
|
||||||
|
}
|
||||||
|
if (tooldata_last_index_get() != 12) {
|
||||||
|
return 81;
|
||||||
|
}
|
||||||
|
CANON_TOOL_TABLE lower_index_loaded = tooldata_entry_init();
|
||||||
|
if (tooldata_get(&lower_index_loaded, 5) != IDX_OK ||
|
||||||
|
lower_index_loaded.toolno != 3 ||
|
||||||
|
lower_index_loaded.pocketno != 5) {
|
||||||
|
return 82;
|
||||||
|
}
|
||||||
|
|
||||||
CANON_TOOL_TABLE loaded = tooldata_entry_init();
|
CANON_TOOL_TABLE loaded = tooldata_entry_init();
|
||||||
if (tooldata_get(&loaded, 12) != IDX_OK) {
|
if (tooldata_get(&loaded, 12) != IDX_OK) {
|
||||||
@@ -298,6 +413,16 @@ int main() {
|
|||||||
reloaded.offset.tran.z != 3.5 || reloaded.diameter != 8.0) {
|
reloaded.offset.tran.z != 3.5 || reloaded.diameter != 8.0) {
|
||||||
return 14;
|
return 14;
|
||||||
}
|
}
|
||||||
|
tooldata_last_index_set(12);
|
||||||
|
tooldata_reset();
|
||||||
|
// LinuxCNC src/emc/tooldata/tooldata_mmap.cc clears table entries in
|
||||||
|
// tooldata_reset() without changing the mmap header last_index.
|
||||||
|
if (tooldata_last_index_get() != 12) {
|
||||||
|
return 78;
|
||||||
|
}
|
||||||
|
if (tooldata_find_index_for_tool(7) != -1) {
|
||||||
|
return 79;
|
||||||
|
}
|
||||||
|
|
||||||
tooldata_init(false);
|
tooldata_init(false);
|
||||||
std::string nonrandom_path_template = make_temp_path("cnc_tooldata_nonrandom_probe_XXXXXX");
|
std::string nonrandom_path_template = make_temp_path("cnc_tooldata_nonrandom_probe_XXXXXX");
|
||||||
@@ -336,5 +461,21 @@ int main() {
|
|||||||
return 39;
|
return 39;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const pid_t recreate_child = fork();
|
||||||
|
if (recreate_child < 0) {
|
||||||
|
return 60;
|
||||||
|
}
|
||||||
|
if (recreate_child == 0) {
|
||||||
|
tool_mmap_creator(nullptr, 1);
|
||||||
|
_exit(61);
|
||||||
|
}
|
||||||
|
int recreate_status = 0;
|
||||||
|
if (waitpid(recreate_child, &recreate_status, 0) != recreate_child) {
|
||||||
|
return 62;
|
||||||
|
}
|
||||||
|
if (!WIFEXITED(recreate_status) || WEXITSTATUS(recreate_status) == 0) {
|
||||||
|
return 63;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
#include "tooldata.hh"
|
#include "tooldata.hh"
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
// LinuxCNC source basis: src/emc/tooldata/tooldata_nml.cc and tooldata_db.cc
|
||||||
|
// provide the NML registration and external tool database entry points.
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr int MAX_DB_PROGRAM_ARGS = 10;
|
constexpr int MAX_DB_PROGRAM_ARGS = 10;
|
||||||
@@ -13,7 +16,11 @@ bool db_live = false;
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
int tool_nml_register(CANON_TOOL_TABLE *tblptr) {
|
int tool_nml_register(CANON_TOOL_TABLE *tblptr) {
|
||||||
return tblptr ? 0 : -1;
|
if (!tblptr) {
|
||||||
|
std::fprintf(stderr, "%5d!!!PROBLEM tool_nml_register()\n", getpid());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tooldata_db_init(char progname_plus_args[], int) {
|
int tooldata_db_init(char progname_plus_args[], int) {
|
||||||
@@ -27,16 +34,22 @@ int tooldata_db_init(char progname_plus_args[], int) {
|
|||||||
for (char *token = program; token; token = strtok_r(nullptr, " ", &saveptr)) {
|
for (char *token = program; token; token = strtok_r(nullptr, " ", &saveptr)) {
|
||||||
++argc;
|
++argc;
|
||||||
if (argc >= MAX_DB_PROGRAM_ARGS) {
|
if (argc >= MAX_DB_PROGRAM_ARGS) {
|
||||||
|
std::fprintf(stderr,
|
||||||
|
"!!!!db_init: argc exceeds MAX_DB_PROGRAM_ARGS=%d\n",
|
||||||
|
MAX_DB_PROGRAM_ARGS);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!program || access(program, X_OK) != 0) {
|
if (!program || access(program, X_OK) != 0) {
|
||||||
|
std::fprintf(stderr, "!!!!db_init: <%s> not executable\n", program ? program : "");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tooldata_db_notify(tool_notify_t, int, int, CANON_TOOL_TABLE) {
|
int tooldata_db_notify(tool_notify_t, int, int, CANON_TOOL_TABLE) {
|
||||||
|
// LinuxCNC source basis: src/emc/tooldata/tooldata_db.cc returns 0 when
|
||||||
|
// db_live is false, before formatting or sending any DB notification.
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ advanced in this order:
|
|||||||
Each step should stay source-backed and close one small gap at a time. A single
|
Each step should stay source-backed and close one small gap at a time. A single
|
||||||
pass should advance one workstream context only; crossing into another context
|
pass should advance one workstream context only; crossing into another context
|
||||||
requires naming the dependency and keeping the edit set minimal.
|
requires naming the dependency and keeping the edit set minimal.
|
||||||
|
The synchronized source-map guard for these contexts is
|
||||||
|
`docs/linuxcnc-workstreams-source-map.md`.
|
||||||
|
|
||||||
## Dialect expansion
|
## Dialect expansion
|
||||||
|
|
||||||
|
|||||||
60
docs/linuxcnc-browser-app-source-map.md
Normal file
60
docs/linuxcnc-browser-app-source-map.md
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
# LinuxCNC browser app source map
|
||||||
|
|
||||||
|
This map records the source basis and boundaries for the browser application
|
||||||
|
entry point. It is an app/bridge guard only; it must not add CNC behavior and
|
||||||
|
must not expand the temporary smoke parser.
|
||||||
|
|
||||||
|
## LinuxCNC Source Basis
|
||||||
|
|
||||||
|
- `src/emc/rs274ngc/interp_internal.hh` defines
|
||||||
|
`RS274NGC_PARAMETER_FILE_NAME_DEFAULT` as `rs274ngc.var`.
|
||||||
|
- `src/emc/rs274ngc/interp_internal.hh` defines
|
||||||
|
`RS274NGC_PARAMETER_FILE_BACKUP_SUFFIX` as `.bak`.
|
||||||
|
- `src/emc/rs274ngc/rs274ngc_pre.cc` restores parameter files through
|
||||||
|
`Interp::restore_parameters()`.
|
||||||
|
- `src/emc/rs274ngc/rs274ngc_pre.cc` saves parameter files through
|
||||||
|
`Interp::save_parameters()`, including `filename + ".new"` and
|
||||||
|
`filename + ".bak"` behavior.
|
||||||
|
- `src/emc/rs274ngc/rs274ngc_pre.cc` orders parameter saves as backup unlink,
|
||||||
|
backup link, then temporary-file rename.
|
||||||
|
- `src/emc/rs274ngc/rs274ngc_pre.cc` reads program blocks through `read_text()`
|
||||||
|
after the browser bridge mirrors the OPFS program into the WASM filesystem.
|
||||||
|
|
||||||
|
## App Coverage
|
||||||
|
|
||||||
|
| Entry | Boundary |
|
||||||
|
| --- | --- |
|
||||||
|
| `web/index.html` | Loads the browser app module and exposes only the LinuxCNC backend selector. |
|
||||||
|
| `web/src/app.js` | Creates a required OPFS-backed simulator workspace and parses browser programs with a LinuxCNC parameter file. |
|
||||||
|
| `web/test-browser-wasm-smoke-app-sections.js` | Verifies app-level OPFS program save, parameter save, parameter backup, and reload behavior. |
|
||||||
|
|
||||||
|
## Boundaries
|
||||||
|
|
||||||
|
- The app must use the existing `createWasmSimulator()` OPFS API; it must not
|
||||||
|
add browser filesystem persistence outside OPFS.
|
||||||
|
- The app must persist the current program at `programs/current.ngc`.
|
||||||
|
- The app must persist LinuxCNC parameter state at `parameters/rs274ngc.var`.
|
||||||
|
- The app must require OPFS for browser program parsing and call
|
||||||
|
`parseFileWithParameterFile(programPath, parameterPath, "linuxcnc", ...)`.
|
||||||
|
- The visible backend selector must stay limited to the LinuxCNC RS274 backend.
|
||||||
|
- The app must not read the backend selector as a dynamic backend source; it
|
||||||
|
must force `linuxCncBackend` for browser parsing.
|
||||||
|
- `test-native.sh`, `test-linuxcnc-source-link.sh`, and `build-wasm.sh` must
|
||||||
|
run `./check-linuxcnc-browser-app-source-map.sh` before consuming app-backed
|
||||||
|
browser artifacts or source-linked LinuxCNC objects.
|
||||||
|
- The browser app source-map guard validates LinuxCNC source-backed bridge
|
||||||
|
routing and OPFS policy only; it does not interpret G-code.
|
||||||
|
|
||||||
|
## Checks
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./check-linuxcnc-browser-app-source-map.sh
|
||||||
|
./test-native.sh
|
||||||
|
./test-linuxcnc-source-link.sh
|
||||||
|
./build-wasm.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
`check-linuxcnc-browser-app-source-map.sh` keeps this document, LinuxCNC source
|
||||||
|
anchors, app OPFS paths, app parse routing, and app smoke coverage synchronized.
|
||||||
69
docs/linuxcnc-build-source-map.md
Normal file
69
docs/linuxcnc-build-source-map.md
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
# LinuxCNC build source map
|
||||||
|
|
||||||
|
This map records the source basis and boundaries for native, source-link, and
|
||||||
|
WASM build entry points. It is a build-policy guard only; it must not add CNC
|
||||||
|
behavior and must not expand the temporary smoke parser.
|
||||||
|
|
||||||
|
## LinuxCNC Source Basis
|
||||||
|
|
||||||
|
- `src/emc/rs274ngc/Submakefile` defines `LIBRS274SRCS`, including
|
||||||
|
`rs274ngc_pre.cc`, and links RS274 against LinuxCNC INI, HAL, Python plugin,
|
||||||
|
and tooldata libraries.
|
||||||
|
- `src/emc/tooldata/Submakefile` defines the LinuxCNC tooldata common, mmap,
|
||||||
|
NML, database, reader, and watcher sources tracked by native and WASM
|
||||||
|
manifests.
|
||||||
|
- `src/emc/ini/Submakefile` defines `liblinuxcncini` and exported INI headers
|
||||||
|
used by source-linked and WASM-safe probes.
|
||||||
|
- `src/emc/pythonplugin/Submakefile` defines `libpyplugin`, which remains a
|
||||||
|
source-tracked Python/remap dependency for source-link and WASM blocker
|
||||||
|
coverage.
|
||||||
|
- `src/emc/kinematics/Submakefile` and `src/emc/kinematics/kinematics.h`
|
||||||
|
define the kinematics source/header surface consumed by switchkins and RTCP
|
||||||
|
build coverage.
|
||||||
|
|
||||||
|
## Build Coverage
|
||||||
|
|
||||||
|
| Entry | Boundary |
|
||||||
|
| --- | --- |
|
||||||
|
| `test-native.sh` | Runs source-map guards, cached manifest checks, native bridge/API/RS274 probes, and Node/browser smoke after LinuxCNC source-backed native coverage. |
|
||||||
|
| `test-linuxcnc-source-link.sh` | Compiles manifest-selected LinuxCNC RS274 objects directly with source-link support objects and project dump bridges. |
|
||||||
|
| `test-linuxcnc-source-syntax.sh` | Keeps native source syntax validation on persistent dependency-tracked stamps. |
|
||||||
|
| `test-linuxcnc-wasm-cmake-safe-probe.sh` | Configures source-backed WASM-safe probe targets with persistent CMake signatures. |
|
||||||
|
| `build-wasm.sh` | Runs source-map guards, blocker, syntax, object, CMake, tooldata, interp, Python, `rs274ngc_pre`, and runtime probes before copying browser artifacts. |
|
||||||
|
|
||||||
|
## Boundaries
|
||||||
|
|
||||||
|
- Default WASM manifest partition must remain 26 wasm-safe `core` sources and
|
||||||
|
9 `blocked` sources.
|
||||||
|
- Build paths must keep `CNC_SIM_BUILD_JOBS` as scheduling only. Persistent
|
||||||
|
object/configure signatures must not include it.
|
||||||
|
- Hot object probes must use explicit `BUILD_RULE_VERSION` or
|
||||||
|
`CONFIGURE_RULE_VERSION` fields instead of hashing whole scripts.
|
||||||
|
- `build-wasm.sh` must configure with `CNC_SIM_ENABLE_SMOKE_BACKEND=OFF` and
|
||||||
|
`CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE=ON`.
|
||||||
|
- `test-native.sh`, `test-linuxcnc-source-link.sh`, and `build-wasm.sh` must
|
||||||
|
run the same source-map guards before consuming generated config aliases,
|
||||||
|
source-linked LinuxCNC objects, or browser artifacts.
|
||||||
|
- `build-wasm.sh` must build `cnc_sim_wasm_runtime_probe` before
|
||||||
|
`cnc_sim_wasm`, copy `cnc_sim.js` and `cnc_sim.wasm` exactly once, compare
|
||||||
|
copied artifacts with `cmp -s`, and run Node smoke before browser smoke.
|
||||||
|
- Build directory overrides must be non-empty, whitespace-free, and outside the
|
||||||
|
filesystem root.
|
||||||
|
- The build source-map guard validates LinuxCNC build-source ownership,
|
||||||
|
manifest partitions, and artifact ordering only; it does not interpret
|
||||||
|
G-code.
|
||||||
|
|
||||||
|
## Checks
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./check-linuxcnc-build-source-map.sh
|
||||||
|
./test-native.sh
|
||||||
|
./test-linuxcnc-source-link.sh
|
||||||
|
./build-wasm.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
`check-linuxcnc-build-source-map.sh` keeps this document, LinuxCNC build-source
|
||||||
|
anchors, manifest partition assumptions, build script ordering, and
|
||||||
|
source-backed build-policy boundaries synchronized.
|
||||||
69
docs/linuxcnc-kinematics-source-map.md
Normal file
69
docs/linuxcnc-kinematics-source-map.md
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
# LinuxCNC kinematics source map
|
||||||
|
|
||||||
|
This map tracks the LinuxCNC source/configuration basis for kinematics,
|
||||||
|
switchkins, RTCP, and M428/M429/M430 remap coverage. It is intentionally a
|
||||||
|
source-coverage document: the manifest entries here must not become browser or
|
||||||
|
application behavior by themselves. It must not add CNC behavior and must not
|
||||||
|
expand the temporary smoke parser.
|
||||||
|
|
||||||
|
## Manifest groups
|
||||||
|
|
||||||
|
The kinematics manifest currently contains these LinuxCNC-root relative source
|
||||||
|
groups:
|
||||||
|
|
||||||
|
| Group | Count | Source coverage role |
|
||||||
|
| --- | ---: | --- |
|
||||||
|
| `core` | 30 | LinuxCNC kinematics and posemath C/C++ sources. |
|
||||||
|
| `config` | 33 | LinuxCNC switchkins INI/HAL/POSTGUI sources used by M428/M429/M430 and RTCP coverage. |
|
||||||
|
| `remap` | 31 | LinuxCNC M428/M429/M430 remap subroutines. |
|
||||||
|
| `component` | 8 | LinuxCNC halcompile component sources referenced by kinematics configs. |
|
||||||
|
| `generated` | 8 | LinuxCNC halcompile-generated C sources adjacent to component coverage. |
|
||||||
|
| `header` | 13 | LinuxCNC kinematics and posemath declarations. |
|
||||||
|
| `metadata` | 34 | LinuxCNC build/manpage metadata adjacent to covered kinematics sources. |
|
||||||
|
| `asset` | 122 | LinuxCNC INI-adjacent HAL, README, GUI, demo, PYVCP, and other source assets. |
|
||||||
|
| `tooldata` | 10 | LinuxCNC tool table files referenced by covered M428/M429/M430 INI files. |
|
||||||
|
|
||||||
|
## LinuxCNC Source Anchors
|
||||||
|
|
||||||
|
- `src/emc/kinematics/Submakefile` defines the kinematics build source surface,
|
||||||
|
including `GENSERKINSSRCS`.
|
||||||
|
- `src/emc/kinematics/kinematics.h` declares `kinematicsInverse()` and
|
||||||
|
`kinematicsSwitch()` for switchkins-capable modules.
|
||||||
|
- `configs/sim/axis/vismach/5axis/bridgemill/5axis.ini` wires
|
||||||
|
`motion.switchkins-type` and representative M428/M429/M430 switchkins
|
||||||
|
configuration.
|
||||||
|
- `configs/sim/axis/vismach/5axis/bridgemill/remap_subs/428remap.ngc`
|
||||||
|
provides a representative `#<kinstype>` remap assignment.
|
||||||
|
|
||||||
|
## Boundaries
|
||||||
|
|
||||||
|
- `asset`, `metadata`, `header`, and `tooldata` entries are source coverage,
|
||||||
|
not browser API surface.
|
||||||
|
- Browser-side file persistence remains OPFS-only and is not expanded by this
|
||||||
|
manifest.
|
||||||
|
- Switchkins/remap generation must continue to derive M428/M429/M430 cases from
|
||||||
|
LinuxCNC INI/HAL/remap sources, not from hand-authored behavior.
|
||||||
|
- RTCP and five-axis coverage must remain tied to LinuxCNC kinematics sources,
|
||||||
|
remap files, and configuration files.
|
||||||
|
- `test-native.sh`, `test-linuxcnc-source-link.sh`, and `build-wasm.sh` must
|
||||||
|
run `./check-linuxcnc-kinematics-source-map.sh` before consuming generated
|
||||||
|
config aliases, source-linked LinuxCNC objects, or browser artifacts.
|
||||||
|
- The kinematics source-map guard validates LinuxCNC source/config ownership
|
||||||
|
and manifest drift only; it does not interpret G-code.
|
||||||
|
|
||||||
|
## Checks
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./check-linuxcnc-kinematics-source-map.sh
|
||||||
|
./check-linuxcnc-inputs.sh linuxcnc-kinematics-source-files.txt --require-kinematics-complete
|
||||||
|
./check-linuxcnc-switchkins-remap-table.sh linuxcnc-kinematics-source-files.txt
|
||||||
|
./test-native.sh
|
||||||
|
./test-linuxcnc-source-link.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
`check-linuxcnc-kinematics-source-map.sh` keeps this document's group counts in
|
||||||
|
sync with `linuxcnc-kinematics-source-files.txt`; the input and table checkers
|
||||||
|
validate LinuxCNC source existence, directory completeness, remap kinstype
|
||||||
|
notes, and switchkins/remap table generation.
|
||||||
74
docs/linuxcnc-opfs-source-map.md
Normal file
74
docs/linuxcnc-opfs-source-map.md
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
# LinuxCNC OPFS source map
|
||||||
|
|
||||||
|
This map records the source basis for browser-side LinuxCNC parameter-file
|
||||||
|
persistence. It is a policy and bridge map only; it must not add independent filesystem behavior or expand the temporary smoke parser.
|
||||||
|
|
||||||
|
## LinuxCNC Source Basis
|
||||||
|
|
||||||
|
- `src/emc/rs274ngc/interp_internal.hh` defines
|
||||||
|
`RS274NGC_PARAMETER_FILE_NAME_DEFAULT` as `rs274ngc.var`.
|
||||||
|
- `src/emc/rs274ngc/interp_internal.hh` defines
|
||||||
|
`RS274NGC_PARAMETER_FILE_BACKUP_SUFFIX` as `.bak`.
|
||||||
|
- `src/emc/rs274ngc/rs274ngc_pre.cc` implements
|
||||||
|
`Interp::restore_parameters()` for parameter-file reads.
|
||||||
|
- `src/emc/rs274ngc/rs274ngc_pre.cc` implements `Interp::open()` by opening
|
||||||
|
the NC program file with `fopen(filename, "r")`, then `Interp::_read()`
|
||||||
|
feeds `_setup.file_pointer` into `read_text()`.
|
||||||
|
- `src/emc/rs274ngc/rs274ngc_pre.cc` implements
|
||||||
|
`Interp::save_parameters()` for `filename + ".new"`, backup, and replacement
|
||||||
|
writes.
|
||||||
|
- `src/emc/rs274ngc/rs274ngc_pre.cc` unlinks `filename + ".bak"`, links the
|
||||||
|
previous parameter file to that backup path, then renames `filename + ".new"`
|
||||||
|
over the configured parameter file.
|
||||||
|
|
||||||
|
## OPFS Coverage
|
||||||
|
|
||||||
|
| Entry | Boundary |
|
||||||
|
| --- | --- |
|
||||||
|
| `web/src/wasm-core.js` | Keeps LinuxCNC program reads and parameter restore/save bridge files under the OPFS workspace and cleans WASM scratch files. |
|
||||||
|
| `web/src/wasm-core.d.ts` | Exposes only the OPFS-backed workspace, directory, parameter-file, and mount/workspace option types used by the bridge. |
|
||||||
|
| `web/src/app.js` | Persists browser programs at `programs/current.ngc` and LinuxCNC parameters at `parameters/rs274ngc.var`. |
|
||||||
|
| `web/test-browser-wasm-smoke-opfs-basic-sections.js` | Verifies OPFS program file reads through LinuxCNC `read_text()` and rejects unsafe paths. |
|
||||||
|
| `web/test-browser-wasm-smoke-opfs-parameter-sections.js` | Verifies OPFS parameter restore, save, `.new`, `.bak`, failure, and stale-state semantics. |
|
||||||
|
| `web/test-browser-wasm-smoke-opfs-workspace-sections.js` | Aggregates OPFS basic, parameter, mirror, and directory checks under browser OPFS availability. |
|
||||||
|
| `web/test-browser-wasm-smoke-opfs-mirror-sections.js` | Verifies OPFS/WASM mirror persistence used by source-backed file parsing and parameter movement. |
|
||||||
|
| `web/test-browser-wasm-smoke-opfs-directory-sections.js` | Verifies OPFS directory persistence and WASM mirror refresh without polluting mirrors during metadata queries. |
|
||||||
|
| `web/test-browser-wasm-smoke-opfs-policy-sections.js` | Verifies OPFS workspace and mount policy, including the browser rule that OPFS cannot be disabled. |
|
||||||
|
| `web/test-browser-wasm-smoke-app-sections.js` | Verifies app-level OPFS program, parameter, backup, and reload behavior. |
|
||||||
|
|
||||||
|
## Browser Bridge Boundary
|
||||||
|
|
||||||
|
- `web/src/wasm-core.js` must keep LinuxCNC parameter persistence routed
|
||||||
|
through an OPFS workspace.
|
||||||
|
- Browser contexts with OPFS support must not disable OPFS.
|
||||||
|
- OPFS workspace paths must remain relative paths inside the CNC workspace, and
|
||||||
|
OPFS mount points must remain absolute paths inside the Emscripten
|
||||||
|
filesystem.
|
||||||
|
- Browser app and wasm bridge code must not use Local Storage, IndexedDB,
|
||||||
|
File Picker, Directory Picker, WebKit filesystem, or FileReader persistence
|
||||||
|
paths for CNC programs, LinuxCNC parameter files, or mirrored workspace files.
|
||||||
|
- Browser OPFS smoke harness files must also stay free of Local Storage,
|
||||||
|
IndexedDB, File Picker, Directory Picker, WebKit filesystem, and FileReader
|
||||||
|
persistence paths; they may only verify OPFS-backed browser persistence.
|
||||||
|
- `web/src/app.js` must parse browser programs through
|
||||||
|
`parseFileWithParameterFile()` so the program file and LinuxCNC parameter
|
||||||
|
state both live under OPFS.
|
||||||
|
- `test-native.sh`, `test-linuxcnc-source-link.sh`, and `build-wasm.sh` must
|
||||||
|
run `./check-linuxcnc-opfs-source-map.sh` before consuming app-backed browser
|
||||||
|
artifacts or source-linked LinuxCNC objects.
|
||||||
|
- The OPFS source-map guard validates LinuxCNC source-backed filesystem bridge
|
||||||
|
policy only; it does not interpret G-code.
|
||||||
|
|
||||||
|
## Checks
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./check-linuxcnc-opfs-source-map.sh
|
||||||
|
./test-native.sh
|
||||||
|
./test-linuxcnc-source-link.sh
|
||||||
|
./build-wasm.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
`check-linuxcnc-opfs-source-map.sh` keeps this document, the LinuxCNC source
|
||||||
|
anchors, and the browser bridge guardrails synchronized.
|
||||||
@@ -34,7 +34,7 @@ This parser only exists to test the ABI and UI before Emscripten and LinuxCNC ar
|
|||||||
- spindle modes `G96`, `G97`
|
- spindle modes `G96`, `G97`
|
||||||
- IJK and R arcs
|
- IJK and R arcs
|
||||||
- numbered and named parameter assignment/reference such as `#1 = ...`, `#<name> = ...`, `X#1`, and nested expressions like `X[[#<name> + 2] * 3]`
|
- numbered and named parameter assignment/reference such as `#1 = ...`, `#<name> = ...`, `X#1`, and nested expressions like `X[[#<name> + 2] * 3]`
|
||||||
- expression functions `ABS[]`, `SQRT[]`, `EXP[]`, `LN[]`, degree-based `SIN[]`/`COS[]`/`TAN[]`/`ASIN[]`/`ACOS[]`, LinuxCNC-style `ATAN[]/[]`, and `FIX[]`/`FUP[]`/`ROUND[]`
|
- expression functions `ABS[]`, `SQRT[]`, `EXP[]`, `LN[]`, degree-based `SIN[]`/`COS[]`/`TAN[]`/`ASIN[]`/`ACOS[]`, LinuxCNC-style `ATAN[]/[]`, `FIX[]`/`FUP[]`/`ROUND[]`, and source-linked `mod`/`**`/logical/relational operator precedence
|
||||||
- numeric and named O-word subprograms such as `O100 call` and `O<name> call`
|
- numeric and named O-word subprograms such as `O100 call` and `O<name> call`
|
||||||
- canned cycles `G73`, `G74`, `G81`, `G82`, `G83`, `G84`, `G85`, `G86`, `G87`, `G88`, `G89` with `G80`, `G98`, `G99`, `L`
|
- canned cycles `G73`, `G74`, `G81`, `G82`, `G83`, `G84`, `G85`, `G86`, `G87`, `G88`, `G89` with `G80`, `G98`, `G99`, `L`
|
||||||
- coordinate offset events for `G10 L2`, `G10 L20`, `G54`-`G59.3`, `G52`, `G92`, `G92.1`, `G92.2`, and `G92.3`
|
- coordinate offset events for `G10 L2`, `G10 L20`, `G54`-`G59.3`, `G52`, `G92`, `G92.1`, `G92.2`, and `G92.3`
|
||||||
@@ -190,27 +190,40 @@ This matrix tracks LinuxCNC feature coverage for the web/WASM simulator. A featu
|
|||||||
|
|
||||||
| Area | Status | Regression |
|
| Area | Status | Regression |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| Basic modal motion `G0/G1/G2/G3` | covered, including `G18`/`G19` arc plane mapping and `G90.1`/`G91.1` IJK modes | `tests/gcode/linuxcnc_basic_motion.ngc`, `tests/gcode/basic_mill.ngc`, `tests/gcode/linuxcnc_arc_planes.ngc`, `tests/gcode/linuxcnc_arc_distance_modes.ngc` |
|
| Basic modal motion `G0/G1/G2/G3` | covered, including `G91` incremental linear motion, R-format arcs, `G18`/`G19` arc plane mapping, `G90.1`/`G91.1` IJK modes, and native/source center-format arc tolerance acceptance/rejection | `tests/gcode/linuxcnc_basic_motion.ngc`, `tests/gcode/basic_mill.ngc`, `tests/gcode/incremental_and_r_arc.ngc`, `tests/gcode/linuxcnc_arc_planes.ngc`, `tests/gcode/linuxcnc_arc_distance_modes.ngc`, `tests/gcode/linuxcnc_arc_center_tolerance_good_imperial.ngc`, `tests/gcode/linuxcnc_arc_center_tolerance_bad_metric.ngc` |
|
||||||
|
| Polar coordinates and extended-plane motion restrictions | covered through LinuxCNC native/source `@`/`^` polar parsing, G17-only polar motion validation, absolute/incremental polar endpoint mapping, G53/origin rejection, and extended-plane arc/NURBS rejection | `tests/gcode/linuxcnc_polar_coordinates.ngc`, `tests/gcode/linuxcnc_polar_coordinate_variants.ngc`, `tests/gcode/linuxcnc_polar_g18_error.ngc`, `tests/gcode/linuxcnc_polar_with_x_error.ngc`, `tests/gcode/linuxcnc_polar_with_y_error.ngc`, `tests/gcode/linuxcnc_polar_g92_error.ngc`, `tests/gcode/linuxcnc_polar_g53_error.ngc`, `tests/gcode/linuxcnc_polar_radius_origin_error.ngc`, `tests/gcode/linuxcnc_polar_incremental_radius_origin_error.ngc`, `tests/gcode/linuxcnc_polar_incremental_theta_origin_error.ngc`, `tests/gcode/linuxcnc_extended_plane_arc_error.ngc`, `tests/gcode/linuxcnc_g62_extended_plane_error.ngc` |
|
||||||
|
| Block-level axis/motion validation | covered through LinuxCNC native/source `enhance_block()` checks for invalid `G80` axes, missing `G52/G92` or motion axes, bare axes without active motion, and modal-0 axis consumers on motion blocks | `tests/gcode/linuxcnc_g80_axis_error.ngc`, `tests/gcode/linuxcnc_g52_missing_axis_error.ngc`, `tests/gcode/linuxcnc_g92_missing_axis_error.ngc`, `tests/gcode/linuxcnc_motion_missing_axis_error.ngc`, `tests/gcode/linuxcnc_axis_without_motion_error.ngc`, `tests/gcode/linuxcnc_motion_with_g92_axis_error.ngc` |
|
||||||
| Predefined position moves `G28/G28.1`, `G30/G30.1` | covered for stored position, waypoint, selected-axis return, and all-axis return | `tests/gcode/linuxcnc_predefined_positions.ngc` |
|
| Predefined position moves `G28/G28.1`, `G30/G30.1` | covered for stored position, waypoint, selected-axis return, and all-axis return | `tests/gcode/linuxcnc_predefined_positions.ngc` |
|
||||||
| Machine-coordinate move `G53` | covered for explicit/modal `G0`, `G1`, and incremental-mode rejection in smoke parser | `tests/gcode/linuxcnc_machine_coordinates.ngc` |
|
| Machine-coordinate move `G53` | covered for explicit/modal `G0`, `G1`, arc rejection, and incremental-mode rejection through LinuxCNC native/source backends | `tests/gcode/linuxcnc_machine_coordinates.ngc`, `tests/gcode/linuxcnc_g53_arc_error.ngc`, `tests/gcode/linuxcnc_g53_incremental_error.ngc` |
|
||||||
| Tool select/change `T... M6` | covered with minimal native tooldata | `tests/gcode/basic_mill.ngc` |
|
| Tool select/change `T... M6` | covered with minimal native tooldata, including native/source `find_tool_index()` rejection of missing `T...` and `M61 Q...` tools | `tests/gcode/basic_mill.ngc`, `tests/gcode/linuxcnc_t_missing_tool_error.ngc`, `tests/gcode/linuxcnc_m61_missing_tool_error.ngc` |
|
||||||
| Tool length offset `G43/G43.1/G43.2/G49` | covered with nonzero tool-table offset, dynamic replacement, additive axis offsets, `G43.2 H...`, and clear through LinuxCNC native/source backends | `tests/gcode/linuxcnc_tool_length.ngc`, `tests/gcode/linuxcnc_dynamic_tool_length.ngc` |
|
| Tool length offset `G43/G43.1/G43.2/G49` | covered with nonzero tool-table offset, dynamic replacement, additive axis offsets, `G43.2 H...`, clear, native/source missing-tool rejection, and native/source `G43.2` H/axis validation | `tests/gcode/linuxcnc_tool_length.ngc`, `tests/gcode/linuxcnc_dynamic_tool_length.ngc`, `tests/gcode/linuxcnc_g43_missing_tool_error.ngc`, `tests/gcode/linuxcnc_g43_2_missing_tool_error.ngc`, `tests/gcode/linuxcnc_g43_2_h_axis_error.ngc`, `tests/gcode/linuxcnc_g43_2_missing_h_axis_error.ngc` |
|
||||||
| RTCP controls `G43.4/G43.5/G49` | covered as simulator-owned control lines | `tests/gcode/linuxcnc_rtcp_controls.ngc` |
|
| RTCP controls `G43.4/G43.5/G49` | covered as simulator-owned control lines | `tests/gcode/linuxcnc_rtcp_controls.ngc` |
|
||||||
| Kinematics switch `M428/M429/M430` | covered as simulator-owned control lines | `tests/gcode/linuxcnc_rtcp_controls.ngc` |
|
| Kinematics switch `M428/M429/M430` | covered as simulator-owned control lines | `tests/gcode/linuxcnc_rtcp_controls.ngc` |
|
||||||
| Spindle, coolant, program stops, and current tool number `M3/M4/M5`, `M7/M8/M9`, `M0/M1/M60/M30`, `M61 Q...` | covered through LinuxCNC native/source backends | `tests/gcode/linuxcnc_spindle_direction.ngc`, `tests/gcode/linuxcnc_coolant.ngc`, `tests/gcode/linuxcnc_program_stops.ngc`, `tests/gcode/linuxcnc_tool_number.ngc` |
|
| Spindle, coolant, program stops, and current tool number `M3/M4/M5`, `M7/M8/M9`, `M0/M1/M2/M60/M30/M99`, `M61 Q...` | covered through LinuxCNC native/source backends, including native/source `convert_m()` rejection of out-of-range `M3/M4/M5 $` spindle selectors and negative `M61 Q`, plus `convert_stop()` coverage for M2 reset/program-end and default main-program M99 program-end without M30-only pallet shuttle | `tests/gcode/linuxcnc_spindle_direction.ngc`, `tests/gcode/linuxcnc_coolant.ngc`, `tests/gcode/linuxcnc_program_stops.ngc`, `tests/gcode/linuxcnc_program_end_m2.ngc`, `tests/gcode/linuxcnc_program_end_m99.ngc`, `tests/gcode/linuxcnc_tool_number.ngc`, `tests/gcode/linuxcnc_m3_spindle_dollar_out_of_range_error.ngc`, `tests/gcode/linuxcnc_m4_spindle_dollar_out_of_range_error.ngc`, `tests/gcode/linuxcnc_m5_spindle_dollar_out_of_range_error.ngc`, `tests/gcode/linuxcnc_m61_negative_q_error.ngc` |
|
||||||
| Spindle speed mode `G96/G97` | covered with Canon `SET_SPINDLE_MODE` preserved as mode state events, including `G96 D...` max-RPM value | `tests/gcode/linuxcnc_spindle_modes.ngc` |
|
| Spindle speed mode `G96/G97` | covered with Canon `SET_SPINDLE_MODE` preserved as mode state events, including `G96 D...` max-RPM value and native/source `check_other_codes()` rejection of missing `S` with `G96` | `tests/gcode/linuxcnc_spindle_modes.ngc`, `tests/gcode/linuxcnc_g96_missing_s_word_error.ngc` |
|
||||||
| Spindle orientation `M19 R... P... Q...` | covered with Canon `ORIENT_SPINDLE` and `WAIT_SPINDLE_ORIENT_COMPLETE` preserved as temporary mode state events | `tests/gcode/linuxcnc_spindle_orient.ngc` |
|
| Spindle orientation `M19 R... P... Q...` | covered with Canon `ORIENT_SPINDLE` and `WAIT_SPINDLE_ORIENT_COMPLETE` preserved as temporary mode state events, including native/source `check_other_codes()` validation for `P/R` words and `convert_m()` rejection of out-of-range `$` selectors and nonpositive `Q` waits | `tests/gcode/linuxcnc_spindle_orient.ngc`, `tests/gcode/linuxcnc_m19_spindle_dollar_out_of_range_error.ngc`, `tests/gcode/linuxcnc_m19_p_not_integer_error.ngc`, `tests/gcode/linuxcnc_m19_p_out_of_range_error.ngc`, `tests/gcode/linuxcnc_m19_r_out_of_range_error.ngc`, `tests/gcode/linuxcnc_m19_zero_q_error.ngc` |
|
||||||
| Spindle-synchronized feed and threading `G33/G33.1/G76` | covered through smoke, LinuxCNC native, and source-linked paths with Canon `START_SPEED_FEED_SYNCH`/`STOP_SPEED_FEED_SYNCH` preserved, rigid-tap line numbers normalized, and G76 multi-pass threading smoke-covered | `tests/gcode/linuxcnc_threading_sync.ngc`, `tests/gcode/linuxcnc_threading_cycle.ngc`, smoke API regression |
|
| Spindle-synchronized feed and threading `G33/G33.1/G76` | covered through smoke, LinuxCNC native, and source-linked paths with Canon `START_SPEED_FEED_SYNCH`/`STOP_SPEED_FEED_SYNCH` preserved, rigid-tap line numbers normalized, G76 multi-pass threading smoke-covered, native/source `check_other_codes()` validation for required/disallowed `K/F/P/I/J` words, and native/source `convert_straight()`/`convert_threading_cycle()` coverage for standalone G76 and rejection of invalid spindle state, spindle selector, rotary-axis motion, and G76 `I/J/K` geometry | `tests/gcode/linuxcnc_threading_sync.ngc`, `tests/gcode/linuxcnc_threading_cycle.ngc`, `tests/gcode/linuxcnc_g76only.ngc`, `tests/gcode/linuxcnc_g33_missing_k_word_error.ngc`, `tests/gcode/linuxcnc_g33_f_word_error.ngc`, `tests/gcode/linuxcnc_g33_1_missing_k_word_error.ngc`, `tests/gcode/linuxcnc_g33_1_f_word_error.ngc`, `tests/gcode/linuxcnc_g76_missing_p_word_error.ngc`, `tests/gcode/linuxcnc_g76_missing_i_word_error.ngc`, `tests/gcode/linuxcnc_g76_missing_j_word_error.ngc`, `tests/gcode/linuxcnc_g76_missing_k_word_error.ngc`, `tests/gcode/linuxcnc_g33_stopped_spindle_error.ngc`, `tests/gcode/linuxcnc_g33_1_stopped_spindle_error.ngc`, `tests/gcode/linuxcnc_g33_spindle_dollar_out_of_range_error.ngc`, `tests/gcode/linuxcnc_g33_1_spindle_dollar_out_of_range_error.ngc`, `tests/gcode/linuxcnc_g76_spindle_dollar_out_of_range_error.ngc`, `tests/gcode/linuxcnc_g76_stopped_spindle_error.ngc`, `tests/gcode/linuxcnc_g76_rotary_axis_error.ngc`, `tests/gcode/linuxcnc_g76_i_zero_error.ngc`, `tests/gcode/linuxcnc_g76_j_zero_error.ngc`, `tests/gcode/linuxcnc_g76_k_less_equal_j_error.ngc`, smoke API regression |
|
||||||
| Lathe diameter/radius mode `G7/G8` | covered in smoke parser for X-axis diameter/radius scaling, including interaction with G76 threading geometry; native/source backends use LinuxCNC interpreter state directly | smoke API regression |
|
| Lathe diameter/radius mode `G7/G8` | covered through LinuxCNC native/source backends for same-block X scaling, `G76` I/J/K threading geometry interaction, and `#<_lathe_diameter_mode>`/`#<_lathe_radius_mode>` readonly named-parameter state | `tests/gcode/linuxcnc_lathe_diameter_mode.ngc`, `tests/gcode/linuxcnc_lathe_diameter_g76.ngc` |
|
||||||
| Digital/analog I/O `M62`-`M68` | covered with Canon digital output, analog output, and input wait callbacks preserved as temporary state events | `tests/gcode/linuxcnc_io_controls.ngc` |
|
| Digital/analog I/O `M62`-`M68` | covered with Canon digital output, analog output, and input wait callbacks preserved as temporary state events, including native/source `convert_m()` validation for required `P`/`E`, M66 input selection, M66 wait timeout, and analog wait restrictions | `tests/gcode/linuxcnc_io_controls.ngc`, `tests/gcode/linuxcnc_m62_missing_p_word_error.ngc`, `tests/gcode/linuxcnc_m63_missing_p_word_error.ngc`, `tests/gcode/linuxcnc_m64_missing_p_word_error.ngc`, `tests/gcode/linuxcnc_m65_missing_p_word_error.ngc`, `tests/gcode/linuxcnc_m66_missing_input_error.ngc`, `tests/gcode/linuxcnc_m66_both_inputs_error.ngc`, `tests/gcode/linuxcnc_m66_zero_timeout_error.ngc`, `tests/gcode/linuxcnc_m66_analog_wait_error.ngc`, `tests/gcode/linuxcnc_m67_missing_e_word_error.ngc`, `tests/gcode/linuxcnc_m68_missing_e_word_error.ngc` |
|
||||||
| Override controls `M48`-`M53` | covered with Canon feed override, spindle speed override, adaptive feed, and feed hold callbacks preserved as temporary state events | `tests/gcode/linuxcnc_override_controls.ngc` |
|
| Override controls `M48`-`M53` | covered with Canon feed override, spindle speed override, adaptive feed, and feed hold callbacks preserved as temporary state events | `tests/gcode/linuxcnc_override_controls.ngc` |
|
||||||
| Modal state save/restore `M70`-`M73` | covered for main-program save/invalidate/restore plus O-word subroutine autorestore in smoke parser, with native/source regressions checking restored distance mode, units, and XY plane motion | `tests/gcode/linuxcnc_modal_state.ngc`, `tests/gcode/linuxcnc_modal_autorestore.ngc` |
|
| Modal state save/restore `M70`-`M73` | covered for main-program save/invalidate/restore plus O-word subroutine autorestore in smoke parser, with native/source regressions checking restored distance mode, units, and XY plane motion | `tests/gcode/linuxcnc_modal_state.ngc`, `tests/gcode/linuxcnc_modal_autorestore.ngc` |
|
||||||
| LinuxCNC read-only named parameters | partially covered in smoke parser for common modal state reads used by O-word macros: `#<_absolute>`, `#<_incremental>`, `#<_metric>`, `#<_imperial>`, `#<_feed>`, `#<_rpm>`, `#<_spindle_on>`, `#<_spindle_cw>`, `#<_lathe_diameter_mode>`, and `#<_lathe_radius_mode>` | smoke API regression |
|
| LinuxCNC read-only named parameters | common modal/unit/feed/spindle reads and additional ccomp/spindle-mode readonly reads are covered through LinuxCNC native/source backends | `tests/gcode/linuxcnc_readonly_named_parameters.ngc`, `tests/gcode/linuxcnc_readonly_named_parameters_extra.ngc`, `tests/gcode/linuxcnc_lathe_diameter_mode.ngc` |
|
||||||
| Feed and motion control modes `G93/G94/G95`, `G61/G61.1/G64` | covered through LinuxCNC native/source backends | `tests/gcode/linuxcnc_feed_modes.ngc`, `tests/gcode/linuxcnc_motion_modes.ngc` |
|
| LinuxCNC numbered parameters | native/source coverage checks readonly numbered-parameter rejection plus probe, tool, G28/G30, G52/G92, G5X/G59.3, debug/M66 result, tool-offset, current-position, `restore_parameters()`/`save_parameters()` persistence, and volatile global numbered parameters from LinuxCNC source state | `tests/gcode/linuxcnc_numbered_parameters.ngc`, `tests/gcode/linuxcnc_rotary_probe_numbered_parameters.ngc`, `tests/gcode/linuxcnc_multi_rotary_probe_numbered_parameters.ngc`, `tests/gcode/linuxcnc_tool_info_zero_parameter.ngc`, `tests/gcode/linuxcnc_spindle_tool_info_parameter.ngc`, `tests/gcode/linuxcnc_readonly_numbered_parameter_assign_error.ngc`, `tests/gcode/linuxcnc_g28_g30_numbered_parameters.ngc`, `tests/gcode/linuxcnc_rotary_g28_g30_numbered_parameters.ngc`, `tests/gcode/linuxcnc_g92_numbered_parameters.ngc`, `tests/gcode/linuxcnc_rotary_g92_numbered_parameters.ngc`, `tests/gcode/linuxcnc_g52_g92_numbered_interaction.ngc`, `tests/gcode/linuxcnc_g5x_numbered_parameters.ngc`, `tests/gcode/linuxcnc_g59_3_numbered_parameters.ngc`, `tests/gcode/linuxcnc_g59_3_rotary_numbered_parameters.ngc`, `tests/gcode/linuxcnc_g5x_rotary_numbered_parameters.ngc`, `tests/gcode/linuxcnc_g54_rotary_numbered_parameters.ngc`, `tests/gcode/linuxcnc_debug_level_parameter.ngc`, `tests/gcode/linuxcnc_m66_result_parameter.ngc`, `tests/gcode/linuxcnc_tool_offset_numbered_parameters.ngc`, `tests/gcode/linuxcnc_rotary_tool_offset_numbered_parameters.ngc`, `tests/gcode/linuxcnc_current_position_numbered_parameters.ngc`, `tests/gcode/linuxcnc_persistent_parameters_set.ngc`, `tests/gcode/linuxcnc_persistent_parameters_check.ngc`, `tests/gcode/linuxcnc_rotary_persistent_parameters_set.ngc`, `tests/gcode/linuxcnc_rotary_persistent_parameters_check.ngc`, `tests/gcode/linuxcnc_g54_persistent_parameters_set.ngc`, `tests/gcode/linuxcnc_g54_persistent_parameters_check.ngc`, `tests/gcode/linuxcnc_user_persistent_parameter_set.ngc`, `tests/gcode/linuxcnc_user_persistent_parameter_check.ngc`, `tests/gcode/linuxcnc_user_persistent_parameter_existing_check.ngc`, `tests/gcode/linuxcnc_user_persistent_parameter_boundary_set.ngc`, `tests/gcode/linuxcnc_user_persistent_parameter_boundary_check.ngc`, `tests/gcode/linuxcnc_volatile_global_parameter_set.ngc`, `tests/gcode/linuxcnc_volatile_global_parameter_check.ngc` |
|
||||||
| Canned cycle `G81/G80` | covered for drilling expand-to-canon path, including `G18/G19` plane-specific depth-axis mapping for representative drilling/boring cycles | `tests/gcode/linuxcnc_canned_cycle.ngc`, `tests/gcode/linuxcnc_canned_cycle_planes.ngc` |
|
| Feed and motion control modes `G93/G94/G95`, `G61/G61.1/G64` | covered through LinuxCNC native/source backends, including `G64 P` path tolerance and `G64 Q` naivecam tolerance from `convert_control_mode()` | `tests/gcode/linuxcnc_feed_modes.ngc`, `tests/gcode/linuxcnc_motion_modes.ngc`, `tests/gcode/linuxcnc_control_mode_tolerances.ngc` |
|
||||||
|
| Dwell `G4 P...` | covered through LinuxCNC native/source backends for missing-P rejection and boundary values around LinuxCNC's exact `P-1.0` missing-value sentinel; neighboring negative P values are passed through to `DWELL` by `convert_dwell()` | `tests/gcode/linuxcnc_g4_missing_p_error.ngc`, `tests/gcode/linuxcnc_g4_negative_p_error.ngc`, `tests/gcode/linuxcnc_g4_dwell_boundaries.ngc` |
|
||||||
|
| Canned cycle `G73`-`G89`/`G80` | covered for drilling expand-to-canon path, `G18/G19` plane-specific depth-axis mapping for representative drilling/boring cycles, `G17.1` extended-plane depth/retract mapping, and native/source LinuxCNC `interp_cycles.cc` validation of repeat counts, required R/depth words, invalid rotary axes including follow-up modal-cycle blocks, Q/P words, and spindle state | `tests/gcode/linuxcnc_canned_cycle.ngc`, `tests/gcode/linuxcnc_canned_cycles_extended.ngc`, `tests/gcode/linuxcnc_canned_cycle_planes.ngc`, `tests/gcode/linuxcnc_extended_plane_cycle.ngc`, `tests/gcode/linuxcnc_canned_cycle_l0_error.ngc`, `tests/gcode/linuxcnc_canned_cycle_missing_r_error.ngc`, `tests/gcode/linuxcnc_canned_cycle_missing_z_error.ngc`, `tests/gcode/linuxcnc_canned_cycle_g18_missing_y_error.ngc`, `tests/gcode/linuxcnc_canned_cycle_g19_missing_x_error.ngc`, `tests/gcode/linuxcnc_canned_cycle_r_less_z_error.ngc`, `tests/gcode/linuxcnc_canned_cycle_g18_r_less_y_error.ngc`, `tests/gcode/linuxcnc_canned_cycle_g19_r_less_x_error.ngc`, `tests/gcode/linuxcnc_canned_cycle_u_in_xy_error.ngc`, `tests/gcode/linuxcnc_canned_cycle_a_error.ngc`, `tests/gcode/linuxcnc_canned_cycle_b_error.ngc`, `tests/gcode/linuxcnc_canned_cycle_c_error.ngc`, `tests/gcode/linuxcnc_canned_cycle_reject_rotary_followup.ngc`, `tests/gcode/linuxcnc_canned_cycle_g73_missing_q_error.ngc`, `tests/gcode/linuxcnc_canned_cycle_g83_missing_q_error.ngc`, `tests/gcode/linuxcnc_canned_cycle_g73_q0_error.ngc`, `tests/gcode/linuxcnc_canned_cycle_g83_q0_error.ngc`, `tests/gcode/linuxcnc_canned_cycle_g82_missing_p_error.ngc`, `tests/gcode/linuxcnc_canned_cycle_g86_missing_p_error.ngc`, `tests/gcode/linuxcnc_canned_cycle_g88_missing_p_error.ngc`, `tests/gcode/linuxcnc_canned_cycle_g89_missing_p_error.ngc`, `tests/gcode/linuxcnc_canned_cycle_g74_clockwise_error.ngc`, `tests/gcode/linuxcnc_canned_cycle_g84_counterclockwise_error.ngc`, `tests/gcode/linuxcnc_canned_cycle_g74_stopped_spindle_error.ngc`, `tests/gcode/linuxcnc_canned_cycle_g84_stopped_spindle_error.ngc`, `tests/gcode/linuxcnc_canned_cycle_g86_stopped_spindle_error.ngc`, `tests/gcode/linuxcnc_canned_cycle_g88_stopped_spindle_error.ngc`, `tests/gcode/linuxcnc_canned_cycle_g87_stopped_spindle_error.ngc`, `tests/gcode/linuxcnc_canned_cycle_g87_missing_k_error.ngc`, `tests/gcode/linuxcnc_canned_cycle_g87_missing_i_error.ngc`, `tests/gcode/linuxcnc_canned_cycle_g87_missing_j_error.ngc` |
|
||||||
| Coordinate offset Canon events `G10 L2`, `G10 L20`, `G10 P0 active-system targeting`, `G54`-`G59.3` selection, `G52`, `G92/G92.1/G92.2/G92.3` | covered through LinuxCNC native/source backends, including all nine G5X indices | `tests/gcode/linuxcnc_coordinate_offsets.ngc`, `tests/gcode/linuxcnc_coordinate_l20.ngc`, `tests/gcode/linuxcnc_coordinate_p0.ngc`, `tests/gcode/linuxcnc_coordinate_select_all.ngc`, `tests/gcode/linuxcnc_g92_restore.ngc`, `tests/gcode/linuxcnc_g52_offset.ngc` |
|
| Coordinate offset Canon events `G10 L2`, `G10 L20`, `G10 P0 active-system targeting`, `G54`-`G59.3` selection, `G52`, `G92/G92.1/G92.2/G92.3` | covered through LinuxCNC native/source backends, including all nine G5X indices | `tests/gcode/linuxcnc_coordinate_offsets.ngc`, `tests/gcode/linuxcnc_coordinate_l20.ngc`, `tests/gcode/linuxcnc_coordinate_p0.ngc`, `tests/gcode/linuxcnc_coordinate_select_all.ngc`, `tests/gcode/linuxcnc_g92_restore.ngc`, `tests/gcode/linuxcnc_g52_offset.ngc` |
|
||||||
| Cutter compensation | covered for tool-table `G41/G42 D...` and explicit-radius `G41.1/G42.1/G40` through LinuxCNC native/source backends | `tests/gcode/linuxcnc_cutter_comp.ngc` |
|
| NURBS `G5.2/G5.3/G6.2` | covered through LinuxCNC native/source backends for `G18` XZ-plane endpoint mapping from `convert_nurbs()` | `tests/gcode/linuxcnc_g52_xz_nurbs.ngc`, `tests/gcode/linuxcnc_g62_xz_nurbs.ngc` |
|
||||||
|
| Cutter compensation | covered for tool-table `G41/G42 D...` and explicit-radius `G41.1/G42.1/G40` through LinuxCNC native/source backends, plus native/source LinuxCNC rejection of tool changes, M66 waits, G71.0, retract-mode changes, arc exits, and gouging while compensation is active | `tests/gcode/linuxcnc_cutter_comp.ngc`, `tests/gcode/linuxcnc_cutter_comp_tool_change_error.ngc`, `tests/gcode/linuxcnc_cutter_comp_wait_input_error.ngc`, `tests/gcode/linuxcnc_cutter_comp_g71_error.ngc`, `tests/gcode/linuxcnc_canned_cycle_g98_retract_error.ngc`, `tests/gcode/linuxcnc_ccomp_arcexit_error.ngc`, `tests/gcode/linuxcnc_ccomp_gouging_error.ngc` |
|
||||||
| Probe moves `G38.2/G38.3/G38.4/G38.5` | covered as distinct probe events with LinuxCNC `probe_type` preserved through smoke, native, and source-linked paths | `tests/gcode/linuxcnc_probe_no_error.ngc` |
|
| Probe moves `G38.2/G38.3/G38.4/G38.5` | covered as distinct probe events with LinuxCNC `probe_type` preserved through smoke, native, and source-linked paths | `tests/gcode/linuxcnc_probe_no_error.ngc` |
|
||||||
| O-word subroutines, calls, and control flow | covered for numeric and named `O... sub/call/return/endsub` plus `if`/`elseif`/`else`, `while`, `repeat`, `do`/`while`, `break`, and `continue` through LinuxCNC file mode; smoke parser also covers named `O<name>` sub/call/return/endsub and loop-control forms | `tests/gcode/smoke_oword_subprogram.ngc`, `tests/gcode/linuxcnc_named_oword_subprogram.ngc`, `tests/gcode/linuxcnc_oword_control_flow.ngc` |
|
| O-word and Fanuc subroutines, calls, and control flow | covered for numeric and named `O... sub/call/return/endsub`, Fanuc-style `M98 P... L...`/`M99`, plus `if`/`elseif`/`else`, `while`, `repeat`, `do`/`while`, `break`, and `continue` through LinuxCNC file mode; native/source read errors cover malformed O-word commands, missing control-expression brackets, too many or empty call parameters, Fanuc-style `M98` missing `P`, and midline/comment-prefixed `M98` falling through to normal M-code handling; native/source execution errors cover duplicate/undefined/unmatched labels, out-of-context `return`/`endsub`, wrong subroutine close style, nested subroutine definitions, numbered subroutine illegal-location rejection, main-file EOF after numeric O-word return, too many nested call levels, lowercase numeric labels, O-word state parameters, `EXISTS[]` word-value checks, and M98/O-word local-parameter style differences; smoke parser also covers named `O<name>` sub/call/return/endsub and loop-control forms | `tests/gcode/smoke_oword_subprogram.ngc`, `tests/gcode/linuxcnc_named_oword_subprogram.ngc`, `tests/gcode/smoke_subprogram_m98.ngc`, `tests/gcode/linuxcnc_oword_control_flow.ngc`, `tests/gcode/linuxcnc_value_returned.ngc`, `tests/gcode/linuxcnc_call_level.ngc`, `tests/gcode/linuxcnc_exists_word_value.ngc`, `tests/gcode/linuxcnc_lowercase_numeric_oword.ngc`, `tests/gcode/linuxcnc_m98_mixed_styles_variables.ngc`, `tests/gcode/linuxcnc_numeric_oword_main_eof_error.ngc`, `tests/gcode/linuxcnc_numeric_oword_sub_illegal_location.ngc`, `tests/gcode/linuxcnc_oword_unknown_command_error.ngc`, `tests/gcode/linuxcnc_oword_unexpected_trailing_item_error.ngc`, `tests/gcode/linuxcnc_oword_if_missing_bracket_error.ngc`, `tests/gcode/linuxcnc_oword_elseif_missing_bracket_error.ngc`, `tests/gcode/linuxcnc_oword_while_missing_bracket_error.ngc`, `tests/gcode/linuxcnc_oword_repeat_missing_bracket_error.ngc`, `tests/gcode/linuxcnc_oword_too_many_call_parameters_error.ngc`, `tests/gcode/linuxcnc_oword_empty_call_parameter_error.ngc`, `tests/gcode/linuxcnc_oword_too_many_call_levels_error.ngc`, `tests/gcode/linuxcnc_oword_endsub_without_sub_error.ngc`, `tests/gcode/linuxcnc_oword_return_without_sub_error.ngc`, `tests/gcode/linuxcnc_oword_duplicate_if_label_error.ngc`, `tests/gcode/linuxcnc_oword_elseif_undefined_label_error.ngc`, `tests/gcode/linuxcnc_oword_else_undefined_label_error.ngc`, `tests/gcode/linuxcnc_oword_endif_undefined_label_error.ngc`, `tests/gcode/linuxcnc_oword_break_undefined_label_error.ngc`, `tests/gcode/linuxcnc_oword_continue_undefined_label_error.ngc`, `tests/gcode/linuxcnc_oword_endwhile_undefined_label_error.ngc`, `tests/gcode/linuxcnc_oword_elseif_no_matching_if_error.ngc`, `tests/gcode/linuxcnc_oword_break_no_matching_loop_error.ngc`, `tests/gcode/linuxcnc_oword_continue_no_matching_loop_error.ngc`, `tests/gcode/linuxcnc_oword_endrepeat_no_matching_repeat_error.ngc`, `tests/gcode/linuxcnc_oword_endif_no_matching_if_error.ngc`, `tests/gcode/linuxcnc_oword_endwhile_no_matching_while_error.ngc`, `tests/gcode/linuxcnc_oword_fanuc_requires_m99_error.ngc`, `tests/gcode/linuxcnc_oword_sub_requires_endsub_error.ngc`, `tests/gcode/linuxcnc_nested_subroutine_definition_error.ngc`, `tests/gcode/linuxcnc_m98_missing_p_word_error.ngc`, `tests/gcode/linuxcnc_midline_m98_error.ngc`, `tests/gcode/linuxcnc_comment_prefixed_m98_error.ngc` |
|
||||||
| Broader canned cycles `G73`, `G74`, `G82`-`G89` | partially covered: `G73`, `G74`, `G82`, `G83`, `G84`, `G85`, `G86`, `G87`, `G88`, `G89`; smoke parser maps supported cycles across `G17/G18/G19`, including LinuxCNC's special non-XY `G74/G84` tapping argument order and `G87` plane-specific `I/J/K` mapping; native/source regressions now check non-XY `G74/G84/G86/G87/G88` sequences | `tests/gcode/linuxcnc_canned_cycles_extended.ngc`, `tests/gcode/linuxcnc_canned_cycle_planes.ngc`, smoke API regression |
|
| Parameter expressions and operators | covered for parameter references, native/source bracketed word-value whitespace, `mod`, `**`, logical, and relational operator precedence, plus LinuxCNC parameter-setting syntax, parameter index validation, named-parameter termination, real-number parsing, divide-by-zero, negative sqrt/power, infinity rejection, malformed and bare `ATAN[]/[]`, unary operation syntax including bare unary items, `read_operation_unary()` unknown-word branches, `read_operation()` binary-operation prefix, unknown operation, and unclosed-expression error paths from `interp_read.cc`/`interp_execute.cc` | `tests/gcode/linuxcnc_parameter_expression_assignment.ngc`, `tests/gcode/linuxcnc_word_bracket_whitespace.ngc`, `tests/gcode/linuxcnc_parameter_missing_equal_error.ngc`, `tests/gcode/linuxcnc_midline_parameter_missing_equal_error.ngc`, `tests/gcode/linuxcnc_parameter_missing_value_error.ngc`, `tests/gcode/linuxcnc_parameter_number_out_of_range_error.ngc`, `tests/gcode/linuxcnc_parameter_reference_out_of_range_error.ngc`, `tests/gcode/linuxcnc_non_integer_parameter_index_error.ngc`, `tests/gcode/linuxcnc_named_parameter_not_terminated_error.ngc`, `tests/gcode/linuxcnc_no_real_value_error.ngc`, `tests/gcode/linuxcnc_bad_number_format_error.ngc`, `tests/gcode/linuxcnc_expression_operators.ngc`, `tests/gcode/linuxcnc_negative_sqrt_error.ngc`, `tests/gcode/linuxcnc_divide_by_zero_error.ngc`, `tests/gcode/linuxcnc_negative_power_error.ngc`, `tests/gcode/linuxcnc_infinite_expression_error.ngc`, `tests/gcode/linuxcnc_atan_missing_slash_error.ngc`, `tests/gcode/linuxcnc_atan_missing_bracket_error.ngc`, `tests/gcode/linuxcnc_top_level_bare_atan_error.ngc`, `tests/gcode/linuxcnc_midline_bare_atan_error.ngc`, `tests/gcode/linuxcnc_unary_missing_bracket_error.ngc`, `tests/gcode/linuxcnc_unary_unknown_word_error.ngc`, `tests/gcode/linuxcnc_unary_unknown_c_word_error.ngc`, `tests/gcode/linuxcnc_unary_unknown_e_word_error.ngc`, `tests/gcode/linuxcnc_unary_unknown_f_word_error.ngc`, `tests/gcode/linuxcnc_unary_unknown_l_word_error.ngc`, `tests/gcode/linuxcnc_unary_unknown_r_word_error.ngc`, `tests/gcode/linuxcnc_unary_unknown_s_word_error.ngc`, `tests/gcode/linuxcnc_unary_unknown_t_word_error.ngc`, `tests/gcode/linuxcnc_unary_unknown_default_word_error.ngc`, `tests/gcode/linuxcnc_top_level_bare_unary_error.ngc`, `tests/gcode/linuxcnc_midline_bare_unary_error.ngc`, `tests/gcode/linuxcnc_binary_unknown_a_operation_error.ngc`, `tests/gcode/linuxcnc_binary_unknown_e_operation_error.ngc`, `tests/gcode/linuxcnc_binary_unknown_g_operation_error.ngc`, `tests/gcode/linuxcnc_binary_unknown_l_operation_error.ngc`, `tests/gcode/linuxcnc_binary_unknown_m_operation_error.ngc`, `tests/gcode/linuxcnc_binary_unknown_n_operation_error.ngc`, `tests/gcode/linuxcnc_binary_unknown_o_operation_error.ngc`, `tests/gcode/linuxcnc_binary_unknown_x_operation_error.ngc`, `tests/gcode/linuxcnc_binary_unknown_operation_error.ngc`, `tests/gcode/linuxcnc_unclosed_expression_error.ngc` |
|
||||||
|
| Word reader validation | LinuxCNC native/source reader errors covered for negative `F/G/H/L/M/S/T` values and repeated `A/B/C/D/E/F/H/I/J/K/L/P/Q/R/S/T/U/V/W/X/Y/Z` words from `interp_read.cc` | `tests/gcode/linuxcnc_negative_f_word_error.ngc`, `tests/gcode/linuxcnc_negative_g_code_error.ngc`, `tests/gcode/linuxcnc_negative_h_word_error.ngc`, `tests/gcode/linuxcnc_negative_l_word_error.ngc`, `tests/gcode/linuxcnc_negative_m_code_error.ngc`, `tests/gcode/linuxcnc_negative_s_word_error.ngc`, `tests/gcode/linuxcnc_negative_t_word_error.ngc`, `tests/gcode/linuxcnc_multiple_a_words_error.ngc`, `tests/gcode/linuxcnc_multiple_b_words_error.ngc`, `tests/gcode/linuxcnc_multiple_c_words_error.ngc`, `tests/gcode/linuxcnc_multiple_d_words_error.ngc`, `tests/gcode/linuxcnc_multiple_e_words_error.ngc`, `tests/gcode/linuxcnc_multiple_f_words_error.ngc`, `tests/gcode/linuxcnc_multiple_h_words_error.ngc`, `tests/gcode/linuxcnc_multiple_i_words_error.ngc`, `tests/gcode/linuxcnc_multiple_j_words_error.ngc`, `tests/gcode/linuxcnc_multiple_k_words_error.ngc`, `tests/gcode/linuxcnc_multiple_l_words_error.ngc`, `tests/gcode/linuxcnc_multiple_p_words_error.ngc`, `tests/gcode/linuxcnc_multiple_q_words_error.ngc`, `tests/gcode/linuxcnc_multiple_r_words_error.ngc`, `tests/gcode/linuxcnc_multiple_s_words_error.ngc`, `tests/gcode/linuxcnc_multiple_t_words_error.ngc`, `tests/gcode/linuxcnc_multiple_u_words_error.ngc`, `tests/gcode/linuxcnc_multiple_v_words_error.ngc`, `tests/gcode/linuxcnc_multiple_w_words_error.ngc`, `tests/gcode/linuxcnc_multiple_x_words_error.ngc`, `tests/gcode/linuxcnc_multiple_y_words_error.ngc`, `tests/gcode/linuxcnc_multiple_z_words_error.ngc` |
|
||||||
|
| G/M reader validation | LinuxCNC native/source `read_g()`/`read_m()` errors covered for range checks, unknown code tables, and same-modal-group conflicts from `interp_read.cc` and `interp_array.cc` | `tests/gcode/linuxcnc_g_code_out_of_range_error.ngc`, `tests/gcode/linuxcnc_unknown_g_code_error.ngc`, `tests/gcode/linuxcnc_two_g_codes_same_modal_group_error.ngc`, `tests/gcode/linuxcnc_m_code_greater_199_error.ngc`, `tests/gcode/linuxcnc_unknown_m_code_error.ngc`, `tests/gcode/linuxcnc_two_m_codes_same_modal_group_error.ngc` |
|
||||||
|
| Block/item reader validation | LinuxCNC native/source errors covered for repeated `$`, block-delete execution skipping, unused `D/$/E/H/I/J/K/L/P/Q/R` words, bad item-start characters, middle-of-line or comment-prefixed `N` words, comment-prefixed or midline `O` words, top-level or midline bracket items, naked top-level numbers, non-leading ATAN argument slashes, malformed leading `N` numbers, EOF/program-end file checks, unclosed/nested parenthesis comments, commented or mid-program `%` characters, overlong file lines, too many M-codes, fractional/extended N-word sequence numbers, `close_and_downcase()` case/whitespace normalization, and percent-file termination from `interp_read.cc`, `interp_internal.cc`, `rs274ngc_pre.cc`, `interp_array.cc`, and `interp_check.cc` | `tests/gcode/linuxcnc_block_delete.ngc`, `tests/gcode/linuxcnc_multiple_spindle_choice_words_error.ngc`, `tests/gcode/linuxcnc_d_word_no_use_error.ngc`, `tests/gcode/linuxcnc_spindle_dollar_no_use_error.ngc`, `tests/gcode/linuxcnc_e_word_no_use_error.ngc`, `tests/gcode/linuxcnc_h_word_no_use_error.ngc`, `tests/gcode/linuxcnc_i_word_no_use_error.ngc`, `tests/gcode/linuxcnc_j_word_no_use_error.ngc`, `tests/gcode/linuxcnc_k_word_no_use_error.ngc`, `tests/gcode/linuxcnc_l_word_no_use_error.ngc`, `tests/gcode/linuxcnc_p_word_no_use_error.ngc`, `tests/gcode/linuxcnc_q_word_no_use_error.ngc`, `tests/gcode/linuxcnc_r_word_no_use_error.ngc`, `tests/gcode/linuxcnc_bad_character_bang_error.ngc`, `tests/gcode/linuxcnc_commented_percent_error.ngc`, `tests/gcode/linuxcnc_mid_program_percent_error.ngc`, `tests/gcode/linuxcnc_bad_character_middle_n_word_error.ngc`, `tests/gcode/linuxcnc_comment_prefixed_n_word_error.ngc`, `tests/gcode/linuxcnc_comment_prefixed_spaced_n_word_error.ngc`, `tests/gcode/linuxcnc_comment_prefixed_oword_error.ngc`, `tests/gcode/linuxcnc_midline_numeric_oword_error.ngc`, `tests/gcode/linuxcnc_midline_named_oword_error.ngc`, `tests/gcode/linuxcnc_bad_character_top_left_bracket_error.ngc`, `tests/gcode/linuxcnc_bad_character_top_right_bracket_error.ngc`, `tests/gcode/linuxcnc_bad_character_midline_left_bracket_error.ngc`, `tests/gcode/linuxcnc_bad_character_midline_right_bracket_error.ngc`, `tests/gcode/linuxcnc_bad_character_top_number_error.ngc`, `tests/gcode/linuxcnc_top_level_bare_atan_error.ngc`, `tests/gcode/linuxcnc_midline_bare_atan_error.ngc`, `tests/gcode/linuxcnc_bad_n_number_format_error.ngc`, `tests/gcode/linuxcnc_file_ended_no_program_end_error.ngc`, `tests/gcode/linuxcnc_file_ended_no_percent_error.ngc`, `tests/gcode/linuxcnc_unclosed_comment_error.ngc`, `tests/gcode/linuxcnc_nested_comment_error.ngc`, generated `linuxcnc_command_too_long_error.ngc`, `tests/gcode/linuxcnc_too_many_m_codes_error.ngc`, `tests/gcode/linuxcnc_fractional_linenumbers.ngc`, `tests/gcode/linuxcnc_extended_linenumbers.ngc`, `tests/gcode/linuxcnc_close_and_downcase_words.ngc`, `tests/gcode/linuxcnc_percent_file_ignores_trailing.ngc` |
|
||||||
|
| Broader canned cycles `G73`, `G74`, `G82`-`G89` | covered for representative LinuxCNC native/source `G73`, `G74`, `G82`, `G83`, `G84`, `G85`, `G86`, `G87`, `G88`, and `G89` sequences from `interp_cycles.cc`, including peck drilling, feed-out boring, stop/restart boring, dwell/feed-out boring, special non-XY `G74/G84` tapping argument order, and `G87` plane-specific `I/J/K` mapping; smoke parser still maps supported cycles across `G17/G18/G19` | `tests/gcode/linuxcnc_canned_cycles_extended.ngc`, `tests/gcode/linuxcnc_canned_cycle_planes.ngc`, smoke API regression |
|
||||||
| Full source-level wasm build | covered for the current wasm-safe RS274 source set, with remaining work focused on broadening browser-hostile dependency replacement and feature coverage | `./build-wasm.sh`, `test-linuxcnc-wasm-runtime-link.sh`, `test-web-wasm-node-smoke.sh`, `test-web-wasm-browser-smoke.sh` |
|
| Full source-level wasm build | covered for the current wasm-safe RS274 source set, with remaining work focused on broadening browser-hostile dependency replacement and feature coverage | `./build-wasm.sh`, `test-linuxcnc-wasm-runtime-link.sh`, `test-web-wasm-node-smoke.sh`, `test-web-wasm-browser-smoke.sh` |
|
||||||
|
|
||||||
|
## Fixture Coverage Audit
|
||||||
|
|
||||||
|
The following LinuxCNC fixtures are also covered by both the native `librs274` runner and the source-link runner. They remain listed here so fixture coverage cannot drift out of the porting matrix: `tests/gcode/linuxcnc_abort_hot_comment.ngc`, `tests/gcode/linuxcnc_comments.ngc`, `tests/gcode/linuxcnc_g10_invalid_l_error.ngc`, `tests/gcode/linuxcnc_g10_l10_p_out_of_range_error.ngc`, `tests/gcode/linuxcnc_g10_l11_p_out_of_range_error.ngc`, `tests/gcode/linuxcnc_g10_l1_missing_tool_error.ngc`, `tests/gcode/linuxcnc_g10_l1_p_out_of_range_error.ngc`, `tests/gcode/linuxcnc_g10_l1_q_negative_error.ngc`, `tests/gcode/linuxcnc_g10_l1_q_noninteger_error.ngc`, `tests/gcode/linuxcnc_g10_l1_q_out_of_range_error.ngc`, `tests/gcode/linuxcnc_g10_l1_without_offsets_error.ngc`, `tests/gcode/linuxcnc_g10_l20_i_word_error.ngc`, `tests/gcode/linuxcnc_g10_l20_p_missing_error.ngc`, `tests/gcode/linuxcnc_g10_l20_p_out_of_range_error.ngc`, `tests/gcode/linuxcnc_g10_l20_r_word_error.ngc`, `tests/gcode/linuxcnc_g10_l2_i_word_error.ngc`, `tests/gcode/linuxcnc_g10_l2_p_missing_error.ngc`, `tests/gcode/linuxcnc_g10_l2_p_out_of_range_error.ngc`, `tests/gcode/linuxcnc_g10_p_non_integer_error.ngc`, `tests/gcode/linuxcnc_g4_arc_p_conflict_error.ngc`, `tests/gcode/linuxcnc_g52_missing_subsequent_weight_error.ngc`, `tests/gcode/linuxcnc_g52_missing_y_control_point_error.ngc`, `tests/gcode/linuxcnc_g52_noninteger_l_error.ngc`, `tests/gcode/linuxcnc_g52_nonpositive_p_error.ngc`, `tests/gcode/linuxcnc_g52_p_without_xy_after_start_error.ngc`, `tests/gcode/linuxcnc_g52_zero_feed_error.ngc`, `tests/gcode/linuxcnc_g53_after_intervening_motion_error.ngc`, `tests/gcode/linuxcnc_g53_short_control_points_error.ngc`, `tests/gcode/linuxcnc_g53_without_g52_error.ngc`, `tests/gcode/linuxcnc_g62_k_non_integer_error.ngc`, `tests/gcode/linuxcnc_g62_missing_p_order_error.ngc`, `tests/gcode/linuxcnc_g62_missing_q_mode_error.ngc`, `tests/gcode/linuxcnc_g62_p_non_integer_error.ngc`, `tests/gcode/linuxcnc_g62_q_non_integer_error.ngc`, `tests/gcode/linuxcnc_g62_q_out_of_range_error.ngc`, `tests/gcode/linuxcnc_g62_zero_feed_error.ngc`, `tests/gcode/linuxcnc_indexed_parameters.ngc`, `tests/gcode/linuxcnc_ini_named_parameter.ngc`, `tests/gcode/linuxcnc_modal_invalidate.ngc`, `tests/gcode/linuxcnc_named_parameter_exists.ngc`, `tests/gcode/linuxcnc_named_parameter_normalization.ngc`, `tests/gcode/linuxcnc_polar_g28_error.ngc`, `tests/gcode/linuxcnc_polar_g30_error.ngc`, `tests/gcode/linuxcnc_readonly_named_parameter_assign_error.ngc`, `tests/gcode/linuxcnc_same_modal_control_mode_error.ngc`, `tests/gcode/linuxcnc_same_modal_g10_g92_error.ngc`, `tests/gcode/linuxcnc_same_modal_g4_g53_error.ngc`, `tests/gcode/linuxcnc_same_modal_g52_g92_error.ngc`, `tests/gcode/linuxcnc_same_modal_g921_g922_error.ngc`, `tests/gcode/linuxcnc_same_modal_predefined_position_error.ngc`, `tests/gcode/linuxcnc_same_modal_probe_motion_error.ngc`, and `tests/gcode/linuxcnc_same_modal_threading_motion_error.ngc`.
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
# LinuxCNC rs274 source map
|
# LinuxCNC rs274 source map
|
||||||
|
|
||||||
This is the working map for moving from native `librs274` linking to source-level compilation and then to wasm.
|
This is the working map for moving from native `librs274` linking to
|
||||||
|
source-level compilation and then to wasm. It is a source-manifest guard only;
|
||||||
|
it must not add CNC behavior and must not expand the temporary smoke parser.
|
||||||
|
|
||||||
## Compile assumptions
|
## Compile assumptions
|
||||||
|
|
||||||
@@ -43,7 +45,6 @@ These are the first group to keep compiling while we peel away native-only depen
|
|||||||
- `interp_remap.cc`
|
- `interp_remap.cc`
|
||||||
- `interp_setup.cc`
|
- `interp_setup.cc`
|
||||||
- `interp_write.cc`
|
- `interp_write.cc`
|
||||||
- `inifile.cc`
|
|
||||||
- `modal_state.cc`
|
- `modal_state.cc`
|
||||||
- `nurbs_additional_functions.cc`
|
- `nurbs_additional_functions.cc`
|
||||||
- `rs274ngc_pre.cc`
|
- `rs274ngc_pre.cc`
|
||||||
@@ -61,6 +62,114 @@ These are not needed for the browser simulator ABI and should not be part of the
|
|||||||
- `pyinterp1.cc`
|
- `pyinterp1.cc`
|
||||||
- `pyparamclass.cc`
|
- `pyparamclass.cc`
|
||||||
|
|
||||||
|
### WASM-safe source core
|
||||||
|
|
||||||
|
These are the LinuxCNC source files compiled by the browser-safe source-link
|
||||||
|
probes from `linuxcnc-rs274-wasm-source-files.txt`:
|
||||||
|
|
||||||
|
- `src/emc/rs274ngc/interp_arc.cc`
|
||||||
|
- `src/emc/rs274ngc/interp_array.cc`
|
||||||
|
- `src/emc/rs274ngc/interp_base.cc`
|
||||||
|
- `src/emc/rs274ngc/interp_check.cc`
|
||||||
|
- `src/emc/rs274ngc/interp_convert.cc`
|
||||||
|
- `src/emc/rs274ngc/interp_cycles.cc`
|
||||||
|
- `src/emc/rs274ngc/interp_execute.cc`
|
||||||
|
- `src/emc/rs274ngc/interp_find.cc`
|
||||||
|
- `src/emc/rs274ngc/interp_g7x.cc`
|
||||||
|
- `src/emc/rs274ngc/interp_inspection.cc`
|
||||||
|
- `src/emc/rs274ngc/interp_internal.cc`
|
||||||
|
- `src/emc/rs274ngc/interp_inverse.cc`
|
||||||
|
- `src/emc/rs274ngc/interp_queue.cc`
|
||||||
|
- `src/emc/rs274ngc/interp_read.cc`
|
||||||
|
- `src/emc/rs274ngc/interp_setup.cc`
|
||||||
|
- `src/emc/rs274ngc/interp_write.cc`
|
||||||
|
- `src/emc/rs274ngc/modal_state.cc`
|
||||||
|
- `src/emc/rs274ngc/nurbs_additional_functions.cc`
|
||||||
|
- `src/emc/rs274ngc/interp_namedparams.cc`
|
||||||
|
- `src/emc/rs274ngc/interp_o_word.cc`
|
||||||
|
- `src/emc/rs274ngc/interp_python.cc`
|
||||||
|
- `src/emc/rs274ngc/interp_remap.cc`
|
||||||
|
- `src/emc/rs274ngc/rs274ngc_pre.cc`
|
||||||
|
- `src/emc/tooldata/tooldata_common.cc`
|
||||||
|
- `src/emc/ini/inifile.cc`
|
||||||
|
- `src/emc/nml_intf/emcops.cc`
|
||||||
|
|
||||||
|
### WASM-blocked sources
|
||||||
|
|
||||||
|
These LinuxCNC sources remain tracked in the wasm manifest, but are not part of
|
||||||
|
the browser-safe core until their Python/Boost.Python or native storage
|
||||||
|
dependencies are replaced:
|
||||||
|
|
||||||
|
- `src/emc/tooldata/tooldata_mmap.cc`
|
||||||
|
- `src/emc/rs274ngc/canonmodule.cc`
|
||||||
|
- `src/emc/rs274ngc/gcodemodule.cc`
|
||||||
|
- `src/emc/rs274ngc/interpmodule.cc`
|
||||||
|
- `src/emc/rs274ngc/pyarrays.cc`
|
||||||
|
- `src/emc/rs274ngc/pyblock.cc`
|
||||||
|
- `src/emc/rs274ngc/pyemctypes.cc`
|
||||||
|
- `src/emc/rs274ngc/pyinterp1.cc`
|
||||||
|
- `src/emc/rs274ngc/pyparamclass.cc`
|
||||||
|
|
||||||
|
### WASM-tracked headers
|
||||||
|
|
||||||
|
These LinuxCNC headers are tracked by the wasm manifest because the
|
||||||
|
browser-safe source-link probes compile against them or shim their declarations:
|
||||||
|
|
||||||
|
- `src/emc/rs274ngc/array1.hh`
|
||||||
|
- `src/emc/rs274ngc/interp_array_types.hh`
|
||||||
|
- `src/emc/rs274ngc/interp_base.hh`
|
||||||
|
- `src/emc/rs274ngc/interp_fwd.hh`
|
||||||
|
- `src/emc/rs274ngc/interp_inspection.hh`
|
||||||
|
- `src/emc/rs274ngc/interp_internal.hh`
|
||||||
|
- `src/emc/rs274ngc/interp_parameter_def.hh`
|
||||||
|
- `src/emc/rs274ngc/interp_python.hh`
|
||||||
|
- `src/emc/rs274ngc/interp_queue.hh`
|
||||||
|
- `src/emc/rs274ngc/modal_state.hh`
|
||||||
|
- `src/emc/rs274ngc/paramclass.hh`
|
||||||
|
- `src/emc/rs274ngc/rs274ngc.hh`
|
||||||
|
- `src/emc/rs274ngc/rs274ngc_interp.hh`
|
||||||
|
- `src/emc/rs274ngc/rs274ngc_return.hh`
|
||||||
|
- `src/emc/rs274ngc/units.h`
|
||||||
|
- `src/emc/pythonplugin/python_plugin.hh`
|
||||||
|
- `src/emc/ini/inifile.h`
|
||||||
|
- `src/emc/ini/inifile.hh`
|
||||||
|
- `src/emc/nml_intf/emc.hh`
|
||||||
|
- `src/emc/nml_intf/emc_nml.hh`
|
||||||
|
- `include/hal.h`
|
||||||
|
- `src/libnml/nml/nmlmsg.hh`
|
||||||
|
- `src/libnml/nml/stat_msg.hh`
|
||||||
|
- `src/libnml/rcs/rcs_print.hh`
|
||||||
|
- `src/rtapi/rtapi.h`
|
||||||
|
- `src/rtapi/rtapi_string.h`
|
||||||
|
|
||||||
|
### WASM-blocked headers
|
||||||
|
|
||||||
|
These headers are tracked for source coverage but should not become browser API
|
||||||
|
surface until their Boost.Python dependency is replaced:
|
||||||
|
|
||||||
|
- `src/emc/rs274ngc/boost_pyenum_macros.hh`
|
||||||
|
|
||||||
|
### WASM metadata sources
|
||||||
|
|
||||||
|
These LinuxCNC source and build metadata files document the source basis for
|
||||||
|
the browser-safe shims and manifest completeness checks:
|
||||||
|
|
||||||
|
- `src/emc/rs274ngc/Submakefile`
|
||||||
|
- `src/emc/rs274ngc/meson.build`
|
||||||
|
- `src/emc/pythonplugin/python_plugin.cc`
|
||||||
|
- `src/emc/task/emctaskmain.cc`
|
||||||
|
- `src/emc/task/taskclass.cc`
|
||||||
|
- `src/emc/task/emccanon.cc`
|
||||||
|
- `src/emc/sai/dummyemcstat.cc`
|
||||||
|
- `src/emc/ini/Submakefile`
|
||||||
|
- `src/libnml/nml/stat_msg.cc`
|
||||||
|
- `src/libnml/nml/nmlmsg.cc`
|
||||||
|
- `src/libnml/rcs/rcs_print.cc`
|
||||||
|
- `src/rtapi/uspace_common.h`
|
||||||
|
- `src/hal/hal_lib.c`
|
||||||
|
- `src/emc/tooldata/tooldata_db.cc`
|
||||||
|
- `src/emc/tooldata/tooldata_nml.cc`
|
||||||
|
|
||||||
### Browser-hostile dependencies to replace
|
### Browser-hostile dependencies to replace
|
||||||
|
|
||||||
- Python/Boost.Python remap and named parameter hooks.
|
- Python/Boost.Python remap and named parameter hooks.
|
||||||
@@ -69,6 +178,33 @@ These are not needed for the browser simulator ABI and should not be part of the
|
|||||||
- persistent parameter file writes.
|
- persistent parameter file writes.
|
||||||
- dynamic INI/HAL queries.
|
- dynamic INI/HAL queries.
|
||||||
|
|
||||||
|
## Boundaries
|
||||||
|
|
||||||
|
- Native and WASM source lists in this document must match
|
||||||
|
`linuxcnc-rs274-source-files.txt` and
|
||||||
|
`linuxcnc-rs274-wasm-source-files.txt` exactly.
|
||||||
|
- Python binding modules, WASM-blocked sources, and WASM-blocked headers are
|
||||||
|
source coverage only.
|
||||||
|
- They must not become browser API fields or temporary smoke parser fallback behavior.
|
||||||
|
- Browser-hostile dependencies must remain tracked blockers until they are
|
||||||
|
ported, trimmed, wrapped, or replaced by source-backed browser-safe LinuxCNC
|
||||||
|
adaptations.
|
||||||
|
- `test-native.sh`, `test-linuxcnc-source-link.sh`, and `build-wasm.sh` must
|
||||||
|
run `./check-linuxcnc-rs274-source-map.sh` before consuming generated native
|
||||||
|
objects, source-linked LinuxCNC objects, or browser artifacts.
|
||||||
|
- The RS274 source-map guard validates LinuxCNC source ownership and manifest
|
||||||
|
drift only; it does not interpret G-code.
|
||||||
|
|
||||||
|
## Checks
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./check-linuxcnc-rs274-source-map.sh
|
||||||
|
./test-native.sh
|
||||||
|
./test-linuxcnc-source-link.sh
|
||||||
|
```
|
||||||
|
|
||||||
The wasm-safe runtime now constructs its detached `EMC_STAT` through LinuxCNC
|
The wasm-safe runtime now constructs its detached `EMC_STAT` through LinuxCNC
|
||||||
`src/emc/nml_intf/emcops.cc`. The singleton follows LinuxCNC
|
`src/emc/nml_intf/emcops.cc`. The singleton follows LinuxCNC
|
||||||
`src/emc/sai/dummyemcstat.cc`, and the required `NMLmsg`/`RCS_STAT_MSG`
|
`src/emc/sai/dummyemcstat.cc`, and the required `NMLmsg`/`RCS_STAT_MSG`
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ Hard rules:
|
|||||||
add browser filesystem paths that bypass OPFS.
|
add browser filesystem paths that bypass OPFS.
|
||||||
- Browser app and wasm bridge code must not persist CNC programs, LinuxCNC
|
- Browser app and wasm bridge code must not persist CNC programs, LinuxCNC
|
||||||
parameter files, or mirrored workspace files through non-OPFS browser storage
|
parameter files, or mirrored workspace files through non-OPFS browser storage
|
||||||
APIs such as Local Storage, IndexedDB, File Picker writes, WebKit filesystem
|
APIs such as Local Storage, IndexedDB, File Picker reads/writes, Directory
|
||||||
APIs, or FileReader-backed import paths.
|
Picker access, WebKit filesystem APIs, or FileReader-backed import paths.
|
||||||
|
|
||||||
Allowed project code:
|
Allowed project code:
|
||||||
|
|
||||||
@@ -63,8 +63,14 @@ Efficiency rules:
|
|||||||
- Each pass should advance one reasonable, reviewable context. Do not use a
|
- Each pass should advance one reasonable, reviewable context. Do not use a
|
||||||
nearby failing smoke assertion, broad fixture, or convenient touched file as
|
nearby failing smoke assertion, broad fixture, or convenient touched file as
|
||||||
permission to expand the pass into another workstream.
|
permission to expand the pass into another workstream.
|
||||||
|
- Keep the six active workstream contexts synchronized with
|
||||||
|
`docs/linuxcnc-workstreams-source-map.md` so source-map guards, architecture,
|
||||||
|
and this policy stay aligned.
|
||||||
- Do not add a smoke-only behavior path when the LinuxCNC-backed route already
|
- Do not add a smoke-only behavior path when the LinuxCNC-backed route already
|
||||||
exists; add coverage to the source-backed route instead.
|
exists; add coverage to the source-backed route instead.
|
||||||
|
- The legacy explicit smoke backend must not be enabled together with the
|
||||||
|
LinuxCNC wasm-safe source probe; browser/WASM builds must keep the LinuxCNC
|
||||||
|
source-linked path separate from the temporary smoke parser.
|
||||||
- Keep browser filesystem guardrails negative as well as positive: require the
|
- Keep browser filesystem guardrails negative as well as positive: require the
|
||||||
OPFS bridge and reject browser app/wasm storage APIs that could bypass it.
|
OPFS bridge and reject browser app/wasm storage APIs that could bypass it.
|
||||||
- Smoke tests are verification guardrails. They must not be expanded with new
|
- Smoke tests are verification guardrails. They must not be expanded with new
|
||||||
@@ -109,10 +115,28 @@ Efficiency rules:
|
|||||||
`CNC_SIM_WASM_BLOCKERS_CACHE_DIR`.
|
`CNC_SIM_WASM_BLOCKERS_CACHE_DIR`.
|
||||||
- Reuse switchkins/remap table check results in the same persistent cache with
|
- Reuse switchkins/remap table check results in the same persistent cache with
|
||||||
`check-linuxcnc-switchkins-remap-table-cached.sh`; its signature must include
|
`check-linuxcnc-switchkins-remap-table-cached.sh`; its signature must include
|
||||||
the cached wrapper script, checker script, generator script, the kinematics
|
the cached wrapper script, checker script, generator script, manifest input
|
||||||
manifest, referenced LinuxCNC source size and nanosecond mtime stats, tracked
|
checker scripts, the kinematics manifest, referenced LinuxCNC source size and
|
||||||
generated table/case outputs, and the thin API/web bridge files that pass
|
nanosecond mtime stats, tracked generated table/case outputs, and the thin API/web bridge files that pass
|
||||||
switchkins config aliases.
|
switchkins config aliases. Native, source-link, and build-wasm entry points
|
||||||
|
must run this cached check before consuming those aliases.
|
||||||
|
The uncached switchkins/remap checker must first run kinematics-manifest
|
||||||
|
completeness validation so standalone table checks also cover the
|
||||||
|
source-manifest contract used by M428/M429/M430 and RTCP coverage.
|
||||||
|
- The switchkins/remap generator must treat LinuxCNC `[EMCIO] TOOL_TABLE`
|
||||||
|
entries as source coverage only. Resolvable tool tables referenced by
|
||||||
|
M428/M429/M430 INI files must be present as `tooldata` manifest entries and
|
||||||
|
must not become new generated API aliases or browser cases.
|
||||||
|
- LinuxCNC `halcmd loadusr` parsing must stay source-tied to
|
||||||
|
`src/hal/utils/halcmd_commands.cc` `do_loadusr_cmd()` and its
|
||||||
|
`getopt("+wWin:")` option model. Generator/checker code that extracts
|
||||||
|
user-space component programs from INI/HAL `loadusr` lines must also preserve
|
||||||
|
the common `-W/-Wn/-w/-iw` forms listed by
|
||||||
|
`src/hal/utils/halcmd_completion.c`, and must skip wait component names such
|
||||||
|
as `-Wn NAME` or `-n NAME` before recording the actual program source.
|
||||||
|
- The kinematics manifest lister must preserve every allowed manifest group
|
||||||
|
used by switchkins and RTCP coverage, including `asset`, `tooldata`, and
|
||||||
|
`generated`, and must reject unknown filters or output modes for those groups.
|
||||||
- Reuse wasm probe objects across the wasm source object probe,
|
- Reuse wasm probe objects across the wasm source object probe,
|
||||||
`rs274ngc_pre` blocker/link probes, and the runtime link probe through
|
`rs274ngc_pre` blocker/link probes, and the runtime link probe through
|
||||||
`build-linuxcnc-wasm-probe-objects.sh`. Its mode-specific object caches must
|
`build-linuxcnc-wasm-probe-objects.sh`. Its mode-specific object caches must
|
||||||
@@ -276,6 +300,9 @@ Build and source-link guardrails:
|
|||||||
`rs274ngc_pre` link checks.
|
`rs274ngc_pre` link checks.
|
||||||
- `build-wasm.sh` must check `cmake`, `emcmake`, `emcc`, and `node` before the
|
- `build-wasm.sh` must check `cmake`, `emcmake`, `emcc`, and `node` before the
|
||||||
Emscripten configure step.
|
Emscripten configure step.
|
||||||
|
- `build-wasm.sh` must explicitly configure `CNC_SIM_ENABLE_SMOKE_BACKEND=OFF`
|
||||||
|
while enabling `CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE=ON`, so stale CMake
|
||||||
|
cache values cannot route browser artifacts through the legacy smoke backend.
|
||||||
- `build-wasm.sh` must reuse a persistent `build/wasm` configure signature
|
- `build-wasm.sh` must reuse a persistent `build/wasm` configure signature
|
||||||
keyed by manifest, `core/CMakeLists.txt`, an explicit
|
keyed by manifest, `core/CMakeLists.txt`, an explicit
|
||||||
`CONFIGURE_RULE_VERSION`, LinuxCNC root, and compiler launcher. The signature
|
`CONFIGURE_RULE_VERSION`, LinuxCNC root, and compiler launcher. The signature
|
||||||
@@ -293,7 +320,8 @@ Build and source-link guardrails:
|
|||||||
- The default wasm manifest partition must remain explicitly checked for 26
|
- The default wasm manifest partition must remain explicitly checked for 26
|
||||||
wasm-safe core sources and 9 blocked sources.
|
wasm-safe core sources and 9 blocked sources.
|
||||||
- Manifest source listers must reject malformed lines, absolute source paths,
|
- Manifest source listers must reject malformed lines, absolute source paths,
|
||||||
unknown groups, unknown filters, and duplicate source entries.
|
unknown groups, unknown filters, unknown output modes, and duplicate source
|
||||||
|
entries.
|
||||||
- Native link flags must include the built LinuxCNC `lib` path and rpath.
|
- Native link flags must include the built LinuxCNC `lib` path and rpath.
|
||||||
- Common CXX flag helpers must surface helper failures instead of hiding them
|
- Common CXX flag helpers must surface helper failures instead of hiding them
|
||||||
behind process substitution or `read`.
|
behind process substitution or `read`.
|
||||||
|
|||||||
54
docs/linuxcnc-switchkins-remap-source-map.md
Normal file
54
docs/linuxcnc-switchkins-remap-source-map.md
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
# LinuxCNC switchkins/remap source map
|
||||||
|
|
||||||
|
This map records the source basis for generated M428/M429/M430 switchkins
|
||||||
|
aliases and config cases. It is a generator/table map only; it must not add CNC behavior, must not expand the temporary smoke parser, and must not expose LinuxCNC source-only assets as browser API fields.
|
||||||
|
|
||||||
|
## LinuxCNC Source Basis
|
||||||
|
|
||||||
|
- `configs/sim/**.ini` files with `REMAP = M428`, `REMAP = M429`, or
|
||||||
|
`REMAP = M430` provide the switchkins configuration roots.
|
||||||
|
- `remap_subs/{428,429,430}remap.ngc` files provide the adjacent
|
||||||
|
`#<kinstype>` assignments.
|
||||||
|
- `src/hal/utils/halcmd_commands.cc` `do_loadusr_cmd()` and
|
||||||
|
`getopt("+wWin:")` define the `loadusr` option model used by the generator.
|
||||||
|
- `src/hal/utils/halcmd_completion.c` documents common `loadusr`
|
||||||
|
`-W/-Wn/-w/-iw` forms preserved by the generator.
|
||||||
|
|
||||||
|
## Generated Coverage
|
||||||
|
|
||||||
|
| Output | Count | Source coverage role |
|
||||||
|
| --- | ---: | --- |
|
||||||
|
| `core/src/linuxcnc_switchkins_remap_table.inc` | 177 | Generated switchkins aliases from LinuxCNC INI/HAL/remap sources. |
|
||||||
|
| `core/tests/linuxcnc_switchkins_remap_config_cases.inc` | 616 | Generated C++ config cases for accepted switchkins config fields. |
|
||||||
|
| `web/public/linuxcnc_switchkins_remap_config_cases.json` | 616 | Generated browser-readable config cases mirroring the C++ cases. |
|
||||||
|
| LinuxCNC M428/M429/M430 remap sources | 31 | LinuxCNC `#<kinstype>` source assignments. |
|
||||||
|
| LinuxCNC switchkins REMAP INI sources | 16 | LinuxCNC configs that declare M428/M429/M430 remaps. |
|
||||||
|
|
||||||
|
## Boundaries
|
||||||
|
|
||||||
|
- `[EMCIO] TOOL_TABLE` entries are source coverage only. They must stay in the kinematics manifest as `tooldata` entries and must not become generated switchkins aliases, config fields, or browser option keys.
|
||||||
|
- `asset`, `metadata`, and `tooldata` inputs referenced by switchkins configs
|
||||||
|
are source coverage, not browser API surface.
|
||||||
|
- Generated tables must continue to derive M428/M429/M430 kinstypes from
|
||||||
|
LinuxCNC remap `#<kinstype>` assignments, not from hand-authored behavior.
|
||||||
|
- `test-native.sh`, `test-linuxcnc-source-link.sh`, and `build-wasm.sh` must
|
||||||
|
run `./check-linuxcnc-switchkins-remap-source-map.sh` before consuming
|
||||||
|
generated config aliases, source-linked LinuxCNC objects, or browser
|
||||||
|
artifacts.
|
||||||
|
- The switchkins/remap source-map guard validates LinuxCNC source/config
|
||||||
|
ownership and generated table drift only; it does not interpret G-code.
|
||||||
|
|
||||||
|
## Checks
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./check-linuxcnc-switchkins-remap-source-map.sh
|
||||||
|
./check-linuxcnc-switchkins-remap-table.sh linuxcnc-kinematics-source-files.txt
|
||||||
|
./test-native.sh
|
||||||
|
./test-linuxcnc-source-link.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
`check-linuxcnc-switchkins-remap-source-map.sh` keeps this document, generator
|
||||||
|
source anchors, generated output counts, and source-only `TOOL_TABLE` boundary
|
||||||
|
synchronized.
|
||||||
99
docs/linuxcnc-tooldata-source-map.md
Normal file
99
docs/linuxcnc-tooldata-source-map.md
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
# LinuxCNC tooldata source map
|
||||||
|
|
||||||
|
This map records the source basis and boundaries for LinuxCNC tooldata coverage
|
||||||
|
in native, source-link, switchkins, and WASM-safe build paths. It is a
|
||||||
|
source-link/shim guard only; it must not add CNC behavior and must not expand the temporary smoke parser.
|
||||||
|
|
||||||
|
## LinuxCNC Source Basis
|
||||||
|
|
||||||
|
- `src/emc/tooldata/Submakefile` selects `tooldata_mmap.cc` or
|
||||||
|
`tooldata_nml.cc`, and always includes `tooldata_common.cc`,
|
||||||
|
`tooldata_db.cc`, and `tool_watch.cc`.
|
||||||
|
- `src/emc/tooldata/tooldata_common.cc` provides tool table parsing,
|
||||||
|
formatting, load/save, and database-mode entry points.
|
||||||
|
- `src/emc/tooldata/tooldata_mmap.cc` provides native mmap tool table storage.
|
||||||
|
- `src/emc/tooldata/tooldata_nml.cc` provides the NML tool table registration
|
||||||
|
backend selected by `TOOL_NML_FLAG`.
|
||||||
|
- `src/emc/tooldata/tooldata_db.cc` provides external database entry points.
|
||||||
|
- `src/emc/tooldata/tooldata_db.cc` returns `-1` from
|
||||||
|
`tooldata_db_getall()` when the external DB is not live, and returns `0`
|
||||||
|
from `tooldata_db_notify()` without side effects in that same state.
|
||||||
|
- `src/emc/tooldata/tool_mmap_read.cc` is the mmap tool table reader utility
|
||||||
|
source from the same LinuxCNC tooldata build metadata; it reads through
|
||||||
|
`tool_mmap_user()`, `tooldata_last_index_get()`, and `tooldata_get()`.
|
||||||
|
- `src/emc/tooldata/tool_watch.cc` is the NML/mmap tool table watcher utility
|
||||||
|
source from the same LinuxCNC tooldata build metadata; it reads
|
||||||
|
`EMC_STAT::io.tool` fields through `RCS_STAT_CHANNEL`.
|
||||||
|
- `src/emc/tooldata/tooldata.hh` declares the tooldata API used by RS274 and
|
||||||
|
WASM-safe shims.
|
||||||
|
|
||||||
|
## Manifest And Shim Coverage
|
||||||
|
|
||||||
|
| Coverage | Boundary |
|
||||||
|
| --- | --- |
|
||||||
|
| `linuxcnc-rs274-source-files.txt` | Tracks the full LinuxCNC `src/emc/tooldata` source directory and `tooldata.hh`. |
|
||||||
|
| `linuxcnc-rs274-wasm-source-files.txt` | Links `tooldata_common.cc`, blocks native `tooldata_mmap.cc`, and records runtime shim source bases for `tooldata_db.cc` and `tooldata_nml.cc`. |
|
||||||
|
| `linuxcnc-kinematics-source-files.txt` | Tracks 10 LinuxCNC `TOOL_TABLE` files referenced by covered M428/M429/M430 INI sources as `tooldata` source coverage only. |
|
||||||
|
| `core/wasm_shims/tooldata/tooldata_mmap_backend.cc` | Adapts LinuxCNC `tooldata_mmap.cc` exports to WASM-safe in-memory storage. |
|
||||||
|
| `core/wasm_shims/tooldata/tooldata_runtime_stubs.cc` | Adapts LinuxCNC `tooldata_nml.cc` and `tooldata_db.cc` runtime entry points without native services. |
|
||||||
|
| `test-linuxcnc-wasm-tooldata-link.sh` | Links the wasm-safe tooldata shim against LinuxCNC tooldata runtime expectations. |
|
||||||
|
| `test-linuxcnc-wasm-tooldata-common-link.sh` | Links LinuxCNC `tooldata_common.cc` through the wasm-safe standalone probe. |
|
||||||
|
| `test-linuxcnc-wasm-tooldata-mmap-symbols.sh` | Compares wasm mmap backend exports with LinuxCNC `tooldata_mmap.cc`. |
|
||||||
|
| `test-linuxcnc-wasm-tooldata-runtime-symbols.sh` | Compares runtime stub exports with LinuxCNC `tooldata_db.cc` and `tooldata_nml.cc`. |
|
||||||
|
|
||||||
|
## External DB Stub Boundaries
|
||||||
|
|
||||||
|
- `core/wasm_shims/tooldata/tooldata_runtime_stubs.cc` must keep external DB services unavailable for browser-safe probes; it must not fork, exec, poll,
|
||||||
|
or keep a live external database process.
|
||||||
|
- `tooldata_db_init()` must preserve the LinuxCNC `MAX_DB_PROGRAM_ARGS=10`, `strtok_r()` argument splitting, and `access(..., X_OK)` rejection edges from
|
||||||
|
`src/emc/tooldata/tooldata_db.cc` before returning `-1`.
|
||||||
|
- `tooldata_db_getall()` must return `-1` while `db_live` is false, and
|
||||||
|
`tooldata_db_notify()` must return `0` without side effects while `db_live`
|
||||||
|
is false.
|
||||||
|
|
||||||
|
## Mmap Backend Boundaries
|
||||||
|
|
||||||
|
- `core/wasm_shims/tooldata/tooldata_mmap_backend.cc` must preserve the
|
||||||
|
LinuxCNC `tooldata_mmap.cc` export surface for the mmap creator/user/close lifecycle while using an in-memory replacement for native mmap.
|
||||||
|
- The mmap backend must keep the LinuxCNC random-toolchanger flag and
|
||||||
|
`last_index` initialization edges from `tool_mmap_creator()`, and
|
||||||
|
`tooldata_last_index_get()` must preserve the last-index unavailable return.
|
||||||
|
- The mmap backend must keep LinuxCNC's single-creator process boundary from
|
||||||
|
the `static inited` guard in `tool_mmap_creator()`, including after
|
||||||
|
`tool_mmap_close()` clears browser-safe in-memory state.
|
||||||
|
- `tool_mmap_user()` must keep LinuxCNC's unavailable mmap failure boundary,
|
||||||
|
and `tool_mmap_close()` must clear the browser-safe in-memory state without
|
||||||
|
adding filesystem mmap behavior.
|
||||||
|
|
||||||
|
## Boundaries
|
||||||
|
|
||||||
|
- Tool table files referenced by switchkins INI files are source coverage only;
|
||||||
|
they must not become browser API fields or generated switchkins aliases.
|
||||||
|
- WASM-safe tooldata shims must preserve the LinuxCNC export surface checked by
|
||||||
|
symbol probes.
|
||||||
|
- WASM-safe tooldata runtime stubs must keep external database services
|
||||||
|
unavailable while preserving LinuxCNC inactive-DB return values.
|
||||||
|
- Native and source-link paths must keep the full LinuxCNC `src/emc/tooldata`
|
||||||
|
source directory tracked in the native RS274 manifest.
|
||||||
|
- `build-wasm.sh` must run tooldata link and symbol probes before copying
|
||||||
|
browser artifacts.
|
||||||
|
- `test-native.sh`, `test-linuxcnc-source-link.sh`, and `build-wasm.sh` must
|
||||||
|
run `./check-linuxcnc-tooldata-source-map.sh` before consuming tooldata-backed
|
||||||
|
native objects, source-linked LinuxCNC objects, or browser artifacts.
|
||||||
|
- The tooldata source-map guard validates LinuxCNC source/shim ownership and
|
||||||
|
manifest drift only; it does not interpret G-code.
|
||||||
|
|
||||||
|
## Checks
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./check-linuxcnc-tooldata-source-map.sh
|
||||||
|
./test-native.sh
|
||||||
|
./test-linuxcnc-source-link.sh
|
||||||
|
./build-wasm.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
`check-linuxcnc-tooldata-source-map.sh` keeps this document, LinuxCNC tooldata
|
||||||
|
source anchors, manifest coverage, WASM shim/probe boundaries, and switchkins
|
||||||
|
TOOL_TABLE source-only policy synchronized.
|
||||||
105
docs/linuxcnc-wasm-blockers-source-map.md
Normal file
105
docs/linuxcnc-wasm-blockers-source-map.md
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
# LinuxCNC WASM blockers source map
|
||||||
|
|
||||||
|
This map records the source basis for the WASM blocker categories used by
|
||||||
|
`analyze-linuxcnc-wasm-blockers.sh` and `test-linuxcnc-wasm-blockers.sh`. It is
|
||||||
|
a source-link/build guard only; it does not add CNC behavior and must not become smoke parser behavior.
|
||||||
|
|
||||||
|
## LinuxCNC Source Basis
|
||||||
|
|
||||||
|
- `src/emc/rs274ngc/gcodemodule.cc`, `canonmodule.cc`, `interpmodule.cc`, and
|
||||||
|
`py*.cc` provide the Python and Boost.Python module binding blockers.
|
||||||
|
- `test-linuxcnc-wasm-python-c-api-symbols.sh` compiles the blocked
|
||||||
|
Python/Boost.Python binding sources with inert Python/runtime shims so their
|
||||||
|
source surface is covered without enabling Python execution.
|
||||||
|
- `src/emc/rs274ngc/interp_python.cc`, `interp_remap.cc`,
|
||||||
|
`interp_namedparams.cc`, `interp_o_word.cc`, `interp_setup.cc`, and
|
||||||
|
`rs274ngc_pre.cc` keep Python/Boost.Python remap paths as shimmed core
|
||||||
|
sources.
|
||||||
|
- `src/emc/rs274ngc/interp_base.cc` is the core source that still carries
|
||||||
|
`dlopen` use satisfied by the wasm dlfcn shim.
|
||||||
|
- `src/emc/tooldata/tooldata_mmap.cc` is the native mmap backend blocker
|
||||||
|
replaced by the wasm-safe tooldata mmap backend shim.
|
||||||
|
- `src/emc/rs274ngc/interp_o_word.cc` uses native directory traversal for
|
||||||
|
O-word subroutine lookup; browser persistence remains OPFS-only.
|
||||||
|
- `src/emc/rs274ngc/rs274ngc_pre.cc` implements LinuxCNC parameter-file replacement through `filename + ".new"`, `filename + ".bak"`, `unlink`, `link`, and `rename`; browser persistence remains OPFS-only.
|
||||||
|
|
||||||
|
## Manifest Coverage
|
||||||
|
|
||||||
|
| Manifest group | Count | Role |
|
||||||
|
| --- | ---: | --- |
|
||||||
|
| `core` | 26 | LinuxCNC RS274/tooldata/INI/NML sources compiled or shim-satisfied by WASM probes. |
|
||||||
|
| `blocked` | 9 | LinuxCNC Python binding and native tooldata sources tracked but replaced for WASM. |
|
||||||
|
| `header` | 26 | LinuxCNC headers needed by the WASM-safe source probes and shims. |
|
||||||
|
| `blocked-header` | 1 | Boost.Python helper header tracked as blocked browser API surface. |
|
||||||
|
| `metadata` | 15 | LinuxCNC build/source-basis files for shim coverage. |
|
||||||
|
|
||||||
|
## Analyzer Coverage
|
||||||
|
|
||||||
|
| Analyzer section | Count | Boundary |
|
||||||
|
| --- | ---: | --- |
|
||||||
|
| `python` | 8 | Blocked Python/Boost.Python module sources. |
|
||||||
|
| `python-compile-covered` | 8 | Blocked Python/Boost.Python module sources compiled by the inert Python C API symbol probe while remaining blockers. |
|
||||||
|
| `dlopen` | 0 | blocked `dlopen` and blocked tooldata-users remain empty. |
|
||||||
|
| `tooldata` | 1 | Native tooldata mmap blocker. |
|
||||||
|
| `tooldata-users` | 0 | Blocked tooldata user sources remain empty. |
|
||||||
|
| `native-backend` | 1 | Native backend source excluded from browser linking. |
|
||||||
|
| `native-fs` | 1 | Native filesystem blocker excluded from browser linking. |
|
||||||
|
| `blocked-replacements` | 9 | Blocked LinuxCNC sources mapped to wasm-safe shim files. |
|
||||||
|
| `core-python-shimmed` | 6 | Core LinuxCNC Python/remap users compiled with Python/Boost.Python shims. |
|
||||||
|
| `core-dlopen-shimmed` | 1 | Core LinuxCNC `dlopen` user compiled with dlfcn shim. |
|
||||||
|
| `core-tooldata-shimmed` | 1 | Core LinuxCNC tooldata source compiled with wasm tooldata shims. |
|
||||||
|
| `core-tooldata-users-shimmed` | 2 | Core RS274/NML users of tooldata declarations. |
|
||||||
|
| `core-native-fs-shimmed` | 16 | Core sources with native filesystem includes or calls covered by WASM shims, including O-word lookup and parameter-file replacement. |
|
||||||
|
|
||||||
|
Required replacement set:
|
||||||
|
|
||||||
|
- `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`
|
||||||
|
|
||||||
|
Required tracked core examples:
|
||||||
|
|
||||||
|
- `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`
|
||||||
|
|
||||||
|
## Boundaries
|
||||||
|
|
||||||
|
- `test-native.sh`, `test-linuxcnc-source-link.sh`, and `build-wasm.sh` must
|
||||||
|
run `./check-linuxcnc-wasm-blockers-source-map.sh` before consuming blocker
|
||||||
|
analyzer output, source-linked LinuxCNC objects, or browser artifacts.
|
||||||
|
- `test-native.sh` and `test-linuxcnc-source-link.sh` must run
|
||||||
|
`test-linuxcnc-wasm-blockers.sh` with `CNC_SIM_WASM_BLOCKERS_SELF_CHECKS=1`
|
||||||
|
so bad manifests, missing LinuxCNC roots, duplicate entries, and missing
|
||||||
|
blocked-source replacements stay executable failures.
|
||||||
|
- The WASM blockers source-map guard validates LinuxCNC source/blocker
|
||||||
|
ownership and manifest drift only; it does not interpret G-code.
|
||||||
|
- Python/Boost.Python blockers are compile-covered by the inert Python C API probe,
|
||||||
|
but they remain tracked blockers until Python/remap execution is ported,
|
||||||
|
wrapped, or replaced by a source-backed browser-safe LinuxCNC adaptation.
|
||||||
|
|
||||||
|
## Checks
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./check-linuxcnc-wasm-blockers-source-map.sh
|
||||||
|
./test-linuxcnc-wasm-blockers.sh linuxcnc-rs274-wasm-source-files.txt
|
||||||
|
./test-native.sh
|
||||||
|
./test-linuxcnc-source-link.sh
|
||||||
|
./build-wasm.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
`check-linuxcnc-wasm-blockers-source-map.sh` keeps this document, the
|
||||||
|
RS274/WASM manifest, LinuxCNC source anchors, analyzer output, and wasm-safe
|
||||||
|
shim replacement set synchronized. Python/Boost.Python remap paths remain tracked blockers until they are ported, wrapped, or replaced by source-backed browser-safe LinuxCNC adaptations.
|
||||||
102
docs/linuxcnc-wasm-shims-source-map.md
Normal file
102
docs/linuxcnc-wasm-shims-source-map.md
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
# 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 LinuxCNC `interp_base.cc` dynamic loader calls and RTLD flags for browser-safe probes while keeping dynamic loading unavailable. |
|
||||||
|
| `core/wasm_shims/emc_status_shim.cc` | `src/emc/sai/dummyemcstat.cc` | Provides the detached LinuxCNC `EMC_STAT` singleton shape without task/NML startup. |
|
||||||
|
| `core/wasm_shims/gettext_shim.cc` | `src/emc/rs274ngc/interp_internal.hh` | Preserves LinuxCNC `_()` diagnostic call sites by satisfying `gettext()` while leaving browser gettext catalogs unavailable. |
|
||||||
|
| `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`, `include/hal.h`, `src/hal/hal_lib.c`, `src/emc/rs274ngc/rs274ngc_pre.cc` | Keeps preview/task, builtin module stubs, unavailable 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 LinuxCNC NML status constructors, clear behavior, and `RCS_STAT_MSG` default status fields 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`, `src/libnml/rcs/rcs_print.hh` | Provides the LinuxCNC `rcs_print_error` split entry points used by NML validation without native print routing. |
|
||||||
|
| `core/wasm_shims/rtapi_compat.cc` | `src/rtapi/uspace_common.h`, `src/rtapi/rtapi.h` | Provides LinuxCNC userspace `rtapi_snprintf()` formatting by following the `rtapi.h` declaration and `uspace_common.h` `vsnprintf()` implementation path. |
|
||||||
|
| `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, preserving inactive-DB return values. |
|
||||||
|
| `core/wasm_shims/pythonplugin/python_plugin.cc` | `src/emc/pythonplugin/python_plugin.cc`, `src/emc/pythonplugin/python_plugin.hh`, `src/emc/ini/inifile.cc` | Keeps LinuxCNC PythonPlugin API and negative-status short-circuit behavior available while Python execution is disabled for browser-safe probes. |
|
||||||
|
|
||||||
|
## Runtime Shim Boundaries
|
||||||
|
|
||||||
|
- `core/wasm_shims/dlfcn.cc` and `core/wasm_shims/dlfcn.h` cover the
|
||||||
|
LinuxCNC `interp_base.cc` dynamic loader calls to `dlopen()`, `dlsym()`,
|
||||||
|
`dlerror()`, `RTLD_GLOBAL`, and `RTLD_NOW`; their unavailable-loader returns
|
||||||
|
must keep handles and symbols null, keep `dlerror()` diagnostic text stable,
|
||||||
|
and leave `dlclose()` side-effect free.
|
||||||
|
- `core/wasm_shims/emc_status_shim.cc` must stay limited to the detached
|
||||||
|
LinuxCNC `EMC_STAT` singleton from `src/emc/sai/dummyemcstat.cc`; it must not
|
||||||
|
pull in task startup, NML channels, or operator callback behavior.
|
||||||
|
- `core/wasm_shims/linuxcnc_runtime_shim.cc` must keep preview `_task=0`,
|
||||||
|
matching `src/emc/rs274ngc/gcodemodule.cc`; it must not switch browser-safe probes into task mode.
|
||||||
|
- The runtime shim's builtin module stubs must match LinuxCNC's
|
||||||
|
`interpreter`/`emccanon` inittab shape from `gcodemodule.cc` and
|
||||||
|
`taskclass.cc` while returning null module pointers; they must not become
|
||||||
|
Python execution.
|
||||||
|
- The HAL lookup shim covers `hal_get_pin_value_by_name()`,
|
||||||
|
`hal_get_signal_value_by_name()`, and `hal_get_param_value_by_name()` from
|
||||||
|
`src/emc/rs274ngc/interp_namedparams.cc`, `include/hal.h`, and
|
||||||
|
`src/hal/hal_lib.c`; it must leave HAL values unavailable by returning `-1`,
|
||||||
|
`HAL_TYPE_UNINITIALIZED`, and null data pointers.
|
||||||
|
- `wordexp()` support is limited to the single-word `wordexp()` path expansion
|
||||||
|
and `wordfree()` cleanup needed by `src/emc/rs274ngc/rs274ngc_pre.cc` for
|
||||||
|
basename, program prefix, and subroutine-directory lookups; it must not become a shell expansion feature.
|
||||||
|
|
||||||
|
## Python Shim Boundaries
|
||||||
|
|
||||||
|
- `core/wasm_shims/python_c_api_shim.cc` satisfies Python C API symbols used by
|
||||||
|
LinuxCNC RS274/remap sources; it must not become Python execution.
|
||||||
|
- The Python C API shim must return inert objects, null exceptions, false
|
||||||
|
callable/type checks, and successful module/type registration statuses only
|
||||||
|
to satisfy LinuxCNC `interp_python.cc`, `gcodemodule.cc`, and
|
||||||
|
`python_plugin.cc` link expectations; it must not call into CPython runtime.
|
||||||
|
- `core/wasm_shims/pythonplugin/python_plugin.cc` must preserve LinuxCNC
|
||||||
|
`PythonPlugin` status edges for `PLUGIN_NO_SECTION`, `PLUGIN_BAD_INIFILE`,
|
||||||
|
`PLUGIN_BAD_PATH`, `PLUGIN_INITTAB_FAILED`,
|
||||||
|
`PLUGIN_PYTHON_NOT_INITIALIZED`, `PLUGIN_NO_CALLABLE`, and
|
||||||
|
`PLUGIN_EXCEPTION` while keeping Python execution disabled.
|
||||||
|
- The PythonPlugin shim must preserve the first-caller `_inittab` `PLUGIN_INITTAB_FAILED` edge from `src/emc/pythonplugin/python_plugin.cc` when a built-in module init function fails, while keeping browser-safe probes detached from real Python module execution.
|
||||||
|
- The PythonPlugin shim must preserve the `TOPLEVEL` missing-path `PLUGIN_BAD_PATH` edge from `src/emc/pythonplugin/python_plugin.cc` before reporting the browser-safe disabled Python runtime status.
|
||||||
|
- The PythonPlugin shim must preserve `PATH_PREPEND` and `PATH_APPEND` tilde-expansion failure statuses from `src/emc/pythonplugin/python_plugin.cc` and `src/emc/ini/inifile.cc` without executing Python path mutation in browser-safe probes.
|
||||||
|
- The PythonPlugin shim must preserve LinuxCNC INI section-name trimming and trailing section-comment handling from `src/emc/ini/inifile.cc` for Python/remap configuration keys.
|
||||||
|
- The PythonPlugin shim must not execute `bp::exec`, `bp::exec_file`, or real
|
||||||
|
Python callable dispatch; `PyObject_Call` is an inert C API symbol only.
|
||||||
|
Browser Python execution remains blocked until implemented as a source-backed
|
||||||
|
browser-safe LinuxCNC adaptation.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
- `test-native.sh`, `test-linuxcnc-source-link.sh`, and `build-wasm.sh` must
|
||||||
|
run `./check-linuxcnc-wasm-shims-source-map.sh` before consuming shim-backed
|
||||||
|
source-linked LinuxCNC objects or browser artifacts.
|
||||||
|
- The WASM shims source-map guard validates LinuxCNC source/shim ownership and
|
||||||
|
manifest drift only; it does not interpret G-code.
|
||||||
|
|
||||||
|
## 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
|
||||||
|
./build-wasm.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.
|
||||||
79
docs/linuxcnc-wasm-smoke-source-map.md
Normal file
79
docs/linuxcnc-wasm-smoke-source-map.md
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
# LinuxCNC WASM smoke source map
|
||||||
|
|
||||||
|
This map records the source basis and boundaries for the Node and browser WASM
|
||||||
|
smoke wrappers. It is a smoke-test guard only; it must not add CNC behavior,
|
||||||
|
must not expand the temporary smoke parser, and must keep browser filesystem
|
||||||
|
persistence OPFS-only. In browser contexts, browser filesystem persistence
|
||||||
|
OPFS-only is the required boundary.
|
||||||
|
|
||||||
|
## LinuxCNC Source Basis
|
||||||
|
|
||||||
|
- `src/emc/rs274ngc/rs274ngc_pre.cc` provides `Interp::ini_load()`,
|
||||||
|
`Interp::restore_parameters()`, `Interp::save_parameters()`, and `read_text()`
|
||||||
|
behavior exercised by Node/browser bridge tests.
|
||||||
|
- `src/emc/rs274ngc/rs274ngc_pre.cc` saves parameters by writing
|
||||||
|
`filename + ".new"`, unlinking `filename + ".bak"`, linking the previous file
|
||||||
|
to `filename + ".bak"`, and renaming the temporary file over the main file.
|
||||||
|
- `src/emc/rs274ngc/interp_convert.cc` provides `convert_m()` coverage for
|
||||||
|
M66 waits and M68 analog output side effects used by generated remap cases.
|
||||||
|
- `src/emc/nml_intf/canon.hh` declares the `WAIT()` and
|
||||||
|
`SET_AUX_OUTPUT_VALUE()` canon entry points surfaced through the event bridge.
|
||||||
|
- `src/emc/kinematics/5axiskins.c`, `src/emc/kinematics/trtfuncs.c`,
|
||||||
|
`src/emc/kinematics/xyzac-trt-kins.c`,
|
||||||
|
`src/emc/kinematics/xyzbc-trt-kins.c`, and
|
||||||
|
`src/emc/kinematics/userkfuncs.c` provide the RTCP/kinematics inverse paths
|
||||||
|
covered by WASM smoke tests.
|
||||||
|
- `configs/sim/**.ini` files and `remap_subs/{428,429,430}remap.ngc` sources
|
||||||
|
generate `web/public/linuxcnc_switchkins_remap_config_cases.json`.
|
||||||
|
|
||||||
|
## Smoke Coverage
|
||||||
|
|
||||||
|
| Smoke entry | Boundary |
|
||||||
|
| --- | --- |
|
||||||
|
| `test-web-wasm-node-smoke.sh` | Runs the Node WASM wrapper with generated LinuxCNC switchkins cases and LinuxCNC-backed RTCP checks. |
|
||||||
|
| `test-web-wasm-node-smoke.cjs` | Exercises the C ABI, `web/src/wasm-core.js`, generated switchkins config cases, and source-backed kinematics events. |
|
||||||
|
| `test-web-wasm-browser-smoke.sh` | Runs browser checks after verifying copied WASM artifacts and smoke module structure. |
|
||||||
|
| `web/test-browser-wasm-smoke-linuxcnc-sections.js` | Exercises LinuxCNC browser parsing and generated switchkins cases without INI_FILE_NAME remap-load aliases. |
|
||||||
|
| `web/test-browser-wasm-smoke-opfs-workspace-sections.js` | Aggregates OPFS basic, parameter, mirror, and directory sections under browser OPFS availability. |
|
||||||
|
| `web/test-browser-wasm-smoke-opfs-basic-sections.js` | Exercises OPFS-backed program file reads through LinuxCNC `read_text()` and rejects unsafe paths. |
|
||||||
|
| `web/test-browser-wasm-smoke-opfs-parameter-sections.js` | Exercises LinuxCNC parameter restore/save/backup semantics through OPFS. |
|
||||||
|
| `web/test-browser-wasm-smoke-opfs-mirror-sections.js` | Exercises OPFS/WASM mirror persistence used by source-backed program parsing and parameter movement. |
|
||||||
|
| `web/test-browser-wasm-smoke-opfs-directory-sections.js` | Exercises OPFS directory persistence and WASM mirror refresh without filesystem persistence outside OPFS. |
|
||||||
|
| `web/test-browser-wasm-smoke-opfs-policy-sections.js` | Exercises browser OPFS workspace and mount policy, including the rule that OPFS cannot be disabled. |
|
||||||
|
| `web/test-browser-wasm-smoke-app-sections.js` | Verifies the browser app uses OPFS program and parameter persistence. |
|
||||||
|
|
||||||
|
## Boundaries
|
||||||
|
|
||||||
|
- Node smoke must leave OPFS disabled and must reject `parseFile*` methods that
|
||||||
|
require an OPFS workspace.
|
||||||
|
- Browser smoke must keep program files, LinuxCNC parameter files, temporary
|
||||||
|
`.new` files, and `.bak` backups under OPFS-backed storage.
|
||||||
|
- Browser smoke harness files must not use Local Storage, IndexedDB, File
|
||||||
|
Picker, Directory Picker, WebKit filesystem, or FileReader persistence paths.
|
||||||
|
- Generated switchkins config smoke cases must continue to come from
|
||||||
|
`web/public/linuxcnc_switchkins_remap_config_cases.json`.
|
||||||
|
- The browser smoke shell guard also pins required section names for LinuxCNC
|
||||||
|
browser parsing, OPFS workspace/policy/directory handling, OPFS parameter
|
||||||
|
restore/save failure handling, and browser app OPFS reload coverage.
|
||||||
|
- Smoke wrappers must keep positive minimum coverage thresholds so accidental
|
||||||
|
section loss is visible.
|
||||||
|
- `test-native.sh`, `test-linuxcnc-source-link.sh`, and `build-wasm.sh` must
|
||||||
|
run `./check-linuxcnc-wasm-smoke-source-map.sh` before consuming Node/browser
|
||||||
|
WASM smoke coverage or browser artifacts.
|
||||||
|
- The WASM smoke source-map guard validates LinuxCNC source-backed smoke
|
||||||
|
coverage only; it does not interpret G-code.
|
||||||
|
|
||||||
|
## Checks
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./check-linuxcnc-wasm-smoke-source-map.sh
|
||||||
|
./test-native.sh
|
||||||
|
./test-linuxcnc-source-link.sh
|
||||||
|
./build-wasm.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
`check-linuxcnc-wasm-smoke-source-map.sh` keeps this document, LinuxCNC source
|
||||||
|
anchors, smoke wrapper boundaries, OPFS-only policy, and generated switchkins
|
||||||
|
case usage synchronized.
|
||||||
59
docs/linuxcnc-workstreams-source-map.md
Normal file
59
docs/linuxcnc-workstreams-source-map.md
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# LinuxCNC workstreams source map
|
||||||
|
|
||||||
|
This map ties the six active workstream contexts to their source-map guards. It
|
||||||
|
is a coordination guard only; it must not add CNC behavior and must not expand
|
||||||
|
the temporary smoke parser.
|
||||||
|
|
||||||
|
## Workstream Coverage
|
||||||
|
|
||||||
|
| Workstream | Source-map documents | Guard scripts |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `switchkins/remap` generator and tables | `docs/linuxcnc-switchkins-remap-source-map.md` | `check-linuxcnc-switchkins-remap-source-map.sh` |
|
||||||
|
| `linuxcnc-kinematics` source manifest | `docs/linuxcnc-kinematics-source-map.md` | `check-linuxcnc-kinematics-source-map.sh` |
|
||||||
|
| WASM filesystem and OPFS persistence | `docs/linuxcnc-opfs-source-map.md`, `docs/linuxcnc-wasm-shims-source-map.md`, `docs/linuxcnc-wasm-blockers-source-map.md`, `docs/linuxcnc-tooldata-source-map.md` | `check-linuxcnc-opfs-source-map.sh`, `check-linuxcnc-wasm-shims-source-map.sh`, `check-linuxcnc-wasm-blockers-source-map.sh`, `check-linuxcnc-tooldata-source-map.sh` |
|
||||||
|
| browser app integration | `docs/linuxcnc-browser-app-source-map.md` | `check-linuxcnc-browser-app-source-map.sh` |
|
||||||
|
| Node and browser smoke coverage | `docs/linuxcnc-wasm-smoke-source-map.md` | `check-linuxcnc-wasm-smoke-source-map.sh` |
|
||||||
|
| source-link, build, and documentation constraints | `docs/linuxcnc-rs274-source-map.md`, `docs/linuxcnc-build-source-map.md`, `docs/linuxcnc-tooldata-source-map.md` | `check-linuxcnc-rs274-source-map.sh`, `check-linuxcnc-build-source-map.sh`, `check-linuxcnc-tooldata-source-map.sh` |
|
||||||
|
|
||||||
|
## LinuxCNC Source Basis
|
||||||
|
|
||||||
|
- `configs/sim/**.ini` and `remap_subs/{428,429,430}remap.ngc` back the
|
||||||
|
switchkins/remap workstream.
|
||||||
|
- `src/emc/kinematics/Submakefile` and `src/emc/kinematics/kinematics.h` back
|
||||||
|
the kinematics manifest workstream.
|
||||||
|
- `src/emc/rs274ngc/interp_internal.hh` and
|
||||||
|
`src/emc/rs274ngc/rs274ngc_pre.cc` back the OPFS/browser parameter-file
|
||||||
|
workstreams.
|
||||||
|
- `src/emc/rs274ngc/Submakefile`, `src/emc/tooldata/Submakefile`,
|
||||||
|
`src/emc/ini/Submakefile`, and `src/emc/pythonplugin/Submakefile` back the
|
||||||
|
source-link and build-policy workstream.
|
||||||
|
|
||||||
|
## Boundaries
|
||||||
|
|
||||||
|
- Every active workstream must have at least one source-map document and guard
|
||||||
|
script.
|
||||||
|
- `docs/linuxcnc-source-policy.md` must keep the same one-context workflow
|
||||||
|
boundary and smoke-verification-only rule used by this index.
|
||||||
|
- `test-native.sh`, `test-linuxcnc-source-link.sh`, and `build-wasm.sh` must
|
||||||
|
run the workstream source-map guard before consuming generated config aliases,
|
||||||
|
source-linked LinuxCNC objects, or browser artifacts.
|
||||||
|
- WASM blocker and shim maps remain source coverage guards, not browser API
|
||||||
|
expansion points.
|
||||||
|
- Browser filesystem persistence remains OPFS-only.
|
||||||
|
- Smoke coverage remains verification only and must not become a functional CNC
|
||||||
|
behavior path.
|
||||||
|
|
||||||
|
## Checks
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./check-linuxcnc-workstreams-source-map.sh
|
||||||
|
./test-native.sh
|
||||||
|
./test-linuxcnc-source-link.sh
|
||||||
|
./build-wasm.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
`check-linuxcnc-workstreams-source-map.sh` keeps the six-workstream architecture
|
||||||
|
list, source-map documents, guard scripts, LinuxCNC source anchors, and native /
|
||||||
|
source-link / build-wasm guard entry points synchronized.
|
||||||
38
generate-linuxcnc-puma560-dh-parameters.sh
Normal file → Executable file
38
generate-linuxcnc-puma560-dh-parameters.sh
Normal file → Executable file
@@ -3,8 +3,42 @@ set -euo pipefail
|
|||||||
|
|
||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
# LinuxCNC source basis: Puma560 DH parameter output is generated from
|
||||||
|
# configs/sim/axis/vismach/puma/puma560_dh.hal setp genserkins.{A,ALPHA,D}-N
|
||||||
|
# values used by LinuxCNC genser/switchkins simulation configs.
|
||||||
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
|
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "usage: $0 [linuxcnc-root-relative-puma560-dh-hal]" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
case "${1:-}" in
|
||||||
|
--help|-h)
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
--*)
|
||||||
|
usage
|
||||||
|
echo "unknown LinuxCNC puma560 DH generator option: $1" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
if [[ -n "${2:-}" ]]; then
|
||||||
|
usage
|
||||||
|
echo "too many LinuxCNC puma560 DH generator arguments" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
source_path=${1:-configs/sim/axis/vismach/puma/puma560_dh.hal}
|
source_path=${1:-configs/sim/axis/vismach/puma/puma560_dh.hal}
|
||||||
|
if [[ "$source_path" = /* || "$source_path" == .. || "$source_path" == ../* || "$source_path" == */.. || "$source_path" == */../* ]]; then
|
||||||
|
echo "LinuxCNC puma560 DH HAL source path must stay LinuxCNC-root relative: $source_path" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ "$source_path" != *.hal ]]; then
|
||||||
|
echo "LinuxCNC puma560 DH source must be a HAL file: $source_path" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
source_file="$linuxcnc_root/$source_path"
|
source_file="$linuxcnc_root/$source_path"
|
||||||
|
|
||||||
if [[ ! -f "$source_file" ]]; then
|
if [[ ! -f "$source_file" ]]; then
|
||||||
@@ -22,8 +56,8 @@ $1 == "setp" && $2 ~ /^genserkins\.(A|ALPHA|D)-[0-5]$/ {
|
|||||||
split($2, qualified, ".")
|
split($2, qualified, ".")
|
||||||
split(qualified[2], name_index, "-")
|
split(qualified[2], name_index, "-")
|
||||||
name = name_index[1]
|
name = name_index[1]
|
||||||
index = name_index[2] + 0
|
joint_index = name_index[2] + 0
|
||||||
values[name, index] = $3
|
values[name, joint_index] = $3
|
||||||
}
|
}
|
||||||
|
|
||||||
END {
|
END {
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ set -euo pipefail
|
|||||||
|
|
||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
# LinuxCNC source basis: generated switchkins/remap tables are extracted from
|
||||||
|
# LinuxCNC configs/sim INI/HAL files and remap_subs/{428,429,430}remap.ngc
|
||||||
|
# sources listed in linuxcnc-kinematics-source-files.txt.
|
||||||
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
|
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
|
||||||
mode=table
|
mode=table
|
||||||
all_output_dir=
|
all_output_dir=
|
||||||
@@ -56,13 +59,21 @@ if [[ ! -f "$manifest" ]]; then
|
|||||||
echo "missing kinematics manifest: $manifest" >&2
|
echo "missing kinematics manifest: $manifest" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh "$manifest"
|
||||||
if [[ "$mode" == all && -z "$all_output_dir" ]]; then
|
if [[ "$mode" == all && -z "$all_output_dir" ]]; then
|
||||||
echo "missing output directory for --all-output-dir" >&2
|
echo "missing output directory for --all-output-dir" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
if [[ "$mode" == all && "$all_output_dir" =~ [[:space:]] ]]; then
|
||||||
|
echo "switchkins remap output directory must not contain whitespace: $all_output_dir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
|
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
|
||||||
|
|
||||||
|
# LinuxCNC source basis: src/hal/utils/halcmd_commands.cc do_loadusr_cmd()
|
||||||
|
# parses loadusr options with getopt("+wWin:"), and
|
||||||
|
# src/hal/utils/halcmd_completion.c lists the common -W/-Wn/-w/-iw forms.
|
||||||
read_ini_switchkins_config() {
|
read_ini_switchkins_config() {
|
||||||
local file=$1
|
local file=$1
|
||||||
awk '
|
awk '
|
||||||
@@ -71,6 +82,31 @@ read_ini_switchkins_config() {
|
|||||||
sub(/[[:space:]]*$/, "", value)
|
sub(/[[:space:]]*$/, "", value)
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
function loadusr_program(value, fields, field_count, i, token) {
|
||||||
|
sub(/^.*(^|[[:space:]])loadusr[[:space:]]+/, "", value)
|
||||||
|
field_count = split(value, fields, /[[:space:]]+/)
|
||||||
|
for (i = 1; i <= field_count; i++) {
|
||||||
|
token = fields[i]
|
||||||
|
if (token == "-W" || token == "-w" || token == "-i") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (token == "-Wn" || token == "-n") {
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (token ~ /^-Wn.+/ || token ~ /^-n.+/) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (token ~ /^-[wWi]+$/) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (token ~ /^-/) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return token
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
{
|
{
|
||||||
line = $0
|
line = $0
|
||||||
sub(/[[:space:]]*[#;].*$/, "", line)
|
sub(/[[:space:]]*[#;].*$/, "", line)
|
||||||
@@ -97,8 +133,26 @@ read_ini_switchkins_config() {
|
|||||||
print "halfile\t" value
|
print "halfile\t" value
|
||||||
} else if (section == "hal" && key == "postgui_halfile") {
|
} else if (section == "hal" && key == "postgui_halfile") {
|
||||||
print "postgui_halfile\t" value
|
print "postgui_halfile\t" value
|
||||||
|
} else if (section == "hal" && key == "halcmd") {
|
||||||
|
lower = tolower(value)
|
||||||
|
if (lower ~ /(^|[[:space:]])loadusr([[:space:]]|$)/) {
|
||||||
|
command = loadusr_program(value)
|
||||||
|
if (command != "") {
|
||||||
|
print "halcmd_loadusr\t" command
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (section == "emcio" && key == "tool_table") {
|
||||||
|
print "tool_table\t" value
|
||||||
} else if (section == "rs274ngc" && key == "subroutine_path") {
|
} else if (section == "rs274ngc" && key == "subroutine_path") {
|
||||||
print "subroutine_path\t" value
|
print "subroutine_path\t" value
|
||||||
|
} else if (section == "rs274ngc" && key == "on_abort_command") {
|
||||||
|
lower = tolower(value)
|
||||||
|
if (match(lower, /o[[:space:]]*<[^>]+>[[:space:]]*call/)) {
|
||||||
|
ngc = substr(value, RSTART, RLENGTH)
|
||||||
|
sub(/^[Oo][[:space:]]*</, "", ngc)
|
||||||
|
sub(/>[[:space:]]*[Cc][Aa][Ll][Ll].*$/, "", ngc)
|
||||||
|
print "on_abort_ngc\t" ngc
|
||||||
|
}
|
||||||
} else if (section == "rs274ngc" && key == "remap") {
|
} else if (section == "rs274ngc" && key == "remap") {
|
||||||
lower = tolower(value)
|
lower = tolower(value)
|
||||||
for (mcode = 428; mcode <= 430; mcode++) {
|
for (mcode = 428; mcode <= 430; mcode++) {
|
||||||
@@ -109,6 +163,69 @@ read_ini_switchkins_config() {
|
|||||||
print "remap_" mcode "\t" ngc
|
print "remap_" mcode "\t" ngc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (match(lower, /ngc[[:space:]]*=[[:space:]]*[^[:space:]]+/)) {
|
||||||
|
ngc = substr(value, RSTART, RLENGTH)
|
||||||
|
sub(/^[Nn][Gg][Cc][[:space:]]*=[[:space:]]*/, "", ngc)
|
||||||
|
print "remap_ngc\t" ngc
|
||||||
|
}
|
||||||
|
if (match(lower, /python[[:space:]]*=[[:space:]]*[^[:space:]]+/)) {
|
||||||
|
python = substr(value, RSTART, RLENGTH)
|
||||||
|
sub(/^[Pp][Yy][Tt][Hh][Oo][Nn][[:space:]]*=[[:space:]]*/, "", python)
|
||||||
|
print "python_remap\t" python
|
||||||
|
}
|
||||||
|
} else if (section == "python" && key == "path_append") {
|
||||||
|
print "python_path_append\t" value
|
||||||
|
} else if (section == "python" && key == "toplevel") {
|
||||||
|
print "python_toplevel\t" value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
' "$file"
|
||||||
|
}
|
||||||
|
|
||||||
|
read_loadusr_commands() {
|
||||||
|
local file=$1
|
||||||
|
awk '
|
||||||
|
function trim(value) {
|
||||||
|
sub(/^[[:space:]]*/, "", value)
|
||||||
|
sub(/[[:space:]]*$/, "", value)
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
function loadusr_program(value, fields, field_count, i, token) {
|
||||||
|
sub(/^.*(^|[[:space:]])loadusr[[:space:]]+/, "", value)
|
||||||
|
field_count = split(value, fields, /[[:space:]]+/)
|
||||||
|
for (i = 1; i <= field_count; i++) {
|
||||||
|
token = fields[i]
|
||||||
|
if (token == "-W" || token == "-w" || token == "-i") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (token == "-Wn" || token == "-n") {
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (token ~ /^-Wn.+/ || token ~ /^-n.+/) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (token ~ /^-[wWi]+$/) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (token ~ /^-/) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return token
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
{
|
||||||
|
line = $0
|
||||||
|
sub(/[[:space:]]*#.*/, "", line)
|
||||||
|
line = trim(line)
|
||||||
|
lower = tolower(line)
|
||||||
|
if (lower !~ /(^|[[:space:]])loadusr([[:space:]]|$)/) {
|
||||||
|
next
|
||||||
|
}
|
||||||
|
command = loadusr_program(line)
|
||||||
|
if (command != "") {
|
||||||
|
print command
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
' "$file"
|
' "$file"
|
||||||
@@ -149,8 +266,14 @@ declare -A manifest_config_sources=()
|
|||||||
declare -A used_config_sources=()
|
declare -A used_config_sources=()
|
||||||
declare -A manifest_remap_sources=()
|
declare -A manifest_remap_sources=()
|
||||||
declare -A used_remap_sources=()
|
declare -A used_remap_sources=()
|
||||||
|
declare -A manifest_tooldata_sources=()
|
||||||
|
declare -A used_tooldata_sources=()
|
||||||
|
declare -A manifest_asset_sources=()
|
||||||
|
declare -A used_asset_sources=()
|
||||||
manifest_config_count=0
|
manifest_config_count=0
|
||||||
manifest_remap_count=0
|
manifest_remap_count=0
|
||||||
|
manifest_tooldata_count=0
|
||||||
|
manifest_asset_count=0
|
||||||
|
|
||||||
validate_manifest_source_path() {
|
validate_manifest_source_path() {
|
||||||
local group=$1
|
local group=$1
|
||||||
@@ -205,6 +328,28 @@ while IFS=: read -r manifest_group manifest_path _manifest_note; do
|
|||||||
manifest_remap_sources[$manifest_path]=1
|
manifest_remap_sources[$manifest_path]=1
|
||||||
manifest_remap_count=$((manifest_remap_count + 1))
|
manifest_remap_count=$((manifest_remap_count + 1))
|
||||||
;;
|
;;
|
||||||
|
tooldata)
|
||||||
|
validate_manifest_source_path "$manifest_group" "$manifest_path"
|
||||||
|
if [[ "$manifest_path" != *.tbl ]]; then
|
||||||
|
echo "LinuxCNC switchkins tooldata source must be a tbl file in $manifest: $manifest_path" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ -n "${manifest_tooldata_sources[$manifest_path]:-}" ]]; then
|
||||||
|
echo "duplicate LinuxCNC switchkins tooldata source in $manifest: $manifest_path" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
manifest_tooldata_sources[$manifest_path]=1
|
||||||
|
manifest_tooldata_count=$((manifest_tooldata_count + 1))
|
||||||
|
;;
|
||||||
|
asset)
|
||||||
|
validate_manifest_source_path "$manifest_group" "$manifest_path"
|
||||||
|
if [[ -n "${manifest_asset_sources[$manifest_path]:-}" ]]; then
|
||||||
|
echo "duplicate LinuxCNC asset source in $manifest: $manifest_path" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
manifest_asset_sources[$manifest_path]=1
|
||||||
|
manifest_asset_count=$((manifest_asset_count + 1))
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
done <"$manifest"
|
done <"$manifest"
|
||||||
|
|
||||||
@@ -280,6 +425,172 @@ config_source_for_halfile() {
|
|||||||
resolved_config_source=$resolved_linuxcnc_path
|
resolved_config_source=$resolved_linuxcnc_path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resolved_tooldata_source=
|
||||||
|
tooldata_source_for_tool_table() {
|
||||||
|
local config=$1
|
||||||
|
local tool_table=$2
|
||||||
|
resolved_tooldata_source=
|
||||||
|
[[ -n "$tool_table" ]] || return 1
|
||||||
|
|
||||||
|
local tool_path=${tool_table%%[[:space:]]*}
|
||||||
|
[[ -n "$tool_path" ]] || return 1
|
||||||
|
|
||||||
|
local config_dir
|
||||||
|
config_dir_for "$config"
|
||||||
|
config_dir=$resolved_config_dir
|
||||||
|
local resolved
|
||||||
|
resolve_config_path "$config_dir" "$tool_path"
|
||||||
|
resolved=$resolved_config_path
|
||||||
|
[[ -f "$resolved" ]] || return 1
|
||||||
|
relative_linuxcnc_path "$resolved"
|
||||||
|
resolved_tooldata_source=$resolved_linuxcnc_path
|
||||||
|
}
|
||||||
|
|
||||||
|
resolved_asset_source=
|
||||||
|
asset_source_for_path() {
|
||||||
|
local config=$1
|
||||||
|
local path=$2
|
||||||
|
resolved_asset_source=
|
||||||
|
[[ -n "$path" ]] || return 1
|
||||||
|
|
||||||
|
local config_dir
|
||||||
|
config_dir_for "$config"
|
||||||
|
config_dir=$resolved_config_dir
|
||||||
|
local resolved
|
||||||
|
resolve_config_path "$config_dir" "$path"
|
||||||
|
resolved=$resolved_config_path
|
||||||
|
[[ -f "$resolved" ]] || return 1
|
||||||
|
relative_linuxcnc_path "$resolved"
|
||||||
|
resolved_asset_source=$resolved_linuxcnc_path
|
||||||
|
}
|
||||||
|
|
||||||
|
resolved_loadusr_asset_source=
|
||||||
|
loadusr_asset_source_for_command() {
|
||||||
|
local config=$1
|
||||||
|
local command=$2
|
||||||
|
resolved_loadusr_asset_source=
|
||||||
|
[[ -n "$command" ]] || return 1
|
||||||
|
|
||||||
|
if [[ "$command" == */* || "$command" == *.py ]]; then
|
||||||
|
if asset_source_for_path "$config" "$command"; then
|
||||||
|
resolved_loadusr_asset_source=$resolved_asset_source
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [[ "$command" != *.py ]] && asset_source_for_path "$config" "$command.py"; then
|
||||||
|
resolved_loadusr_asset_source=$resolved_asset_source
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local source="src/hal/user_comps/vismach/$command.py"
|
||||||
|
if [[ -f "$linuxcnc_root/$source" ]]; then
|
||||||
|
resolved_loadusr_asset_source=$source
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if asset_source_for_path "$config" "$command.py"; then
|
||||||
|
resolved_loadusr_asset_source=$resolved_asset_source
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
record_loadusr_asset() {
|
||||||
|
local config_path=$1
|
||||||
|
local config=$2
|
||||||
|
local command=$3
|
||||||
|
local label=$4
|
||||||
|
if loadusr_asset_source_for_command "$config" "$command"; then
|
||||||
|
loadusr_asset_source=$resolved_loadusr_asset_source
|
||||||
|
record_asset_source "$config_path" "$loadusr_asset_source" "$label"
|
||||||
|
record_python_import_assets "$config_path" "$loadusr_asset_source"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
resolved_python_module_source=
|
||||||
|
python_module_source_for_name() {
|
||||||
|
local config=$1
|
||||||
|
local module=$2
|
||||||
|
local python_paths=${3:-}
|
||||||
|
resolved_python_module_source=
|
||||||
|
[[ -n "$module" ]] || return 1
|
||||||
|
[[ "$module" == *.* ]] && return 1
|
||||||
|
|
||||||
|
local module_path=${module//.//}
|
||||||
|
if [[ -z "$python_paths" ]]; then
|
||||||
|
python_paths="."
|
||||||
|
fi
|
||||||
|
|
||||||
|
local config_dir
|
||||||
|
config_dir_for "$config"
|
||||||
|
config_dir=$resolved_config_dir
|
||||||
|
local python_path
|
||||||
|
while IFS= read -r python_path; do
|
||||||
|
[[ -n "$python_path" ]] || continue
|
||||||
|
local path_part
|
||||||
|
IFS=: read -ra path_parts <<<"$python_path"
|
||||||
|
for path_part in "${path_parts[@]}"; do
|
||||||
|
[[ -n "$path_part" ]] || continue
|
||||||
|
local candidate_dir
|
||||||
|
resolve_config_path "$config_dir" "$path_part"
|
||||||
|
candidate_dir=$resolved_config_path
|
||||||
|
if [[ -f "$candidate_dir/$module_path.py" ]]; then
|
||||||
|
relative_linuxcnc_path "$candidate_dir/$module_path.py"
|
||||||
|
resolved_python_module_source=$resolved_linuxcnc_path
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done <<<"$python_paths"
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
record_asset_source() {
|
||||||
|
local config_path=$1
|
||||||
|
local source=$2
|
||||||
|
local label=$3
|
||||||
|
if [[ -z "${manifest_asset_sources[$source]:-}" ]]; then
|
||||||
|
echo "LinuxCNC $label asset source is not listed as asset in $manifest: $source ($config_path)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
used_asset_sources[$source]=1
|
||||||
|
}
|
||||||
|
|
||||||
|
declare -A walked_python_import_assets=()
|
||||||
|
record_python_import_assets() {
|
||||||
|
local config_path=$1
|
||||||
|
local source=$2
|
||||||
|
local walk_key="$config_path|$source"
|
||||||
|
if [[ -n "${walked_python_import_assets[$walk_key]:-}" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
walked_python_import_assets[$walk_key]=1
|
||||||
|
|
||||||
|
local source_dir=${source%/*}
|
||||||
|
local imported
|
||||||
|
while IFS= read -r imported; do
|
||||||
|
[[ -n "$imported" ]] || continue
|
||||||
|
local import_source="$source_dir/$imported.py"
|
||||||
|
if [[ -f "$linuxcnc_root/$import_source" ]]; then
|
||||||
|
record_asset_source "$config_path" "$import_source" "Python import"
|
||||||
|
record_python_import_assets "$config_path" "$import_source"
|
||||||
|
fi
|
||||||
|
done < <(awk '
|
||||||
|
/^[[:space:]]*import[[:space:]]+[A-Za-z_][A-Za-z0-9_]*([[:space:]]|$)/ {
|
||||||
|
value = $0
|
||||||
|
sub(/^[[:space:]]*import[[:space:]]+/, "", value)
|
||||||
|
sub(/[[:space:],].*$/, "", value)
|
||||||
|
print value
|
||||||
|
}
|
||||||
|
/^[[:space:]]*from[[:space:]]+[A-Za-z_][A-Za-z0-9_]*[[:space:]]+import[[:space:]]+/ {
|
||||||
|
value = $0
|
||||||
|
sub(/^[[:space:]]*from[[:space:]]+/, "", value)
|
||||||
|
sub(/[[:space:]].*$/, "", value)
|
||||||
|
print value
|
||||||
|
}
|
||||||
|
' "$linuxcnc_root/$source")
|
||||||
|
}
|
||||||
|
|
||||||
resolved_kinstype=
|
resolved_kinstype=
|
||||||
declare -A kinstype_cache=()
|
declare -A kinstype_cache=()
|
||||||
kinstype_for_remap_source() {
|
kinstype_for_remap_source() {
|
||||||
@@ -350,6 +661,16 @@ record_config_source() {
|
|||||||
used_config_sources[$source]=1
|
used_config_sources[$source]=1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
record_tooldata_source() {
|
||||||
|
local config_path=$1
|
||||||
|
local source=$2
|
||||||
|
if [[ -z "${manifest_tooldata_sources[$source]:-}" ]]; then
|
||||||
|
echo "LinuxCNC TOOL_TABLE source is not listed as tooldata in $manifest: $source ($config_path)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
used_tooldata_sources[$source]=1
|
||||||
|
}
|
||||||
|
|
||||||
declare -A seen_aliases=()
|
declare -A seen_aliases=()
|
||||||
declare -A seen_cases=()
|
declare -A seen_cases=()
|
||||||
declare -A seen_case_values=()
|
declare -A seen_case_values=()
|
||||||
@@ -360,6 +681,7 @@ processed_config_count=0
|
|||||||
resolved_remap_count=0
|
resolved_remap_count=0
|
||||||
unique_config_source_count=0
|
unique_config_source_count=0
|
||||||
unique_resolved_remap_count=0
|
unique_resolved_remap_count=0
|
||||||
|
unique_tooldata_source_count=0
|
||||||
|
|
||||||
add_alias() {
|
add_alias() {
|
||||||
local alias=$1
|
local alias=$1
|
||||||
@@ -609,7 +931,14 @@ while IFS=: read -r group path _note; do
|
|||||||
kinematics=
|
kinematics=
|
||||||
halfiles=
|
halfiles=
|
||||||
postgui_halfiles=
|
postgui_halfiles=
|
||||||
|
tool_tables=
|
||||||
subroutine_paths=
|
subroutine_paths=
|
||||||
|
remap_ngcs=
|
||||||
|
on_abort_ngcs=
|
||||||
|
python_paths=
|
||||||
|
python_toplevels=
|
||||||
|
python_remaps=
|
||||||
|
halcmd_loadusrs=
|
||||||
remap_ngc_428=
|
remap_ngc_428=
|
||||||
remap_ngc_429=
|
remap_ngc_429=
|
||||||
remap_ngc_430=
|
remap_ngc_430=
|
||||||
@@ -627,9 +956,30 @@ while IFS=: read -r group path _note; do
|
|||||||
postgui_halfile)
|
postgui_halfile)
|
||||||
postgui_halfiles+="${postgui_halfiles:+$'\n'}$ini_value"
|
postgui_halfiles+="${postgui_halfiles:+$'\n'}$ini_value"
|
||||||
;;
|
;;
|
||||||
|
tool_table)
|
||||||
|
tool_tables+="${tool_tables:+$'\n'}$ini_value"
|
||||||
|
;;
|
||||||
subroutine_path)
|
subroutine_path)
|
||||||
subroutine_paths+="${subroutine_paths:+$'\n'}$ini_value"
|
subroutine_paths+="${subroutine_paths:+$'\n'}$ini_value"
|
||||||
;;
|
;;
|
||||||
|
remap_ngc)
|
||||||
|
remap_ngcs+="${remap_ngcs:+$'\n'}$ini_value"
|
||||||
|
;;
|
||||||
|
on_abort_ngc)
|
||||||
|
on_abort_ngcs+="${on_abort_ngcs:+$'\n'}$ini_value"
|
||||||
|
;;
|
||||||
|
python_path_append)
|
||||||
|
python_paths+="${python_paths:+$'\n'}$ini_value"
|
||||||
|
;;
|
||||||
|
python_toplevel)
|
||||||
|
python_toplevels+="${python_toplevels:+$'\n'}$ini_value"
|
||||||
|
;;
|
||||||
|
python_remap)
|
||||||
|
python_remaps+="${python_remaps:+$'\n'}$ini_value"
|
||||||
|
;;
|
||||||
|
halcmd_loadusr)
|
||||||
|
halcmd_loadusrs+="${halcmd_loadusrs:+$'\n'}$ini_value"
|
||||||
|
;;
|
||||||
remap_428)
|
remap_428)
|
||||||
[[ -n "$remap_ngc_428" ]] || remap_ngc_428=$ini_value
|
[[ -n "$remap_ngc_428" ]] || remap_ngc_428=$ini_value
|
||||||
;;
|
;;
|
||||||
@@ -723,6 +1073,10 @@ while IFS=: read -r group path _note; do
|
|||||||
record_config_source "$path" "$hal_source" "HALFILE"
|
record_config_source "$path" "$hal_source" "HALFILE"
|
||||||
add_alias "$hal_source" "$m428" "$m429" "$m430"
|
add_alias "$hal_source" "$m428" "$m429" "$m430"
|
||||||
add_halfile_source_cases "$hal_source" "$m428" "$m429" "$m430"
|
add_halfile_source_cases "$hal_source" "$m428" "$m429" "$m430"
|
||||||
|
while IFS= read -r loadusr_command; do
|
||||||
|
[[ -n "$loadusr_command" ]] || continue
|
||||||
|
record_loadusr_asset "$path" "$linuxcnc_root/$hal_source" "$loadusr_command" "HALFILE loadusr"
|
||||||
|
done < <(read_loadusr_commands "$linuxcnc_root/$hal_source")
|
||||||
fi
|
fi
|
||||||
add_halfile_cases "$halfile" "$m428" "$m429" "$m430"
|
add_halfile_cases "$halfile" "$m428" "$m429" "$m430"
|
||||||
add_halfile_aliases "$halfile" "$m428" "$m429" "$m430"
|
add_halfile_aliases "$halfile" "$m428" "$m429" "$m430"
|
||||||
@@ -733,10 +1087,53 @@ while IFS=: read -r group path _note; do
|
|||||||
record_config_source "$path" "$hal_source" "POSTGUI_HALFILE"
|
record_config_source "$path" "$hal_source" "POSTGUI_HALFILE"
|
||||||
add_alias "$hal_source" "$m428" "$m429" "$m430"
|
add_alias "$hal_source" "$m428" "$m429" "$m430"
|
||||||
add_postgui_halfile_source_cases "$hal_source" "$m428" "$m429" "$m430"
|
add_postgui_halfile_source_cases "$hal_source" "$m428" "$m429" "$m430"
|
||||||
|
while IFS= read -r loadusr_command; do
|
||||||
|
[[ -n "$loadusr_command" ]] || continue
|
||||||
|
record_loadusr_asset "$path" "$linuxcnc_root/$hal_source" "$loadusr_command" "POSTGUI_HALFILE loadusr"
|
||||||
|
done < <(read_loadusr_commands "$linuxcnc_root/$hal_source")
|
||||||
fi
|
fi
|
||||||
add_postgui_halfile_cases "$halfile" "$m428" "$m429" "$m430"
|
add_postgui_halfile_cases "$halfile" "$m428" "$m429" "$m430"
|
||||||
add_halfile_aliases "$halfile" "$m428" "$m429" "$m430"
|
add_halfile_aliases "$halfile" "$m428" "$m429" "$m430"
|
||||||
done <<<"$postgui_halfiles"
|
done <<<"$postgui_halfiles"
|
||||||
|
while IFS= read -r tool_table; do
|
||||||
|
if tooldata_source_for_tool_table "$config" "$tool_table"; then
|
||||||
|
record_tooldata_source "$path" "$resolved_tooldata_source"
|
||||||
|
fi
|
||||||
|
done <<<"$tool_tables"
|
||||||
|
while IFS= read -r ngc_name; do
|
||||||
|
[[ -n "$ngc_name" ]] || continue
|
||||||
|
if remap_source_for_ngc "$config" "$ngc_name" "$subroutine_paths"; then
|
||||||
|
if [[ -z "${manifest_remap_sources[$resolved_remap_source]:-}" ]]; then
|
||||||
|
record_asset_source "$path" "$resolved_remap_source" "RS274NGC REMAP ngc"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done <<<"$remap_ngcs"
|
||||||
|
while IFS= read -r ngc_name; do
|
||||||
|
[[ -n "$ngc_name" ]] || continue
|
||||||
|
if remap_source_for_ngc "$config" "$ngc_name" "$subroutine_paths"; then
|
||||||
|
record_asset_source "$path" "$resolved_remap_source" "ON_ABORT_COMMAND"
|
||||||
|
fi
|
||||||
|
done <<<"$on_abort_ngcs"
|
||||||
|
while IFS= read -r toplevel; do
|
||||||
|
[[ -n "$toplevel" ]] || continue
|
||||||
|
if asset_source_for_path "$config" "$toplevel"; then
|
||||||
|
python_toplevel_source=$resolved_asset_source
|
||||||
|
record_asset_source "$path" "$python_toplevel_source" "PYTHON TOPLEVEL"
|
||||||
|
record_python_import_assets "$path" "$python_toplevel_source"
|
||||||
|
fi
|
||||||
|
done <<<"$python_toplevels"
|
||||||
|
while IFS= read -r python_remap; do
|
||||||
|
[[ -n "$python_remap" ]] || continue
|
||||||
|
if python_module_source_for_name "$config" "$python_remap" "$python_paths"; then
|
||||||
|
python_remap_source=$resolved_python_module_source
|
||||||
|
record_asset_source "$path" "$python_remap_source" "Python REMAP module"
|
||||||
|
record_python_import_assets "$path" "$python_remap_source"
|
||||||
|
fi
|
||||||
|
done <<<"$python_remaps"
|
||||||
|
while IFS= read -r loadusr_command; do
|
||||||
|
[[ -n "$loadusr_command" ]] || continue
|
||||||
|
record_loadusr_asset "$path" "$config" "$loadusr_command" "HALCMD loadusr"
|
||||||
|
done <<<"$halcmd_loadusrs"
|
||||||
|
|
||||||
for remap_source in "$remap_source_428" "$remap_source_429" "$remap_source_430"; do
|
for remap_source in "$remap_source_428" "$remap_source_429" "$remap_source_430"; do
|
||||||
if [[ -n "$remap_source" ]]; then
|
if [[ -n "$remap_source" ]]; then
|
||||||
@@ -758,6 +1155,8 @@ done <"$manifest"
|
|||||||
|
|
||||||
unique_config_source_count=${#used_config_sources[@]}
|
unique_config_source_count=${#used_config_sources[@]}
|
||||||
unique_resolved_remap_count=${#used_remap_sources[@]}
|
unique_resolved_remap_count=${#used_remap_sources[@]}
|
||||||
|
unique_tooldata_source_count=${#used_tooldata_sources[@]}
|
||||||
|
unique_asset_source_count=${#used_asset_sources[@]}
|
||||||
|
|
||||||
validate_generated_content() {
|
validate_generated_content() {
|
||||||
if ((manifest_config_count == 0)); then
|
if ((manifest_config_count == 0)); then
|
||||||
@@ -784,6 +1183,10 @@ validate_generated_content() {
|
|||||||
echo "no unique LinuxCNC config sources used from manifest: $manifest" >&2
|
echo "no unique LinuxCNC config sources used from manifest: $manifest" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
if ((manifest_tooldata_count > 0 && unique_tooldata_source_count == 0)); then
|
||||||
|
echo "no unique LinuxCNC TOOL_TABLE sources used from manifest: $manifest" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
if ((unique_config_source_count != manifest_config_count)); then
|
if ((unique_config_source_count != manifest_config_count)); then
|
||||||
echo "LinuxCNC config manifest coverage mismatch: used $unique_config_source_count of $manifest_config_count config sources" >&2
|
echo "LinuxCNC config manifest coverage mismatch: used $unique_config_source_count of $manifest_config_count config sources" >&2
|
||||||
local config_source
|
local config_source
|
||||||
@@ -794,6 +1197,20 @@ validate_generated_content() {
|
|||||||
done
|
done
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
if ((unique_tooldata_source_count != manifest_tooldata_count)); then
|
||||||
|
echo "LinuxCNC tooldata manifest coverage mismatch: used $unique_tooldata_source_count of $manifest_tooldata_count tooldata sources" >&2
|
||||||
|
local tooldata_source
|
||||||
|
for tooldata_source in "${!manifest_tooldata_sources[@]}"; do
|
||||||
|
if [[ -z "${used_tooldata_sources[$tooldata_source]:-}" ]]; then
|
||||||
|
echo "unused LinuxCNC tooldata manifest source: $tooldata_source" >&2
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ((unique_asset_source_count > manifest_asset_count)); then
|
||||||
|
echo "LinuxCNC asset manifest coverage mismatch: used $unique_asset_source_count of $manifest_asset_count asset sources" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
if ((unique_resolved_remap_count != manifest_remap_count)); then
|
if ((unique_resolved_remap_count != manifest_remap_count)); then
|
||||||
echo "LinuxCNC remap manifest coverage mismatch: resolved $unique_resolved_remap_count of $manifest_remap_count remap sources" >&2
|
echo "LinuxCNC remap manifest coverage mismatch: resolved $unique_resolved_remap_count of $manifest_remap_count remap sources" >&2
|
||||||
local remap_source
|
local remap_source
|
||||||
@@ -875,6 +1292,11 @@ elif [[ "$mode" == all ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
mkdir -p "$all_output_dir"
|
mkdir -p "$all_output_dir"
|
||||||
|
all_output_dir=$(cd "$all_output_dir" && pwd)
|
||||||
|
if [[ "$all_output_dir" == "/" ]]; then
|
||||||
|
echo "switchkins remap output directory must not be the filesystem root" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
write_generated_file "$all_output_dir/linuxcnc_switchkins_remap_table.inc" "table" write_table
|
write_generated_file "$all_output_dir/linuxcnc_switchkins_remap_table.inc" "table" write_table
|
||||||
write_generated_file "$all_output_dir/linuxcnc_switchkins_remap_config_cases.inc" "config cases" write_config_cases
|
write_generated_file "$all_output_dir/linuxcnc_switchkins_remap_config_cases.inc" "config cases" write_config_cases
|
||||||
write_generated_file "$all_output_dir/linuxcnc_switchkins_remap_config_cases.json" "JSON config cases" write_json_cases
|
write_generated_file "$all_output_dir/linuxcnc_switchkins_remap_config_cases.json" "JSON config cases" write_json_cases
|
||||||
|
|||||||
@@ -277,9 +277,15 @@ metadata:docs/man/man9/tripodkins.9:LinuxCNC tripodkins generated manpage source
|
|||||||
metadata:src/objects/hal/components/gantry.mak:halcompile-generated gantry component make metadata
|
metadata:src/objects/hal/components/gantry.mak:halcompile-generated gantry component make metadata
|
||||||
metadata:src/objects/man/man9/gantry.9.adoc:halcompile-generated gantry component manpage source
|
metadata:src/objects/man/man9/gantry.9.adoc:halcompile-generated gantry component manpage source
|
||||||
metadata:src/objects/hal/components/matrixkins.mak:halcompile-generated matrixkins make metadata adjacent to generated kinematics source
|
metadata:src/objects/hal/components/matrixkins.mak:halcompile-generated matrixkins make metadata adjacent to generated kinematics source
|
||||||
|
metadata:src/objects/man/man9/matrixkins.9.adoc:halcompile-generated matrixkins manpage source adjacent to generated kinematics source
|
||||||
metadata:src/objects/hal/components/millturn.mak:halcompile-generated millturn make metadata adjacent to generated kinematics source
|
metadata:src/objects/hal/components/millturn.mak:halcompile-generated millturn make metadata adjacent to generated kinematics source
|
||||||
|
metadata:src/objects/man/man9/millturn.9.adoc:halcompile-generated millturn manpage source adjacent to generated kinematics source
|
||||||
metadata:src/objects/hal/components/userkins.mak:halcompile-generated userkins make metadata adjacent to generated kinematics source
|
metadata:src/objects/hal/components/userkins.mak:halcompile-generated userkins make metadata adjacent to generated kinematics source
|
||||||
|
metadata:src/objects/man/man9/userkins.9.adoc:halcompile-generated userkins manpage source adjacent to generated kinematics source
|
||||||
metadata:src/objects/hal/components/xyzab_tdr_kins.mak:halcompile-generated XYZAB table dual rotary make metadata adjacent to generated kinematics source
|
metadata:src/objects/hal/components/xyzab_tdr_kins.mak:halcompile-generated XYZAB table dual rotary make metadata adjacent to generated kinematics source
|
||||||
|
metadata:src/objects/man/man9/xyzab_tdr_kins.9.adoc:halcompile-generated XYZAB table dual rotary manpage source adjacent to generated kinematics source
|
||||||
metadata:src/objects/hal/components/xyzacb_trsrn.mak:halcompile-generated XYZACB TRSRN make metadata adjacent to generated kinematics source
|
metadata:src/objects/hal/components/xyzacb_trsrn.mak:halcompile-generated XYZACB TRSRN make metadata adjacent to generated kinematics source
|
||||||
|
metadata:src/objects/man/man9/xyzacb_trsrn.9.adoc:halcompile-generated XYZACB TRSRN manpage source adjacent to generated kinematics source
|
||||||
metadata:src/objects/hal/components/xyzbca_trsrn.mak:halcompile-generated XYZBCA TRSRN make metadata adjacent to generated kinematics source
|
metadata:src/objects/hal/components/xyzbca_trsrn.mak:halcompile-generated XYZBCA TRSRN make metadata adjacent to generated kinematics source
|
||||||
|
metadata:src/objects/man/man9/xyzbca_trsrn.9.adoc:halcompile-generated XYZBCA TRSRN manpage source adjacent to generated kinematics source
|
||||||
asset:src/emc/kinematics/blend.fig:LinuxCNC kinematics reference asset tracked for full directory coverage
|
asset:src/emc/kinematics/blend.fig:LinuxCNC kinematics reference asset tracked for full directory coverage
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ binding:src/emc/rs274ngc/pyparamclass.cc:Boost.Python bindings; exclude from was
|
|||||||
tooldata:src/emc/tooldata/tooldata_common.cc:shared tool table parser/format logic
|
tooldata:src/emc/tooldata/tooldata_common.cc:shared tool table parser/format logic
|
||||||
tooldata:src/emc/tooldata/tooldata_mmap.cc:native mmap implementation; replace for wasm
|
tooldata:src/emc/tooldata/tooldata_mmap.cc:native mmap implementation; replace for wasm
|
||||||
tooldata:src/emc/tooldata/tooldata_db.cc:external database integration; exclude from wasm core
|
tooldata:src/emc/tooldata/tooldata_db.cc:external database integration; exclude from wasm core
|
||||||
|
tooldata:src/emc/tooldata/tooldata_nml.cc:NML tool table backend selected by LinuxCNC TOOL_NML_FLAG
|
||||||
|
tooldata:src/emc/tooldata/tool_mmap_read.cc:LinuxCNC tooldata mmap reader utility source from tooldata Submakefile
|
||||||
|
tooldata:src/emc/tooldata/tool_watch.cc:LinuxCNC tooldata watcher utility source from tooldata Submakefile
|
||||||
header:src/emc/rs274ngc/array1.hh:LinuxCNC rs274 header tracked for full interpreter source coverage
|
header:src/emc/rs274ngc/array1.hh:LinuxCNC rs274 header tracked for full interpreter source coverage
|
||||||
header:src/emc/rs274ngc/boost_pyenum_macros.hh:LinuxCNC rs274 Boost.Python enum helper header
|
header:src/emc/rs274ngc/boost_pyenum_macros.hh:LinuxCNC rs274 Boost.Python enum helper header
|
||||||
header:src/emc/rs274ngc/interp_array_types.hh:LinuxCNC rs274 block array type definitions
|
header:src/emc/rs274ngc/interp_array_types.hh:LinuxCNC rs274 block array type definitions
|
||||||
@@ -50,5 +53,15 @@ header:src/emc/rs274ngc/rs274ngc.hh:LinuxCNC rs274 public interface header
|
|||||||
header:src/emc/rs274ngc/rs274ngc_interp.hh:LinuxCNC rs274 interpreter class header
|
header:src/emc/rs274ngc/rs274ngc_interp.hh:LinuxCNC rs274 interpreter class header
|
||||||
header:src/emc/rs274ngc/rs274ngc_return.hh:LinuxCNC rs274 return code header
|
header:src/emc/rs274ngc/rs274ngc_return.hh:LinuxCNC rs274 return code header
|
||||||
header:src/emc/rs274ngc/units.h:LinuxCNC rs274 units helper header
|
header:src/emc/rs274ngc/units.h:LinuxCNC rs274 units helper header
|
||||||
|
header:src/emc/tooldata/tooldata.hh:LinuxCNC tooldata declarations used by RS274 and wasm-safe tooldata shims
|
||||||
|
header:src/emc/nml_intf/emc.hh:LinuxCNC EMC status declarations used by native source-link support objects
|
||||||
|
header:src/emc/nml_intf/emc_nml.hh:LinuxCNC EMC NML aggregate declarations used by native source-link support objects
|
||||||
|
header:src/libnml/nml/nmlmsg.hh:LinuxCNC NML message declarations used by native source-link stat_msg support object
|
||||||
|
header:src/libnml/nml/stat_msg.hh:LinuxCNC status message declarations used by native source-link stat_msg support object
|
||||||
metadata:src/emc/rs274ngc/Submakefile:LinuxCNC rs274 make build metadata tracked for full directory coverage
|
metadata:src/emc/rs274ngc/Submakefile:LinuxCNC rs274 make build metadata tracked for full directory coverage
|
||||||
metadata:src/emc/rs274ngc/meson.build:LinuxCNC rs274 meson build metadata tracked for full directory coverage
|
metadata:src/emc/rs274ngc/meson.build:LinuxCNC rs274 meson build metadata tracked for full directory coverage
|
||||||
|
metadata:src/emc/tooldata/Submakefile:LinuxCNC tooldata build metadata defining mmap and NML backend source selection
|
||||||
|
metadata:src/emc/nml_intf/emcops.cc:source basis for native source-link EMC status constructor support object
|
||||||
|
metadata:src/emc/sai/dummyemcstat.cc:source basis for native source-link emcStatus singleton support object
|
||||||
|
metadata:src/emc/sai/Submakefile:LinuxCNC SAI build metadata defining dummyemcstat support object
|
||||||
|
metadata:src/libnml/nml/stat_msg.cc:source basis for native source-link RCS_STAT_MSG support object
|
||||||
|
|||||||
@@ -51,7 +51,29 @@ header:src/emc/rs274ngc/rs274ngc.hh:LinuxCNC rs274 public interface header
|
|||||||
header:src/emc/rs274ngc/rs274ngc_interp.hh:LinuxCNC rs274 interpreter class header
|
header:src/emc/rs274ngc/rs274ngc_interp.hh:LinuxCNC rs274 interpreter class header
|
||||||
header:src/emc/rs274ngc/rs274ngc_return.hh:LinuxCNC rs274 return code header
|
header:src/emc/rs274ngc/rs274ngc_return.hh:LinuxCNC rs274 return code header
|
||||||
header:src/emc/rs274ngc/units.h:LinuxCNC rs274 units helper header
|
header:src/emc/rs274ngc/units.h:LinuxCNC rs274 units helper header
|
||||||
|
header:src/emc/pythonplugin/python_plugin.hh:LinuxCNC PythonPlugin declarations used by wasm-safe Python plugin shim
|
||||||
|
header:src/emc/ini/inifile.h:LinuxCNC C INI reader declarations exported by ini Submakefile
|
||||||
|
header:src/emc/ini/inifile.hh:LinuxCNC C++ INI reader declarations used by inifile.cc
|
||||||
|
header:src/emc/nml_intf/emc.hh:LinuxCNC EMC status declarations used by wasm-safe EMC status shim
|
||||||
|
header:src/emc/nml_intf/emc_nml.hh:LinuxCNC EMC NML aggregate declarations used by wasm-safe EMC status shim
|
||||||
|
header:include/hal.h:LinuxCNC HAL declarations used by wasm-safe runtime HAL lookup shim
|
||||||
|
header:src/libnml/nml/nmlmsg.hh:LinuxCNC NML message declarations used by wasm-safe NMLmsg constructor shim
|
||||||
|
header:src/libnml/nml/stat_msg.hh:LinuxCNC status message declarations used by wasm-safe RCS_STAT_MSG constructor shim
|
||||||
|
header:src/libnml/rcs/rcs_print.hh:LinuxCNC RCS print declarations used by wasm-safe NML status diagnostics
|
||||||
|
header:src/rtapi/rtapi.h:LinuxCNC RTAPI snprintf declarations used by wasm-safe RTAPI compatibility shim
|
||||||
|
header:src/rtapi/rtapi_string.h:LinuxCNC RTAPI string helpers used by RS274 and RCS print sources
|
||||||
metadata:src/emc/rs274ngc/Submakefile:LinuxCNC rs274 make build metadata tracked for full directory coverage
|
metadata:src/emc/rs274ngc/Submakefile:LinuxCNC rs274 make build metadata tracked for full directory coverage
|
||||||
metadata:src/emc/rs274ngc/meson.build:LinuxCNC rs274 meson build metadata tracked for full directory coverage
|
metadata:src/emc/rs274ngc/meson.build:LinuxCNC rs274 meson build metadata tracked for full directory coverage
|
||||||
|
metadata:src/emc/pythonplugin/python_plugin.cc:source basis for wasm-safe PythonPlugin shim
|
||||||
|
metadata:src/emc/task/emctaskmain.cc:source basis for wasm-safe _task preview/task mode runtime shim
|
||||||
|
metadata:src/emc/task/taskclass.cc:source basis for wasm-safe builtin Python module table runtime shim
|
||||||
|
metadata:src/emc/task/emccanon.cc:source basis for wasm-safe tooldata API declaration shim
|
||||||
|
metadata:src/emc/sai/dummyemcstat.cc:source basis for wasm-safe emcStatus singleton shim
|
||||||
|
metadata:src/emc/ini/Submakefile:LinuxCNC ini build metadata defining liblinuxcncini source and exported headers
|
||||||
metadata:src/libnml/nml/stat_msg.cc:source basis for wasm-safe RCS_STAT_MSG constructor shim
|
metadata:src/libnml/nml/stat_msg.cc:source basis for wasm-safe RCS_STAT_MSG constructor shim
|
||||||
metadata:src/libnml/nml/nmlmsg.cc:source basis for wasm-safe NMLmsg constructor shim
|
metadata:src/libnml/nml/nmlmsg.cc:source basis for wasm-safe NMLmsg constructor shim
|
||||||
|
metadata:src/libnml/rcs/rcs_print.cc:source basis for wasm-safe RCS print diagnostic shim
|
||||||
|
metadata:src/rtapi/uspace_common.h:source basis for wasm-safe RTAPI snprintf compatibility shim
|
||||||
|
metadata:src/hal/hal_lib.c:source basis for wasm-safe runtime HAL lookup shim
|
||||||
|
metadata:src/emc/tooldata/tooldata_db.cc:source basis for wasm-safe tooldata runtime export shim
|
||||||
|
metadata:src/emc/tooldata/tooldata_nml.cc:source basis for wasm-safe tooldata runtime NML export shim
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ set -euo pipefail
|
|||||||
|
|
||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
# LinuxCNC source basis: combined CMake source-set listers expose project
|
||||||
|
# bridge/shim/adapter sources declared in core/CMakeLists.txt so LinuxCNC
|
||||||
|
# source-linked probes can verify their thin wrapper objects without inventing
|
||||||
|
# G-code, canon, or kinematics behavior.
|
||||||
if [[ "$#" -lt 2 || "$#" -gt 3 ]]; then
|
if [[ "$#" -lt 2 || "$#" -gt 3 ]]; then
|
||||||
echo "usage: $0 <set-name> <append-name> [relative-prefix]" >&2
|
echo "usage: $0 <set-name> <append-name> [relative-prefix]" >&2
|
||||||
exit 1
|
exit 1
|
||||||
@@ -11,9 +15,22 @@ fi
|
|||||||
set_name=$1
|
set_name=$1
|
||||||
append_name=$2
|
append_name=$2
|
||||||
relative_prefix=${3:-}
|
relative_prefix=${3:-}
|
||||||
|
if ! [[ "$set_name" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]]; then
|
||||||
|
echo "CMake source set name must be a CMake variable name: $set_name" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! [[ "$append_name" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]]; then
|
||||||
|
echo "CMake append source set name must be a CMake variable name: $append_name" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ "$relative_prefix" = /* || "$relative_prefix" == .. || "$relative_prefix" == ../* || "$relative_prefix" == */.. || "$relative_prefix" == */../* ]]; then
|
||||||
|
echo "CMake source relative prefix must stay project-relative: $relative_prefix" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
awk -v set_name="$set_name" -v append_name="$append_name" -v relative_prefix="$relative_prefix" '
|
awk -v set_name="$set_name" -v append_name="$append_name" -v relative_prefix="$relative_prefix" '
|
||||||
$0 ~ "^[[:space:]]*set\\(" set_name "([[:space:]]|$)" {
|
$0 ~ "^[[:space:]]*set\\(" set_name "([[:space:]]|$)" {
|
||||||
|
found_set = 1
|
||||||
in_set = 1
|
in_set = 1
|
||||||
next
|
next
|
||||||
}
|
}
|
||||||
@@ -22,6 +39,7 @@ awk -v set_name="$set_name" -v append_name="$append_name" -v relative_prefix="$r
|
|||||||
next
|
next
|
||||||
}
|
}
|
||||||
$0 ~ "^[[:space:]]*list\\(APPEND " append_name "([[:space:]]|$)" {
|
$0 ~ "^[[:space:]]*list\\(APPEND " append_name "([[:space:]]|$)" {
|
||||||
|
found_append = 1
|
||||||
in_append = 1
|
in_append = 1
|
||||||
next
|
next
|
||||||
}
|
}
|
||||||
@@ -42,4 +60,14 @@ awk -v set_name="$set_name" -v append_name="$append_name" -v relative_prefix="$r
|
|||||||
}
|
}
|
||||||
print line
|
print line
|
||||||
}
|
}
|
||||||
|
END {
|
||||||
|
if (!found_set) {
|
||||||
|
print "missing CMake source set: " set_name > "/dev/stderr"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
if (!found_append) {
|
||||||
|
print "missing CMake append source set: " append_name > "/dev/stderr"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
' core/CMakeLists.txt
|
' core/CMakeLists.txt
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user