Harden LinuxCNC wasm and native probe workflows

This commit is contained in:
cnc
2026-05-26 17:00:58 +08:00
parent 86734622d7
commit ce2f3f3769
98 changed files with 7384 additions and 877 deletions

View File

@@ -0,0 +1,88 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
manifest=${1:-linuxcnc-rs274-wasm-source-files.txt}
if [[ ! -f "$manifest" ]]; then
echo "missing manifest: $manifest" >&2
exit 1
fi
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
python_blockers=()
dlopen_blockers=()
tooldata_blockers=()
shimmed_tooldata_users=()
native_backend_blockers=()
native_fs_blockers=()
while IFS=: read -r group path note; do
[[ -z "$group" || "$group" == \#* ]] && continue
case "$group" in
core)
continue
;;
blocked)
;;
*)
echo "unknown manifest group: $group" >&2
exit 1
;;
esac
full_path="$linuxcnc_root/$path"
if [[ ! -f "$full_path" ]]; then
echo "missing manifest source: $path" >&2
exit 1
fi
if [[ "$path" == src/emc/tooldata/* ]]; then
tooldata_blockers+=("$path")
fi
if [[ "$path" == "src/emc/tooldata/tooldata_mmap.cc" ]]; then
native_backend_blockers+=("$path")
fi
if grep -Eq 'Python\.h|boost/python|pythonplugin|PyObject|PyInit_' "$full_path"; then
python_blockers+=("$path")
fi
if grep -Eq 'dlopen|dlsym|RTLD_' "$full_path"; then
dlopen_blockers+=("$path")
fi
if grep -Eq 'mkstemp|mkstemps|dirent\.h|opendir|readdir|unistd\.h' "$full_path"; then
native_fs_blockers+=("$path")
fi
if [[ "$path" != src/emc/tooldata/* ]] && grep -Eq 'tooldata/tooldata.hh|tooldata_' "$full_path"; then
shimmed_tooldata_users+=("$path")
fi
done < "$manifest"
print_group() {
local title=$1
shift
echo "[$title]"
if [[ $# -eq 0 ]]; then
echo "(none)"
else
printf '%s\n' "$@" | LC_ALL=C sort -u
fi
echo
}
print_group "python" "${python_blockers[@]}"
print_group "dlopen" "${dlopen_blockers[@]}"
print_group "tooldata" "${tooldata_blockers[@]}"
print_group "tooldata-users" "${shimmed_tooldata_users[@]}"
print_group "native-backend" "${native_backend_blockers[@]}"
print_group "native-fs" "${native_fs_blockers[@]}"

View File

@@ -3,12 +3,68 @@ set -euo pipefail
cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
# This script writes fixed build and public artifact paths, so serialize it.
exec 9>"${TMPDIR:-/tmp}/cnc_sim_build_wasm.lock"
flock 9
echo "LinuxCNC wasm blocker scan"
./test-linuxcnc-wasm-blockers.sh
echo "LinuxCNC wasm-safe source syntax probe"
./test-linuxcnc-wasm-source-syntax.sh
echo "LinuxCNC wasm-safe source object probe"
./test-linuxcnc-wasm-source-objects.sh
echo "LinuxCNC wasm-safe CMake probe target"
./test-linuxcnc-wasm-cmake-safe-probe.sh
echo "LinuxCNC wasm tooldata shim link probe"
./test-linuxcnc-wasm-tooldata-link.sh
echo "LinuxCNC wasm tooldata_common link probe"
./test-linuxcnc-wasm-tooldata-common-link.sh
echo "LinuxCNC wasm tooldata_mmap symbol probe"
./test-linuxcnc-wasm-tooldata-mmap-symbols.sh
echo "LinuxCNC wasm tooldata runtime symbol probe"
./test-linuxcnc-wasm-tooldata-runtime-symbols.sh
echo "LinuxCNC wasm interp_base link probe"
./test-linuxcnc-wasm-interp-base-link.sh
echo "LinuxCNC wasm interp_find link probe"
./test-linuxcnc-wasm-interp-find-link.sh
echo "LinuxCNC wasm python_plugin link probe"
./test-linuxcnc-wasm-python-plugin-link.sh
echo "LinuxCNC wasm rs274ngc_pre object probe"
./test-linuxcnc-wasm-rs274ngc-pre-object.sh
echo "LinuxCNC wasm rs274ngc_pre link blocker scan"
./test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh
echo "LinuxCNC wasm rs274ngc_pre link probe"
./test-linuxcnc-wasm-rs274ngc-pre-link.sh
if ! command -v emcmake >/dev/null 2>&1; then
echo "Emscripten is required. Install/activate emsdk so emcmake and emcc are in PATH." >&2
exit 1
fi
emcmake cmake -S core -B build/wasm -DCMAKE_BUILD_TYPE=Release
emcmake cmake -S core -B build/wasm \
-DCMAKE_BUILD_TYPE=Release \
-DCNC_SIM_LINUXCNC_ROOT="$linuxcnc_root" \
-DCNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE=ON
cmake --build build/wasm
mkdir -p web/public

View File

@@ -3,7 +3,13 @@ project(cnc_sim_wasm_core LANGUAGES CXX)
option(CNC_SIM_ENABLE_LINUXCNC_BRIDGE "Build the experimental LinuxCNC canon bridge" 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)
set(CNC_SIM_LINUXCNC_ROOT "" CACHE PATH "LinuxCNC source root for the experimental bridge")
set(CNC_SIM_LINUXCNC_WASM_SOURCE_MANIFEST
"${CMAKE_CURRENT_SOURCE_DIR}/../linuxcnc-rs274-wasm-source-files.txt"
CACHE FILEPATH
"Manifest that partitions LinuxCNC rs274 sources into wasm-safe core and blocked groups"
)
set(cnc_sim_core_sources
src/canon_event_sink.cpp
@@ -28,6 +34,137 @@ if(CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND)
)
endif()
if(CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE)
if(NOT CNC_SIM_LINUXCNC_ROOT)
message(FATAL_ERROR "Set CNC_SIM_LINUXCNC_ROOT to the LinuxCNC source root")
endif()
get_filename_component(CNC_SIM_LINUXCNC_ROOT_ABS
"${CNC_SIM_LINUXCNC_ROOT}"
ABSOLUTE
BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/.."
)
if(NOT IS_DIRECTORY "${CNC_SIM_LINUXCNC_ROOT_ABS}")
message(FATAL_ERROR "LinuxCNC source root does not exist: ${CNC_SIM_LINUXCNC_ROOT_ABS}")
endif()
if(NOT EXISTS "${CNC_SIM_LINUXCNC_WASM_SOURCE_MANIFEST}")
message(FATAL_ERROR "Missing LinuxCNC wasm source manifest: ${CNC_SIM_LINUXCNC_WASM_SOURCE_MANIFEST}")
endif()
if(IS_DIRECTORY "${CNC_SIM_LINUXCNC_WASM_SOURCE_MANIFEST}")
message(FATAL_ERROR "LinuxCNC wasm source manifest is not a file: ${CNC_SIM_LINUXCNC_WASM_SOURCE_MANIFEST}")
endif()
set(linuxcnc_wasm_safe_probe_sources)
set(linuxcnc_wasm_blocked_sources)
file(STRINGS "${CNC_SIM_LINUXCNC_WASM_SOURCE_MANIFEST}" linuxcnc_wasm_manifest_lines)
foreach(manifest_line IN LISTS linuxcnc_wasm_manifest_lines)
string(STRIP "${manifest_line}" manifest_line)
if(manifest_line STREQUAL "" OR manifest_line MATCHES "^#")
continue()
endif()
if(NOT manifest_line MATCHES "^([^:]+):([^:]+):(.*)$")
message(FATAL_ERROR "Bad LinuxCNC wasm source manifest line: ${manifest_line}")
endif()
set(manifest_group "${CMAKE_MATCH_1}")
set(manifest_path "${CMAKE_MATCH_2}")
if(NOT manifest_group STREQUAL "core" AND NOT manifest_group STREQUAL "blocked")
message(FATAL_ERROR "Unknown LinuxCNC wasm source manifest group: ${manifest_group}")
endif()
set(manifest_source "${CNC_SIM_LINUXCNC_ROOT_ABS}/${manifest_path}")
if(NOT EXISTS "${manifest_source}")
message(FATAL_ERROR "LinuxCNC wasm manifest source does not exist: ${manifest_source}")
endif()
if(IS_DIRECTORY "${manifest_source}")
message(FATAL_ERROR "LinuxCNC wasm manifest source is not a file: ${manifest_source}")
endif()
if(manifest_group STREQUAL "core")
list(APPEND linuxcnc_wasm_safe_probe_sources "${manifest_source}")
else()
list(APPEND linuxcnc_wasm_blocked_sources "${manifest_source}")
endif()
endforeach()
list(LENGTH linuxcnc_wasm_safe_probe_sources linuxcnc_wasm_safe_probe_source_count)
list(LENGTH linuxcnc_wasm_blocked_sources linuxcnc_wasm_blocked_source_count)
if(linuxcnc_wasm_safe_probe_source_count EQUAL 0)
message(FATAL_ERROR "LinuxCNC wasm source manifest has no core entries")
endif()
if(linuxcnc_wasm_blocked_source_count EQUAL 0)
message(FATAL_ERROR "LinuxCNC wasm source manifest has no blocked entries")
endif()
message(STATUS "LinuxCNC wasm-safe core sources: ${linuxcnc_wasm_safe_probe_source_count}")
message(STATUS "LinuxCNC wasm blocked sources: ${linuxcnc_wasm_blocked_source_count}")
set(linuxcnc_wasm_safe_probe_shims
${CMAKE_CURRENT_SOURCE_DIR}/wasm_shims/dlfcn.cc
${CMAKE_CURRENT_SOURCE_DIR}/wasm_shims/emc_status_shim.cc
${CMAKE_CURRENT_SOURCE_DIR}/wasm_shims/gettext_shim.cc
${CMAKE_CURRENT_SOURCE_DIR}/wasm_shims/linuxcnc_runtime_shim.cc
${CMAKE_CURRENT_SOURCE_DIR}/wasm_shims/python_c_api_shim.cc
${CMAKE_CURRENT_SOURCE_DIR}/wasm_shims/rtapi_compat.cc
${CMAKE_CURRENT_SOURCE_DIR}/wasm_shims/tooldata/tooldata_mmap_backend.cc
${CMAKE_CURRENT_SOURCE_DIR}/wasm_shims/tooldata/tooldata_runtime_stubs.cc
${CMAKE_CURRENT_SOURCE_DIR}/wasm_shims/pythonplugin/python_plugin.cc
)
add_library(linuxcnc_rs274_wasm_safe_probe_objects OBJECT
${linuxcnc_wasm_safe_probe_shims}
${linuxcnc_wasm_safe_probe_sources}
)
target_compile_features(linuxcnc_rs274_wasm_safe_probe_objects PUBLIC cxx_std_20)
target_compile_definitions(linuxcnc_rs274_wasm_safe_probe_objects PRIVATE ULAPI)
target_compile_options(linuxcnc_rs274_wasm_safe_probe_objects PRIVATE -include wctype.h)
target_include_directories(linuxcnc_rs274_wasm_safe_probe_objects
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/wasm_shims
${CNC_SIM_LINUXCNC_ROOT_ABS}/src
${CNC_SIM_LINUXCNC_ROOT_ABS}/src/emc
${CNC_SIM_LINUXCNC_ROOT_ABS}/src/emc/nml_intf
${CNC_SIM_LINUXCNC_ROOT_ABS}/src/emc/rs274ngc
${CNC_SIM_LINUXCNC_ROOT_ABS}/src/emc/motion
${CNC_SIM_LINUXCNC_ROOT_ABS}/include
)
set(linuxcnc_rs274_wasm_safe_probe_project_sources
${CMAKE_CURRENT_SOURCE_DIR}/src/canon_event_sink.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_canon_bridge.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_tooldata_fixture.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/rtcp_kinematics.cpp
)
add_executable(linuxcnc_rs274_wasm_safe_probe EXCLUDE_FROM_ALL
$<TARGET_OBJECTS:linuxcnc_rs274_wasm_safe_probe_objects>
${linuxcnc_rs274_wasm_safe_probe_project_sources}
${CMAKE_CURRENT_SOURCE_DIR}/wasm_shims/rs274ngc_pre_probe_main.cc
)
target_compile_features(linuxcnc_rs274_wasm_safe_probe PRIVATE cxx_std_20)
target_compile_definitions(linuxcnc_rs274_wasm_safe_probe PRIVATE ULAPI)
target_compile_options(linuxcnc_rs274_wasm_safe_probe PRIVATE -include wctype.h)
target_include_directories(linuxcnc_rs274_wasm_safe_probe
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/wasm_shims
${CNC_SIM_LINUXCNC_ROOT_ABS}/src
${CNC_SIM_LINUXCNC_ROOT_ABS}/src/emc
${CNC_SIM_LINUXCNC_ROOT_ABS}/src/emc/nml_intf
${CNC_SIM_LINUXCNC_ROOT_ABS}/src/emc/rs274ngc
${CNC_SIM_LINUXCNC_ROOT_ABS}/src/emc/motion
${CNC_SIM_LINUXCNC_ROOT_ABS}/include
)
target_link_libraries(linuxcnc_rs274_wasm_safe_probe PRIVATE fmt)
if(linuxcnc_wasm_blocked_sources)
add_custom_target(linuxcnc_rs274_wasm_blocked_sources
SOURCES ${linuxcnc_wasm_blocked_sources}
)
endif()
endif()
add_library(cnc_sim_objects OBJECT
${cnc_sim_core_sources}
)

View File

@@ -39,6 +39,8 @@ void CanonEventSink::reset() {
feed_hold_ = false;
mist_on_ = false;
flood_on_ = false;
has_pending_motion_start_ = false;
pending_motion_start_ = {};
}
void CanonEventSink::set_callback(CncSimEventCallback callback, void *user_data) {
@@ -55,6 +57,10 @@ bool CanonEventSink::callback_aborted() const {
return callback_status_ != 0;
}
void CanonEventSink::set_block_delete(bool enabled) {
block_delete_ = enabled;
}
void CanonEventSink::configure_rtcp(bool enabled, double tool_length) {
rtcp_enabled_ = enabled;
default_rtcp_tool_length_ = tool_length;
@@ -80,6 +86,10 @@ void CanonEventSink::set_tool_length_offset(const CncSimPose &offset) {
}
void CanonEventSink::apply_tool_length_offset(const CncSimPose &offset) {
if (!has_pending_motion_start_) {
pending_motion_start_ = position_;
has_pending_motion_start_ = true;
}
double dx = tool_length_offset_.x - offset.x;
double dy = tool_length_offset_.y - offset.y;
rotate_xy(&dx, &dy, -xy_rotation_degrees_);
@@ -305,41 +315,45 @@ void CanonEventSink::change_tool(int line) {
void CanonEventSink::straight_traverse(int line, const CncSimPose &end) {
CncSimEvent event = base_event(CNC_SIM_EVENT_RAPID, line);
event.start = position_;
event.start = has_pending_motion_start_ ? pending_motion_start_ : position_;
event.end = end;
emit(event);
emit_rtcp_pivot(line, position_, end);
emit_rtcp_pivot(line, event.start, end);
has_pending_motion_start_ = false;
position_ = end;
}
void CanonEventSink::straight_feed(int line, const CncSimPose &end) {
CncSimEvent event = base_event(CNC_SIM_EVENT_LINEAR_FEED, line);
event.start = position_;
event.start = has_pending_motion_start_ ? pending_motion_start_ : position_;
event.end = end;
emit(event);
emit_rtcp_pivot(line, position_, end);
emit_rtcp_pivot(line, event.start, end);
has_pending_motion_start_ = false;
position_ = end;
}
void CanonEventSink::arc_feed(int line, const CncSimPose &end, const CncSimPose &center, int turns) {
CncSimEvent event = base_event(CNC_SIM_EVENT_ARC_FEED, line);
event.start = position_;
event.start = has_pending_motion_start_ ? pending_motion_start_ : position_;
event.end = end;
event.center = center;
event.arc_turns = turns;
emit(event);
emit_rtcp_pivot(line, position_, end);
emit_rtcp_pivot(line, event.start, end);
has_pending_motion_start_ = false;
position_ = end;
}
void CanonEventSink::straight_probe(int line, const CncSimPose &end, int probe_type) {
CncSimEvent event = base_event(CNC_SIM_EVENT_PROBE, line);
event.start = position_;
event.start = has_pending_motion_start_ ? pending_motion_start_ : position_;
event.end = end;
event.reserved = probe_type;
emit(event);
probe_position_ = end;
probe_tripped_ = (probe_type & 2) == 0;
has_pending_motion_start_ = false;
position_ = end;
}
@@ -446,6 +460,10 @@ bool CanonEventSink::flood_on() const {
return flood_on_;
}
bool CanonEventSink::block_delete() const {
return block_delete_;
}
void CanonEventSink::set_mist_on(bool enabled) {
mist_on_ = enabled;
}

View File

@@ -10,6 +10,7 @@ public:
void set_callback(CncSimEventCallback callback, void *user_data);
void clear_callback_status();
bool callback_aborted() const;
void set_block_delete(bool enabled);
void configure_rtcp(bool enabled, double tool_length);
void set_tool_length(int h_code, double tool_length);
void set_tool_length_offset(const CncSimPose &offset);
@@ -70,6 +71,7 @@ public:
bool feed_hold() const;
bool mist_on() const;
bool flood_on() const;
bool block_delete() const;
void set_mist_on(bool enabled);
void set_flood_on(bool enabled);
@@ -104,5 +106,8 @@ private:
bool feed_hold_ = false;
bool mist_on_ = false;
bool flood_on_ = false;
bool block_delete_ = false;
std::unordered_map<int, double> tool_lengths_;
bool has_pending_motion_start_ = false;
CncSimPose pending_motion_start_{};
};

View File

@@ -152,6 +152,11 @@ int apply_config(CncSimHandle *handle, const std::string &json) {
if (has_rtcp_enabled || has_tool_length) {
handle->sink.configure_rtcp(rtcp_enabled, tool_length);
}
bool block_delete = false;
if (contains_bool_value(compact, "blockdelete", &block_delete) ||
contains_bool_value(compact, "block_delete", &block_delete)) {
handle->sink.set_block_delete(block_delete);
}
apply_tool_length_table(handle->sink, compact);
return 0;
}

View File

@@ -77,8 +77,12 @@ bool write_temp_program(const char *program,
size_t program_len,
std::string *path,
std::string *error) {
char tmpl[] = "/tmp/cnc_sim_api_XXXXXX.ngc";
const int fd = mkstemps(tmpl, 4);
const char *tmpdir = std::getenv("TMPDIR");
std::string tmpl = std::string(tmpdir ? tmpdir : "/tmp") + "/cnc_sim_api_XXXXXX.ngc";
std::vector<char> tmpl_buffer(tmpl.begin(), tmpl.end());
tmpl_buffer.push_back('\0');
const int fd = mkstemps(tmpl_buffer.data(), 4);
if (fd < 0) {
if (error) {
*error = "failed to create temporary ngc file";
@@ -100,13 +104,13 @@ bool write_temp_program(const char *program,
ok = false;
}
if (!ok) {
unlink(tmpl);
unlink(tmpl_buffer.data());
if (error) {
*error = "failed to write temporary ngc file";
}
return false;
}
*path = tmpl;
*path = tmpl_buffer.data();
return true;
}
@@ -185,6 +189,7 @@ int parse_linuxcnc_rs274_backend(CanonEventSink &sink,
cnc_sim_linuxcnc_set_canon_sink(nullptr);
return -1;
}
SET_BLOCK_DELETE(sink.block_delete());
if (file_mode_enabled()) {
std::string temp_path;

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,7 @@
#include "canon_event_sink.h"
#include <cstddef>
#include <unordered_map>
#include <string>
class SmokeGcodeParser {
@@ -12,6 +13,14 @@ public:
int parse(const char *program, size_t program_len, std::string *error);
private:
struct SmokeToolTableEntry {
CncSimPose offset{};
double diameter = 0.0;
double frontangle = 0.0;
double backangle = 0.0;
double orientation = 0.0;
};
CanonEventSink &sink_;
bool absolute_ = true;
bool arc_absolute_ = false;
@@ -55,4 +64,5 @@ private:
double oword_return_value_ = 0.0;
bool oword_value_returned_ = false;
int selected_tool_id_ = 0;
std::unordered_map<int, SmokeToolTableEntry> tool_table_;
};

View File

@@ -1200,6 +1200,47 @@ int main() {
ok &= expect(saw_g52_restore, "expected LinuxCNC G52/G92 shared offset to restore with G92.3");
ok &= expect(saw_g52_end_line, "expected LinuxCNC G52 program end line number");
const char block_delete_program[] =
"G21 G90 G17 F100\n"
" /N10 G1 X5\n"
"G1 X2\n"
"M30\n";
const char block_delete_on_config[] = "{\"backend\":\"linuxcnc-rs274\",\"blockDelete\":true}";
events.clear();
ok &= expect(cnc_sim_load_config_json(sim, block_delete_on_config, sizeof(block_delete_on_config) - 1) == 0,
cnc_sim_last_error(sim));
ok &= expect(cnc_sim_parse_program(sim, block_delete_program, sizeof(block_delete_program) - 1) == 0,
cnc_sim_last_error(sim));
bool saw_block_delete_skipped_line = false;
bool saw_block_delete_followup_line = false;
for (const auto &event : events) {
saw_block_delete_skipped_line = saw_block_delete_skipped_line ||
(event.type == CNC_SIM_EVENT_LINEAR_FEED &&
event.line == 2 &&
event.end.x == 5.0);
saw_block_delete_followup_line = saw_block_delete_followup_line ||
(event.type == CNC_SIM_EVENT_LINEAR_FEED &&
event.line == 3 &&
event.end.x == 2.0);
}
ok &= expect(!saw_block_delete_skipped_line, "expected LinuxCNC block-delete line to be skipped");
ok &= expect(saw_block_delete_followup_line, "expected LinuxCNC block-delete followup line to execute");
const char block_delete_off_config[] = "{\"backend\":\"linuxcnc-rs274\",\"blockDelete\":false}";
events.clear();
ok &= expect(cnc_sim_load_config_json(sim, block_delete_off_config, sizeof(block_delete_off_config) - 1) == 0,
cnc_sim_last_error(sim));
ok &= expect(cnc_sim_parse_program(sim, block_delete_program, sizeof(block_delete_program) - 1) == 0,
cnc_sim_last_error(sim));
bool saw_block_delete_disabled_line = false;
for (const auto &event : events) {
saw_block_delete_disabled_line = saw_block_delete_disabled_line ||
(event.type == CNC_SIM_EVENT_LINEAR_FEED &&
event.line == 2 &&
event.end.x == 5.0);
}
ok &= expect(saw_block_delete_disabled_line, "expected LinuxCNC disabled block-delete line to execute");
cnc_sim_destroy(sim);
int abort_count = 0;

File diff suppressed because it is too large Load Diff

View File

@@ -128,6 +128,11 @@ bool trace_enabled() {
return std::getenv("CNC_SIM_TRACE_RS274") != nullptr;
}
bool block_delete_enabled() {
const char *value = std::getenv("CNC_SIM_BLOCK_DELETE");
return value && std::string(value) != "0" && std::string(value) != "false";
}
bool execute_line(InterpBase *interp,
CanonEventSink &sink,
const std::string &line,
@@ -251,6 +256,7 @@ int main(int argc, char **argv) {
cnc_sim_init_minimal_linuxcnc_tooldata();
CanonEventSink sink;
sink.set_block_delete(block_delete_enabled());
bool first = true;
sink.set_callback(print_event, &first);
cnc_sim_linuxcnc_set_canon_sink(&sink);
@@ -271,6 +277,7 @@ int main(int argc, char **argv) {
if (trace_enabled()) {
std::cerr << "rs274:init done\n";
}
SET_BLOCK_DELETE(sink.block_delete());
bool ok = true;
if (use_file_mode) {

View File

@@ -0,0 +1,4 @@
#pragma once
#include "boost/python/object.hpp"

View File

@@ -0,0 +1,4 @@
#pragma once
#include "boost/python/object.hpp"

View File

@@ -0,0 +1,4 @@
#pragma once
#include "boost/python/object.hpp"

View File

@@ -0,0 +1,4 @@
#pragma once
#include "boost/python/object.hpp"

View File

@@ -0,0 +1,211 @@
#pragma once
#include <string>
extern "C" {
struct _typeobject {
const char *tp_name;
};
using PyTypeObject = _typeobject;
struct _object {
PyTypeObject *ob_type;
};
using PyObject = _object;
struct _inittab {
const char *name;
PyObject *(*initfunc)(void);
};
int Py_IsInitialized(void);
PyObject *PyErr_Occurred(void);
void PyErr_Clear(void);
int PyErr_ExceptionMatches(PyObject *);
int PyCallable_Check(PyObject *);
void PyErr_Fetch(PyObject **, PyObject **, PyObject **);
void PyErr_NormalizeException(PyObject **, PyObject **, PyObject **);
void PyErr_Print(void);
int PyUnicode_Check(PyObject *);
int PyLong_Check(PyObject *);
int PyFloat_Check(PyObject *);
int PyGen_Check(PyObject *);
PyObject *PyObject_Str(PyObject *);
const char *PyUnicode_AsUTF8(PyObject *);
void Py_DecRef(PyObject *);
extern PyObject *PyExc_KeyError;
extern PyObject *PyExc_StopIteration;
extern PyObject *_Py_NoneStructPtr;
#define Py_None (_Py_NoneStructPtr)
#define Py_TYPE(ob) ((ob)->ob_type)
#define Py_XDECREF(ob) \
do { \
if (ob) { \
Py_DecRef(ob); \
} \
} while (0)
}
namespace boost {
template <typename T>
const T &cref(const T &value) {
return value;
}
namespace python {
class error_already_set {};
class object {
public:
object() = default;
template <typename T>
object(const T &) {
}
~object() = default;
class attr_proxy {
public:
template <typename T>
attr_proxy &operator=(const T &) {
return *this;
}
operator object() const {
return object();
}
};
attr_proxy attr(const char *) const {
return {};
}
object operator[](const char *) const {
return {};
}
object operator[](const std::string &) const {
return {};
}
object operator[](int) const {
return {};
}
PyObject *ptr() const {
return nullptr;
}
template <typename... Args>
object operator()(Args &&...) const {
return {};
}
};
inline object getattr(const object &, const char *) {
return {};
}
class list : public object {
public:
template <typename T>
void append(const T &) {
}
};
class dict : public object {
public:
list keys() const {
return {};
}
};
class tuple : public object {
public:
tuple() = default;
tuple(const list &) {
}
};
class scope {
public:
explicit scope(const object &) {
}
object::attr_proxy attr(const char *) const {
return {};
}
};
template <typename T>
struct extract {
explicit extract(const object &) {
}
operator T() const {
return T();
}
};
inline object import(const char *) {
return {};
}
inline int len(const list &) {
return 0;
}
inline void handle_exception() {
}
template <typename T>
struct borrowed_result {
explicit borrowed_result(T *) {
}
};
template <typename T>
struct allow_null_result {
explicit allow_null_result(T *) {
}
};
template <typename T>
borrowed_result<T> borrowed(T *value) {
return borrowed_result<T>(value);
}
template <typename T>
allow_null_result<T> allow_null(T *value) {
return allow_null_result<T>(value);
}
template <typename T = void>
class handle {
public:
template <typename U>
explicit handle(const U &) {
}
explicit operator bool() const {
return false;
}
};
class str : public object {
public:
template <typename T>
explicit str(const T &) {
}
object join(const object &) const {
return {};
}
};
} // namespace python
} // namespace boost

View File

@@ -0,0 +1,10 @@
#pragma once
namespace boost {
namespace python {
class object;
} // namespace python
} // namespace boost

View File

@@ -0,0 +1,4 @@
#pragma once
#include "boost/python/object.hpp"

View File

@@ -0,0 +1,3 @@
#pragma once
#include "boost/python/object.hpp"

View File

@@ -0,0 +1,3 @@
#pragma once
#include "boost/python/object.hpp"

30
core/wasm_shims/dlfcn.cc Normal file
View File

@@ -0,0 +1,30 @@
#include "dlfcn.h"
#include <cstring>
namespace {
thread_local char last_error[128] = "dynamic loading is unavailable in wasm-safe probe";
} // namespace
extern "C" {
void *dlopen(const char *, int) {
return nullptr;
}
void *dlsym(void *, const char *) {
return nullptr;
}
char *dlerror(void) {
return last_error;
}
int dlclose(void *) {
return 0;
}
} // extern "C"

18
core/wasm_shims/dlfcn.h Normal file
View File

@@ -0,0 +1,18 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#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);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,4 @@
#include "nml_intf/emc_nml.hh"
EMC_STAT *emcStatus = nullptr;

View File

@@ -0,0 +1,6 @@
#include <cstddef>
extern "C" char *gettext(const char *msgid) {
return const_cast<char *>(msgid ? msgid : "");
}

View File

@@ -0,0 +1,7 @@
#include "interp_base.hh"
int main() {
InterpBase *interp = interp_from_shlib("nonexistent-interpreter.so");
return interp == nullptr ? 0 : 1;
}

View File

@@ -0,0 +1,40 @@
#include "tooldata/tooldata.hh"
#include "rs274ngc/rs274ngc_interp.hh"
int main() {
if (tool_mmap_creator(nullptr, 1) != 0) {
return 7;
}
tooldata_reset();
CANON_TOOL_TABLE spindle = tooldata_entry_init();
spindle.toolno = 0;
spindle.pocketno = 0;
if (tooldata_put(spindle, 0) != IDX_OK) {
return 1;
}
CANON_TOOL_TABLE tool = tooldata_entry_init();
tool.toolno = 7;
tool.pocketno = 12;
if (tooldata_put(tool, 12) != IDX_NEW) {
return 2;
}
int idx = -1;
int pocket = -1;
auto *interp = reinterpret_cast<Interp *>(0x1);
if (interp->find_tool_index(nullptr, 7, &idx) != INTERP_OK) {
return 3;
}
if (idx != 12) {
return 4;
}
if (interp->find_tool_pocket(nullptr, 7, &pocket) != INTERP_OK) {
return 5;
}
if (pocket != 12) {
return 6;
}
return 0;
}

View File

@@ -0,0 +1,7 @@
#include "rs274ngc/rs274ngc_interp.hh"
#include <cstdarg>
void Interp::setError(const char *, ...) {
}

View File

@@ -0,0 +1,72 @@
#include "boost/python/object.hpp"
#include "hal.h"
int _task = 0;
extern "C" PyObject *PyInit_interpreter(void) {
return nullptr;
}
extern "C" PyObject *PyInit_emccanon(void) {
return nullptr;
}
extern "C" {
struct _inittab builtin_modules[] = {
{"interpreter", PyInit_interpreter},
{"emccanon", PyInit_emccanon},
{nullptr, nullptr},
};
}
extern "C" int hal_init(const char *) {
return 1;
}
extern "C" int hal_ready(int) {
return 0;
}
extern "C" int hal_get_pin_value_by_name(const char *,
hal_type_t *type,
hal_data_u **data,
bool *connected) {
if (type) {
*type = HAL_TYPE_UNINITIALIZED;
}
if (data) {
*data = nullptr;
}
if (connected) {
*connected = false;
}
return -1;
}
extern "C" int hal_get_signal_value_by_name(const char *,
hal_type_t *type,
hal_data_u **data,
bool *has_writers) {
if (type) {
*type = HAL_TYPE_UNINITIALIZED;
}
if (data) {
*data = nullptr;
}
if (has_writers) {
*has_writers = false;
}
return -1;
}
extern "C" int hal_get_param_value_by_name(const char *,
hal_type_t *type,
hal_data_u **data) {
if (type) {
*type = HAL_TYPE_UNINITIALIZED;
}
if (data) {
*data = nullptr;
}
return -1;
}

View File

@@ -0,0 +1,76 @@
#include "boost/python/object.hpp"
extern "C" {
static PyTypeObject none_type = {"NoneType"};
static PyObject none_object = {&none_type};
PyObject *PyExc_KeyError = reinterpret_cast<PyObject *>(1);
PyObject *PyExc_StopIteration = reinterpret_cast<PyObject *>(2);
PyObject *_Py_NoneStructPtr = &none_object;
int Py_IsInitialized(void) {
return 1;
}
PyObject *PyErr_Occurred(void) {
return nullptr;
}
void PyErr_Clear(void) {
}
int PyErr_ExceptionMatches(PyObject *) {
return 0;
}
int PyCallable_Check(PyObject *) {
return 0;
}
void PyErr_Fetch(PyObject **exc, PyObject **val, PyObject **tb) {
if (exc) {
*exc = nullptr;
}
if (val) {
*val = nullptr;
}
if (tb) {
*tb = nullptr;
}
}
void PyErr_NormalizeException(PyObject **, PyObject **, PyObject **) {
}
void PyErr_Print(void) {
}
int PyUnicode_Check(PyObject *) {
return 0;
}
int PyLong_Check(PyObject *) {
return 0;
}
int PyFloat_Check(PyObject *) {
return 0;
}
int PyGen_Check(PyObject *) {
return 0;
}
PyObject *PyObject_Str(PyObject *) {
return &none_object;
}
const char *PyUnicode_AsUTF8(PyObject *) {
return "";
}
void Py_DecRef(PyObject *) {
}
}

View File

@@ -0,0 +1,50 @@
#include "pythonplugin/python_plugin.hh"
__attribute__((weak)) std::string handle_pyerror() {
return "python disabled in wasm-safe probe";
}
PythonPlugin *python_plugin = nullptr;
PythonPlugin *PythonPlugin::instantiate(struct _inittab *) {
static PythonPlugin plugin;
python_plugin = &plugin;
python_plugin->status = PLUGIN_OK;
return python_plugin;
}
int PythonPlugin::configure(const char *, const char *) {
status = PLUGIN_OK;
return status;
}
bool PythonPlugin::is_callable(const char *, const char *) {
return false;
}
int PythonPlugin::call(const char *,
const char *,
boost::python::object,
boost::python::object,
boost::python::object &retval) {
retval = boost::python::object();
status = PLUGIN_NO_CALLABLE;
return status;
}
int PythonPlugin::run_string(const char *, boost::python::object &retval, bool) {
retval = boost::python::object();
status = PLUGIN_OK;
return status;
}
int PythonPlugin::call_method(boost::python::object, boost::python::object &retval) {
retval = boost::python::object();
status = PLUGIN_OK;
return status;
}
int PythonPlugin::initialize() {
status = PLUGIN_OK;
return status;
}

View File

@@ -0,0 +1,60 @@
#pragma once
#include <string>
#include <vector>
#include <sys/types.h>
#include "boost/python/object.hpp"
extern std::string handle_pyerror();
enum pp_status {
PLUGIN_NO_SECTION = -1,
PLUGIN_NO_INIFILE = -2,
PLUGIN_BAD_INIFILE = -3,
PLUGIN_NO_TOPLEVEL = -4,
PLUGIN_BAD_PATH = -5,
PLUGIN_STAT_FAILED = -6,
PLUGIN_INITTAB_FAILED = -7,
PLUGIN_PYTHON_ALREADY_INITIALIZED = -8,
PLUGIN_EXCEPTION_DURING_PATH_PREPEND = -9,
PLUGIN_EXCEPTION_DURING_PATH_APPEND = -10,
PLUGIN_INIT_EXCEPTION = -11,
PLUGIN_PYTHON_NOT_INITIALIZED = -12,
PLUGIN_PATH_TOO_LONG = -13,
PLUGIN_OK = 0,
PLUGIN_NO_CALLABLE = 1,
PLUGIN_EXCEPTION = 2
};
class PythonPlugin {
public:
static PythonPlugin *instantiate(struct _inittab *inittab = nullptr);
int configure(const char *iniFilename = nullptr, const char *section = nullptr);
bool is_callable(const char *module, const char *funcname);
int call(const char *module,
const char *callable,
boost::python::object tupleargs,
boost::python::object kwargs,
boost::python::object &retval);
int run_string(const char *cmd, boost::python::object &retval, bool as_file = false);
int call_method(boost::python::object method, boost::python::object &retval);
int plugin_status() { return status; }
bool usable() { return status >= PLUGIN_OK; }
int initialize();
const std::string &last_exception() { return exception_msg; }
const std::string &last_errmsg() { return error_msg; }
boost::python::object main_namespace;
private:
PythonPlugin() = default;
int status = PLUGIN_OK;
std::string exception_msg;
std::string error_msg;
int log_level = 0;
};
extern PythonPlugin *python_plugin;

View File

@@ -0,0 +1,23 @@
#include "pythonplugin/python_plugin.hh"
int main() {
auto *plugin = PythonPlugin::instantiate(nullptr);
if (!plugin) {
return 1;
}
if (!plugin->usable()) {
return 2;
}
boost::python::object retval;
if (plugin->run_string("noop", retval, false) != PLUGIN_OK) {
return 3;
}
if (plugin->is_callable(nullptr, "noop")) {
return 4;
}
if (handle_pyerror().empty()) {
return 5;
}
return 0;
}

View File

@@ -0,0 +1,33 @@
#include "canon_event_sink.h"
#include "linuxcnc_canon_bridge.h"
#include "linuxcnc_tooldata_fixture.h"
#include "rs274ngc_interp.hh"
#include <cmath>
int main() {
cnc_sim_init_minimal_linuxcnc_tooldata();
CanonEventSink sink;
cnc_sim_linuxcnc_set_canon_sink(&sink);
Interp interp;
if (interp.init() != INTERP_OK) {
return 1;
}
if (interp.sequence_number() != 0) {
return 2;
}
const double start_x = sink.position().x;
cnc_sim_linuxcnc_set_current_line(1);
const int read_rc = interp.read("G91 G0 X1");
if (read_rc != INTERP_OK) {
return 3;
}
const int execute_rc = interp.execute();
if (execute_rc != INTERP_OK && execute_rc != INTERP_EXECUTE_FINISH) {
return 4;
}
return std::fabs(sink.position().x - (start_x + 1.0)) < 1e-9 ? 0 : 5;
}

View File

@@ -0,0 +1,11 @@
#include <cstdarg>
#include <cstdio>
extern "C" int rtapi_snprintf(char *buf, unsigned long size, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
const int rc = std::vsnprintf(buf, static_cast<std::size_t>(size), fmt, ap);
va_end(ap);
return rc;
}

View File

@@ -0,0 +1,58 @@
#pragma once
#include "nml_intf/canon.hh"
#include "nml_intf/emc_nml.hh"
extern "C" {
typedef enum {
IDX_OK = 0,
IDX_NEW,
IDX_FAIL,
} toolidx_t;
typedef enum {
DB_NOTUSED = 0,
DB_ACTIVE,
} tooldb_t;
typedef enum {
SPINDLE_LOAD,
SPINDLE_UNLOAD,
TOOL_OFFSET,
} tool_notify_t;
struct CANON_TOOL_TABLE tooldata_entry_init(void);
toolidx_t tooldata_put(struct CANON_TOOL_TABLE tdata, int idx);
toolidx_t tooldata_get(CANON_TOOL_TABLE *pdata, int idx);
void tooldata_init(bool random_tool_changer);
void tooldata_reset(void);
void tooldata_last_index_set(int idx);
int tooldata_last_index_get(void);
int tooldata_find_index_for_tool(int toolno);
void tooldata_format_toolline(int idx,
bool ignore_zero_values,
CANON_TOOL_TABLE tdata,
char formatted_line[CANON_TOOL_ENTRY_LEN]);
void tooldata_add_init(int nonrandom_start_idx);
int tooldata_read_entry(const char *input_line);
void tooldata_set_db(tooldb_t mode);
int tooldata_load(const char *filename);
int tooldata_save(const char *filename);
int tool_mmap_creator(EMC_TOOL_STAT const *ptr, int random_toolchanger);
int tool_mmap_user(void);
void tool_mmap_close(void);
bool tool_mmap_is_random_toolchanger(void);
int tool_nml_register(CANON_TOOL_TABLE *tblptr);
int tooldata_db_init(char progname_plus_args[], int random_toolchanger);
int tooldata_db_notify(tool_notify_t ntype,
int toolno,
int pocketno,
CANON_TOOL_TABLE tdata);
int tooldata_db_getall(void);
} // extern "C"

View File

@@ -0,0 +1,101 @@
#include "tooldata.hh"
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <unistd.h>
namespace {
std::string make_temp_template(const char *name) {
const char *tmpdir = std::getenv("TMPDIR");
std::string tmpl = std::string(tmpdir ? tmpdir : "/tmp") + "/" + name;
return tmpl;
}
} // namespace
int main() {
if (tool_mmap_user() != -1) {
return 11;
}
if (tool_mmap_creator(nullptr, 1) != 0) {
return 12;
}
if (tool_mmap_user() != 0) {
return 13;
}
if (!tool_mmap_is_random_toolchanger()) {
return 14;
}
tooldata_init(true);
tooldata_reset();
tooldata_add_init(0);
if (tooldata_read_entry("T7 P12 Z3.5 D8 ;probe\n") != 12) {
return 1;
}
CANON_TOOL_TABLE loaded = tooldata_entry_init();
if (tooldata_get(&loaded, 12) != IDX_OK) {
return 2;
}
if (loaded.toolno != 7 || loaded.pocketno != 12 ||
loaded.offset.tran.z != 3.5 || loaded.diameter != 8.0 ||
std::strcmp(loaded.comment, "probe") != 0) {
return 3;
}
char line[CANON_TOOL_ENTRY_LEN] = {};
tooldata_format_toolline(12, true, loaded, line);
if (!std::strstr(line, "T7") || !std::strstr(line, "P12") ||
!std::strstr(line, "D+8.000000") || !std::strstr(line, ";probe")) {
return 4;
}
std::string path_template = make_temp_template("cnc_tooldata_common_probe_XXXXXX");
std::vector<char> path(path_template.begin(), path_template.end());
path.push_back('\0');
const int fd = mkstemp(path.data());
if (fd < 0) {
return 5;
}
close(fd);
if (tooldata_save(path.data()) != 0) {
std::remove(path.data());
return 6;
}
tooldata_reset();
if (tooldata_find_index_for_tool(7) != -1) {
std::remove(path.data());
return 7;
}
if (tooldata_load(path.data()) != 0) {
std::remove(path.data());
return 8;
}
std::remove(path.data());
if (tooldata_get(&loaded, 12) != IDX_OK || loaded.toolno != 7 ||
loaded.pocketno != 12 || loaded.offset.tran.z != 3.5 ||
loaded.diameter != 8.0) {
return 9;
}
tooldata_set_db(DB_ACTIVE);
if (tooldata_load(path.data()) != -1) {
return 10;
}
tool_mmap_close();
if (tool_mmap_user() != -1) {
return 15;
}
return 0;
}

View File

@@ -0,0 +1,127 @@
#include "tooldata.hh"
#include <cstdlib>
#include <cstdio>
#include <unistd.h>
namespace {
CANON_TOOL_TABLE tool_table[CANON_POCKETS_MAX];
EMC_TOOL_STAT const *toolstat = nullptr;
int last_index = 0;
bool mmap_created = false;
bool mmap_random_toolchanger = false;
bool mmap_creator_called = false;
} // namespace
extern "C" {
toolidx_t tooldata_put(CANON_TOOL_TABLE tdata, int idx) {
if (!mmap_created) {
return IDX_FAIL;
}
if (idx < 0 || idx >= CANON_POCKETS_MAX) {
return IDX_FAIL;
}
const toolidx_t result = idx > last_index ? IDX_NEW : IDX_OK;
tool_table[idx] = tdata;
if (idx > last_index) {
last_index = idx;
}
if (idx == 0 && toolstat) {
*(CANON_TOOL_TABLE *)(&toolstat->toolTableCurrent) = tdata;
}
return result;
}
toolidx_t tooldata_get(CANON_TOOL_TABLE *pdata, int idx) {
if (!mmap_created) {
std::fprintf(stderr, "%5d tooldata_get() not mmapped BYE\n", getpid());
std::exit(EXIT_FAILURE);
}
if (!pdata || idx < 0 || idx >= CANON_POCKETS_MAX) {
return IDX_FAIL;
}
*pdata = tool_table[idx];
return IDX_OK;
}
void tooldata_reset(void) {
if (!mmap_created) {
return;
}
const CANON_TOOL_TABLE empty = tooldata_entry_init();
for (int idx = 0; idx < CANON_POCKETS_MAX; ++idx) {
tool_table[idx] = empty;
}
}
void tooldata_last_index_set(int idx) {
if (!mmap_created) {
return;
}
if (idx < 0 || idx >= CANON_POCKETS_MAX) {
idx = 0;
}
last_index = idx;
}
int tooldata_last_index_get(void) {
if (!mmap_created) {
return -1;
}
return last_index;
}
int tooldata_find_index_for_tool(int toolno) {
if (!mmap_created || toolno == -1) {
return -1;
}
if (!mmap_random_toolchanger && toolno == 0) {
return 0;
}
int found = -1;
for (int idx = 0; idx <= last_index && idx < CANON_POCKETS_MAX; ++idx) {
if (tool_table[idx].toolno == toolno) {
found = idx;
if (idx != 0) {
break;
}
}
}
return found;
}
int tool_mmap_creator(EMC_TOOL_STAT const *ptr, int random_toolchanger) {
if (mmap_creator_called) {
std::fprintf(stderr, "Error: tool_mmap_creator already called BYE\n");
std::exit(EXIT_FAILURE);
}
toolstat = ptr;
mmap_creator_called = true;
mmap_created = true;
mmap_random_toolchanger = random_toolchanger;
last_index = 0;
return 0;
}
int tool_mmap_user(void) {
return mmap_created ? 0 : -1;
}
void tool_mmap_close(void) {
mmap_created = false;
mmap_random_toolchanger = false;
toolstat = nullptr;
last_index = 0;
}
bool tool_mmap_is_random_toolchanger(void) {
return mmap_random_toolchanger;
}
} // extern "C"

View File

@@ -0,0 +1,340 @@
#include "tooldata/tooldata.hh"
#include <fcntl.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <sys/wait.h>
#include <unistd.h>
#include <vector>
namespace {
std::string make_temp_path(const char *name) {
const char *tmpdir = std::getenv("TMPDIR");
return std::string(tmpdir ? tmpdir : "/tmp") + "/" + name;
}
class ScopedTempWorkdir {
public:
explicit ScopedTempWorkdir(const char *name_template) {
cwd_fd_ = open(".", O_RDONLY);
if (cwd_fd_ < 0) {
return;
}
std::string path_template = make_temp_path(name_template);
dir_buffer_.assign(path_template.begin(), path_template.end());
dir_buffer_.push_back('\0');
dir_ = mkdtemp(dir_buffer_.data());
if (!dir_) {
return;
}
if (chdir(dir_) != 0) {
dir_ = nullptr;
return;
}
ok_ = true;
}
~ScopedTempWorkdir() {
if (cwd_fd_ >= 0) {
if (ok_) {
fchdir(cwd_fd_);
}
close(cwd_fd_);
}
if (dir_) {
rmdir(dir_);
}
}
bool ok() const {
return ok_;
}
private:
int cwd_fd_ = -1;
std::vector<char> dir_buffer_;
char *dir_ = nullptr;
bool ok_ = false;
};
} // namespace
int main() {
const pid_t creator_child = fork();
if (creator_child < 0) {
return 41;
}
if (creator_child == 0) {
if (tool_mmap_creator(nullptr, 1) != 0) {
_exit(42);
}
tool_mmap_creator(nullptr, 0);
_exit(43);
}
int creator_status = 0;
if (waitpid(creator_child, &creator_status, 0) != creator_child) {
return 44;
}
if (!WIFEXITED(creator_status) || WEXITSTATUS(creator_status) == 0) {
return 45;
}
if (tool_mmap_user() != -1) {
return 25;
}
CANON_TOOL_TABLE uncreated_tool = tooldata_entry_init();
if (tooldata_put(uncreated_tool, 0) != IDX_FAIL) {
return 27;
}
const pid_t get_child = fork();
if (get_child < 0) {
return 46;
}
if (get_child == 0) {
tooldata_get(&uncreated_tool, 0);
_exit(47);
}
int get_status = 0;
if (waitpid(get_child, &get_status, 0) != get_child) {
return 48;
}
if (!WIFEXITED(get_status) || WEXITSTATUS(get_status) == 0) {
return 38;
}
if (tooldata_last_index_get() != -1) {
return 40;
}
if (tooldata_db_getall() != -1) {
return 28;
}
std::string missing_db_program_with_arg_string =
make_temp_path("cnc_tooldata_probe_missing_db_program arg");
std::vector<char> missing_db_program_with_arg(
missing_db_program_with_arg_string.begin(),
missing_db_program_with_arg_string.end());
missing_db_program_with_arg.push_back('\0');
if (tooldata_db_init(missing_db_program_with_arg.data(), 1) != -1) {
return 55;
}
const auto separator = missing_db_program_with_arg_string.find(' ');
if (separator == std::string::npos || missing_db_program_with_arg[separator] != 0) {
return 56;
}
std::string missing_db_program_string = make_temp_path("cnc_tooldata_probe_missing_db_program");
std::vector<char> missing_db_program(
missing_db_program_string.begin(),
missing_db_program_string.end());
missing_db_program.push_back('\0');
if (tooldata_db_init(missing_db_program.data(), 1) != -1) {
return 31;
}
char too_many_db_args[] = "/bin/true a b c d e f g h i";
if (tooldata_db_init(too_many_db_args, 1) != -1) {
return 32;
}
char non_db_program[] = "/bin/true";
if (tooldata_db_init(non_db_program, 1) != -1) {
return 33;
}
if (tool_nml_register(nullptr) != -1) {
return 29;
}
alignas(EMC_TOOL_STAT) unsigned char toolstat_storage[sizeof(EMC_TOOL_STAT)] = {};
auto *toolstat = reinterpret_cast<EMC_TOOL_STAT *>(toolstat_storage);
if (tool_mmap_creator(toolstat, 1) != 0) {
return 15;
}
if (tool_mmap_user() != 0) {
return 26;
}
if (!tool_mmap_is_random_toolchanger()) {
return 16;
}
tooldata_set_db(DB_ACTIVE);
const std::string unavailable_db_path = make_temp_path("cnc_tooldata_probe_db_unavailable.tbl");
if (tooldata_load(unavailable_db_path.c_str()) != -1) {
return 30;
}
tooldata_set_db(DB_ACTIVE);
tooldata_init(false);
if (tooldata_save(nullptr) != 0) {
return 34;
}
tooldata_set_db(DB_NOTUSED);
tooldata_init(true);
tooldata_reset();
CANON_TOOL_TABLE spindle = tooldata_entry_init();
spindle.toolno = 0;
spindle.pocketno = 0;
if (tooldata_put(spindle, 0) != IDX_OK) {
return 1;
}
if (toolstat->toolTableCurrent.toolno != 0 || toolstat->toolTableCurrent.pocketno != 0) {
return 49;
}
CANON_TOOL_TABLE tool = tooldata_entry_init();
tool.toolno = 7;
tool.pocketno = 12;
tool.offset.tran.z = 3.5;
tool.diameter = 8.0;
std::strncpy(tool.comment, "probe", CANON_TOOL_COMMENT_SIZE - 1);
if (tooldata_put(tool, 12) != IDX_NEW) {
return 2;
}
CANON_TOOL_TABLE loaded = tooldata_entry_init();
if (tooldata_get(&loaded, 12) != IDX_OK) {
return 3;
}
if (loaded.toolno != 7 || loaded.pocketno != 12 || loaded.offset.tran.z != 3.5 || loaded.diameter != 8.0) {
return 4;
}
char db_spindle_line[CANON_TOOL_ENTRY_LEN] = {};
{
ScopedTempWorkdir db_spindle_workdir("cnc_tooldata_probe_workdir.XXXXXX");
if (!db_spindle_workdir.ok()) {
return 35;
}
std::remove("./db_spindle.tbl");
tooldata_set_db(DB_ACTIVE);
if (tooldata_save(nullptr) != 0) {
return 36;
}
FILE *db_spindle_fp = std::fopen("./db_spindle.tbl", "r");
if (!db_spindle_fp) {
return 37;
}
std::fread(db_spindle_line, 1, sizeof(db_spindle_line) - 1, db_spindle_fp);
std::fclose(db_spindle_fp);
std::remove("./db_spindle.tbl");
tooldata_set_db(DB_NOTUSED);
}
if (!std::strstr(db_spindle_line, "T0") || std::strstr(db_spindle_line, "T7")) {
return 38;
}
if (tooldata_find_index_for_tool(7) != 12) {
return 5;
}
if (tooldata_find_index_for_tool(0) != 0) {
return 6;
}
if (tooldata_find_index_for_tool(-1) != -1) {
return 17;
}
tooldata_init(false);
if (tooldata_find_index_for_tool(0) != 0) {
return 18;
}
tooldata_init(true);
tooldata_last_index_set(12);
if (tooldata_last_index_get() != 12) {
return 7;
}
if (tooldata_put(tool, -1) != IDX_FAIL) {
return 50;
}
if (tooldata_put(tool, CANON_POCKETS_MAX) != IDX_FAIL) {
return 51;
}
if (tooldata_last_index_get() != 12) {
return 52;
}
if (tooldata_get(&loaded, -1) != IDX_FAIL) {
return 53;
}
if (tooldata_get(&loaded, CANON_POCKETS_MAX) != IDX_FAIL) {
return 54;
}
tooldata_last_index_set(CANON_POCKETS_MAX);
if (tooldata_last_index_get() != 0) {
return 24;
}
tooldata_last_index_set(12);
char line[CANON_TOOL_ENTRY_LEN] = {};
tooldata_format_toolline(12, true, loaded, line);
if (!std::strstr(line, "T7") || !std::strstr(line, "P12") || !std::strstr(line, "D+8.000000")) {
return 8;
}
std::string path_template = make_temp_path("cnc_tooldata_probe_XXXXXX");
std::vector<char> path(path_template.begin(), path_template.end());
path.push_back('\0');
const int fd = mkstemp(path.data());
if (fd < 0) {
return 9;
}
close(fd);
tooldata_last_index_set(0);
if (tooldata_save(path.data()) != 0) {
std::remove(path.data());
return 10;
}
tooldata_reset();
if (tooldata_find_index_for_tool(7) != -1) {
std::remove(path.data());
return 11;
}
if (tooldata_load(path.data()) != 0) {
std::remove(path.data());
return 12;
}
std::remove(path.data());
CANON_TOOL_TABLE reloaded = tooldata_entry_init();
if (tooldata_get(&reloaded, 12) != IDX_OK) {
return 13;
}
if (reloaded.toolno != 7 || reloaded.pocketno != 12 ||
reloaded.offset.tran.z != 3.5 || reloaded.diameter != 8.0) {
return 14;
}
tooldata_init(false);
std::string nonrandom_path_template = make_temp_path("cnc_tooldata_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 19;
}
FILE *nonrandom_fp = fdopen(nonrandom_fd, "w");
if (!nonrandom_fp) {
close(nonrandom_fd);
std::remove(nonrandom_path.data());
return 20;
}
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 21;
}
std::remove(nonrandom_path.data());
CANON_TOOL_TABLE nonrandom_spindle = tooldata_entry_init();
if (tooldata_get(&nonrandom_spindle, 0) != IDX_OK) {
return 22;
}
if (nonrandom_spindle.toolno != -1 || nonrandom_spindle.pocketno != 5 ||
nonrandom_spindle.offset.tran.z != 4.25) {
return 23;
}
tool_mmap_close();
if (tool_mmap_user() != -1) {
return 39;
}
return 0;
}

View File

@@ -0,0 +1,47 @@
#include "tooldata.hh"
#include <cstring>
#include <unistd.h>
namespace {
constexpr int MAX_DB_PROGRAM_ARGS = 10;
bool db_live = false;
} // namespace
extern "C" {
int tool_nml_register(CANON_TOOL_TABLE *tblptr) {
return tblptr ? 0 : -1;
}
int tooldata_db_init(char progname_plus_args[], int) {
if (!progname_plus_args) {
return -1;
}
char *saveptr = nullptr;
char *program = strtok_r(progname_plus_args, " ", &saveptr);
int argc = 0;
for (char *token = program; token; token = strtok_r(nullptr, " ", &saveptr)) {
++argc;
if (argc >= MAX_DB_PROGRAM_ARGS) {
return -1;
}
}
if (!program || access(program, X_OK) != 0) {
return -1;
}
return -1;
}
int tooldata_db_notify(tool_notify_t, int, int, CANON_TOOL_TABLE) {
return 0;
}
int tooldata_db_getall(void) {
return db_live ? 0 : -1;
}
} // extern "C"

View File

@@ -11,8 +11,8 @@ The browser calls only `cnc_sim_*` functions from `core/include/cnc_sim_api.h`.
Test:
```bash
g++ -std=c++17 -I core/include core/src/cnc_sim_api.cpp core/tests/cnc_sim_api_smoke.cpp -o /tmp/cnc_sim_api_smoke
/tmp/cnc_sim_api_smoke
g++ -std=c++17 -I core/include core/src/cnc_sim_api.cpp core/tests/cnc_sim_api_smoke.cpp -o ./cnc_sim_api_smoke
./cnc_sim_api_smoke
```
## Step 1: Temporary event parser
@@ -102,8 +102,8 @@ Before porting `rs274ngc` sources to wasm, use the already-built native LinuxCNC
This builds `core/tools/linuxcnc_rs274_dump.cpp`, links LinuxCNC `librs274`, and writes:
- `/tmp/cnc_sim_linuxcnc_basic_motion.json`
- `/tmp/cnc_sim_linuxcnc_basic_mill.json`
- run-local output files under a per-run temporary `build_dir/outputs/`
- example files: `cnc_sim_linuxcnc_basic_motion.json`, `cnc_sim_linuxcnc_basic_mill.json`
This is a native-only stepping stone. Once stable, the same event sink is used by the wasm build.

View File

@@ -43,6 +43,7 @@ These are the first group to keep compiling while we peel away native-only depen
- `interp_remap.cc`
- `interp_setup.cc`
- `interp_write.cc`
- `inifile.cc`
- `modal_state.cc`
- `nurbs_additional_functions.cc`
- `rs274ngc_pre.cc`
@@ -64,7 +65,7 @@ These are not needed for the browser simulator ABI and should not be part of the
- Python/Boost.Python remap and named parameter hooks.
- `dlopen`/`dlsym` interpreter loading in `interp_base.cc`.
- mmap-backed `tooldata_mmap.cc`.
- native mmap/filesystem storage in `tooldata_mmap.cc`.
- persistent parameter file writes.
- dynamic INI/HAL queries.
@@ -92,7 +93,9 @@ The next boundary is linking. Expected link risks:
- `PythonPlugin` and Boost.Python symbols from remap/named parameter paths.
- Python module initialization symbols if the source backend reuses the existing builtin module setup.
- `tooldata_*` implementations, currently mmap-backed in native LinuxCNC and unsuitable for wasm.
- `tooldata_mmap.cc` native mmap/filesystem storage. `tooldata_common.cc` now
remains in the wasm-safe core and links against browser-safe tooldata backend
shims.
- dynamic loader code in `interp_base.cc`.
- parameter file persistence in `rs274ngc_pre.cc`.
@@ -112,13 +115,26 @@ and libraries for the dependencies that have not been replaced yet:
- `libpyplugin` for Python remap and named-parameter hooks.
- `liblinuxcncini` and `liblinuxcnchal` for INI/HAL named parameter paths.
- `liblinuxcnc-uspace-posix` for `rtapi_*` user-space helpers.
- `libtooldata` for the current mmap-backed native tool table.
- `libtooldata` for the native source-link runner. The wasm-safe source probes
instead compile LinuxCNC `tooldata_common.cc` with `tooldata_mmap_backend.cc`
and `tooldata_runtime_stubs.cc`.
This is not wasm-ready yet, but it proves the simulator can own and compile the
RS274 interpreter core sources directly. The next source-port boundary is to
replace each support dependency with browser-safe shims instead of pulling in
the LinuxCNC task, motion, HAL, and Python runtime layers.
The wasm-safe source manifest is `linuxcnc-rs274-wasm-source-files.txt`. The
`rs274ngc_pre` wasm link and undefined-symbol probes read their LinuxCNC source
list from that manifest so `inifile.cc`, `tooldata_common.cc`, and the
interpreter sources stay on one source of truth.
When `CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE=ON`, CMake also exposes
`linuxcnc_rs274_wasm_safe_probe_objects` and the `EXCLUDE_FROM_ALL`
`linuxcnc_rs274_wasm_safe_probe` executable. The executable mirrors the
`rs274ngc_pre` shell link probe and is built only when explicitly requested, so
the normal browser wasm artifact does not accidentally link the probe runtime.
The native runner used by this probe now recognizes simulator-owned control
lines before handing code to LinuxCNC: `M428`, `M429`, `M430`, `G43.4`,
`G43.5`, and `G49`. This keeps the direct runner aligned with the public

View File

@@ -0,0 +1,28 @@
# LinuxCNC rs274 source subset for browser-safe source-link probing.
# Format: group:path:note
core:src/emc/rs274ngc/interp_arc.cc:arc geometry and canonical arc conversion
core:src/emc/rs274ngc/interp_array.cc:block array initialization
core:src/emc/rs274ngc/interp_base.cc:base interpreter interface; compile-safe with wasm dlfcn shim, runtime replacement still required
core:src/emc/rs274ngc/interp_check.cc:error text and block checks
core:src/emc/rs274ngc/interp_convert.cc:block-to-canon conversion
core:src/emc/rs274ngc/interp_cycles.cc:canned cycles
core:src/emc/rs274ngc/interp_execute.cc:execute parsed blocks
core:src/emc/rs274ngc/interp_find.cc:geometry and tool lookup helpers; still depends on tooldata declarations
core:src/emc/rs274ngc/interp_g7x.cc:lathe roughing cycles
core:src/emc/rs274ngc/interp_inspection.cc:inspection helpers
core:src/emc/rs274ngc/interp_internal.cc:internal setup and utility logic
core:src/emc/rs274ngc/interp_inverse.cc:inverse time/feed helpers
core:src/emc/rs274ngc/interp_queue.cc:canonical command queue
core:src/emc/rs274ngc/interp_read.cc:line lexer/parser
core:src/emc/rs274ngc/interp_setup.cc:setup initialization helpers; satisfied by wasm Boost.Python shim
core:src/emc/rs274ngc/interp_write.cc:modal state writers
core:src/emc/rs274ngc/modal_state.cc:state tag modal representation
core:src/emc/rs274ngc/nurbs_additional_functions.cc:NURBS helpers
core:src/emc/rs274ngc/interp_namedparams.cc:Python named-parameter hooks satisfied by wasm Boost.Python/Python C API shims
core:src/emc/rs274ngc/interp_o_word.cc:O-word control flow and directory traversal; compile-safe with wasm shims
core:src/emc/rs274ngc/interp_python.cc:Python remap support satisfied by wasm Python plugin shim
core:src/emc/rs274ngc/interp_remap.cc:Python remap support satisfied by wasm Python plugin shim
core:src/emc/rs274ngc/rs274ngc_pre.cc:Interp implementation covered by rs274ngc_pre link probe
core:src/emc/tooldata/tooldata_common.cc:shared tool table parser/format logic linked with wasm-safe mmap backend
core:src/emc/ini/inifile.cc:INI reader needed by rs274ngc_pre source-link probe
blocked:src/emc/tooldata/tooldata_mmap.cc:native mmap implementation

View File

@@ -16,8 +16,8 @@
5187 0.000000
5188 0.000000
5189 0.000000
5210 0.000000
5211 0.000000
5210 1.000000
5211 -1.000000
5212 0.000000
5213 0.000000
5214 0.000000
@@ -27,8 +27,8 @@
5218 0.000000
5219 0.000000
5220 1.000000
5221 0.000000
5222 0.000000
5221 0.015192
5222 -1.041888
5223 0.000000
5224 0.000000
5225 0.000000
@@ -36,7 +36,7 @@
5227 0.000000
5228 0.000000
5229 0.000000
5230 0.000000
5230 30.000000
5241 0.000000
5242 0.000000
5243 0.000000

119
rs274ngc.var.bak Normal file
View File

@@ -0,0 +1,119 @@
5161 0.000000
5162 0.000000
5163 0.000000
5164 0.000000
5165 0.000000
5166 0.000000
5167 0.000000
5168 0.000000
5169 0.000000
5181 0.000000
5182 0.000000
5183 0.000000
5184 0.000000
5185 0.000000
5186 0.000000
5187 0.000000
5188 0.000000
5189 0.000000
5210 1.000000
5211 -1.000000
5212 0.000000
5213 0.000000
5214 0.000000
5215 0.000000
5216 0.000000
5217 0.000000
5218 0.000000
5219 0.000000
5220 1.000000
5221 0.015192
5222 -1.041888
5223 0.000000
5224 0.000000
5225 0.000000
5226 0.000000
5227 0.000000
5228 0.000000
5229 0.000000
5230 30.000000
5241 0.000000
5242 0.000000
5243 0.000000
5244 0.000000
5245 0.000000
5246 0.000000
5247 0.000000
5248 0.000000
5249 0.000000
5250 0.000000
5261 0.000000
5262 0.000000
5263 0.000000
5264 0.000000
5265 0.000000
5266 0.000000
5267 0.000000
5268 0.000000
5269 0.000000
5270 0.000000
5281 0.000000
5282 0.000000
5283 0.000000
5284 0.000000
5285 0.000000
5286 0.000000
5287 0.000000
5288 0.000000
5289 0.000000
5290 0.000000
5301 0.000000
5302 0.000000
5303 0.000000
5304 0.000000
5305 0.000000
5306 0.000000
5307 0.000000
5308 0.000000
5309 0.000000
5310 0.000000
5321 0.000000
5322 0.000000
5323 0.000000
5324 0.000000
5325 0.000000
5326 0.000000
5327 0.000000
5328 0.000000
5329 0.000000
5330 0.000000
5341 0.000000
5342 0.000000
5343 0.000000
5344 0.000000
5345 0.000000
5346 0.000000
5347 0.000000
5348 0.000000
5349 0.000000
5350 0.000000
5361 0.000000
5362 0.000000
5363 0.000000
5364 0.000000
5365 0.000000
5366 0.000000
5367 0.000000
5368 0.000000
5369 0.000000
5370 0.000000
5381 0.000000
5382 0.000000
5383 0.000000
5384 0.000000
5385 0.000000
5386 0.000000
5387 0.000000
5388 0.000000
5389 0.000000
5390 0.000000

View File

@@ -3,6 +3,61 @@ set -euo pipefail
cd "$(dirname "$0")"
missing_root=/tmp/does-not-exist-linuxcnc
missing_root_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_native_missing_root.XXXXXX.log")
grep -F 'exec 9>"${TMPDIR:-/tmp}/cnc_sim_rs274_source_link.lock"' test-linuxcnc-source-link.sh >/dev/null
grep -F "flock 9" test-linuxcnc-source-link.sh >/dev/null
grep -F 'exec 9>"${TMPDIR:-/tmp}/cnc_sim_linuxcnc_rs274_native.lock"' test-linuxcnc-rs274-native.sh >/dev/null
grep -F "flock 9" test-linuxcnc-rs274-native.sh >/dev/null
for script in \
test-linuxcnc-bridge-native.sh \
test-linuxcnc-rs274-native.sh \
test-linuxcnc-api-native.sh; do
if LINUXCNC_ROOT="$missing_root" "./$script" >"$missing_root_log" 2>&1; then
echo "$script accepted missing LinuxCNC root: $missing_root" >&2
exit 1
fi
if ! grep -F "missing LinuxCNC root: $missing_root" "$missing_root_log" >/dev/null; then
echo "$script did not report missing LinuxCNC root clearly" >&2
sed -n '1,20p' "$missing_root_log" >&2
exit 1
fi
done
rm -f "$missing_root_log"
unknown_group_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_native_unknown_manifest_group.XXXXXX.txt")
unknown_group_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_native_unknown_manifest_group.XXXXXX.log")
printf 'bogus:src/emc/rs274ngc/interp_arc.cc:test unknown group\n' >"$unknown_group_manifest"
for script in \
test-linuxcnc-source-syntax.sh \
test-linuxcnc-source-objects.sh \
test-linuxcnc-source-link.sh; do
if "./$script" "$unknown_group_manifest" >"$unknown_group_log" 2>&1; then
echo "$script accepted unknown manifest group" >&2
exit 1
fi
if ! grep -F "unknown manifest group: bogus" "$unknown_group_log" >/dev/null; then
echo "$script did not report unknown manifest group clearly" >&2
sed -n '1,20p' "$unknown_group_log" >&2
exit 1
fi
done
rm -f "$unknown_group_manifest" "$unknown_group_log"
missing_source_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_native_syntax_missing_source_manifest.XXXXXX.txt")
missing_source_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_native_syntax_missing_source.XXXXXX.log")
printf 'core:src/emc/rs274ngc/does_not_exist.cc:test missing source\n' >"$missing_source_manifest"
if ./test-linuxcnc-source-syntax.sh "$missing_source_manifest" >"$missing_source_log" 2>&1; then
echo "test-linuxcnc-source-syntax.sh accepted missing manifest file" >&2
exit 1
fi
if ! grep -F "missing manifest file: src/emc/rs274ngc/does_not_exist.cc" "$missing_source_log" >/dev/null; then
echo "test-linuxcnc-source-syntax.sh did not report missing manifest file clearly" >&2
sed -n '1,20p' "$missing_source_log" >&2
exit 1
fi
rm -f "$missing_source_manifest" "$missing_source_log"
./test-native.sh
./test-linuxcnc-source-syntax.sh
./test-linuxcnc-source-objects.sh

View File

@@ -5,8 +5,14 @@ cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
build_dir=${TMPDIR:-/tmp}/cnc_sim_native
mkdir -p "$build_dir"
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_native.XXXXXX")
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
trap 'rm -rf "$build_dir"' EXIT
var_file="$build_dir/rs274ngc-api.var"
cp "$linuxcnc_root/tests/halui/jogging/sim.var" "$var_file"

View File

@@ -5,8 +5,14 @@ cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
build_dir=${TMPDIR:-/tmp}/cnc_sim_native
mkdir -p "$build_dir"
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_native.XXXXXX")
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
trap 'rm -rf "$build_dir"' EXIT
"$cxx" -std=c++17 \
-I core/include \

View File

@@ -5,11 +5,23 @@ cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
build_dir=${TMPDIR:-/tmp}/cnc_sim_native
mkdir -p "$build_dir"
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_native.XXXXXX")
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
trap 'rm -rf "$build_dir"' EXIT
# The LinuxCNC rs274 backend path can bus-error when two dumps run concurrently.
exec 9>"${TMPDIR:-/tmp}/cnc_sim_linuxcnc_rs274_native.lock"
flock 9
var_file="$build_dir/rs274ngc.var"
base_var_file="$build_dir/rs274ngc-base.var"
ini_named_parameter_file="$(pwd)/tests/gcode/linuxcnc_ini_named_parameter.ini"
output_dir="$build_dir/outputs"
mkdir -p "$output_dir"
cp "$linuxcnc_root/tests/halui/jogging/sim.var" "$base_var_file"
cp "$base_var_file" "$var_file"
@@ -36,62 +48,65 @@ cp "$base_var_file" "$var_file"
-lpython3.13 \
-o "$build_dir/linuxcnc_rs274_dump"
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_basic_motion.ngc >/tmp/cnc_sim_linuxcnc_basic_motion.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/basic_mill.ngc >/tmp/cnc_sim_linuxcnc_basic_mill.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_rtcp_controls.ngc >/tmp/cnc_sim_linuxcnc_rtcp_controls.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_spindle_direction.ngc >/tmp/cnc_sim_linuxcnc_spindle_direction.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_spindle_modes.ngc >/tmp/cnc_sim_linuxcnc_spindle_modes.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_spindle_orient.ngc >/tmp/cnc_sim_linuxcnc_spindle_orient.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_io_controls.ngc >/tmp/cnc_sim_linuxcnc_io_controls.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_override_controls.ngc >/tmp/cnc_sim_linuxcnc_override_controls.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_modal_state.ngc >/tmp/cnc_sim_linuxcnc_modal_state.json
CNC_SIM_RS274_FILE_MODE=1 CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_modal_autorestore.ngc >/tmp/cnc_sim_linuxcnc_modal_autorestore.json
if CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_modal_invalidate.ngc >/tmp/cnc_sim_linuxcnc_modal_invalidate.json 2>/tmp/cnc_sim_linuxcnc_modal_invalidate.err; then
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_basic_motion.ngc >"$output_dir/cnc_sim_linuxcnc_basic_motion.json"
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/basic_mill.ngc >"$output_dir/cnc_sim_linuxcnc_basic_mill.json"
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_rtcp_controls.ngc >"$output_dir/cnc_sim_linuxcnc_rtcp_controls.json"
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_spindle_direction.ngc >"$output_dir/cnc_sim_linuxcnc_spindle_direction.json"
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_spindle_modes.ngc >"$output_dir/cnc_sim_linuxcnc_spindle_modes.json"
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_spindle_orient.ngc >"$output_dir/cnc_sim_linuxcnc_spindle_orient.json"
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_io_controls.ngc >"$output_dir/cnc_sim_linuxcnc_io_controls.json"
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_override_controls.ngc >"$output_dir/cnc_sim_linuxcnc_override_controls.json"
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_modal_state.ngc >"$output_dir/cnc_sim_linuxcnc_modal_state.json"
CNC_SIM_RS274_FILE_MODE=1 CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_modal_autorestore.ngc >"$output_dir/cnc_sim_linuxcnc_modal_autorestore.json"
if CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_modal_invalidate.ngc >"$output_dir/cnc_sim_linuxcnc_modal_invalidate.json" 2>"$output_dir/cnc_sim_linuxcnc_modal_invalidate.err"; then
echo "expected linuxcnc_modal_invalidate.ngc to fail" >&2
exit 1
fi
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_threading_sync.ngc >/tmp/cnc_sim_linuxcnc_threading_sync.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_threading_cycle.ngc >/tmp/cnc_sim_linuxcnc_threading_cycle.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_tool_number.ngc >/tmp/cnc_sim_linuxcnc_tool_number.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_program_stops.ngc >/tmp/cnc_sim_linuxcnc_program_stops.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_coolant.ngc >/tmp/cnc_sim_linuxcnc_coolant.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_feed_modes.ngc >/tmp/cnc_sim_linuxcnc_feed_modes.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_motion_modes.ngc >/tmp/cnc_sim_linuxcnc_motion_modes.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_tool_length.ngc >/tmp/cnc_sim_linuxcnc_tool_length.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_dynamic_tool_length.ngc >/tmp/cnc_sim_linuxcnc_dynamic_tool_length.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_canned_cycle.ngc >/tmp/cnc_sim_linuxcnc_canned_cycle.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_canned_cycles_extended.ngc >/tmp/cnc_sim_linuxcnc_canned_cycles_extended.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_canned_cycle_planes.ngc >/tmp/cnc_sim_linuxcnc_canned_cycle_planes.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_arc_planes.ngc >/tmp/cnc_sim_linuxcnc_arc_planes.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_arc_distance_modes.ngc >/tmp/cnc_sim_linuxcnc_arc_distance_modes.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_predefined_positions.ngc >/tmp/cnc_sim_linuxcnc_predefined_positions.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_machine_coordinates.ngc >/tmp/cnc_sim_linuxcnc_machine_coordinates.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_cutter_comp.ngc >/tmp/cnc_sim_linuxcnc_cutter_comp.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_comments.ngc >/tmp/cnc_sim_linuxcnc_comments.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_probe_no_error.ngc >/tmp/cnc_sim_linuxcnc_probe_no_error.json
CNC_SIM_RS274_FILE_MODE=1 CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/smoke_oword_subprogram.ngc >/tmp/cnc_sim_linuxcnc_oword_subprogram.json
CNC_SIM_RS274_FILE_MODE=1 CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_named_oword_subprogram.ngc >/tmp/cnc_sim_linuxcnc_named_oword_subprogram.json
CNC_SIM_RS274_FILE_MODE=1 CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_oword_control_flow.ngc >/tmp/cnc_sim_linuxcnc_oword_control_flow.json
CNC_SIM_RS274_FILE_MODE=1 CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_named_parameter_exists.ngc >/tmp/cnc_sim_linuxcnc_named_parameter_exists.json
CNC_SIM_RS274_FILE_MODE=1 CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_indexed_parameters.ngc >/tmp/cnc_sim_linuxcnc_indexed_parameters.json
CNC_SIM_RS274_FILE_MODE=1 CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_named_parameter_normalization.ngc >/tmp/cnc_sim_linuxcnc_named_parameter_normalization.json
INI_FILE_NAME="$ini_named_parameter_file" CNC_SIM_RS274_FILE_MODE=1 CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_ini_named_parameter.ngc >/tmp/cnc_sim_linuxcnc_ini_named_parameter.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_threading_sync.ngc >"$output_dir/cnc_sim_linuxcnc_threading_sync.json"
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_threading_cycle.ngc >"$output_dir/cnc_sim_linuxcnc_threading_cycle.json"
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_tool_number.ngc >"$output_dir/cnc_sim_linuxcnc_tool_number.json"
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_program_stops.ngc >"$output_dir/cnc_sim_linuxcnc_program_stops.json"
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_coolant.ngc >"$output_dir/cnc_sim_linuxcnc_coolant.json"
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_feed_modes.ngc >"$output_dir/cnc_sim_linuxcnc_feed_modes.json"
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_motion_modes.ngc >"$output_dir/cnc_sim_linuxcnc_motion_modes.json"
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_tool_length.ngc >"$output_dir/cnc_sim_linuxcnc_tool_length.json"
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_dynamic_tool_length.ngc >"$output_dir/cnc_sim_linuxcnc_dynamic_tool_length.json"
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_canned_cycle.ngc >"$output_dir/cnc_sim_linuxcnc_canned_cycle.json"
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_canned_cycles_extended.ngc >"$output_dir/cnc_sim_linuxcnc_canned_cycles_extended.json"
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_canned_cycle_planes.ngc >"$output_dir/cnc_sim_linuxcnc_canned_cycle_planes.json"
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_arc_planes.ngc >"$output_dir/cnc_sim_linuxcnc_arc_planes.json"
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_arc_distance_modes.ngc >"$output_dir/cnc_sim_linuxcnc_arc_distance_modes.json"
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_predefined_positions.ngc >"$output_dir/cnc_sim_linuxcnc_predefined_positions.json"
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_machine_coordinates.ngc >"$output_dir/cnc_sim_linuxcnc_machine_coordinates.json"
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_cutter_comp.ngc >"$output_dir/cnc_sim_linuxcnc_cutter_comp.json"
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_comments.ngc >"$output_dir/cnc_sim_linuxcnc_comments.json"
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_probe_no_error.ngc >"$output_dir/cnc_sim_linuxcnc_probe_no_error.json"
CNC_SIM_RS274_FILE_MODE=1 CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/smoke_oword_subprogram.ngc >"$output_dir/cnc_sim_linuxcnc_oword_subprogram.json"
CNC_SIM_RS274_FILE_MODE=1 CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_named_oword_subprogram.ngc >"$output_dir/cnc_sim_linuxcnc_named_oword_subprogram.json"
CNC_SIM_RS274_FILE_MODE=1 CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_oword_control_flow.ngc >"$output_dir/cnc_sim_linuxcnc_oword_control_flow.json"
CNC_SIM_RS274_FILE_MODE=1 CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_named_parameter_exists.ngc >"$output_dir/cnc_sim_linuxcnc_named_parameter_exists.json"
CNC_SIM_RS274_FILE_MODE=1 CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_indexed_parameters.ngc >"$output_dir/cnc_sim_linuxcnc_indexed_parameters.json"
CNC_SIM_RS274_FILE_MODE=1 CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_named_parameter_normalization.ngc >"$output_dir/cnc_sim_linuxcnc_named_parameter_normalization.json"
INI_FILE_NAME="$ini_named_parameter_file" CNC_SIM_RS274_FILE_MODE=1 CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_ini_named_parameter.ngc >"$output_dir/cnc_sim_linuxcnc_ini_named_parameter.json"
cp "$base_var_file" "$var_file"
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_coordinate_offsets.ngc >/tmp/cnc_sim_linuxcnc_coordinate_offsets.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_coordinate_offsets.ngc >"$output_dir/cnc_sim_linuxcnc_coordinate_offsets.json"
cp "$base_var_file" "$var_file"
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_coordinate_l20.ngc >/tmp/cnc_sim_linuxcnc_coordinate_l20.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_coordinate_l20.ngc >"$output_dir/cnc_sim_linuxcnc_coordinate_l20.json"
cp "$base_var_file" "$var_file"
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_coordinate_p0.ngc >/tmp/cnc_sim_linuxcnc_coordinate_p0.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_coordinate_p0.ngc >"$output_dir/cnc_sim_linuxcnc_coordinate_p0.json"
cp "$base_var_file" "$var_file"
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_coordinate_select_all.ngc >/tmp/cnc_sim_linuxcnc_coordinate_select_all.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_coordinate_select_all.ngc >"$output_dir/cnc_sim_linuxcnc_coordinate_select_all.json"
cp "$base_var_file" "$var_file"
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_g92_restore.ngc >/tmp/cnc_sim_linuxcnc_g92_restore.json
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_g92_restore.ngc >"$output_dir/cnc_sim_linuxcnc_g92_restore.json"
cp "$base_var_file" "$var_file"
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_g52_offset.ngc >/tmp/cnc_sim_linuxcnc_g52_offset.json
python3 - <<'PY'
CNC_SIM_RS274_VAR="$var_file" "$build_dir/linuxcnc_rs274_dump" tests/gcode/linuxcnc_g52_offset.ngc >"$output_dir/cnc_sim_linuxcnc_g52_offset.json"
CNC_SIM_RS274_NATIVE_OUTPUT_DIR="$output_dir" python3 - <<'PY'
import json
import os
from pathlib import Path
OUTPUT_DIR = Path(os.environ["CNC_SIM_RS274_NATIVE_OUTPUT_DIR"])
def check(path, required):
events = json.loads(Path(path).read_text())
types = [event["type"] for event in events]
@@ -103,11 +118,14 @@ def check(path, required):
raise SystemExit(f"{path}: unexpected final XY position")
return events
required_motion = {"set-units", "set-plane", "set-spindle", "rapid", "set-feed", "linear-feed", "arc-feed", "dwell", "program-end"}
check("/tmp/cnc_sim_linuxcnc_basic_motion.json", required_motion)
check("/tmp/cnc_sim_linuxcnc_basic_mill.json", required_motion | {"tool-change"})
def load(name):
return json.loads((OUTPUT_DIR / name).read_text())
controls = json.loads(Path("/tmp/cnc_sim_linuxcnc_rtcp_controls.json").read_text())
required_motion = {"set-units", "set-plane", "set-spindle", "rapid", "set-feed", "linear-feed", "arc-feed", "dwell", "program-end"}
check(OUTPUT_DIR / "cnc_sim_linuxcnc_basic_motion.json", required_motion)
check(OUTPUT_DIR / "cnc_sim_linuxcnc_basic_mill.json", required_motion | {"tool-change"})
controls = load("cnc_sim_linuxcnc_rtcp_controls.json")
kin = [event for event in controls if event["type"] == "kinematics-switch"]
if [(event["reserved"], event["feed"]) for event in kin] != [(1, 1), (0, 0), (2, 1)]:
raise SystemExit("unexpected kinematics switch sequence")
@@ -115,7 +133,7 @@ rtcp = [event for event in controls if event["type"] == "rtcp-state"]
if [(event["line"], event["tool"], event["feed"]) for event in rtcp] != [(3, 0, 0), (4, 7, 1)]:
raise SystemExit("unexpected RTCP state sequence")
spindle = json.loads(Path("/tmp/cnc_sim_linuxcnc_spindle_direction.json").read_text())
spindle = load("cnc_sim_linuxcnc_spindle_direction.json")
spindle_states = [
(event["line"], event["spindle"], event["reserved"])
for event in spindle
@@ -128,7 +146,7 @@ if (3, 1200, 2) not in spindle_states:
if (4, 0, 0) not in spindle_states:
raise SystemExit("missing M5 stopped spindle state")
spindle_modes = json.loads(Path("/tmp/cnc_sim_linuxcnc_spindle_modes.json").read_text())
spindle_modes = load("cnc_sim_linuxcnc_spindle_modes.json")
spindle_mode_states = [
(event["line"], event["reserved"], event["feed"], event["tool"])
for event in spindle_modes
@@ -142,7 +160,7 @@ expected_spindle_mode_states = [
if spindle_mode_states != expected_spindle_mode_states:
raise SystemExit(f"unexpected G96/G97 spindle mode states: {spindle_mode_states!r}")
spindle_orient = json.loads(Path("/tmp/cnc_sim_linuxcnc_spindle_orient.json").read_text())
spindle_orient = load("cnc_sim_linuxcnc_spindle_orient.json")
if not any(
event["type"] == "comment" and
event["line"] == 2 and
@@ -163,7 +181,7 @@ if not any(
):
raise SystemExit("missing M19 spindle orient wait state")
io_controls = json.loads(Path("/tmp/cnc_sim_linuxcnc_io_controls.json").read_text())
io_controls = load("cnc_sim_linuxcnc_io_controls.json")
io_states = [
(event["line"], event["reserved"], event["tool"], event["feed"], event["arcTurns"], event["dwellSeconds"])
for event in io_controls
@@ -182,7 +200,7 @@ expected_io_states = [
if io_states != expected_io_states:
raise SystemExit(f"unexpected M62-M68 I/O states: {io_states!r}")
override_controls = json.loads(Path("/tmp/cnc_sim_linuxcnc_override_controls.json").read_text())
override_controls = load("cnc_sim_linuxcnc_override_controls.json")
override_states = [
(event["line"], event["reserved"], event["tool"], event["feed"])
for event in override_controls
@@ -206,7 +224,7 @@ expected_override_states = [
if override_states != expected_override_states:
raise SystemExit(f"unexpected M48-M53 override states: {override_states!r}")
modal_state = json.loads(Path("/tmp/cnc_sim_linuxcnc_modal_state.json").read_text())
modal_state = load("cnc_sim_linuxcnc_modal_state.json")
if not any(
event["type"] == "rapid" and
event["line"] == 7 and
@@ -226,7 +244,7 @@ if not any(
):
raise SystemExit("missing M72 restored XY plane arc")
modal_autorestore = json.loads(Path("/tmp/cnc_sim_linuxcnc_modal_autorestore.json").read_text())
modal_autorestore = load("cnc_sim_linuxcnc_modal_autorestore.json")
if not any(
event["type"] == "rapid" and
event["line"] == 9 and
@@ -243,7 +261,7 @@ if not any(
):
raise SystemExit("missing M73 autorestored incremental caller move")
threading_sync = json.loads(Path("/tmp/cnc_sim_linuxcnc_threading_sync.json").read_text())
threading_sync = load("cnc_sim_linuxcnc_threading_sync.json")
sync_states = [
(event["line"], event["reserved"], event["feed"], event["tool"])
for event in threading_sync
@@ -263,7 +281,7 @@ if not any(
):
raise SystemExit("missing G33.1 rigid tap motion line")
threading_cycle = json.loads(Path("/tmp/cnc_sim_linuxcnc_threading_cycle.json").read_text())
threading_cycle = load("cnc_sim_linuxcnc_threading_cycle.json")
if not any(
event["type"] == "comment" and
event["line"] == 4 and
@@ -282,7 +300,7 @@ if not any(
if not any(event["type"] == "program-end" and event["line"] == 5 for event in threading_cycle):
raise SystemExit("missing G76 program end line number")
tool_number = json.loads(Path("/tmp/cnc_sim_linuxcnc_tool_number.json").read_text())
tool_number = load("cnc_sim_linuxcnc_tool_number.json")
if not any(
event["type"] == "tool-change" and
event["line"] == 2 and
@@ -291,7 +309,7 @@ if not any(
):
raise SystemExit("missing M61 Q1 tool-number event line")
stops = json.loads(Path("/tmp/cnc_sim_linuxcnc_program_stops.json").read_text())
stops = load("cnc_sim_linuxcnc_program_stops.json")
stop_states = [
(event["line"], event["reserved"])
for event in stops
@@ -301,7 +319,7 @@ for expected in [(2, 1), (3, 2), (4, 3), (4, 1), (5, 0)]:
if expected not in stop_states:
raise SystemExit(f"missing program stop state {expected!r}")
coolant = json.loads(Path("/tmp/cnc_sim_linuxcnc_coolant.json").read_text())
coolant = load("cnc_sim_linuxcnc_coolant.json")
coolant_states = [
(event["line"], event["reserved"])
for event in coolant
@@ -311,7 +329,7 @@ for expected in [(2, 11), (3, 21), (4, 10), (4, 20)]:
if expected not in coolant_states:
raise SystemExit(f"missing coolant state {expected!r}")
feed_modes = json.loads(Path("/tmp/cnc_sim_linuxcnc_feed_modes.json").read_text())
feed_modes = load("cnc_sim_linuxcnc_feed_modes.json")
feed_mode_comments = [
(event["line"], event["reserved"])
for event in feed_modes
@@ -329,7 +347,7 @@ for expected in [(3, 0), (4, 0)]:
if expected not in feed_mode_events:
raise SystemExit(f"missing feed mode event {expected!r}")
motion_modes = json.loads(Path("/tmp/cnc_sim_linuxcnc_motion_modes.json").read_text())
motion_modes = load("cnc_sim_linuxcnc_motion_modes.json")
motion_mode_states = [
(event["line"], event["reserved"], event["feed"])
for event in motion_modes
@@ -339,7 +357,7 @@ for expected in [(2, 611, 0), (3, 612, 0), (4, 640, 0.05)]:
if expected not in motion_mode_states:
raise SystemExit(f"missing motion mode state {expected!r}")
tool_length = json.loads(Path("/tmp/cnc_sim_linuxcnc_tool_length.json").read_text())
tool_length = load("cnc_sim_linuxcnc_tool_length.json")
if not any(
event["type"] == "comment" and
event["line"] == 2 and
@@ -357,7 +375,7 @@ if not any(
):
raise SystemExit("missing G49 tool length clear event")
dynamic_tool_length = json.loads(Path("/tmp/cnc_sim_linuxcnc_dynamic_tool_length.json").read_text())
dynamic_tool_length = load("cnc_sim_linuxcnc_dynamic_tool_length.json")
dynamic_offsets = [
(event["line"], event["start"]["x"], event["start"]["z"])
for event in dynamic_tool_length
@@ -367,7 +385,7 @@ expected_dynamic_offsets = [(2, 1, 2), (3, 1.25, 2.5), (4, 1.25, 15), (5, 0, 0)]
if dynamic_offsets != expected_dynamic_offsets:
raise SystemExit(f"unexpected G43.1/G43.2 tool length offsets: {dynamic_offsets!r}")
cycle = json.loads(Path("/tmp/cnc_sim_linuxcnc_canned_cycle.json").read_text())
cycle = load("cnc_sim_linuxcnc_canned_cycle.json")
motions = [event for event in cycle if event["type"] in {"rapid", "linear-feed"}]
expected_cycle = [
("rapid", 0, 0, 5),
@@ -380,7 +398,7 @@ actual_cycle = [(event["type"], event["end"]["x"], event["end"]["y"], event["end
if actual_cycle != expected_cycle:
raise SystemExit(f"unexpected G81/G80 expansion: {actual_cycle!r}")
cycle_planes = json.loads(Path("/tmp/cnc_sim_linuxcnc_canned_cycle_planes.json").read_text())
cycle_planes = load("cnc_sim_linuxcnc_canned_cycle_planes.json")
if not any(
event["type"] == "linear-feed" and
event["line"] == 4 and
@@ -526,7 +544,7 @@ expected_plane_g87_sequence = [
if plane_g87_sequence != expected_plane_g87_sequence:
raise SystemExit(f"unexpected G19 G87 plane sequence: {plane_g87_sequence!r}")
extended_cycle = json.loads(Path("/tmp/cnc_sim_linuxcnc_canned_cycles_extended.json").read_text())
extended_cycle = load("cnc_sim_linuxcnc_canned_cycles_extended.json")
if not any(
event["type"] == "linear-feed" and
event["line"] == 3 and
@@ -653,7 +671,7 @@ expected_g87_sequence = [
if g87_sequence != expected_g87_sequence:
raise SystemExit(f"unexpected G87 back-boring sequence: {g87_sequence!r}")
arc_planes = json.loads(Path("/tmp/cnc_sim_linuxcnc_arc_planes.json").read_text())
arc_planes = load("cnc_sim_linuxcnc_arc_planes.json")
if not any(
event["type"] == "arc-feed" and
event["line"] == 5 and
@@ -683,7 +701,7 @@ if not any(
):
raise SystemExit("missing G19 YZ plane arc-feed mapping")
arc_distance = json.loads(Path("/tmp/cnc_sim_linuxcnc_arc_distance_modes.json").read_text())
arc_distance = load("cnc_sim_linuxcnc_arc_distance_modes.json")
arc_centers = [
(event["line"], event["center"]["x"], event["center"]["y"])
for event in arc_distance
@@ -693,7 +711,7 @@ expected_arc_centers = [(5, 5, 0), (7, 15, 0), (9, 25, 0)]
if arc_centers != expected_arc_centers:
raise SystemExit(f"unexpected G90.1/G91.1 arc centers: {arc_centers!r}")
predefined = json.loads(Path("/tmp/cnc_sim_linuxcnc_predefined_positions.json").read_text())
predefined = load("cnc_sim_linuxcnc_predefined_positions.json")
predefined_rapids = [
(
event["line"],
@@ -712,7 +730,7 @@ expected_predefined_rapids = [
if predefined_rapids != expected_predefined_rapids:
raise SystemExit(f"unexpected G28/G30 rapid sequence: {predefined_rapids!r}")
machine_coords = json.loads(Path("/tmp/cnc_sim_linuxcnc_machine_coordinates.json").read_text())
machine_coords = load("cnc_sim_linuxcnc_machine_coordinates.json")
machine_moves = [
(
event["type"], event["line"],
@@ -730,7 +748,7 @@ expected_machine_moves = [
if machine_moves != expected_machine_moves:
raise SystemExit(f"unexpected G53 machine-coordinate moves: {machine_moves!r}")
cutter_comp = json.loads(Path("/tmp/cnc_sim_linuxcnc_cutter_comp.json").read_text())
cutter_comp = load("cnc_sim_linuxcnc_cutter_comp.json")
cutter_comp_motions = [
event
for event in cutter_comp
@@ -758,11 +776,11 @@ actual_cutter_comp = [
if actual_cutter_comp != expected_cutter_comp:
raise SystemExit(f"unexpected cutter compensation path: {actual_cutter_comp!r}")
comments = json.loads(Path("/tmp/cnc_sim_linuxcnc_comments.json").read_text())
comments = load("cnc_sim_linuxcnc_comments.json")
if not any(event["type"] == "comment" and event["line"] == 2 for event in comments):
raise SystemExit("missing LinuxCNC comment line event")
probe = json.loads(Path("/tmp/cnc_sim_linuxcnc_probe_no_error.json").read_text())
probe = load("cnc_sim_linuxcnc_probe_no_error.json")
probe_events = [
(event["line"], event["reserved"], event["start"]["z"], event["end"]["z"])
for event in probe
@@ -787,7 +805,7 @@ if not any(
):
raise SystemExit("missing #5070 probe-tripped conditional motion")
oword = json.loads(Path("/tmp/cnc_sim_linuxcnc_oword_subprogram.json").read_text())
oword = load("cnc_sim_linuxcnc_oword_subprogram.json")
if not any(
event["type"] == "linear-feed" and
event["line"] == 8 and
@@ -812,7 +830,7 @@ if any(
):
raise SystemExit("LinuxCNC O-word return should skip remaining subprogram body")
named_oword = json.loads(Path("/tmp/cnc_sim_linuxcnc_named_oword_subprogram.json").read_text())
named_oword = load("cnc_sim_linuxcnc_named_oword_subprogram.json")
if not any(
event["type"] == "linear-feed" and
event["line"] == 9 and
@@ -845,7 +863,7 @@ if any(
):
raise SystemExit("LinuxCNC named O-word return should skip remaining subprogram body")
oword_flow = json.loads(Path("/tmp/cnc_sim_linuxcnc_oword_control_flow.json").read_text())
oword_flow = load("cnc_sim_linuxcnc_oword_control_flow.json")
if not any(event["type"] == "linear-feed" and event["line"] == 7 and event["end"]["x"] == 2 for event in oword_flow):
raise SystemExit("missing LinuxCNC O-word elseif branch motion")
if any(
@@ -910,7 +928,7 @@ if not any(
):
raise SystemExit("missing LinuxCNC O-word do/continue skipped-body counter motion")
exists = json.loads(Path("/tmp/cnc_sim_linuxcnc_named_parameter_exists.json").read_text())
exists = load("cnc_sim_linuxcnc_named_parameter_exists.json")
exists_moves = [
(event["line"], event["end"]["x"], event["end"]["y"])
for event in exists
@@ -920,7 +938,7 @@ expected_exists_moves = [(5, 1, 0), (8, 1, 1)]
if exists_moves != expected_exists_moves:
raise SystemExit(f"unexpected LinuxCNC EXISTS named parameter branch motions: {exists_moves!r}")
indexed = json.loads(Path("/tmp/cnc_sim_linuxcnc_indexed_parameters.json").read_text())
indexed = load("cnc_sim_linuxcnc_indexed_parameters.json")
indexed_moves = [
(
event["line"],
@@ -947,7 +965,7 @@ expected_indexed_moves = [
if indexed_moves != expected_indexed_moves:
raise SystemExit(f"unexpected LinuxCNC indexed/indirect parameter motions: {indexed_moves!r}")
normalized = json.loads(Path("/tmp/cnc_sim_linuxcnc_named_parameter_normalization.json").read_text())
normalized = load("cnc_sim_linuxcnc_named_parameter_normalization.json")
normalized_moves = [
(event["line"], event["end"]["x"], event["end"]["y"])
for event in normalized
@@ -957,7 +975,7 @@ expected_normalized_moves = [(4, 12, 0), (6, 12, 1)]
if normalized_moves != expected_normalized_moves:
raise SystemExit(f"unexpected LinuxCNC normalized named parameter motions: {normalized_moves!r}")
ini_named = json.loads(Path("/tmp/cnc_sim_linuxcnc_ini_named_parameter.json").read_text())
ini_named = load("cnc_sim_linuxcnc_ini_named_parameter.json")
ini_named_moves = [
(event["line"], event["end"]["x"], event["end"]["y"])
for event in ini_named
@@ -967,7 +985,7 @@ expected_ini_named_moves = [(3, 3.25, 0), (5, 3.25, 1)]
if ini_named_moves != expected_ini_named_moves:
raise SystemExit(f"unexpected LinuxCNC INI named parameter motions: {ini_named_moves!r}")
coords = json.loads(Path("/tmp/cnc_sim_linuxcnc_coordinate_offsets.json").read_text())
coords = load("cnc_sim_linuxcnc_coordinate_offsets.json")
g5x = [event for event in coords if event["type"] == "set-g5x-offset"]
g92 = [event for event in coords if event["type"] == "set-g92-offset"]
rot = [event for event in coords if event["type"] == "set-xy-rotation"]
@@ -993,7 +1011,7 @@ if not any(
if not any(event["type"] == "program-end" and event["line"] == 5 for event in coords):
raise SystemExit("missing coordinate program end line number")
l20 = json.loads(Path("/tmp/cnc_sim_linuxcnc_coordinate_l20.json").read_text())
l20 = load("cnc_sim_linuxcnc_coordinate_l20.json")
if not any(
event["type"] == "set-g5x-offset" and
event["line"] == 4 and
@@ -1007,7 +1025,7 @@ if not any(
if not any(event["type"] == "program-end" and event["line"] == 5 for event in l20):
raise SystemExit("missing G10 L20 program end line number")
p0 = json.loads(Path("/tmp/cnc_sim_linuxcnc_coordinate_p0.json").read_text())
p0 = load("cnc_sim_linuxcnc_coordinate_p0.json")
if not any(
event["type"] == "set-g5x-offset" and
event["line"] == 3 and
@@ -1021,7 +1039,7 @@ if not any(
if not any(event["type"] == "program-end" and event["line"] == 4 for event in p0):
raise SystemExit("missing G10 P0 program end line number")
select_all = json.loads(Path("/tmp/cnc_sim_linuxcnc_coordinate_select_all.json").read_text())
select_all = load("cnc_sim_linuxcnc_coordinate_select_all.json")
select_events = [
(event["line"], event["tool"], event["start"]["x"])
for event in select_all
@@ -1041,7 +1059,7 @@ expected_select_events = [
if select_events != expected_select_events:
raise SystemExit(f"unexpected G54-G59.3 selection events: {select_events!r}")
g92_restore = json.loads(Path("/tmp/cnc_sim_linuxcnc_g92_restore.json").read_text())
g92_restore = load("cnc_sim_linuxcnc_g92_restore.json")
g92_states = [
(event["line"], event["start"]["x"], event["start"]["y"], event["start"]["z"])
for event in g92_restore
@@ -1059,7 +1077,7 @@ if g92_states != expected_g92_states:
if not any(event["type"] == "program-end" and event["line"] == 7 for event in g92_restore):
raise SystemExit("missing G92 restore program end line number")
g52_offset = json.loads(Path("/tmp/cnc_sim_linuxcnc_g52_offset.json").read_text())
g52_offset = load("cnc_sim_linuxcnc_g52_offset.json")
g52_states = [
(event["line"], event["start"]["x"], event["start"]["y"], event["start"]["z"])
for event in g52_offset
@@ -1079,45 +1097,45 @@ if not any(event["type"] == "program-end" and event["line"] == 7 for event in g5
PY
echo "linuxcnc rs274 native smoke passed"
echo "dumped /tmp/cnc_sim_linuxcnc_basic_motion.json"
echo "dumped /tmp/cnc_sim_linuxcnc_basic_mill.json"
echo "dumped /tmp/cnc_sim_linuxcnc_rtcp_controls.json"
echo "dumped /tmp/cnc_sim_linuxcnc_spindle_direction.json"
echo "dumped /tmp/cnc_sim_linuxcnc_spindle_modes.json"
echo "dumped /tmp/cnc_sim_linuxcnc_spindle_orient.json"
echo "dumped /tmp/cnc_sim_linuxcnc_io_controls.json"
echo "dumped /tmp/cnc_sim_linuxcnc_override_controls.json"
echo "dumped /tmp/cnc_sim_linuxcnc_modal_state.json"
echo "dumped /tmp/cnc_sim_linuxcnc_modal_autorestore.json"
echo "dumped /tmp/cnc_sim_linuxcnc_threading_sync.json"
echo "dumped /tmp/cnc_sim_linuxcnc_threading_cycle.json"
echo "dumped /tmp/cnc_sim_linuxcnc_tool_number.json"
echo "dumped /tmp/cnc_sim_linuxcnc_program_stops.json"
echo "dumped /tmp/cnc_sim_linuxcnc_coolant.json"
echo "dumped /tmp/cnc_sim_linuxcnc_feed_modes.json"
echo "dumped /tmp/cnc_sim_linuxcnc_motion_modes.json"
echo "dumped /tmp/cnc_sim_linuxcnc_tool_length.json"
echo "dumped /tmp/cnc_sim_linuxcnc_dynamic_tool_length.json"
echo "dumped /tmp/cnc_sim_linuxcnc_canned_cycle.json"
echo "dumped /tmp/cnc_sim_linuxcnc_canned_cycles_extended.json"
echo "dumped /tmp/cnc_sim_linuxcnc_canned_cycle_planes.json"
echo "dumped /tmp/cnc_sim_linuxcnc_arc_planes.json"
echo "dumped /tmp/cnc_sim_linuxcnc_arc_distance_modes.json"
echo "dumped /tmp/cnc_sim_linuxcnc_predefined_positions.json"
echo "dumped /tmp/cnc_sim_linuxcnc_machine_coordinates.json"
echo "dumped /tmp/cnc_sim_linuxcnc_cutter_comp.json"
echo "dumped /tmp/cnc_sim_linuxcnc_comments.json"
echo "dumped /tmp/cnc_sim_linuxcnc_probe_no_error.json"
echo "dumped /tmp/cnc_sim_linuxcnc_oword_subprogram.json"
echo "dumped /tmp/cnc_sim_linuxcnc_named_oword_subprogram.json"
echo "dumped /tmp/cnc_sim_linuxcnc_oword_control_flow.json"
echo "dumped /tmp/cnc_sim_linuxcnc_named_parameter_exists.json"
echo "dumped /tmp/cnc_sim_linuxcnc_indexed_parameters.json"
echo "dumped /tmp/cnc_sim_linuxcnc_named_parameter_normalization.json"
echo "dumped /tmp/cnc_sim_linuxcnc_ini_named_parameter.json"
echo "dumped /tmp/cnc_sim_linuxcnc_coordinate_offsets.json"
echo "dumped /tmp/cnc_sim_linuxcnc_coordinate_l20.json"
echo "dumped /tmp/cnc_sim_linuxcnc_coordinate_p0.json"
echo "dumped /tmp/cnc_sim_linuxcnc_coordinate_select_all.json"
echo "dumped /tmp/cnc_sim_linuxcnc_g92_restore.json"
echo "dumped /tmp/cnc_sim_linuxcnc_g52_offset.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_basic_motion.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_basic_mill.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_rtcp_controls.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_spindle_direction.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_spindle_modes.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_spindle_orient.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_io_controls.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_override_controls.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_modal_state.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_modal_autorestore.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_threading_sync.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_threading_cycle.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_tool_number.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_program_stops.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_coolant.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_feed_modes.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_motion_modes.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_tool_length.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_dynamic_tool_length.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_canned_cycle.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_canned_cycles_extended.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_canned_cycle_planes.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_arc_planes.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_arc_distance_modes.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_predefined_positions.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_machine_coordinates.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_cutter_comp.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_comments.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_probe_no_error.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_oword_subprogram.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_named_oword_subprogram.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_oword_control_flow.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_named_parameter_exists.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_indexed_parameters.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_named_parameter_normalization.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_ini_named_parameter.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_coordinate_offsets.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_coordinate_l20.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_coordinate_p0.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_coordinate_select_all.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_g92_restore.json"
echo "dumped $output_dir/cnc_sim_linuxcnc_g52_offset.json"

File diff suppressed because it is too large Load Diff

View File

@@ -5,11 +5,21 @@ cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
build_dir=${TMPDIR:-/tmp}/cnc_sim_rs274_objects
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_rs274_objects.XXXXXX")
python_includes=$(python3.13-config --includes 2>/dev/null || python3-config --includes)
manifest=${1:-linuxcnc-rs274-source-files.txt}
rm -rf "$build_dir"
mkdir -p "$build_dir"
if [[ ! -f "$manifest" ]]; then
echo "missing manifest: $manifest" >&2
exit 1
fi
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
trap 'rm -rf "$build_dir"' EXIT
common_flags=(
-std=c++17
@@ -27,6 +37,10 @@ sources=()
while IFS=: read -r group path note; do
case "$group" in
core)
if [[ ! -f "$linuxcnc_root/$path" ]]; then
echo "missing manifest file: $path" >&2
exit 1
fi
sources+=("$path")
;;
""|\#*|binding|tooldata)
@@ -36,14 +50,15 @@ while IFS=: read -r group path note; do
exit 1
;;
esac
done < linuxcnc-rs274-source-files.txt
done < "$manifest"
source_index=0
for source in "${sources[@]}"; do
obj="$build_dir/$(basename "$source" .cc).o"
obj="$build_dir/linuxcnc_${source_index}.o"
# shellcheck disable=SC2086
"$cxx" "${common_flags[@]}" $python_includes -c "$linuxcnc_root/$source" -o "$obj"
source_index=$((source_index + 1))
done
echo "linuxcnc rs274 source object build passed (${#sources[@]} objects)"
echo "built objects in $build_dir"

View File

@@ -6,16 +6,58 @@ cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
python_includes=$(python3.13-config --includes 2>/dev/null || python3-config --includes)
manifest=${1:-linuxcnc-rs274-source-files.txt}
if [[ ! -f "$manifest" ]]; then
echo "missing manifest: $manifest" >&2
exit 1
fi
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
while IFS=: read -r group path note; do
case "$group" in
""|\#*) continue ;;
""|\#*)
continue
;;
core|binding|tooldata)
;;
*)
echo "unknown manifest group: $group" >&2
exit 1
;;
esac
if [[ ! -f "$linuxcnc_root/$path" ]]; then
echo "missing manifest file: $path" >&2
exit 1
fi
done < linuxcnc-rs274-source-files.txt
done < "$manifest"
missing_source_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_rs274_manifest_source.XXXXXX.txt")
missing_source_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_rs274_manifest_source.XXXXXX.log")
printf 'core:src/emc/rs274ngc/does_not_exist.cc:test missing source\n' >"$missing_source_manifest"
if ./test-linuxcnc-source-objects.sh "$missing_source_manifest" >"$missing_source_log" 2>&1; then
echo "linuxcnc source object probe accepted missing manifest file" >&2
exit 1
fi
if ! grep -F "missing manifest file: src/emc/rs274ngc/does_not_exist.cc" "$missing_source_log" >/dev/null; then
echo "linuxcnc source object probe did not report missing manifest file clearly" >&2
sed -n '1,20p' "$missing_source_log" >&2
exit 1
fi
if ./test-linuxcnc-source-link.sh "$missing_source_manifest" >"$missing_source_log" 2>&1; then
echo "linuxcnc source link probe accepted missing manifest file" >&2
exit 1
fi
if ! grep -F "missing manifest file: src/emc/rs274ngc/does_not_exist.cc" "$missing_source_log" >/dev/null; then
echo "linuxcnc source link probe did not report missing manifest file clearly" >&2
sed -n '1,20p' "$missing_source_log" >&2
exit 1
fi
rm -f "$missing_source_manifest" "$missing_source_log"
common_flags=(
-std=c++17
@@ -45,4 +87,3 @@ for source in "${probe_sources[@]}"; do
done
echo "linuxcnc rs274 source syntax probe passed"

50
test-linuxcnc-wasm-blockers.sh Executable file
View File

@@ -0,0 +1,50 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
manifest=${1:-linuxcnc-rs274-wasm-source-files.txt}
missing_root=/tmp/does-not-exist-linuxcnc
missing_root_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_wasm_blocker_missing_root.XXXXXX.log")
if LINUXCNC_ROOT="$missing_root" ./analyze-linuxcnc-wasm-blockers.sh "$manifest" >"$missing_root_log" 2>&1; then
echo "wasm blocker analyzer accepted missing LinuxCNC root" >&2
exit 1
fi
if ! grep -F "missing LinuxCNC root: $missing_root" "$missing_root_log" >/dev/null; then
echo "wasm blocker analyzer did not report missing LinuxCNC root clearly" >&2
sed -n '1,20p' "$missing_root_log" >&2
exit 1
fi
rm -f "$missing_root_log"
output=$(./analyze-linuxcnc-wasm-blockers.sh "$manifest")
missing_source_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_wasm_blocker_manifest_source.XXXXXX.txt")
missing_source_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_wasm_blocker_manifest_source.XXXXXX.log")
printf 'blocked:src/emc/tooldata/does_not_exist.cc:test missing blocked source\n' >"$missing_source_manifest"
if ./analyze-linuxcnc-wasm-blockers.sh "$missing_source_manifest" >"$missing_source_log" 2>&1; then
echo "wasm blocker analyzer accepted missing manifest source" >&2
exit 1
fi
if ! grep -F "missing manifest source: src/emc/tooldata/does_not_exist.cc" "$missing_source_log" >/dev/null; then
echo "wasm blocker analyzer did not report missing manifest source clearly" >&2
sed -n '1,20p' "$missing_source_log" >&2
exit 1
fi
rm -f "$missing_source_manifest" "$missing_source_log"
grep -F "[python]" <<<"$output" >/dev/null
grep -F "(none)" <<<"$(sed -n '/^\[python\]/,/^$/p' <<<"$output")" >/dev/null
grep -F "[dlopen]" <<<"$output" >/dev/null
grep -F "(none)" <<<"$(sed -n '/^\[dlopen\]/,/^$/p' <<<"$output")" >/dev/null
grep -F "[tooldata]" <<<"$output" >/dev/null
grep -F "src/emc/tooldata/tooldata_mmap.cc" <<<"$output" >/dev/null
grep -F "[tooldata-users]" <<<"$output" >/dev/null
grep -F "(none)" <<<"$(sed -n '/^\[tooldata-users\]/,/^$/p' <<<"$output")" >/dev/null
grep -F "[native-backend]" <<<"$output" >/dev/null
grep -F "src/emc/tooldata/tooldata_mmap.cc" <<<"$(sed -n '/^\[native-backend\]/,/^$/p' <<<"$output")" >/dev/null
grep -F "[native-fs]" <<<"$output" >/dev/null
grep -F "src/emc/tooldata/tooldata_mmap.cc" <<<"$(sed -n '/^\[native-fs\]/,/^$/p' <<<"$output")" >/dev/null
echo "linuxcnc wasm blocker scan passed"

View File

@@ -0,0 +1,260 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_cmake_safe_probe.XXXXXX")
manifest=${1:-linuxcnc-rs274-wasm-source-files.txt}
if [[ ! -f "$manifest" ]]; then
echo "missing manifest: $manifest" >&2
exit 1
fi
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
trap 'rm -rf "$build_dir"' EXIT
missing_root=/tmp/does-not-exist-linuxcnc
missing_root_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_build_wasm_missing_root.XXXXXX.log")
if LINUXCNC_ROOT="$missing_root" ./build-wasm.sh >"$missing_root_log" 2>&1; then
echo "build-wasm accepted missing LinuxCNC root: $missing_root" >&2
exit 1
fi
if ! grep -F "missing LinuxCNC root: $missing_root" "$missing_root_log" >/dev/null; then
echo "build-wasm did not report missing LinuxCNC root clearly" >&2
sed -n '1,20p' "$missing_root_log" >&2
exit 1
fi
grep -F 'exec 9>"${TMPDIR:-/tmp}/cnc_sim_build_wasm.lock"' build-wasm.sh >/dev/null
grep -F "flock 9" build-wasm.sh >/dev/null
rm -f "$missing_root_log"
missing_root_scripts=(
test-linuxcnc-wasm-interp-base-link.sh
test-linuxcnc-wasm-interp-find-link.sh
test-linuxcnc-wasm-python-plugin-link.sh
test-linuxcnc-wasm-rs274ngc-pre-object.sh
test-linuxcnc-wasm-tooldata-common-link.sh
test-linuxcnc-wasm-tooldata-link.sh
test-linuxcnc-wasm-tooldata-mmap-symbols.sh
test-linuxcnc-wasm-tooldata-runtime-symbols.sh
)
for script in "${missing_root_scripts[@]}"; do
if LINUXCNC_ROOT="$missing_root" "./$script" >"$missing_root_log" 2>&1; then
echo "$script accepted missing LinuxCNC root: $missing_root" >&2
exit 1
fi
if ! grep -F "missing LinuxCNC root: $missing_root" "$missing_root_log" >/dev/null; then
echo "$script did not report missing LinuxCNC root clearly" >&2
sed -n '1,20p' "$missing_root_log" >&2
exit 1
fi
done
rm -f "$missing_root_log"
missing_source_root=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_missing_linuxcnc_source_root.XXXXXX")
missing_source_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_linuxcnc_source.XXXXXX.log")
mkdir -p "$missing_source_root/src" "$missing_source_root/include"
fixed_source_scripts=(
"test-linuxcnc-wasm-interp-base-link.sh:src/emc/rs274ngc/interp_base.cc"
"test-linuxcnc-wasm-interp-find-link.sh:src/emc/tooldata/tooldata_common.cc"
"test-linuxcnc-wasm-rs274ngc-pre-object.sh:src/emc/rs274ngc/rs274ngc_pre.cc"
"test-linuxcnc-wasm-tooldata-common-link.sh:src/emc/tooldata/tooldata_common.cc"
"test-linuxcnc-wasm-tooldata-link.sh:src/emc/tooldata/tooldata_common.cc"
)
for script_and_path in "${fixed_source_scripts[@]}"; do
script=${script_and_path%%:*}
source_path=${script_and_path#*:}
if LINUXCNC_ROOT="$missing_source_root" "./$script" >"$missing_source_log" 2>&1; then
echo "$script accepted missing LinuxCNC source: $source_path" >&2
exit 1
fi
if ! grep -F "missing LinuxCNC source: $source_path" "$missing_source_log" >/dev/null; then
echo "$script did not report missing LinuxCNC source clearly" >&2
sed -n '1,20p' "$missing_source_log" >&2
exit 1
fi
done
rm -rf "$missing_source_root"
rm -f "$missing_source_log"
unknown_group_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_unknown_wasm_manifest_group.XXXXXX.txt")
unknown_group_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_unknown_wasm_manifest_group.XXXXXX.log")
printf 'bogus:src/emc/rs274ngc/interp_arc.cc:test unknown group\n' >"$unknown_group_manifest"
for script in \
analyze-linuxcnc-wasm-blockers.sh \
test-linuxcnc-wasm-source-syntax.sh \
test-linuxcnc-wasm-source-objects.sh \
test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh \
test-linuxcnc-wasm-rs274ngc-pre-link.sh; do
if "./$script" "$unknown_group_manifest" >"$unknown_group_log" 2>&1; then
echo "$script accepted unknown manifest group" >&2
exit 1
fi
if ! grep -F "unknown manifest group: bogus" "$unknown_group_log" >/dev/null; then
echo "$script did not report unknown manifest group clearly" >&2
sed -n '1,20p' "$unknown_group_log" >&2
exit 1
fi
done
rm -f "$unknown_group_manifest" "$unknown_group_log"
blocker_scan_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_wasm_blocker_scan.XXXXXX.log")
./test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh >"$blocker_scan_log" 2>&1
blocker_report_file=$(awk '/^wrote / { print $2 }' "$blocker_scan_log")
if [[ -z "$blocker_report_file" || ! -f "$blocker_report_file" ]]; then
echo "wasm rs274ngc_pre blocker scan did not preserve its report file" >&2
sed -n '1,20p' "$blocker_scan_log" >&2
exit 1
fi
rm -f "$blocker_report_file" "$blocker_scan_log"
missing_source_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_wasm_manifest_source.XXXXXX.txt")
missing_source_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_wasm_manifest_source.XXXXXX.log")
printf 'core:src/emc/rs274ngc/does_not_exist.cc:test missing source\n' >"$missing_source_manifest"
if ./test-linuxcnc-wasm-source-syntax.sh "$missing_source_manifest" >"$missing_source_log" 2>&1; then
echo "wasm source syntax probe accepted missing manifest source" >&2
exit 1
fi
if ! grep -F "missing manifest source: src/emc/rs274ngc/does_not_exist.cc" "$missing_source_log" >/dev/null; then
echo "wasm source syntax probe did not report missing manifest source clearly" >&2
sed -n '1,20p' "$missing_source_log" >&2
exit 1
fi
if ./test-linuxcnc-wasm-source-objects.sh "$missing_source_manifest" >"$missing_source_log" 2>&1; then
echo "wasm source object probe accepted missing manifest source" >&2
exit 1
fi
if ! grep -F "missing manifest source: src/emc/rs274ngc/does_not_exist.cc" "$missing_source_log" >/dev/null; then
echo "wasm source object probe did not report missing manifest source clearly" >&2
sed -n '1,20p' "$missing_source_log" >&2
exit 1
fi
if ./test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh "$missing_source_manifest" >"$missing_source_log" 2>&1; then
echo "wasm rs274ngc_pre blocker scan accepted missing manifest source" >&2
exit 1
fi
if ! grep -F "missing manifest source: src/emc/rs274ngc/does_not_exist.cc" "$missing_source_log" >/dev/null; then
echo "wasm rs274ngc_pre blocker scan did not report missing manifest source clearly" >&2
sed -n '1,20p' "$missing_source_log" >&2
exit 1
fi
if ./test-linuxcnc-wasm-rs274ngc-pre-link.sh "$missing_source_manifest" >"$missing_source_log" 2>&1; then
echo "wasm rs274ngc_pre link probe accepted missing manifest source" >&2
exit 1
fi
if ! grep -F "missing manifest source: src/emc/rs274ngc/does_not_exist.cc" "$missing_source_log" >/dev/null; then
echo "wasm rs274ngc_pre link probe did not report missing manifest source clearly" >&2
sed -n '1,20p' "$missing_source_log" >&2
exit 1
fi
rm -f "$missing_source_manifest" "$missing_source_log"
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
default_manifest="$PWD/linuxcnc-rs274-wasm-source-files.txt"
manifest_core_count=0
manifest_blocked_count=0
while IFS=: read -r group path note; do
case "$group" in
core)
manifest_core_count=$((manifest_core_count + 1))
;;
blocked)
manifest_blocked_count=$((manifest_blocked_count + 1))
;;
""|\#*)
continue
;;
*)
echo "unknown manifest group: $group" >&2
exit 1
;;
esac
if [[ ! -f "$linuxcnc_root/$path" ]]; then
echo "missing manifest source: $path" >&2
exit 1
fi
done < "$manifest"
required_shims=(
wasm_shims/dlfcn.cc
wasm_shims/emc_status_shim.cc
wasm_shims/gettext_shim.cc
wasm_shims/linuxcnc_runtime_shim.cc
wasm_shims/python_c_api_shim.cc
wasm_shims/rtapi_compat.cc
wasm_shims/tooldata/tooldata_mmap_backend.cc
wasm_shims/tooldata/tooldata_runtime_stubs.cc
wasm_shims/pythonplugin/python_plugin.cc
)
required_project_sources=(
src/canon_event_sink.cpp
src/linuxcnc_canon_bridge.cpp
src/linuxcnc_tooldata_fixture.cpp
src/rtcp_kinematics.cpp
)
if [[ "$manifest" == "$default_manifest" ]] && [[ "$manifest_core_count" -ne 25 || "$manifest_blocked_count" -ne 1 ]]; then
echo "unexpected wasm manifest partition: core=$manifest_core_count blocked=$manifest_blocked_count" >&2
exit 1
fi
if [[ "$manifest_core_count" -eq 0 || "$manifest_blocked_count" -eq 0 ]]; then
echo "unexpected wasm manifest partition: core=$manifest_core_count blocked=$manifest_blocked_count" >&2
exit 1
fi
for shim in "${required_shims[@]}"; do
if ! grep -F "$shim" core/CMakeLists.txt >/dev/null; then
echo "missing CMake wasm-safe shim: $shim" >&2
exit 1
fi
done
for source in "${required_project_sources[@]}"; do
if ! grep -F "$source" core/CMakeLists.txt >/dev/null; then
echo "missing CMake wasm-safe project source: $source" >&2
exit 1
fi
done
grep -F "add_executable(linuxcnc_rs274_wasm_safe_probe EXCLUDE_FROM_ALL" core/CMakeLists.txt >/dev/null
grep -F "wasm_shims/rs274ngc_pre_probe_main.cc" core/CMakeLists.txt >/dev/null
grep -F "target_link_libraries(linuxcnc_rs274_wasm_safe_probe PRIVATE fmt)" core/CMakeLists.txt >/dev/null
grep -F "add_custom_target(linuxcnc_rs274_wasm_blocked_sources" core/CMakeLists.txt >/dev/null
grep -F "LinuxCNC source root does not exist" core/CMakeLists.txt >/dev/null
grep -F 'if(NOT IS_DIRECTORY "${CNC_SIM_LINUXCNC_ROOT_ABS}")' core/CMakeLists.txt >/dev/null
grep -F "LinuxCNC wasm source manifest is not a file" core/CMakeLists.txt >/dev/null
grep -F 'if(IS_DIRECTORY "${CNC_SIM_LINUXCNC_WASM_SOURCE_MANIFEST}")' core/CMakeLists.txt >/dev/null
grep -F "LinuxCNC wasm manifest source does not exist" core/CMakeLists.txt >/dev/null
grep -F 'if(NOT EXISTS "${manifest_source}")' core/CMakeLists.txt >/dev/null
grep -F "LinuxCNC wasm manifest source is not a file" core/CMakeLists.txt >/dev/null
grep -F 'if(IS_DIRECTORY "${manifest_source}")' core/CMakeLists.txt >/dev/null
awk '
/set\(manifest_source/ { manifest_source = NR }
/Unknown LinuxCNC wasm source manifest group/ { unknown_group = NR }
END { exit !(unknown_group && manifest_source && unknown_group < manifest_source) }
' core/CMakeLists.txt
grep -F "LinuxCNC wasm source manifest has no blocked entries" core/CMakeLists.txt >/dev/null
if ! command -v cmake >/dev/null 2>&1; then
echo "cmake not found; skipping linuxcnc wasm-safe CMake probe target"
exit 0
fi
cmake -S core -B "$build_dir" \
-DCMAKE_BUILD_TYPE=Release \
-DCNC_SIM_LINUXCNC_ROOT="$linuxcnc_root" \
-DCNC_SIM_LINUXCNC_WASM_SOURCE_MANIFEST="$manifest" \
-DCNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE=ON
cmake --build "$build_dir" --target linuxcnc_rs274_wasm_safe_probe_objects
cmake --build "$build_dir" --target linuxcnc_rs274_wasm_safe_probe
"$build_dir/linuxcnc_rs274_wasm_safe_probe"
echo "linuxcnc wasm-safe CMake probe target passed"

View File

@@ -0,0 +1,40 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_interp_base_link.XXXXXX")
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
if [[ ! -f "$linuxcnc_root/src/emc/rs274ngc/interp_base.cc" ]]; then
echo "missing LinuxCNC source: src/emc/rs274ngc/interp_base.cc" >&2
exit 1
fi
trap 'rm -rf "$build_dir"' EXIT
common_flags=(
-std=c++17
-DULAPI
-I "$(pwd)/core/wasm_shims"
-I "$linuxcnc_root/src"
-I "$linuxcnc_root/src/emc"
-I "$linuxcnc_root/src/emc/nml_intf"
-I "$linuxcnc_root/src/emc/rs274ngc"
-I "$linuxcnc_root/src/emc/motion"
-I "$linuxcnc_root/include"
)
"$cxx" "${common_flags[@]}" \
core/wasm_shims/dlfcn.cc \
"$linuxcnc_root/src/emc/rs274ngc/interp_base.cc" \
core/wasm_shims/interp_base_probe_main.cc \
-o "$build_dir/interp_base_probe"
"$build_dir/interp_base_probe"
echo "linuxcnc wasm interp_base link probe passed"

View File

@@ -0,0 +1,73 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_interp_find_link.XXXXXX")
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
for required_source in \
src/emc/tooldata/tooldata_common.cc \
src/emc/rs274ngc/interp_find.cc; do
if [[ ! -f "$linuxcnc_root/$required_source" ]]; then
echo "missing LinuxCNC source: $required_source" >&2
exit 1
fi
done
trap 'rm -rf "$build_dir"' EXIT
common_flags=(
-std=c++17
-DULAPI
-ffunction-sections
-fdata-sections
-I "$(pwd)/core/wasm_shims"
-I "$linuxcnc_root/src"
-I "$linuxcnc_root/src/emc"
-I "$linuxcnc_root/src/emc/nml_intf"
-I "$linuxcnc_root/src/emc/rs274ngc"
-I "$linuxcnc_root/src/emc/motion"
-I "$linuxcnc_root/include"
)
"$cxx" "${common_flags[@]}" \
-c "$linuxcnc_root/src/emc/tooldata/tooldata_common.cc" \
-o "$build_dir/tooldata_common.o"
"$cxx" "${common_flags[@]}" \
-c core/wasm_shims/tooldata/tooldata_mmap_backend.cc \
-o "$build_dir/tooldata_mmap_backend.o"
"$cxx" "${common_flags[@]}" \
-c core/wasm_shims/tooldata/tooldata_runtime_stubs.cc \
-o "$build_dir/tooldata_runtime_stubs.o"
"$cxx" "${common_flags[@]}" \
-c core/wasm_shims/rtapi_compat.cc \
-o "$build_dir/rtapi_compat.o"
"$cxx" "${common_flags[@]}" \
-c core/wasm_shims/interp_find_probe_stubs.cc \
-o "$build_dir/interp_find_probe_stubs.o"
"$cxx" "${common_flags[@]}" \
-c "$linuxcnc_root/src/emc/rs274ngc/interp_find.cc" \
-o "$build_dir/interp_find.o"
"$cxx" "${common_flags[@]}" \
-c core/wasm_shims/interp_find_probe_main.cc \
-o "$build_dir/interp_find_probe_main.o"
"$cxx" \
"$build_dir/tooldata_common.o" \
"$build_dir/tooldata_mmap_backend.o" \
"$build_dir/tooldata_runtime_stubs.o" \
"$build_dir/rtapi_compat.o" \
"$build_dir/interp_find_probe_stubs.o" \
"$build_dir/interp_find.o" \
"$build_dir/interp_find_probe_main.o" \
-Wl,--gc-sections \
-o "$build_dir/interp_find_probe"
"$build_dir/interp_find_probe"
echo "linuxcnc wasm interp_find link probe passed"

View File

@@ -0,0 +1,35 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_python_plugin_link.XXXXXX")
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
trap 'rm -rf "$build_dir"' EXIT
common_flags=(
-std=c++17
-DULAPI
-I "$(pwd)/core/wasm_shims"
-I "$linuxcnc_root/src"
-I "$linuxcnc_root/src/emc"
-I "$linuxcnc_root/src/emc/nml_intf"
-I "$linuxcnc_root/src/emc/rs274ngc"
-I "$linuxcnc_root/src/emc/motion"
-I "$linuxcnc_root/include"
)
"$cxx" "${common_flags[@]}" \
core/wasm_shims/pythonplugin/python_plugin.cc \
core/wasm_shims/pythonplugin/python_plugin_probe_main.cc \
-o "$build_dir/python_plugin_probe"
"$build_dir/python_plugin_probe"
echo "linuxcnc wasm python_plugin link probe passed"

View File

@@ -0,0 +1,167 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_rs274ngc_pre_link_blockers.XXXXXX")
report_file=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_rs274ngc_pre_undefined.XXXXXX.txt")
manifest=${1:-linuxcnc-rs274-wasm-source-files.txt}
if [[ ! -f "$manifest" ]]; then
echo "missing manifest: $manifest" >&2
exit 1
fi
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
trap 'rm -rf "$build_dir"' EXIT
common_flags=(
-std=c++20
-DULAPI
-ffunction-sections
-fdata-sections
-include wctype.h
-I "$(pwd)/core/include"
-I "$(pwd)/core/wasm_shims"
-I "$linuxcnc_root/src"
-I "$linuxcnc_root/src/emc"
-I "$linuxcnc_root/src/emc/nml_intf"
-I "$linuxcnc_root/src/emc/rs274ngc"
-I "$linuxcnc_root/src/emc/motion"
-I "$linuxcnc_root/include"
)
shim_sources=(
core/wasm_shims/dlfcn.cc
core/wasm_shims/emc_status_shim.cc
core/wasm_shims/gettext_shim.cc
core/wasm_shims/linuxcnc_runtime_shim.cc
core/wasm_shims/python_c_api_shim.cc
core/wasm_shims/rtapi_compat.cc
core/wasm_shims/tooldata/tooldata_mmap_backend.cc
core/wasm_shims/tooldata/tooldata_runtime_stubs.cc
core/wasm_shims/pythonplugin/python_plugin.cc
)
project_sources=(
core/src/canon_event_sink.cpp
core/src/linuxcnc_canon_bridge.cpp
core/src/linuxcnc_tooldata_fixture.cpp
core/src/rtcp_kinematics.cpp
)
linuxcnc_sources=()
while IFS=: read -r group path note; do
case "$group" in
core)
if [[ ! -f "$linuxcnc_root/$path" ]]; then
echo "missing manifest source: $path" >&2
exit 1
fi
linuxcnc_sources+=("$linuxcnc_root/$path")
;;
""|\#*|blocked)
;;
*)
echo "unknown manifest group: $group" >&2
exit 1
;;
esac
done < "$manifest"
shim_index=0
for source in "${shim_sources[@]}"; do
obj="$build_dir/shim_${shim_index}.o"
"$cxx" "${common_flags[@]}" -c "$source" -o "$obj"
shim_index=$((shim_index + 1))
done
project_index=0
for source in "${project_sources[@]}"; do
obj="$build_dir/project_${project_index}.o"
"$cxx" "${common_flags[@]}" -c "$source" -o "$obj"
project_index=$((project_index + 1))
done
linuxcnc_index=0
for source in "${linuxcnc_sources[@]}"; do
obj="$build_dir/linuxcnc_${linuxcnc_index}.o"
"$cxx" "${common_flags[@]}" -c "$source" -o "$obj"
linuxcnc_index=$((linuxcnc_index + 1))
done
undefined_file="$build_dir/undefined.raw"
defined_file="$build_dir/defined.raw"
nm -u "$build_dir"/*.o \
| awk '{print $2}' \
| sed '/^$/d' \
| LC_ALL=C sort -u \
> "$undefined_file"
nm --defined-only "$build_dir"/*.o \
| awk '{print $3}' \
| sed '/^$/d' \
| LC_ALL=C sort -u \
> "$defined_file"
{
comm -23 "$undefined_file" "$defined_file" \
| grep -Ev '^(_GLOBAL_OFFSET_TABLE_|_Unwind_Resume)$' \
| grep -Ev '^(__assert_fail|__cxa_|__divdc3|__dso_handle|__errno_location|__gxx_personality_v0|__isoc23_|__muldc3|__stack_chk_fail)' \
| grep -Ev '^(abort|access|acos|asin|atan2|atof|atoi|basename|cabs|calloc|ceil|close|closedir|copysign|cos|cosl|exit|exp|fclose|fdatasync|fdopen|feof|fflush|fgetc|fgets|fileno|floor|fmod|fopen|fprintf|fputs|free|freelocale|fseek|fstat|ftell|fwrite|getcwd|getenv|getpid|gettimeofday|hypot|islower|isprint|isspace|isupper|isxdigit|link|localtime|log|lstat|malloc|memchr|memcmp|memcpy|memmove|memset|mkstemp|nearbyint|newlocale|open|opendir|perror|pow|printf|pthread_mutex_lock|pthread_mutex_unlock|putc|puts|read|readdir|realpath|realloc|remove|rename|setlocale|sin|sinl|snprintf|sprintf|sqrt|sscanf|stat|stderr|stdout|strcasecmp|strcat|strchr|strcmp|strcpy|strdup|strerror|strlen|strncasecmp|strncat|strncmp|strncpy|strrchr|strspn|strstr|strtod|strtok_r|strtol|strtoul|tan|tanl|tolower|toupper|towlower|unlink|uselocale|vfprintf|vsnprintf|wordexp|wordfree)$' \
| grep -Ev '^(_ZN3fmt|_ZSt|_ZNSt|_ZNKSt|_ZNKRSt|_ZNS|_ZTI|_ZTS|_ZTV|_Zdl|_Znwm|_Znam|_Zda|_ZdaPv|_ZdaPvm|_ZdlPvm|_ZTv|_ZTh)'
} > "$report_file" || true
if grep -F "_ZN6Interp13read_commentEPcPiP12block_structPd" "$report_file" >/dev/null; then
echo "unexpected unresolved read_comment after interp_read.cc" >&2
exit 1
fi
if grep -F "_ZN6Interp15find_tool_indexEP5setupiPi" "$report_file" >/dev/null; then
echo "unexpected unresolved find_tool_index after interp_find.cc" >&2
exit 1
fi
if grep -F "_ZN6Interp16find_named_paramEPKcPiPd" "$report_file" >/dev/null; then
echo "unexpected unresolved find_named_param after interp_namedparams.cc" >&2
exit 1
fi
if grep -Fx "emcStatus" "$report_file" >/dev/null; then
echo "unexpected unresolved emcStatus after shim" >&2
exit 1
fi
if grep -Fx "gettext" "$report_file" >/dev/null; then
echo "unexpected unresolved gettext after shim" >&2
exit 1
fi
if grep -Fx "_task" "$report_file" >/dev/null; then
echo "unexpected unresolved _task after runtime shim" >&2
exit 1
fi
if grep -Fx "builtin_modules" "$report_file" >/dev/null; then
echo "unexpected unresolved builtin_modules after runtime shim" >&2
exit 1
fi
for hal_symbol in \
hal_get_param_value_by_name \
hal_get_pin_value_by_name \
hal_get_signal_value_by_name \
hal_init \
hal_ready; do
if grep -Fx "$hal_symbol" "$report_file" >/dev/null; then
echo "unexpected unresolved $hal_symbol after runtime shim" >&2
exit 1
fi
done
if [[ -s "$report_file" ]]; then
echo "unexpected unresolved project symbols after rs274ngc_pre shim coverage:" >&2
sed -n '1,120p' "$report_file" >&2
exit 1
fi
echo "linuxcnc wasm rs274ngc_pre link blocker scan passed"
echo "wrote $report_file"

View File

@@ -0,0 +1,91 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_rs274ngc_pre_link.XXXXXX")
manifest=${1:-linuxcnc-rs274-wasm-source-files.txt}
if [[ ! -f "$manifest" ]]; then
echo "missing manifest: $manifest" >&2
exit 1
fi
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
trap 'rm -rf "$build_dir"' EXIT
common_flags=(
-std=c++20
-DULAPI
-ffunction-sections
-fdata-sections
-I "$(pwd)/core/src"
-include wctype.h
-I "$(pwd)/core/include"
-I "$(pwd)/core/wasm_shims"
-I "$linuxcnc_root/src"
-I "$linuxcnc_root/src/emc"
-I "$linuxcnc_root/src/emc/nml_intf"
-I "$linuxcnc_root/src/emc/rs274ngc"
-I "$linuxcnc_root/src/emc/motion"
-I "$linuxcnc_root/include"
)
sources=(
core/wasm_shims/dlfcn.cc
core/wasm_shims/emc_status_shim.cc
core/wasm_shims/gettext_shim.cc
core/wasm_shims/linuxcnc_runtime_shim.cc
core/wasm_shims/python_c_api_shim.cc
core/wasm_shims/rtapi_compat.cc
core/wasm_shims/tooldata/tooldata_mmap_backend.cc
core/wasm_shims/tooldata/tooldata_runtime_stubs.cc
core/wasm_shims/pythonplugin/python_plugin.cc
core/src/canon_event_sink.cpp
core/src/linuxcnc_canon_bridge.cpp
core/src/linuxcnc_tooldata_fixture.cpp
core/src/rtcp_kinematics.cpp
)
while IFS=: read -r group path note; do
case "$group" in
core)
if [[ ! -f "$linuxcnc_root/$path" ]]; then
echo "missing manifest source: $path" >&2
exit 1
fi
sources+=("$linuxcnc_root/$path")
;;
""|\#*|blocked)
;;
*)
echo "unknown manifest group: $group" >&2
exit 1
;;
esac
done < "$manifest"
sources+=(core/wasm_shims/rs274ngc_pre_probe_main.cc)
objects=()
object_index=0
for source in "${sources[@]}"; do
obj="$build_dir/source_${object_index}.o"
"$cxx" "${common_flags[@]}" -c "$source" -o "$obj"
objects+=("$obj")
object_index=$((object_index + 1))
done
"$cxx" "${objects[@]}" \
-Wl,--gc-sections \
-lfmt \
-o "$build_dir/rs274ngc_pre_probe"
"$build_dir/rs274ngc_pre_probe"
echo "linuxcnc wasm rs274ngc_pre link probe passed"

View File

@@ -0,0 +1,36 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_rs274ngc_pre_object.XXXXXX")
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
if [[ ! -f "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" ]]; then
echo "missing LinuxCNC source: src/emc/rs274ngc/rs274ngc_pre.cc" >&2
exit 1
fi
trap 'rm -rf "$build_dir"' EXIT
common_flags=(
-std=c++17
-DULAPI
-I "$(pwd)/core/wasm_shims"
-I "$linuxcnc_root/src"
-I "$linuxcnc_root/src/emc"
-I "$linuxcnc_root/src/emc/nml_intf"
-I "$linuxcnc_root/src/emc/rs274ngc"
-I "$linuxcnc_root/src/emc/motion"
-I "$linuxcnc_root/include"
)
"$cxx" "${common_flags[@]}" \
-c "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" \
-o "$build_dir/rs274ngc_pre.o"
echo "linuxcnc wasm rs274ngc_pre object probe passed"

View File

@@ -0,0 +1,81 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
manifest=${1:-linuxcnc-rs274-wasm-source-files.txt}
cxx=${CXX:-g++}
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_safe_probe.XXXXXX")
shim_sources=(
core/wasm_shims/dlfcn.cc
core/wasm_shims/emc_status_shim.cc
core/wasm_shims/gettext_shim.cc
core/wasm_shims/linuxcnc_runtime_shim.cc
core/wasm_shims/python_c_api_shim.cc
core/wasm_shims/rtapi_compat.cc
core/wasm_shims/tooldata/tooldata_mmap_backend.cc
core/wasm_shims/tooldata/tooldata_runtime_stubs.cc
core/wasm_shims/pythonplugin/python_plugin.cc
)
if [[ ! -f "$manifest" ]]; then
echo "missing manifest: $manifest" >&2
exit 1
fi
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
trap 'rm -rf "$build_dir"' EXIT
common_flags=(
-std=c++20
-DULAPI
-include wctype.h
-I "$(pwd)/core/include"
-I "$(pwd)/core/src"
-I "$(pwd)/core/wasm_shims"
-I "$linuxcnc_root/src"
-I "$linuxcnc_root/src/emc"
-I "$linuxcnc_root/src/emc/nml_intf"
-I "$linuxcnc_root/src/emc/rs274ngc"
-I "$linuxcnc_root/src/emc/motion"
-I "$linuxcnc_root/include"
)
sources=()
while IFS=: read -r group path note; do
case "$group" in
core)
if [[ ! -f "$linuxcnc_root/$path" ]]; then
echo "missing manifest source: $path" >&2
exit 1
fi
sources+=("$path")
;;
""|\#*|blocked)
;;
*)
echo "unknown manifest group: $group" >&2
exit 1
;;
esac
done < "$manifest"
shim_index=0
for source in "${shim_sources[@]}"; do
obj="$build_dir/shim_${shim_index}.o"
"$cxx" "${common_flags[@]}" -c "$source" -o "$obj"
shim_index=$((shim_index + 1))
done
source_index=0
for source in "${sources[@]}"; do
obj="$build_dir/linuxcnc_${source_index}.o"
"$cxx" "${common_flags[@]}" -c "$linuxcnc_root/$source" -o "$obj"
source_index=$((source_index + 1))
done
echo "linuxcnc wasm-safe source object probe passed (${#sources[@]} linuxcnc objects + ${#shim_sources[@]} shims)"

View File

@@ -0,0 +1,59 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
manifest=${1:-linuxcnc-rs274-wasm-source-files.txt}
cxx=${CXX:-g++}
if [[ ! -f "$manifest" ]]; then
echo "missing manifest: $manifest" >&2
exit 1
fi
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
common_flags=(
-std=c++20
-DULAPI
-fsyntax-only
-include wctype.h
-I "$(pwd)/core/include"
-I "$(pwd)/core/src"
-I "$(pwd)/core/wasm_shims"
-I "$linuxcnc_root/src"
-I "$linuxcnc_root/src/emc"
-I "$linuxcnc_root/src/emc/nml_intf"
-I "$linuxcnc_root/src/emc/rs274ngc"
-I "$linuxcnc_root/src/emc/motion"
-I "$linuxcnc_root/include"
)
sources=()
while IFS=: read -r group path note; do
case "$group" in
core)
if [[ ! -f "$linuxcnc_root/$path" ]]; then
echo "missing manifest source: $path" >&2
exit 1
fi
sources+=("$path")
;;
""|\#*|blocked)
;;
*)
echo "unknown manifest group: $group" >&2
exit 1
;;
esac
done < "$manifest"
for source in "${sources[@]}"; do
"$cxx" "${common_flags[@]}" "$linuxcnc_root/$source"
done
echo "linuxcnc wasm-safe source syntax probe passed (${#sources[@]} files)"

View File

@@ -0,0 +1,41 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_tooldata_common_link.XXXXXX")
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
if [[ ! -f "$linuxcnc_root/src/emc/tooldata/tooldata_common.cc" ]]; then
echo "missing LinuxCNC source: src/emc/tooldata/tooldata_common.cc" >&2
exit 1
fi
trap 'rm -rf "$build_dir"' EXIT
common_flags=(
-std=c++17
-DULAPI
-I "$linuxcnc_root/src/emc/tooldata"
-I "$linuxcnc_root/src"
-I "$linuxcnc_root/src/emc"
-I "$linuxcnc_root/src/emc/nml_intf"
-I "$linuxcnc_root/src/emc/rs274ngc"
-I "$linuxcnc_root/src/emc/motion"
-I "$linuxcnc_root/include"
)
"$cxx" "${common_flags[@]}" \
"$linuxcnc_root/src/emc/tooldata/tooldata_common.cc" \
core/wasm_shims/tooldata/tooldata_mmap_backend.cc \
core/wasm_shims/tooldata/tooldata_runtime_stubs.cc \
core/wasm_shims/tooldata/tooldata_common_probe_main.cc \
-o "$build_dir/tooldata_common_probe"
"$build_dir/tooldata_common_probe"
echo "linuxcnc wasm tooldata_common link probe passed"

View File

@@ -0,0 +1,41 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_tooldata_link.XXXXXX")
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
if [[ ! -f "$linuxcnc_root/src/emc/tooldata/tooldata_common.cc" ]]; then
echo "missing LinuxCNC source: src/emc/tooldata/tooldata_common.cc" >&2
exit 1
fi
trap 'rm -rf "$build_dir"' EXIT
common_flags=(
-std=c++17
-DULAPI
-I "$(pwd)/core/wasm_shims"
-I "$linuxcnc_root/src"
-I "$linuxcnc_root/src/emc"
-I "$linuxcnc_root/src/emc/nml_intf"
-I "$linuxcnc_root/src/emc/rs274ngc"
-I "$linuxcnc_root/src/emc/motion"
-I "$linuxcnc_root/include"
)
"$cxx" "${common_flags[@]}" \
"$linuxcnc_root/src/emc/tooldata/tooldata_common.cc" \
core/wasm_shims/tooldata/tooldata_mmap_backend.cc \
core/wasm_shims/tooldata/tooldata_runtime_stubs.cc \
core/wasm_shims/tooldata/tooldata_probe_main.cc \
-o "$build_dir/tooldata_probe"
"$build_dir/tooldata_probe"
echo "linuxcnc wasm tooldata shim link probe passed"

View File

@@ -0,0 +1,69 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_tooldata_mmap_symbols.XXXXXX")
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
trap 'rm -rf "$build_dir"' EXIT
common_flags=(
-std=c++17
-DULAPI
-I "$(pwd)/core/wasm_shims"
-I "$linuxcnc_root/src"
-I "$linuxcnc_root/src/emc"
-I "$linuxcnc_root/src/emc/nml_intf"
-I "$linuxcnc_root/src/emc/rs274ngc"
-I "$linuxcnc_root/src/emc/motion"
-I "$linuxcnc_root/include"
)
"$cxx" "${common_flags[@]}" \
-c core/wasm_shims/tooldata/tooldata_mmap_backend.cc \
-o "$build_dir/tooldata_mmap_backend.o"
nm --defined-only "$build_dir/tooldata_mmap_backend.o" \
| awk '$2 ~ /^[A-Z]$/ {print $3}' \
| LC_ALL=C sort -u \
> "$build_dir/backend.exports"
expected_exports=(
tool_mmap_close
tool_mmap_creator
tool_mmap_is_random_toolchanger
tool_mmap_user
tooldata_find_index_for_tool
tooldata_get
tooldata_last_index_get
tooldata_last_index_set
tooldata_put
tooldata_reset
)
printf '%s\n' "${expected_exports[@]}" | LC_ALL=C sort -u > "$build_dir/expected.exports"
if ! comm -23 "$build_dir/expected.exports" "$build_dir/backend.exports" > "$build_dir/missing.exports"; then
exit 1
fi
if [[ -s "$build_dir/missing.exports" ]]; then
echo "missing wasm tooldata mmap backend exports:" >&2
cat "$build_dir/missing.exports" >&2
exit 1
fi
comm -13 "$build_dir/expected.exports" "$build_dir/backend.exports" > "$build_dir/unexpected.exports"
if [[ -s "$build_dir/unexpected.exports" ]]; then
echo "unexpected wasm tooldata mmap backend exports:" >&2
cat "$build_dir/unexpected.exports" >&2
exit 1
fi
echo "linuxcnc wasm tooldata_mmap symbol probe passed"

View File

@@ -0,0 +1,60 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_tooldata_runtime_symbols.XXXXXX")
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
trap 'rm -rf "$build_dir"' EXIT
common_flags=(
-std=c++17
-DULAPI
-I "$(pwd)/core/wasm_shims"
-I "$linuxcnc_root/src"
-I "$linuxcnc_root/src/emc"
-I "$linuxcnc_root/src/emc/nml_intf"
-I "$linuxcnc_root/src/emc/rs274ngc"
-I "$linuxcnc_root/src/emc/motion"
-I "$linuxcnc_root/include"
)
"$cxx" "${common_flags[@]}" \
-c core/wasm_shims/tooldata/tooldata_runtime_stubs.cc \
-o "$build_dir/tooldata_runtime_stubs.o"
nm --defined-only "$build_dir/tooldata_runtime_stubs.o" \
| awk '$2 ~ /^[A-Z]$/ {print $3}' \
| LC_ALL=C sort -u \
> "$build_dir/runtime.exports"
expected_exports=(
tool_nml_register
tooldata_db_getall
tooldata_db_init
tooldata_db_notify
)
printf '%s\n' "${expected_exports[@]}" | LC_ALL=C sort -u > "$build_dir/expected.exports"
comm -23 "$build_dir/expected.exports" "$build_dir/runtime.exports" > "$build_dir/missing.exports"
if [[ -s "$build_dir/missing.exports" ]]; then
echo "missing wasm tooldata runtime stub exports:" >&2
cat "$build_dir/missing.exports" >&2
exit 1
fi
comm -13 "$build_dir/expected.exports" "$build_dir/runtime.exports" > "$build_dir/unexpected.exports"
if [[ -s "$build_dir/unexpected.exports" ]]; then
echo "unexpected wasm tooldata runtime stub exports:" >&2
cat "$build_dir/unexpected.exports" >&2
exit 1
fi
echo "linuxcnc wasm tooldata runtime symbol probe passed"

View File

@@ -4,8 +4,8 @@ set -euo pipefail
cd "$(dirname "$0")"
cxx=${CXX:-g++}
build_dir=${TMPDIR:-/tmp}/cnc_sim_native
mkdir -p "$build_dir"
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_native.XXXXXX")
trap 'rm -rf "$build_dir"' EXIT
"$cxx" -std=c++17 -I core/include -I core/src \
core/src/canon_event_sink.cpp \
@@ -49,13 +49,18 @@ mkdir -p "$build_dir"
"$build_dir/rtcp_kinematics_smoke"
"$build_dir/simulator_gcode_controls_smoke"
"$build_dir/cnc_sim_api_smoke"
"$build_dir/cnc_sim_dump" tests/gcode/basic_mill.ngc >/tmp/cnc_sim_basic_mill.json
"$build_dir/cnc_sim_dump" tests/gcode/incremental_and_r_arc.ngc >/tmp/cnc_sim_incremental_and_r_arc.json
"$build_dir/cnc_sim_dump" tests/gcode/smoke_subprogram_m98.ngc >/tmp/cnc_sim_smoke_subprogram_m98.json
"$build_dir/cnc_sim_dump" tests/gcode/smoke_oword_subprogram.ngc >/tmp/cnc_sim_smoke_oword_subprogram.json
basic_mill_json="$build_dir/cnc_sim_basic_mill.json"
incremental_arc_json="$build_dir/cnc_sim_incremental_and_r_arc.json"
subprogram_m98_json="$build_dir/cnc_sim_smoke_subprogram_m98.json"
oword_subprogram_json="$build_dir/cnc_sim_smoke_oword_subprogram.json"
"$build_dir/cnc_sim_dump" tests/gcode/basic_mill.ngc >"$basic_mill_json"
"$build_dir/cnc_sim_dump" tests/gcode/incremental_and_r_arc.ngc >"$incremental_arc_json"
"$build_dir/cnc_sim_dump" tests/gcode/smoke_subprogram_m98.ngc >"$subprogram_m98_json"
"$build_dir/cnc_sim_dump" tests/gcode/smoke_oword_subprogram.ngc >"$oword_subprogram_json"
echo "native tests passed"
echo "dumped /tmp/cnc_sim_basic_mill.json"
echo "dumped /tmp/cnc_sim_incremental_and_r_arc.json"
echo "dumped /tmp/cnc_sim_smoke_subprogram_m98.json"
echo "dumped /tmp/cnc_sim_smoke_oword_subprogram.json"
echo "dumped $basic_mill_json"
echo "dumped $incremental_arc_json"
echo "dumped $subprogram_m98_json"
echo "dumped $oword_subprogram_json"

View File

@@ -0,0 +1,4 @@
G21 G90 G17 F100
/N10 G1 X5
G1 X2
M30

View File

@@ -0,0 +1,2 @@
G21 G90 G17
G81 X1 A10 Z-1 R1

View File

@@ -0,0 +1,2 @@
G21 G90 G17
G81 X1 B10 Z-1 R1

View File

@@ -0,0 +1,2 @@
G21 G90 G17
G81 X1 C10 Z-1 R1

View File

@@ -0,0 +1,3 @@
G21 G90 G18
F100
G81 X1 Z-1 R1

View File

@@ -0,0 +1,3 @@
G21 G90 G18
F100
G81 X1 Y2 Z-1 R1

View File

@@ -0,0 +1,3 @@
G21 G90 G19
F100
G81 Y1 Z-1 R1

View File

@@ -0,0 +1,3 @@
G21 G90 G19
F100
G81 X2 Y1 Z-1 R1

View File

@@ -0,0 +1,3 @@
G21 G90 G17
F100
G73 X1 Z-1 R1

View File

@@ -0,0 +1,3 @@
G21 G90 G17
F100
G73 X1 Z-1 R1 Q0

View File

@@ -0,0 +1,4 @@
G21 G90 G17
F100
S100 M3
G74 X1 Z-1 R1

View File

@@ -0,0 +1,3 @@
G21 G90 G17
F100
G74 X1 Z-1 R1

View File

@@ -0,0 +1,3 @@
G21 G90 G17
F100
G82 X1 Z-1 R1

View File

@@ -0,0 +1,3 @@
G21 G90 G17
F100
G83 X1 Z-1 R1

View File

@@ -0,0 +1,3 @@
G21 G90 G17
F100
G83 X1 Z-1 R1 Q0

View File

@@ -0,0 +1,4 @@
G21 G90 G17
F100
S100 M4
G84 X1 Z-1 R1

View File

@@ -0,0 +1,3 @@
G21 G90 G17
F100
G84 X1 Z-1 R1

View File

@@ -0,0 +1,3 @@
G21 G90 G17
F100
G86 X1 Z-1 R1

View File

@@ -0,0 +1,3 @@
G21 G90 G17
F100
G86 X1 Z-1 R1 P0.1

View File

@@ -0,0 +1,4 @@
G21 G90 G17
F100
S100 M3
G87 X1 Z-1 R1 J0.1 K0

View File

@@ -0,0 +1,4 @@
G21 G90 G17
F100
S100 M3
G87 X1 Z-1 R1 I0.1 K0

View File

@@ -0,0 +1,4 @@
G21 G90 G17
F100
S100 M3
G87 X1 Z-1 R1 I0.1 J0.1

View File

@@ -0,0 +1,3 @@
G21 G90 G17
F100
G87 X1 Z-1 R1 I0.1 J0.1 K0

View File

@@ -0,0 +1,4 @@
G21 G90 G17
F100
S100 M3
G88 X1 Z-1 R1

View File

@@ -0,0 +1,3 @@
G21 G90 G17
F100
G88 X1 Z-1 R1 P0.1

View File

@@ -0,0 +1,3 @@
G21 G90 G17
F100
G89 X1 Z-1 R1

View File

@@ -0,0 +1,3 @@
G21 G90 G17
F100
G81 X1 Z-1 R1 L0

View File

@@ -0,0 +1,3 @@
G21 G90 G17
F100
G81 X1 Z-1

View File

@@ -0,0 +1,3 @@
G21 G90 G17
F100
G81 X1 R1

View File

@@ -0,0 +1,3 @@
G21 G90 G17
F100
G81 X1 Z2 R1

View File

@@ -0,0 +1,3 @@
G21 G90 G17
F100
G81 X1 U2 Z-1 R1