diff --git a/analyze-linuxcnc-wasm-blockers.sh b/analyze-linuxcnc-wasm-blockers.sh index 1755ac7..c42f61a 100755 --- a/analyze-linuxcnc-wasm-blockers.sh +++ b/analyze-linuxcnc-wasm-blockers.sh @@ -6,7 +6,8 @@ cd "$(dirname "$0")" linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} manifest=${1:-linuxcnc-rs274-wasm-source-files.txt} -LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest" +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh "$manifest" +linuxcnc_root=$(cd "$linuxcnc_root" && pwd) python_blockers=() core_python_sources=() @@ -53,19 +54,38 @@ wasm_safe_shim_in_build() { return 1 } -core_source_output=$(./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core) -mapfile -t core_sources <<<"$core_source_output" -blocked_source_output=$(./list-linuxcnc-wasm-manifest-sources.sh "$manifest" blocked) -mapfile -t blocked_sources <<<"$blocked_source_output" +core_sources=() +blocked_sources=() +while IFS=: read -r group path note extra; do + case "$group" in + ""|\#*) + continue + ;; + core) + core_sources+=("$path") + ;; + blocked) + blocked_sources+=("$path") + ;; + header|metadata|blocked-header) + ;; + *) + echo "unknown manifest group: $group" >&2 + exit 1 + ;; + esac +done < "$manifest" core_count=${#core_sources[@]} blocked_count=${#blocked_sources[@]} scan_source() { local group=$1 local path=$2 - full_path=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-files.sh \ - --missing-message "missing manifest source" \ - "$path") + full_path="$linuxcnc_root/$path" + if [[ ! -f "$full_path" ]]; then + echo "missing manifest source: $path" >&2 + exit 1 + fi if [[ "$group" == "blocked" ]]; then if ! replacement=$(blocked_replacement_for "$path"); then echo "missing blocked replacement mapping: $path" >&2 diff --git a/build-linuxcnc-native-bridge-objects.sh b/build-linuxcnc-native-bridge-objects.sh new file mode 100755 index 0000000..179ad24 --- /dev/null +++ b/build-linuxcnc-native-bridge-objects.sh @@ -0,0 +1,101 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd "$(dirname "$0")" + +linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} +cxx=${CXX:-g++} +if [[ -z ${CXX:-} ]] && command -v ccache >/dev/null 2>&1; then + cxx="ccache $cxx" +fi +build_jobs=${CNC_SIM_BUILD_JOBS:-8} +if ! [[ "$build_jobs" =~ ^[1-9][0-9]*$ ]]; then + echo "CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs" >&2 + exit 1 +fi +cache_dir=${CNC_SIM_NATIVE_BRIDGE_OBJECT_CACHE_DIR-build/native-bridge-object-cache} +if [[ -z "$cache_dir" ]]; then + echo "CNC_SIM_NATIVE_BRIDGE_OBJECT_CACHE_DIR must not be empty" >&2 + exit 1 +fi +if [[ "$cache_dir" =~ [[:space:]] ]]; then + echo "CNC_SIM_NATIVE_BRIDGE_OBJECT_CACHE_DIR must not contain whitespace: $cache_dir" >&2 + exit 1 +fi +mkdir -p "$cache_dir" +cache_dir=$(cd "$cache_dir" && pwd) +if [[ "$cache_dir" == "/" ]]; then + echo "CNC_SIM_NATIVE_BRIDGE_OBJECT_CACHE_DIR must not be the filesystem root" >&2 + exit 1 +fi +exec 8>"$cache_dir/native-bridge-objects.lock" +flock 8 + +preflight_cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-"$cache_dir/preflight-cache"} +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" --root-only +linuxcnc_root=$(cd "$linuxcnc_root" && pwd) + +source_output=$(./list-linuxcnc-bridge-smoke-project-sources.sh) +mapfile -t sources <<<"$source_output" +common_flag_output=$( + CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" \ + LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-common-cxxflags.sh \ + --no-ulapi \ + --core-include \ + --core-src +) +mapfile -t common_flags <<<"$common_flag_output" + +objects=() +source_index=0 +for source in "${sources[@]}"; do + objects+=("$cache_dir/source_${source_index}.o") + source_index=$((source_index + 1)) +done + +signature="$cache_dir/native-bridge-objects.signature" +next_signature="$cache_dir/native-bridge-objects.signature.next" +{ + printf 'CXX=%s\n' "$cxx" + printf 'PWD=%s\n' "$PWD" + printf 'LINUXCNC_ROOT=%s\n' "$linuxcnc_root" + printf 'BUILD_RULE_VERSION=1\n' + printf 'COMMON_FLAGS=' + printf ' %s' "${common_flags[@]}" + printf '\n' + printf 'SOURCES:\n' + printf '%s\n' "${sources[@]}" +} > "$next_signature" +if [[ ! -f "$signature" ]] || ! cmp -s "$signature" "$next_signature"; then + rm -f "$cache_dir"/source_*.o "$cache_dir"/source_*.d +fi +mv "$next_signature" "$signature" + +makefile="$cache_dir/native-bridge-objects.mk" +{ + printf 'CXX := %s\n' "$cxx" + printf 'COMMON_FLAGS :=' + printf ' %s' "${common_flags[@]}" + printf '\n\n' + printf 'OBJECTS :=' + printf ' %s' "${objects[@]}" + printf '\n\n' + printf 'DEP_FILES :=\n' + for object in "${objects[@]}"; do + printf 'DEP_FILES += %s.d\n' "$object" + done + printf '\n.PHONY: all\n' + printf 'all: $(OBJECTS)\n\n' + + source_index=0 + for source in "${sources[@]}"; do + printf '%s: %s\n' "${objects[$source_index]}" "$source" + printf '\t@$(CXX) $(COMMON_FLAGS) -MMD -MP -MF %s.d -c %s -o %s\n\n' \ + "${objects[$source_index]}" "$source" "${objects[$source_index]}" + source_index=$((source_index + 1)) + done + printf '\n-include $(DEP_FILES)\n' +} > "$makefile" +make --output-sync=target -j"$build_jobs" -f "$makefile" >/dev/null + +printf '%s\n' "${objects[@]}" diff --git a/build-linuxcnc-wasm-link-probe.sh b/build-linuxcnc-wasm-link-probe.sh new file mode 100755 index 0000000..bda88d5 --- /dev/null +++ b/build-linuxcnc-wasm-link-probe.sh @@ -0,0 +1,145 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd "$(dirname "$0")" + +usage() { + echo "usage: $0 --mode rs274ngc-pre|runtime [manifest]" >&2 +} + +mode= +manifest=linuxcnc-rs274-wasm-source-files.txt +while [[ "$#" -gt 0 ]]; do + case "$1" in + --mode) + if [[ "$#" -lt 2 ]]; then + echo "missing --mode value" >&2 + exit 1 + fi + mode=$2 + shift 2 + ;; + --help|-h) + usage + exit 0 + ;; + --*) + usage + echo "unknown wasm link probe option: $1" >&2 + exit 1 + ;; + *) + manifest=$1 + shift + ;; + esac +done +case "$mode" in + rs274ngc-pre|runtime) + ;; + "") + usage + echo "missing --mode" >&2 + exit 1 + ;; + *) + usage + echo "unknown wasm link probe mode: $mode" >&2 + exit 1 + ;; +esac + +linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} +cxx=${CXX:-g++} +if [[ -z ${CXX:-} ]] && command -v ccache >/dev/null 2>&1; then + cxx="ccache $cxx" +fi +build_jobs=${CNC_SIM_BUILD_JOBS:-8} +if ! [[ "$build_jobs" =~ ^[1-9][0-9]*$ ]]; then + echo "CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs" >&2 + exit 1 +fi +cache_dir=${CNC_SIM_WASM_LINK_PROBE_CACHE_DIR-build/wasm-link-probe-cache} +if [[ -z "$cache_dir" ]]; then + echo "CNC_SIM_WASM_LINK_PROBE_CACHE_DIR must not be empty" >&2 + exit 1 +fi +if [[ "$cache_dir" =~ [[:space:]] ]]; then + echo "CNC_SIM_WASM_LINK_PROBE_CACHE_DIR must not contain whitespace: $cache_dir" >&2 + exit 1 +fi +mkdir -p "$cache_dir" +cache_dir=$(cd "$cache_dir" && pwd) +if [[ "$cache_dir" == "/" ]]; then + echo "CNC_SIM_WASM_LINK_PROBE_CACHE_DIR must not be the filesystem root" >&2 + exit 1 +fi + +preflight_cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-"$cache_dir/preflight-cache"} +object_mode=$mode +main_source=core/wasm_shims/rs274ngc_pre_probe_main.cc +target_name=rs274ngc_pre_probe +common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh --profile core20-sections) +mapfile -t common_flags <<<"$common_flag_output" +case "$mode" in + runtime) + object_mode=runtime + main_source=core/wasm_shims/cnc_sim_wasm_runtime_probe_main.cc + target_name=cnc_sim_wasm_runtime_probe + common_flags+=(-DCNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND) + ;; +esac + +object_output=$(CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" LINUXCNC_ROOT="$linuxcnc_root" ./build-linuxcnc-wasm-probe-objects.sh --mode "$object_mode" "$manifest") +mapfile -t objects <<<"$object_output" +manifest=$(cd "$(dirname "$manifest")" && pwd)/$(basename "$manifest") +linuxcnc_root=$(cd "$linuxcnc_root" && pwd) + +mode_dir="$cache_dir/$mode" +mkdir -p "$mode_dir" +exec 8>"$mode_dir/link.lock" +flock 8 + +main_object="$mode_dir/main.o" +target="$mode_dir/$target_name" +signature="$mode_dir/link.signature" +next_signature="$mode_dir/link.signature.next" +{ + printf 'CXX=%s\n' "$cxx" + printf 'PWD=%s\n' "$PWD" + printf 'LINUXCNC_ROOT=%s\n' "$linuxcnc_root" + printf 'MANIFEST=%s\n' "$manifest" + printf 'MODE=%s\n' "$mode" + printf 'BUILD_RULE_VERSION=1\n' + printf 'COMMON_FLAGS=' + printf ' %s' "${common_flags[@]}" + printf '\n' + printf 'MAIN_SOURCE=%s\n' "$main_source" + printf 'OBJECTS:\n' + printf '%s\n' "${objects[@]}" +} > "$next_signature" +if [[ ! -f "$signature" ]] || ! cmp -s "$signature" "$next_signature"; then + rm -f "$main_object" "$main_object.d" "$target" "$mode_dir/runtime.symbols" +fi +mv "$next_signature" "$signature" + +makefile="$mode_dir/link.mk" +{ + printf 'CXX := %s\n' "$cxx" + printf 'COMMON_FLAGS :=' + printf ' %s' "${common_flags[@]}" + printf '\n\n' + printf 'OBJECTS :=' + printf ' %s' "${objects[@]}" + printf '\n\n' + printf '.PHONY: all\n' + printf 'all: %s\n\n' "$target" + printf '%s: %s\n' "$main_object" "$main_source" + printf '\t@$(CXX) $(COMMON_FLAGS) -MMD -MP -MF %s.d -c %s -o %s\n\n' "$main_object" "$main_source" "$main_object" + printf '%s: $(OBJECTS) %s\n' "$target" "$main_object" + printf '\t@$(CXX) $(OBJECTS) %s -Wl,--gc-sections -lfmt -o %s\n\n' "$main_object" "$target" + printf '\n-include %s.d\n' "$main_object" +} > "$makefile" +make --output-sync=target -j"$build_jobs" -f "$makefile" >/dev/null + +printf '%s\n' "$target" diff --git a/build-linuxcnc-wasm-probe-objects.sh b/build-linuxcnc-wasm-probe-objects.sh new file mode 100755 index 0000000..5711ac1 --- /dev/null +++ b/build-linuxcnc-wasm-probe-objects.sh @@ -0,0 +1,196 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd "$(dirname "$0")" + +usage() { + echo "usage: $0 --mode source-objects|rs274ngc-pre|runtime [manifest]" >&2 +} + +mode= +manifest=linuxcnc-rs274-wasm-source-files.txt +while [[ "$#" -gt 0 ]]; do + case "$1" in + --mode) + if [[ "$#" -lt 2 ]]; then + echo "missing --mode value" >&2 + exit 1 + fi + mode=$2 + shift 2 + ;; + --help|-h) + usage + exit 0 + ;; + --*) + usage + echo "unknown wasm probe object cache option: $1" >&2 + exit 1 + ;; + *) + manifest=$1 + shift + ;; + esac +done +case "$mode" in + source-objects|rs274ngc-pre|runtime) + ;; + "") + usage + echo "missing --mode" >&2 + exit 1 + ;; + *) + usage + echo "unknown wasm probe object cache mode: $mode" >&2 + exit 1 + ;; +esac + +linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} +cxx=${CXX:-g++} +if [[ -z ${CXX:-} ]] && command -v ccache >/dev/null 2>&1; then + cxx="ccache $cxx" +fi +build_jobs=${CNC_SIM_BUILD_JOBS:-8} +if ! [[ "$build_jobs" =~ ^[1-9][0-9]*$ ]]; then + echo "CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs" >&2 + exit 1 +fi +cache_dir=${CNC_SIM_WASM_PROBE_OBJECT_CACHE_DIR-build/wasm-probe-object-cache} +if [[ -z "$cache_dir" ]]; then + echo "CNC_SIM_WASM_PROBE_OBJECT_CACHE_DIR must not be empty" >&2 + exit 1 +fi +if [[ "$cache_dir" =~ [[:space:]] ]]; then + echo "CNC_SIM_WASM_PROBE_OBJECT_CACHE_DIR must not contain whitespace: $cache_dir" >&2 + exit 1 +fi +mkdir -p "$cache_dir" +cache_dir=$(cd "$cache_dir" && pwd) +if [[ "$cache_dir" == "/" ]]; then + echo "CNC_SIM_WASM_PROBE_OBJECT_CACHE_DIR must not be the filesystem root" >&2 + exit 1 +fi + +preflight_cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-"$cache_dir/preflight-cache"} +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" "$manifest" --require-rs274-complete +manifest=$(cd "$(dirname "$manifest")" && pwd)/$(basename "$manifest") +linuxcnc_root=$(cd "$linuxcnc_root" && pwd) + +common_flag_profile=core20-sections +case "$mode" in + source-objects) + common_flag_profile=core20 + ;; +esac +common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh --profile "$common_flag_profile") +mapfile -t common_flags <<<"$common_flag_output" +case "$mode" in + runtime) + common_flags+=(-DCNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND) + ;; +esac + +sources=() +shim_output=$(./list-linuxcnc-wasm-safe-shims.sh) +mapfile -t shim_sources <<<"$shim_output" +sources+=("${shim_sources[@]}") +case "$mode" in + source-objects) + ;; + rs274ngc-pre) + project_source_output=$(./list-linuxcnc-wasm-safe-project-sources.sh) + if [[ -n "$project_source_output" ]]; then + mapfile -t project_sources <<<"$project_source_output" + sources+=("${project_sources[@]}") + fi + ;; + runtime) + core_source_output=$(./list-cmake-set-sources.sh cnc_sim_core_sources core/) + mapfile -t core_sources <<<"$core_source_output" + sources+=("${core_sources[@]}") + backend_source_output=$(./list-cmake-set-sources.sh cnc_sim_linuxcnc_rs274_backend_sources core/) + mapfile -t backend_sources <<<"$backend_source_output" + sources+=("${backend_sources[@]}") + ;; +esac +source_output=$(CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core full) +mapfile -t linuxcnc_sources <<<"$source_output" +sources+=("${linuxcnc_sources[@]}") +for source in "${sources[@]}"; do + if [[ -z "$source" ]]; then + echo "wasm probe object source list contains an empty path" >&2 + exit 1 + fi +done + +mode_dir="$cache_dir/$mode" +mkdir -p "$mode_dir" +exec 8>"$mode_dir/objects.lock" +flock 8 + +objects=() +object_index=0 +for source in "${sources[@]}"; do + objects+=("$mode_dir/source_${object_index}.o") + object_index=$((object_index + 1)) +done + +signature="$mode_dir/objects.signature" +next_signature="$mode_dir/objects.signature.next" +{ + printf 'CXX=%s\n' "$cxx" + printf 'PWD=%s\n' "$PWD" + printf 'LINUXCNC_ROOT=%s\n' "$linuxcnc_root" + printf 'MANIFEST=%s\n' "$manifest" + printf 'MODE=%s\n' "$mode" + printf 'BUILD_RULE_VERSION=1\n' + printf 'COMMON_FLAGS=' + printf ' %s' "${common_flags[@]}" + printf '\n' + printf 'SOURCES:\n' + printf '%s\n' "${sources[@]}" +} > "$next_signature" +if [[ ! -f "$signature" ]] || ! cmp -s "$signature" "$next_signature"; then + rm -f "$mode_dir"/source_*.o "$mode_dir"/source_*.d +fi +mv "$next_signature" "$signature" + +makefile="$mode_dir/objects.mk" +{ + printf 'CXX := %s\n' "$cxx" + printf 'COMMON_FLAGS :=' + printf ' %s' "${common_flags[@]}" + printf '\n\n' + printf 'OBJECTS :=' + printf ' %s' "${objects[@]}" + printf '\n\n' + printf 'DEP_FILES :=\n' + for object in "${objects[@]}"; do + printf 'DEP_FILES += %s.d\n' "$object" + done + printf '\n.PHONY: all\n' + printf 'all: $(OBJECTS)\n\n' + + object_index=0 + for source in "${sources[@]}"; do + source_flags=() + case "$source" in + core/src/linuxcnc_corexykins_adapter.c|core/src/linuxcnc_genserfuncs_adapter.c|core/src/linuxcnc_xyzab_tdr_kins_adapter.c) + source_flags+=(-fpermissive) + ;; + esac + printf '%s: %s\n' "${objects[$object_index]}" "$source" + printf '\t@$(CXX) $(COMMON_FLAGS)' + printf ' %s' "${source_flags[@]}" + printf ' -MMD -MP -MF %s.d -c %s -o %s\n\n' "${objects[$object_index]}" "$source" "${objects[$object_index]}" + object_index=$((object_index + 1)) + done + printf '\n-include $(DEP_FILES)\n' +} > "$makefile" +make --output-sync=target -j"$build_jobs" -f "$makefile" >/dev/null + +printf '%s\n' "${objects[@]}" diff --git a/build-linuxcnc-wasm-standalone-probe.sh b/build-linuxcnc-wasm-standalone-probe.sh new file mode 100755 index 0000000..5d35835 --- /dev/null +++ b/build-linuxcnc-wasm-standalone-probe.sh @@ -0,0 +1,196 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd "$(dirname "$0")" + +usage() { + echo "usage: $0 --mode tooldata-link|tooldata-common-link|interp-base-link|python-plugin-link|rs274ngc-pre-object" >&2 +} + +mode= +while [[ "$#" -gt 0 ]]; do + case "$1" in + --mode) + if [[ "$#" -lt 2 ]]; then + echo "missing --mode value" >&2 + exit 1 + fi + mode=$2 + shift 2 + ;; + --help|-h) + usage + exit 0 + ;; + *) + usage + echo "unknown wasm standalone probe option: $1" >&2 + exit 1 + ;; + esac +done +case "$mode" in + tooldata-link|tooldata-common-link|interp-base-link|python-plugin-link|rs274ngc-pre-object) + ;; + "") + usage + echo "missing --mode" >&2 + exit 1 + ;; + *) + usage + echo "unknown wasm standalone probe mode: $mode" >&2 + exit 1 + ;; +esac + +linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} +cxx=${CXX:-g++} +if [[ -z ${CXX:-} ]] && command -v ccache >/dev/null 2>&1; then + cxx="ccache $cxx" +fi +build_jobs=${CNC_SIM_BUILD_JOBS:-8} +if ! [[ "$build_jobs" =~ ^[1-9][0-9]*$ ]]; then + echo "CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs" >&2 + exit 1 +fi +cache_dir=${CNC_SIM_WASM_STANDALONE_PROBE_CACHE_DIR-build/wasm-standalone-probe-cache} +if [[ -z "$cache_dir" ]]; then + echo "CNC_SIM_WASM_STANDALONE_PROBE_CACHE_DIR must not be empty" >&2 + exit 1 +fi +if [[ "$cache_dir" =~ [[:space:]] ]]; then + echo "CNC_SIM_WASM_STANDALONE_PROBE_CACHE_DIR must not contain whitespace: $cache_dir" >&2 + exit 1 +fi +mkdir -p "$cache_dir" +cache_dir=$(cd "$cache_dir" && pwd) +if [[ "$cache_dir" == "/" ]]; then + echo "CNC_SIM_WASM_STANDALONE_PROBE_CACHE_DIR must not be the filesystem root" >&2 + exit 1 +fi + +preflight_cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-"$cache_dir/preflight-cache"} +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" --root-only +linuxcnc_root=$(cd "$linuxcnc_root" && pwd) + +common_flag_args=() +sources=() +target_name= +case "$mode" in + tooldata-link|tooldata-common-link) + if [[ "$mode" == "tooldata-common-link" ]]; then + common_flag_args+=(--tooldata-include) + fi + tooldata_mmap_backend_shim=$(./list-linuxcnc-wasm-safe-shims.sh tooldata_mmap_backend.cc) + tooldata_runtime_stubs_shim=$(./list-linuxcnc-wasm-safe-shims.sh tooldata_runtime_stubs.cc) + tooldata_common_source=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-files.sh src/emc/tooldata/tooldata_common.cc) + sources+=( + "$tooldata_common_source" + "$tooldata_mmap_backend_shim" + "$tooldata_runtime_stubs_shim" + ) + if [[ "$mode" == "tooldata-link" ]]; then + sources+=(core/wasm_shims/tooldata/tooldata_probe_main.cc) + target_name=tooldata_probe + else + sources+=(core/wasm_shims/tooldata/tooldata_common_probe_main.cc) + target_name=tooldata_common_probe + fi + ;; + interp-base-link) + dlfcn_shim=$(./list-linuxcnc-wasm-safe-shims.sh dlfcn.cc) + interp_base_source=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-files.sh src/emc/rs274ngc/interp_base.cc) + sources+=( + "$dlfcn_shim" + "$interp_base_source" + core/wasm_shims/interp_base_probe_main.cc + ) + target_name=interp_base_probe + ;; + python-plugin-link) + python_plugin_shim=$(./list-linuxcnc-wasm-safe-shims.sh python_plugin.cc) + sources+=( + "$python_plugin_shim" + core/wasm_shims/pythonplugin/python_plugin_probe_main.cc + ) + target_name=python_plugin_probe + ;; + rs274ngc-pre-object) + rs274ngc_pre_source=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-files.sh src/emc/rs274ngc/rs274ngc_pre.cc) + sources+=("$rs274ngc_pre_source") + target_name=rs274ngc_pre.o + ;; +esac + +common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh "${common_flag_args[@]}") +mapfile -t common_flags <<<"$common_flag_output" + +mode_dir="$cache_dir/$mode" +mkdir -p "$mode_dir" +exec 8>"$mode_dir/probe.lock" +flock 8 + +objects=() +object_index=0 +for source in "${sources[@]}"; do + objects+=("$mode_dir/source_${object_index}.o") + object_index=$((object_index + 1)) +done +target="$mode_dir/$target_name" +if [[ "$mode" == "rs274ngc-pre-object" ]]; then + target=${objects[0]} +fi + +signature="$mode_dir/probe.signature" +next_signature="$mode_dir/probe.signature.next" +{ + printf 'CXX=%s\n' "$cxx" + printf 'PWD=%s\n' "$PWD" + printf 'LINUXCNC_ROOT=%s\n' "$linuxcnc_root" + printf 'MODE=%s\n' "$mode" + printf 'BUILD_RULE_VERSION=1\n' + printf 'COMMON_FLAGS=' + printf ' %s' "${common_flags[@]}" + printf '\n' + printf 'SOURCES:\n' + printf '%s\n' "${sources[@]}" +} > "$next_signature" +if [[ ! -f "$signature" ]] || ! cmp -s "$signature" "$next_signature"; then + rm -f "$mode_dir"/source_*.o "$mode_dir"/source_*.d "$target" +fi +mv "$next_signature" "$signature" + +makefile="$mode_dir/probe.mk" +{ + printf 'CXX := %s\n' "$cxx" + printf 'COMMON_FLAGS :=' + printf ' %s' "${common_flags[@]}" + printf '\n\n' + printf 'OBJECTS :=' + printf ' %s' "${objects[@]}" + printf '\n\n' + printf 'DEP_FILES :=\n' + for object in "${objects[@]}"; do + printf 'DEP_FILES += %s.d\n' "$object" + done + printf '\n.PHONY: all\n' + printf 'all: %s\n\n' "$target" + + object_index=0 + for source in "${sources[@]}"; do + printf '%s: %s\n' "${objects[$object_index]}" "$source" + printf '\t@$(CXX) $(COMMON_FLAGS) -MMD -MP -MF %s.d -c %s -o %s\n\n' \ + "${objects[$object_index]}" "$source" "${objects[$object_index]}" + object_index=$((object_index + 1)) + done + + if [[ "$mode" != "rs274ngc-pre-object" ]]; then + printf '%s: $(OBJECTS)\n' "$target" + printf '\t@$(CXX) $(OBJECTS) -o %s\n\n' "$target" + fi + printf '\n-include $(DEP_FILES)\n' +} > "$makefile" +make --output-sync=target -j"$build_jobs" -f "$makefile" >/dev/null + +printf '%s\n' "$target" diff --git a/build-wasm.sh b/build-wasm.sh index 0db7908..e11fb9b 100755 --- a/build-wasm.sh +++ b/build-wasm.sh @@ -10,15 +10,18 @@ if ! [[ "$build_jobs" =~ ^[1-9][0-9]*$ ]]; then echo "CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs" >&2 exit 1 fi +cmake_compiler_launcher_mode=none cmake_compiler_launcher_args=() if [[ -z ${CXX:-} ]] && command -v ccache >/dev/null 2>&1; then + cmake_compiler_launcher_mode=ccache cmake_compiler_launcher_args+=( -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache ) fi -LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest" +preflight_cache_dir=build/wasm-preflight-cache +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" "$manifest" manifest=$(cd "$(dirname "$manifest")" && pwd)/$(basename "$manifest") linuxcnc_root=$(cd "$linuxcnc_root" && pwd) default_manifest="$PWD/linuxcnc-rs274-wasm-source-files.txt" @@ -37,6 +40,51 @@ count_nonempty_lines() { printf '%s\n' "$count" } +parallel_probe_labels=() +parallel_probe_logs=() +parallel_probe_pids=() + +run_parallel_wasm_probe() { + local label=$1 + shift + local probe_index=${#parallel_probe_pids[@]} + local log_dir=build/wasm-preflight-cache/parallel-probe-logs + local log="$log_dir/probe_${probe_index}.log" + + mkdir -p "$log_dir" + ( + CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" "$@" + ) >"$log" 2>&1 & + parallel_probe_labels+=("$label") + parallel_probe_logs+=("$log") + parallel_probe_pids+=("$!") +} + +wait_parallel_wasm_probes() { + local failed=0 + local probe_index + local status + + for probe_index in "${!parallel_probe_pids[@]}"; do + echo "${parallel_probe_labels[$probe_index]}" + if wait "${parallel_probe_pids[$probe_index]}"; then + cat "${parallel_probe_logs[$probe_index]}" + else + status=$? + cat "${parallel_probe_logs[$probe_index]}" >&2 || true + echo "${parallel_probe_labels[$probe_index]} failed with status $status" >&2 + failed=1 + fi + done + parallel_probe_labels=() + parallel_probe_logs=() + parallel_probe_pids=() + + if [[ "$failed" -ne 0 ]]; then + exit 1 + fi +} + # This script writes fixed build and public artifact paths, so serialize it. if [[ "${CNC_SIM_BUILD_WASM_SKIP_LOCK:-0}" != "1" ]]; then exec 9>"${TMPDIR:-/tmp}/cnc_sim_build_wasm.lock" @@ -45,8 +93,8 @@ fi manifest_core_count=0 manifest_blocked_count=0 -manifest_core_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core) -manifest_blocked_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" blocked) +manifest_core_output=$(CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core) +manifest_blocked_output=$(CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" blocked) manifest_core_count=$(count_nonempty_lines "$manifest_core_output") manifest_blocked_count=$(count_nonempty_lines "$manifest_blocked_output") @@ -58,55 +106,39 @@ if [[ "$manifest" == "$default_manifest" ]] && [[ "$manifest_core_count" -ne 25 echo "unexpected wasm manifest partition: core=$manifest_core_count blocked=$manifest_blocked_count" >&2 exit 1 fi -LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest" --require-rs274-complete +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" "$manifest" --require-rs274-complete echo "LinuxCNC wasm blocker scan" -./test-linuxcnc-wasm-blockers.sh "$manifest" +CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" ./test-linuxcnc-wasm-blockers.sh "$manifest" echo "LinuxCNC wasm-safe source syntax probe" -./test-linuxcnc-wasm-source-syntax.sh "$manifest" +CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" ./test-linuxcnc-wasm-source-syntax.sh "$manifest" echo "LinuxCNC wasm-safe source object probe" -./test-linuxcnc-wasm-source-objects.sh "$manifest" +CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" ./test-linuxcnc-wasm-source-objects.sh "$manifest" echo "LinuxCNC wasm-safe CMake probe target" -./test-linuxcnc-wasm-cmake-safe-probe.sh "$manifest" +CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" ./test-linuxcnc-wasm-cmake-safe-probe.sh "$manifest" -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 Python C API symbol probe" -./test-linuxcnc-wasm-python-c-api-symbols.sh - -echo "LinuxCNC wasm rs274ngc_pre object probe" -./test-linuxcnc-wasm-rs274ngc-pre-object.sh +run_parallel_wasm_probe "LinuxCNC wasm tooldata shim link probe" ./test-linuxcnc-wasm-tooldata-link.sh +run_parallel_wasm_probe "LinuxCNC wasm tooldata_common link probe" ./test-linuxcnc-wasm-tooldata-common-link.sh +run_parallel_wasm_probe "LinuxCNC wasm tooldata_mmap symbol probe" ./test-linuxcnc-wasm-tooldata-mmap-symbols.sh +run_parallel_wasm_probe "LinuxCNC wasm tooldata runtime symbol probe" ./test-linuxcnc-wasm-tooldata-runtime-symbols.sh +run_parallel_wasm_probe "LinuxCNC wasm interp_base link probe" ./test-linuxcnc-wasm-interp-base-link.sh +run_parallel_wasm_probe "LinuxCNC wasm interp_find link probe" ./test-linuxcnc-wasm-interp-find-link.sh +run_parallel_wasm_probe "LinuxCNC wasm python_plugin link probe" ./test-linuxcnc-wasm-python-plugin-link.sh +run_parallel_wasm_probe "LinuxCNC wasm Python C API symbol probe" ./test-linuxcnc-wasm-python-c-api-symbols.sh +run_parallel_wasm_probe "LinuxCNC wasm rs274ngc_pre object probe" ./test-linuxcnc-wasm-rs274ngc-pre-object.sh +wait_parallel_wasm_probes echo "LinuxCNC wasm rs274ngc_pre link blocker scan" -./test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh "$manifest" +CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" ./test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh "$manifest" echo "LinuxCNC wasm rs274ngc_pre link probe" -./test-linuxcnc-wasm-rs274ngc-pre-link.sh "$manifest" +CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" ./test-linuxcnc-wasm-rs274ngc-pre-link.sh "$manifest" echo "LinuxCNC wasm runtime link probe" -./test-linuxcnc-wasm-runtime-link.sh "$manifest" +CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" ./test-linuxcnc-wasm-runtime-link.sh "$manifest" missing_tools=() for required_tool in cmake emcmake emcc node; do @@ -120,14 +152,37 @@ if ((${#missing_tools[@]} > 0)); then exit 1 fi +wasm_build_signature=build/wasm/wasm-build.signature +wasm_build_next_signature=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_wasm_build.signature.XXXXXX") +{ + printf 'CXX=%s\n' "${CXX:-g++}" + printf 'PWD=%s\n' "$PWD" + printf 'LINUXCNC_ROOT=%s\n' "$linuxcnc_root" + printf 'MANIFEST=%s\n' "$manifest" + printf 'MANIFEST_HASH=' + sha256sum "$manifest" + printf 'COMPILER_LAUNCHER=%s\n' "$cmake_compiler_launcher_mode" + printf 'CMAKE_LISTS=' + sha256sum core/CMakeLists.txt + printf 'CONFIGURE_RULE_VERSION=1\n' +} > "$wasm_build_next_signature" +configure_wasm=1 +if [[ ! -f "$wasm_build_signature" ]] || ! cmp -s "$wasm_build_signature" "$wasm_build_next_signature"; then + mkdir -p build/wasm +elif [[ -f build/wasm/CMakeCache.txt ]]; then + configure_wasm=0 +fi + +if [[ "$configure_wasm" == "1" ]]; then emcmake cmake -S core -B build/wasm \ -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_compiler_launcher_args[@]}" -cmake --build build/wasm --target cnc_sim_wasm_runtime_probe --parallel "$build_jobs" -cmake --build build/wasm --target cnc_sim_wasm --parallel "$build_jobs" +fi +mv "$wasm_build_next_signature" "$wasm_build_signature" +cmake --build build/wasm --target cnc_sim_wasm_runtime_probe cnc_sim_wasm --parallel "$build_jobs" mkdir -p web/public cp build/wasm/cnc_sim.js web/public/cnc_sim.js cp build/wasm/cnc_sim.wasm web/public/cnc_sim.wasm diff --git a/check-linuxcnc-inputs-cached.sh b/check-linuxcnc-inputs-cached.sh new file mode 100755 index 0000000..77c1107 --- /dev/null +++ b/check-linuxcnc-inputs-cached.sh @@ -0,0 +1,131 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd "$(dirname "$0")" + +cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-} +check_args=("$@") +if [[ "${1:-}" == "--cache-dir" ]]; then + if [[ "$#" -lt 2 ]]; then + echo "missing --cache-dir value" >&2 + exit 1 + fi + cache_dir=$2 + shift 2 + check_args=("$@") +fi + +manifest=${1:-} +require_complete=${2:-} +linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} + +if [[ -z "$cache_dir" ]]; then + LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "${check_args[@]}" + exit 0 +fi +if [[ "$cache_dir" =~ [[:space:]] ]]; then + echo "CNC_SIM_PREFLIGHT_CACHE_DIR must not contain whitespace: $cache_dir" >&2 + exit 1 +fi + +mkdir -p "$cache_dir" +cache_dir=$(cd "$cache_dir" && pwd) +if [[ "$cache_dir" == "/" ]]; then + echo "CNC_SIM_PREFLIGHT_CACHE_DIR must not be the filesystem root" >&2 + exit 1 +fi +exec 8>"$cache_dir/check-linuxcnc-inputs.lock" +flock 8 + +if [[ ! -d "$linuxcnc_root" ]]; then + LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "${check_args[@]}" + exit 0 +fi +linuxcnc_root=$(cd "$linuxcnc_root" && pwd) + +manifest_key=ROOT_ONLY +manifest_path= +if [[ -n "$manifest" && "$manifest" != "--root-only" ]]; then + if [[ ! -f "$manifest" ]]; then + LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "${check_args[@]}" + exit 0 + fi + manifest_path=$(cd "$(dirname "$manifest")" && pwd)/$(basename "$manifest") + manifest_key=$(printf '%s' "$manifest_path" | sha256sum | awk '{ print $1 }') +fi + +safe_require=${require_complete:-none} +safe_require=${safe_require//[^A-Za-z0-9_.-]/_} +args_key=$( + { + printf 'ARGS=' + printf ' %q' "${check_args[@]}" + printf '\n' + } | sha256sum | awk '{ print $1 }' +) +cache_file="$cache_dir/check-linuxcnc-inputs.${manifest_key}.${safe_require}.${args_key}.signature" +next_signature=$(mktemp "$cache_file.next.XXXXXX") + +write_source_stat_signature() { + local source_path + [[ -z "$manifest_path" ]] && return 0 + while IFS=: read -r group path note extra; do + case "$group" in + ""|\#*) + continue + ;; + esac + [[ -z "${path:-}" ]] && continue + source_path="$linuxcnc_root/$path" + if [[ ! -f "$source_path" ]]; then + return 1 + fi + stat -c 'SOURCE=%n:%s:%y' "$source_path" + done < "$manifest_path" +} + +write_complete_mode_signature() { + case "$require_complete" in + --require-rs274-complete) + find "$linuxcnc_root/src/emc/rs274ngc" -maxdepth 1 -type f \ + -printf 'RS274=%P:%s:%T@\n' | sort + ;; + --require-kinematics-complete) + for source_dir in \ + "$linuxcnc_root/src/emc/kinematics" \ + "$linuxcnc_root/src/libnml/posemath" \ + "$linuxcnc_root/configs/sim"; do + [[ -d "$source_dir" ]] || return 1 + find "$source_dir" -type f -printf "KIN=$source_dir/%P:%s:%T@\n" | sort + done + ;; + esac +} + +if ! { + printf 'CHECK_SCRIPT=' + sha256sum check-linuxcnc-inputs.sh + printf 'LINUXCNC_ROOT=%s\n' "$linuxcnc_root" + printf 'ARGS=' + printf ' %q' "${check_args[@]}" + printf '\n' + if [[ -n "$manifest_path" ]]; then + printf 'MANIFEST_PATH=%s\n' "$manifest_path" + printf 'MANIFEST=' + sha256sum "$manifest_path" + write_source_stat_signature + fi + write_complete_mode_signature +} > "$next_signature"; then + rm -f "$next_signature" + LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "${check_args[@]}" + exit 0 +fi + +if [[ -f "$cache_file" ]] && cmp -s "$cache_file" "$next_signature"; then + rm -f "$next_signature" + exit 0 +fi + +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "${check_args[@]}" +mv "$next_signature" "$cache_file" diff --git a/check-linuxcnc-switchkins-remap-table-cached.sh b/check-linuxcnc-switchkins-remap-table-cached.sh new file mode 100755 index 0000000..b1c9f27 --- /dev/null +++ b/check-linuxcnc-switchkins-remap-table-cached.sh @@ -0,0 +1,111 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd "$(dirname "$0")" + +cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-} +check_args=("$@") +if [[ "${1:-}" == "--cache-dir" ]]; then + if [[ "$#" -lt 2 ]]; then + echo "missing --cache-dir value" >&2 + exit 1 + fi + cache_dir=$2 + shift 2 + check_args=("$@") +fi + +linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} +manifest=${1:-linuxcnc-kinematics-source-files.txt} + +if [[ -z "$cache_dir" ]]; then + LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-switchkins-remap-table.sh "${check_args[@]}" + exit 0 +fi +if [[ "$cache_dir" =~ [[:space:]] ]]; then + echo "CNC_SIM_PREFLIGHT_CACHE_DIR must not contain whitespace: $cache_dir" >&2 + exit 1 +fi + +mkdir -p "$cache_dir" +cache_dir=$(cd "$cache_dir" && pwd) +if [[ "$cache_dir" == "/" ]]; then + echo "CNC_SIM_PREFLIGHT_CACHE_DIR must not be the filesystem root" >&2 + exit 1 +fi +exec 8>"$cache_dir/check-linuxcnc-switchkins-remap-table.lock" +flock 8 + +if [[ ! -d "$linuxcnc_root" || ! -f "$manifest" ]]; then + LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-switchkins-remap-table.sh "${check_args[@]}" + exit 0 +fi +linuxcnc_root=$(cd "$linuxcnc_root" && pwd) +manifest_path=$(cd "$(dirname "$manifest")" && pwd)/$(basename "$manifest") +manifest_key=$(printf '%s' "$manifest_path" | sha256sum | awk '{ print $1 }') +args_key=$( + { + printf 'ARGS=' + printf ' %q' "${check_args[@]}" + printf '\n' + } | sha256sum | awk '{ print $1 }' +) +cache_file="$cache_dir/check-linuxcnc-switchkins-remap-table.${manifest_key}.${args_key}.signature" +next_signature=$(mktemp "$cache_file.next.XXXXXX") + +write_manifest_source_stats() { + local source_path + while IFS=: read -r group path note extra; do + case "$group" in + ""|\#*) + continue + ;; + esac + [[ -z "${path:-}" ]] && continue + source_path="$linuxcnc_root/$path" + if [[ ! -f "$source_path" ]]; then + return 1 + fi + stat -c 'SOURCE=%n:%s:%y' "$source_path" + done < "$manifest_path" +} + +write_project_output_hashes() { + local project_output + for project_output in \ + core/src/linuxcnc_switchkins_remap_table.inc \ + core/tests/linuxcnc_switchkins_remap_config_cases.inc \ + web/public/linuxcnc_switchkins_remap_config_cases.json; do + [[ -f "$project_output" ]] || return 1 + sha256sum "$project_output" + done +} + +if ! { + printf 'CHECK_SCRIPT=' + sha256sum check-linuxcnc-switchkins-remap-table.sh + printf 'GENERATOR_SCRIPT=' + sha256sum generate-linuxcnc-switchkins-remap-table.sh + printf 'LINUXCNC_ROOT=%s\n' "$linuxcnc_root" + printf 'ARGS=' + printf ' %q' "${check_args[@]}" + printf '\n' + printf 'MANIFEST_PATH=%s\n' "$manifest_path" + printf 'MANIFEST=' + sha256sum "$manifest_path" + write_manifest_source_stats + printf 'PROJECT_OUTPUTS:\n' + write_project_output_hashes +} > "$next_signature"; then + rm -f "$next_signature" + LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-switchkins-remap-table.sh "${check_args[@]}" + exit 0 +fi + +if [[ -f "$cache_file" ]] && cmp -s "$cache_file" "$next_signature"; then + rm -f "$next_signature" + exit 0 +fi + +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-switchkins-remap-table.sh "${check_args[@]}" +mv "$next_signature" "$cache_file" diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 5589b2b..629d541 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -196,84 +196,6 @@ if(CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE) target_compile_definitions(linuxcnc_rs274_wasm_safe_probe_objects PRIVATE FMT_HEADER_ONLY=1) endif() - 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_rs274_backend.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_tooldata_fixture.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_5axiskins_adapter.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_5axiskins_adapter.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_corexykins_adapter.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_corexykins_adapter.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_cubic_adapter.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_cubic_adapter.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_genserfuncs_adapter.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_genserfuncs_adapter.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_genhexkins_adapter.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_genhexkins_adapter.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_kins_util_adapter.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_kins_util_adapter.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_lineardeltakins_adapter.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_lineardeltakins_adapter.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_maxkins_adapter.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_maxkins_adapter.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_pentakins_adapter.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_pentakins_adapter.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_pumakins_adapter.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_pumakins_adapter.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_rotatekins_adapter.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_rotatekins_adapter.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_rosekins_adapter.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_rosekins_adapter.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_rotarydeltakins_adapter.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_rotarydeltakins_adapter.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_scarakins_adapter.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_scarakins_adapter.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_scorbot_kins_adapter.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_scorbot_kins_adapter.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_tripodkins_adapter.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_tripodkins_adapter.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_trtfuncs_adapter.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_trtfuncs_adapter.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_userkfuncs_adapter.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_userkfuncs_adapter.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_xyzab_tdr_kins_adapter.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_xyzab_tdr_kins_adapter.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/rtcp_kinematics.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/simulator_gcode_controls.cpp - ) - - add_executable(linuxcnc_rs274_wasm_safe_probe EXCLUDE_FROM_ALL - $ - ${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 - CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND - CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE - ULAPI - ) - target_compile_options(linuxcnc_rs274_wasm_safe_probe PRIVATE ${cnc_sim_wasm_safe_preinclude_options}) - 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 - ) - if(EMSCRIPTEN) - target_compile_definitions(linuxcnc_rs274_wasm_safe_probe PRIVATE FMT_HEADER_ONLY=1) - else() - target_link_libraries(linuxcnc_rs274_wasm_safe_probe PRIVATE fmt) - endif() - set(cnc_sim_wasm_runtime_probe_main ${CMAKE_CURRENT_SOURCE_DIR}/wasm_shims/cnc_sim_wasm_runtime_probe_main.cc ) @@ -371,6 +293,37 @@ if(CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE) endif() if(CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE) + add_executable(linuxcnc_rs274_wasm_safe_probe EXCLUDE_FROM_ALL + $ + $ + ${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 + CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND + CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE + ULAPI + ) + target_compile_options(linuxcnc_rs274_wasm_safe_probe PRIVATE ${cnc_sim_wasm_safe_preinclude_options}) + 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 + ) + if(EMSCRIPTEN) + target_compile_definitions(linuxcnc_rs274_wasm_safe_probe PRIVATE FMT_HEADER_ONLY=1) + else() + target_link_libraries(linuxcnc_rs274_wasm_safe_probe PRIVATE fmt) + endif() + add_executable(cnc_sim_wasm_runtime_probe EXCLUDE_FROM_ALL $ $ diff --git a/docs/linuxcnc-source-policy.md b/docs/linuxcnc-source-policy.md index 2728ad5..daa8807 100644 --- a/docs/linuxcnc-source-policy.md +++ b/docs/linuxcnc-source-policy.md @@ -44,30 +44,170 @@ Required workflow for every functional change: and guardrail checks: `./test-all-native.sh`, `./test-linuxcnc-source-syntax.sh`, and `./test-linuxcnc-wasm-cmake-safe-probe.sh`. + When `./test-native.sh` and `./test-linuxcnc-source-link.sh` have already + been run in the same pass, use + `CNC_SIM_ALL_NATIVE_GUARDRAILS_ONLY=1 ./test-all-native.sh` for the + aggregate guardrail pass instead of recompiling the same native/source-link + targets again. +8. Keep `./test-linuxcnc-wasm-cmake-safe-probe.sh` focused on the actual CMake + probe by default. Run + `CNC_SIM_WASM_CMAKE_SAFE_PROBE_SELF_CHECKS=1 ./test-linuxcnc-wasm-cmake-safe-probe.sh` + only when changing its meta-guardrails or the surrounding build-wasm policy. Efficiency rules: +- Each pass should advance one reasonable, reviewable context. Do not use a + nearby failing smoke assertion, broad fixture, or convenient touched file as + permission to expand the pass into another workstream. - Do not add a smoke-only behavior path when the LinuxCNC-backed route already exists; add coverage to the source-backed route instead. +- Smoke tests are verification guardrails. They must not be expanded with new + functional CNC behavior unless that behavior is directly routed to, copied + from, or checked against the relevant LinuxCNC source in the same narrow + context. - Do not mix generator/table work, manifests, OPFS/browser work, and build policy cleanup in the same pass unless the files directly depend on each other. - Prefer tightening an existing probe or manifest over adding another broad end-to-end smoke case. +- Do not run the full native aggregate after already running its expensive + native/source-link children in the same pass; use the explicit guardrails-only + aggregate mode for build-policy cleanup. +- Keep meta-guardrails out of hot build paths. The wasm-safe CMake probe may + run extended self-checks with `CNC_SIM_WASM_CMAKE_SAFE_PROBE_SELF_CHECKS=1`, + and the source syntax probe may run extended self-checks with + `CNC_SIM_SOURCE_SYNTAX_SELF_CHECKS=1`. The wasm blocker probe may run + analyzer failure-path self-checks with `CNC_SIM_WASM_BLOCKERS_SELF_CHECKS=1`. + The default actual CMake, syntax, and blocker probe paths remain fast. +- Keep `test-linuxcnc-wasm-source-syntax.sh` on the same persistent incremental + build pattern as the native syntax probe. It must use + `build/wasm-source-syntax-test` by default, dependency files, a signature, + `make --output-sync=target`, and `CNC_SIM_BUILD_JOBS:-8`. +- Reuse source preflight results in persistent build directories through + `check-linuxcnc-inputs-cached.sh`; its signature must include the LinuxCNC + root, manifest content, source file size and nanosecond mtime stats, the check + mode, and the checker script hash. Cache updates must be serialized with a + lock inside the cache directory and must write next signatures through unique + temporary files. + Root-only source, flag, and link helper scripts must also use this cached + preflight, defaulting to `build/linuxcnc-helper-preflight-cache` when a caller + has not supplied `CNC_SIM_PREFLIGHT_CACHE_DIR`. + `build-wasm.sh` must pass its preflight cache into nested build probes such as + `test-linuxcnc-wasm-blockers.sh` and + `test-linuxcnc-wasm-cmake-safe-probe.sh` instead of forcing a second cache + walk in the same pass. +- Cache wasm blocker analyzer output in `test-linuxcnc-wasm-blockers.sh`. Its + signature must include the analyzer script, manifest content, LinuxCNC source + size and nanosecond mtime stats, shim lister script, and shim source hashes. + The cache must be locked and may be overridden with + `CNC_SIM_WASM_BLOCKERS_CACHE_DIR`. +- Reuse switchkins/remap table check results in the same persistent cache with + `check-linuxcnc-switchkins-remap-table-cached.sh`; its signature must include + the checker and generator scripts, the kinematics manifest, referenced + LinuxCNC source size and nanosecond mtime stats, and tracked generated + table/case outputs. +- Reuse wasm probe objects across the wasm source object probe, + `rs274ngc_pre` blocker/link probes, and the runtime link probe through + `build-linuxcnc-wasm-probe-objects.sh`. Its mode-specific object caches must + be keyed by compiler, LinuxCNC root, manifest, flags, and source list, and + must include make dependency files so repeated build-wasm passes and the + paired `rs274ngc_pre` probes do not recompile unchanged LinuxCNC core files, + shims, or kinematics adapters. +- Reuse standalone wasm probe objects and binaries for the tooldata link, + tooldata_common link, `interp_base` link, python_plugin link, and + `rs274ngc_pre` object probes through + `build-linuxcnc-wasm-standalone-probe.sh`. Its mode-specific caches must keep + compiler, LinuxCNC root, flags, source list, dependency files, and a lock so + repeated build-wasm passes do not rebuild unchanged probe inputs. +- Reuse wasm final link probes for `rs274ngc_pre` and runtime link checks + through `build-linuxcnc-wasm-link-probe.sh`. It must consume + `build-linuxcnc-wasm-probe-objects.sh`, cache probe main objects and linked + executables with dependency files and a lock, and keep runtime symbol checks + in `test-linuxcnc-wasm-runtime-link.sh`. +- Cache the runtime link probe symbol table in `test-linuxcnc-wasm-runtime-link.sh` + using the probe binary size and mtime plus an explicit `SYMBOL_RULE_VERSION`, + so repeated build-wasm passes do not rerun `nm -C` on an unchanged probe. +- Cache the `rs274ngc_pre` pre-link undefined-symbol scan in + `test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh`. Its signature must + include the LinuxCNC root, manifest, object list, object size and mtime stats, + and an explicit `FILTER_RULE_VERSION`, with a lock and + `CNC_SIM_WASM_RS274NGC_PRE_LINK_BLOCKERS_CACHE_DIR` override. +- Keep `test-linuxcnc-wasm-python-c-api-symbols.sh` persistent for repeated + build-wasm passes. It must use `build/wasm-python-c-api-symbols`, + dependency files, a build signature, cached source token and object symbol + signatures, `make --output-sync=target`, and `CNC_SIM_BUILD_JOBS:-8` while + still deriving required Python C API symbols from LinuxCNC RS274 Python/remap + sources. +- Keep `test-linuxcnc-wasm-interp-find-link.sh` persistent for repeated + build-wasm passes. It must use `build/wasm-interp-find-link`, dependency + files, a signature, `make --output-sync=target`, and `CNC_SIM_BUILD_JOBS:-8` + while still linking LinuxCNC `interp_find.cc` with the wasm-safe tooldata and + RTAPI shims. +- Keep tooldata symbol probes persistent for repeated build-wasm passes. + `test-linuxcnc-wasm-tooldata-mmap-symbols.sh` must use + `build/wasm-tooldata-mmap-symbols`, and + `test-linuxcnc-wasm-tooldata-runtime-symbols.sh` must use + `build/wasm-tooldata-runtime-symbols`; both must use dependency files, + build signatures, cached `nm --defined-only` symbol export signatures, + `make --output-sync=target`, and `CNC_SIM_BUILD_JOBS:-8` while comparing wasm + shim exports against LinuxCNC tooldata sources. - Default native, source-link, build-wasm, and wasm-safe CMake probe parallelism to `CNC_SIM_BUILD_JOBS:-8`; raise it only by explicit positive-integer environment override. -- When `ccache` is available and `CXX` is unset, native and wasm build paths - should enable compiler caching automatically rather than requiring a manual - wrapper. -- Persistent native, source-link, and wasm-safe CMake probe build directories - may be overridden for local workflows, but must not point at the filesystem - root. +- Persistent object/stamp signatures must not include `CNC_SIM_BUILD_JOBS`. + Parallelism affects scheduling only; changing it must not invalidate otherwise + unchanged LinuxCNC source, shim, syntax, or CMake probe build outputs. +- Persistent native, source-object, source-link, source-syntax, RS274 native, + bridge native, API native, shared bridge-object, wasm source-syntax, + wasm probe-object, wasm link-probe, wasm standalone-probe, wasm Python C API, + wasm interp_find, and wasm tooldata symbol signatures must include an explicit + `BUILD_RULE_VERSION` instead of hashing the whole probe script. Bump that + version when generated compile or link rule semantics change; ordinary + guardrail and smoke assertion edits must not invalidate unchanged LinuxCNC + source, shim, or project objects. +- The LinuxCNC canon bridge native smoke probe must use persistent + `build/bridge-native-test` objects, dependency files, a build-rule signature, + `make --output-sync=target`, and `CNC_SIM_BUILD_JOBS:-8`; it must not compile + all bridge sources in a throwaway temporary directory on every aggregate pass. + Its 41 LinuxCNC bridge/project objects must be built through the shared + `build-linuxcnc-native-bridge-objects.sh` cache so the RS274 API native smoke + probe can link the same objects instead of recompiling them. +- The LinuxCNC RS274 API native smoke probe must use persistent + `build/api-native-test` objects, dependency files, a build-rule signature, + `make --output-sync=target`, and `CNC_SIM_BUILD_JOBS:-8`; it must keep + resetting its RS274 parameter file from LinuxCNC `tests/halui/jogging/sim.var` + before running the smoke executable. It must reuse the shared bridge object + cache and compile only its extra API/backend sources locally. Its LinuxCNC + RS274 backend compile define must be applied only to source files that use + that define, currently `core/src/gcode_backend.cpp`, so unrelated bridge and + kinematics adapter objects are not rebuilt by backend-specific rule changes. +- When `ccache` is available and `CXX` is unset, native, source-link, + source-syntax, and wasm build paths should enable compiler caching + automatically rather than requiring a manual wrapper, including the + wasm-safe CMake probe. +- Persistent native, source-link, source-syntax, and wasm-safe CMake probe + build directories, plus the wasm source-syntax, Python C API, and interp_find + probe build directories, tooldata symbol probe build directories, and wasm + standalone/link probe cache directories may be overridden for local workflows, + but must not point at the filesystem root. - Build policy knobs are intentionally narrow: `CNC_SIM_BUILD_JOBS`, `CNC_SIM_NATIVE_BUILD_DIR`, - `CNC_SIM_SOURCE_LINK_BUILD_DIR`, `CNC_SIM_SOURCE_SYNTAX_BUILD_DIR`, and - `CNC_SIM_WASM_CMAKE_SAFE_PROBE_BUILD_DIR`. Build directory overrides must - stay non-empty, whitespace-free, and outside the filesystem root. + `CNC_SIM_SOURCE_LINK_BUILD_DIR`, `CNC_SIM_SOURCE_SYNTAX_BUILD_DIR`, + `CNC_SIM_SOURCE_OBJECTS_BUILD_DIR`, `CNC_SIM_BRIDGE_NATIVE_BUILD_DIR`, + `CNC_SIM_API_NATIVE_BUILD_DIR`, `CNC_SIM_NATIVE_BRIDGE_OBJECT_CACHE_DIR`, and + `CNC_SIM_RS274_NATIVE_BUILD_DIR`, and + `CNC_SIM_WASM_CMAKE_SAFE_PROBE_BUILD_DIR`. The wasm source-syntax probe also + accepts `CNC_SIM_WASM_SOURCE_SYNTAX_BUILD_DIR` because it owns a persistent + incremental cache. The wasm Python C API probe accepts + `CNC_SIM_WASM_PYTHON_C_API_BUILD_DIR` for the same reason, and the + wasm interp_find probe accepts `CNC_SIM_WASM_INTERP_FIND_BUILD_DIR`. + Tooldata symbol probes accept + `CNC_SIM_WASM_TOOLDATA_MMAP_SYMBOLS_BUILD_DIR` and + `CNC_SIM_WASM_TOOLDATA_RUNTIME_SYMBOLS_BUILD_DIR`. + Standalone wasm probes accept `CNC_SIM_WASM_STANDALONE_PROBE_CACHE_DIR`. + Final wasm link probes accept `CNC_SIM_WASM_LINK_PROBE_CACHE_DIR`. + Build directory overrides must stay non-empty, whitespace-free, and outside + the filesystem root. - `build-wasm.sh` keeps fixed artifact paths: CMake writes `build/wasm`, then the script copies and byte-compares `cnc_sim.js` and `cnc_sim.wasm` under `web/public` before running Node and browser smoke checks. @@ -82,23 +222,61 @@ Build and source-link guardrails: - `test-linuxcnc-source-syntax.sh` uses persistent `build/source-syntax-test` stamps, dependency files, and `source-syntax.signature`. +- `test-linuxcnc-source-objects.sh` uses persistent + `build/source-objects-test` objects, dependency files, and + `source-objects.signature`. +- `test-linuxcnc-rs274-native.sh` uses persistent + `build/rs274-native-test` objects, dependency files, and + `rs274-native.signature`. - `test-linuxcnc-wasm-cmake-safe-probe.sh` uses persistent - `build/wasm-cmake-safe-probe` and `wasm-cmake-safe-probe.signature`. + `build/wasm-cmake-safe-probe` and `wasm-cmake-safe-probe.signature`; signature + changes must reconfigure in place, its signature must include an explicit + `CONFIGURE_RULE_VERSION`, and its two probe executables must be built with one + combined `cmake --build` invocation. +- `test-linuxcnc-wasm-source-syntax.sh` uses persistent + `build/wasm-source-syntax-test` stamps, dependency files, and + `wasm-source-syntax.signature`. +- `test-linuxcnc-wasm-python-c-api-symbols.sh` uses persistent + `build/wasm-python-c-api-symbols` objects, dependency files, and + `python-c-api.signature`. +- `test-linuxcnc-wasm-interp-find-link.sh` uses persistent + `build/wasm-interp-find-link` objects, dependency files, and + `interp-find.signature`. +- `test-linuxcnc-wasm-tooldata-mmap-symbols.sh` uses persistent + `build/wasm-tooldata-mmap-symbols` objects, dependency files, and + `tooldata-mmap-symbols.signature`. +- `test-linuxcnc-wasm-tooldata-runtime-symbols.sh` uses persistent + `build/wasm-tooldata-runtime-symbols` objects, dependency files, and + `tooldata-runtime-symbols.signature`. - Native, source-link, source-syntax, wasm-safe CMake probe, and build-wasm paths must reject empty, whitespace-containing, or filesystem-root build directory overrides before doing destructive cleanup. - Native, source-link, source-syntax, and build-wasm entry points must serialize shared non-concurrency-safe paths with lock files before writing fixed outputs. -- Source-link and syntax probes must build from generated makefiles with - `-MMD -MP` dependency tracking and `make --output-sync=target`. +- `CNC_SIM_ALL_NATIVE_GUARDRAILS_ONLY=1 ./test-all-native.sh` must run the + aggregate guardrail and failure-path assertions without invoking the expensive + child native/source-link compile and smoke scripts. +- Native, source-object, source-link, RS274 native, and syntax probes must build + from generated makefiles with `-MMD -MP` dependency tracking and + `make --output-sync=target`. - `build-wasm.sh` must run blocker, syntax, object, CMake, tooldata, `interp_*`, Python, `rs274ngc_pre`, and runtime link probes before copying browser artifacts. +- Independent tooldata, `interp_*`, Python, and `rs274ngc_pre` object probes in + `build-wasm.sh` may run in parallel, but their logs must be replayed in a + deterministic order and the script must wait for all of them before + `rs274ngc_pre` link checks. - `build-wasm.sh` must check `cmake`, `emcmake`, `emcc`, and `node` before the Emscripten configure step. +- `build-wasm.sh` must reuse a persistent `build/wasm` configure signature + keyed by manifest, `core/CMakeLists.txt`, an explicit + `CONFIGURE_RULE_VERSION`, LinuxCNC root, and compiler launcher. The signature + must not include `CNC_SIM_BUILD_JOBS`, and signature changes must reconfigure + the existing build tree in place instead of deleting compiled objects. - `build-wasm.sh` must build `cnc_sim_wasm_runtime_probe` before - `cnc_sim_wasm`. + `cnc_sim_wasm`, using one combined `cmake --build` invocation for both + targets. - `build-wasm.sh` must copy `build/wasm/cnc_sim.js` and `build/wasm/cnc_sim.wasm` exactly once each. - `build-wasm.sh` must compare copied artifacts with `cmp -s` before running diff --git a/list-linuxcnc-manifest-sources.sh b/list-linuxcnc-manifest-sources.sh index 6ea8322..98261f8 100755 --- a/list-linuxcnc-manifest-sources.sh +++ b/list-linuxcnc-manifest-sources.sh @@ -23,7 +23,8 @@ checks_duplicate_group() { [[ "$groups" == "all" ]] || contains_group "$groups" "$group" } -LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest" +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh "$manifest" +linuxcnc_root=$(cd "$linuxcnc_root" && pwd) if [[ "$filter_group" != "all" ]] && ! contains_group "$allowed_groups" "$filter_group"; then echo "unknown manifest source filter: $filter_group" >&2 @@ -52,9 +53,11 @@ while IFS=: read -r group path note; do exit 1 fi - full_path=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-files.sh \ - --missing-message "$missing_source_message" \ - "$path") + full_path="$linuxcnc_root/$path" + if [[ ! -f "$full_path" ]]; then + echo "$missing_source_message: $path" >&2 + exit 1 + fi if checks_duplicate_group "$duplicate_groups" "$group"; then if printf '%s\n' "${seen_sources[@]}" | grep -Fx -- "$path" >/dev/null; then echo "duplicate manifest source: $path" >&2 diff --git a/list-linuxcnc-native-linkflags.sh b/list-linuxcnc-native-linkflags.sh index 8f5a79c..c2fde32 100755 --- a/list-linuxcnc-native-linkflags.sh +++ b/list-linuxcnc-native-linkflags.sh @@ -4,8 +4,9 @@ set -euo pipefail cd "$(dirname "$0")" linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} +preflight_cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-build/linuxcnc-helper-preflight-cache} -LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" --root-only printf '%s\n' \ -L "$linuxcnc_root/lib" \ diff --git a/list-linuxcnc-source-common-cxxflags.sh b/list-linuxcnc-source-common-cxxflags.sh index da110da..48b0ad7 100755 --- a/list-linuxcnc-source-common-cxxflags.sh +++ b/list-linuxcnc-source-common-cxxflags.sh @@ -4,6 +4,7 @@ set -euo pipefail cd "$(dirname "$0")" linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} +preflight_cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-build/linuxcnc-helper-preflight-cache} include_python=0 include_core_include=0 include_core_src=0 @@ -43,7 +44,7 @@ while [[ "$#" -gt 0 ]]; do esac done -LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" --root-only python_includes=() if [[ "$include_python" -eq 1 ]]; then diff --git a/list-linuxcnc-source-files.sh b/list-linuxcnc-source-files.sh index db41f00..ec94e7e 100755 --- a/list-linuxcnc-source-files.sh +++ b/list-linuxcnc-source-files.sh @@ -5,8 +5,9 @@ cd "$(dirname "$0")" linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} missing_source_message="missing LinuxCNC source" +preflight_cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-build/linuxcnc-helper-preflight-cache} -LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" --root-only while [[ "$#" -gt 0 ]]; do case "$1" in diff --git a/list-linuxcnc-source-support-objects.sh b/list-linuxcnc-source-support-objects.sh index 53f95f5..38dfef7 100755 --- a/list-linuxcnc-source-support-objects.sh +++ b/list-linuxcnc-source-support-objects.sh @@ -5,8 +5,9 @@ cd "$(dirname "$0")" linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} filter=${1:-} +preflight_cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-build/linuxcnc-helper-preflight-cache} -LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" --root-only objects=( src/objects/emc/rs274ngc/interpmodule.o diff --git a/list-linuxcnc-wasm-common-cxxflags.sh b/list-linuxcnc-wasm-common-cxxflags.sh index feada0e..a778caf 100755 --- a/list-linuxcnc-wasm-common-cxxflags.sh +++ b/list-linuxcnc-wasm-common-cxxflags.sh @@ -4,6 +4,7 @@ set -euo pipefail cd "$(dirname "$0")" linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} +preflight_cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-build/linuxcnc-helper-preflight-cache} std_flag=-std=c++17 include_core_include=0 include_core_src=0 @@ -96,7 +97,7 @@ case "$profile" in ;; esac -LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" --root-only printf '%s\n' \ "$std_flag" \ diff --git a/list-linuxcnc-wasm-safe-project-sources.sh b/list-linuxcnc-wasm-safe-project-sources.sh index 5d8ca84..bcc7deb 100755 --- a/list-linuxcnc-wasm-safe-project-sources.sh +++ b/list-linuxcnc-wasm-safe-project-sources.sh @@ -5,7 +5,10 @@ cd "$(dirname "$0")" filter=${1:-} -./list-cmake-set-sources.sh linuxcnc_rs274_wasm_safe_probe_project_sources | ./list-filtered-existing-paths.sh \ +{ + ./list-cmake-set-sources.sh cnc_sim_core_sources core/ + ./list-cmake-set-sources.sh cnc_sim_linuxcnc_rs274_backend_sources core/ +} | ./list-filtered-existing-paths.sh \ "$filter" \ "missing wasm-safe project source" \ "missing wasm-safe project source in CMake list" diff --git a/test-all-native.sh b/test-all-native.sh index 3c523a4..f4e639f 100755 --- a/test-all-native.sh +++ b/test-all-native.sh @@ -5,6 +5,11 @@ cd "$(dirname "$0")" manifest=${1:-linuxcnc-rs274-source-files.txt} linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} +guardrails_only=${CNC_SIM_ALL_NATIVE_GUARDRAILS_ONLY:-0} +if [[ "$guardrails_only" != "0" && "$guardrails_only" != "1" ]]; then + echo "CNC_SIM_ALL_NATIVE_GUARDRAILS_ONLY must be 0 or 1: $guardrails_only" >&2 + exit 1 +fi missing_root=/tmp/does-not-exist-linuxcnc missing_root_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_native_missing_root.XXXXXX.log") @@ -62,6 +67,54 @@ cleanup_native_test_temps() { trap cleanup_native_test_temps EXIT grep -Fx 'trap cleanup_native_test_temps EXIT' test-all-native.sh >/dev/null +grep -F 'guardrails_only=${CNC_SIM_ALL_NATIVE_GUARDRAILS_ONLY:-0}' test-all-native.sh >/dev/null +grep -F 'CNC_SIM_ALL_NATIVE_GUARDRAILS_ONLY must be 0 or 1: $guardrails_only' test-all-native.sh >/dev/null +grep -F 'all native guardrail checks passed' test-all-native.sh >/dev/null +test -x check-linuxcnc-inputs-cached.sh +grep -F 'CNC_SIM_PREFLIGHT_CACHE_DIR' check-linuxcnc-inputs-cached.sh >/dev/null +grep -F 'sha256sum check-linuxcnc-inputs.sh' check-linuxcnc-inputs-cached.sh >/dev/null +grep -F 'exec 8>"$cache_dir/check-linuxcnc-inputs.lock"' check-linuxcnc-inputs-cached.sh >/dev/null +grep -F 'flock 8' check-linuxcnc-inputs-cached.sh >/dev/null +grep -F 'stat -c '\''SOURCE=%n:%s:%y'\''' check-linuxcnc-inputs-cached.sh >/dev/null +grep -F 'next_signature=$(mktemp "$cache_file.next.XXXXXX")' check-linuxcnc-inputs-cached.sh >/dev/null +test -x check-linuxcnc-switchkins-remap-table-cached.sh +grep -F 'check-linuxcnc-switchkins-remap-table.lock' check-linuxcnc-switchkins-remap-table-cached.sh >/dev/null +grep -F 'sha256sum check-linuxcnc-switchkins-remap-table.sh' check-linuxcnc-switchkins-remap-table-cached.sh >/dev/null +grep -F 'sha256sum generate-linuxcnc-switchkins-remap-table.sh' check-linuxcnc-switchkins-remap-table-cached.sh >/dev/null +grep -F 'stat -c '\''SOURCE=%n:%s:%y'\''' check-linuxcnc-switchkins-remap-table-cached.sh >/dev/null +grep -F 'core/src/linuxcnc_switchkins_remap_table.inc' check-linuxcnc-switchkins-remap-table-cached.sh >/dev/null +grep -F 'core/tests/linuxcnc_switchkins_remap_config_cases.inc' check-linuxcnc-switchkins-remap-table-cached.sh >/dev/null +grep -F 'web/public/linuxcnc_switchkins_remap_config_cases.json' check-linuxcnc-switchkins-remap-table-cached.sh >/dev/null +grep -F 'check-linuxcnc-switchkins-remap-table-cached.sh --cache-dir "$preflight_cache_dir" linuxcnc-kinematics-source-files.txt' test-native.sh >/dev/null +grep -F 'check-linuxcnc-switchkins-remap-table-cached.sh --cache-dir "$preflight_cache_dir" linuxcnc-kinematics-source-files.txt' test-linuxcnc-source-link.sh >/dev/null +grep -F 'CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir"' build-wasm.sh >/dev/null +grep -F 'CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir"' test-linuxcnc-source-link.sh >/dev/null +grep -F 'CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir"' test-linuxcnc-source-syntax.sh >/dev/null +grep -F 'CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null +grep -F 'CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" ./test-linuxcnc-wasm-cmake-safe-probe.sh "$manifest"' build-wasm.sh >/dev/null +grep -F 'preflight_cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-"$build_dir/preflight-cache"}' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null +grep -F 'check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir"' test-native.sh >/dev/null +for helper_script in \ + list-linuxcnc-source-files.sh \ + list-linuxcnc-source-support-objects.sh \ + list-linuxcnc-source-common-cxxflags.sh \ + list-linuxcnc-native-linkflags.sh; do + grep -F 'preflight_cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-build/linuxcnc-helper-preflight-cache}' "$helper_script" >/dev/null + grep -F 'check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" --root-only' "$helper_script" >/dev/null +done +grep -F 'self_checks=${CNC_SIM_SOURCE_SYNTAX_SELF_CHECKS:-0}' test-linuxcnc-source-syntax.sh >/dev/null +grep -F 'CNC_SIM_SOURCE_SYNTAX_SELF_CHECKS must be 0 or 1: $self_checks' test-linuxcnc-source-syntax.sh >/dev/null +grep -F 'CNC_SIM_ALL_NATIVE_GUARDRAILS_ONLY=1 ./test-all-native.sh' docs/linuxcnc-source-policy.md >/dev/null +grep -F 'without invoking the expensive' docs/linuxcnc-source-policy.md >/dev/null +if ! awk ' + index($0, "if [[ \"$guardrails_only\" == \"1\" ]]; then") { guardrail_line = NR } + index($0, "./test-native.sh") { native_line = NR } + index($0, "./test-linuxcnc-source-link.sh \"$manifest\"") { source_link_line = NR } + END { exit !(guardrail_line && native_line && source_link_line && guardrail_line < native_line && guardrail_line < source_link_line) } +' test-all-native.sh; then + echo "native aggregate guardrails-only exit must precede expensive child tests" >&2 + exit 1 +fi if ! awk ' /cleanup_native_test_temps\(\)/ { in_cleanup = 1 } in_cleanup && index($0, "\"$missing_manifest\"") { found_missing_manifest = 1 } @@ -128,20 +181,48 @@ grep -Fx -- "$linuxcnc_root/lib" <<<"$native_link_flags" >/dev/null grep -Fx -- "-Wl,-rpath,$linuxcnc_root/lib" <<<"$native_link_flags" >/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 build_dir_probe_script in \ - test-linuxcnc-source-objects.sh \ - test-linuxcnc-bridge-native.sh \ - test-linuxcnc-rs274-native.sh \ - test-linuxcnc-api-native.sh; do - if ! awk ' - index($0, "trap '\''rm -rf \"$build_dir\"'\'' EXIT") { trap_line = NR } - index($0, "./check-linuxcnc-inputs.sh") && !check_line { check_line = NR } - END { exit !(trap_line && check_line && trap_line < check_line) } - ' "$build_dir_probe_script"; then - echo "$build_dir_probe_script installs build_dir cleanup after input validation" >&2 - exit 1 - fi -done +grep -F 'build_dir=${CNC_SIM_BRIDGE_NATIVE_BUILD_DIR-build/bridge-native-test}' test-linuxcnc-bridge-native.sh >/dev/null +grep -F 'CNC_SIM_BRIDGE_NATIVE_BUILD_DIR must not be empty' test-linuxcnc-bridge-native.sh >/dev/null +grep -F 'CNC_SIM_BRIDGE_NATIVE_BUILD_DIR must not contain whitespace: $build_dir' test-linuxcnc-bridge-native.sh >/dev/null +grep -F 'CNC_SIM_BRIDGE_NATIVE_BUILD_DIR must not be the filesystem root' test-linuxcnc-bridge-native.sh >/dev/null +grep -F 'build_jobs=${CNC_SIM_BUILD_JOBS:-8}' test-linuxcnc-bridge-native.sh >/dev/null +grep -F 'CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs' test-linuxcnc-bridge-native.sh >/dev/null +grep -F 'exec 9>"$build_dir/bridge-native.lock"' test-linuxcnc-bridge-native.sh >/dev/null +grep -F 'check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" --root-only' test-linuxcnc-bridge-native.sh >/dev/null +test -x build-linuxcnc-native-bridge-objects.sh +grep -F 'CNC_SIM_NATIVE_BRIDGE_OBJECT_CACHE_DIR' build-linuxcnc-native-bridge-objects.sh >/dev/null +grep -F 'cache_dir=${CNC_SIM_NATIVE_BRIDGE_OBJECT_CACHE_DIR-build/native-bridge-object-cache}' build-linuxcnc-native-bridge-objects.sh >/dev/null +grep -F 'native-bridge-objects.signature' build-linuxcnc-native-bridge-objects.sh >/dev/null +grep -F "printf 'BUILD_RULE_VERSION=1\n'" build-linuxcnc-native-bridge-objects.sh >/dev/null +grep -F 'make --output-sync=target -j"$build_jobs" -f "$makefile" >/dev/null' build-linuxcnc-native-bridge-objects.sh >/dev/null +grep -F 'build-linuxcnc-native-bridge-objects.sh' test-linuxcnc-bridge-native.sh >/dev/null +grep -F 'BRIDGE_OBJECTS :=' test-linuxcnc-bridge-native.sh >/dev/null +if grep -F 'for source in "${project_sources[@]}"' test-linuxcnc-bridge-native.sh >/dev/null; then + echo "bridge native smoke still compiles shared project sources locally" >&2 + exit 1 +fi +grep -F 'bridge_signature="$build_dir/bridge-native.signature"' test-linuxcnc-bridge-native.sh >/dev/null +grep -F "printf 'BUILD_RULE_VERSION=1\n'" test-linuxcnc-bridge-native.sh >/dev/null +grep -F 'make --output-sync=target -j"$build_jobs" -f "$bridge_makefile"' test-linuxcnc-bridge-native.sh >/dev/null +grep -F -- '-MMD -MP -MF %s.d' test-linuxcnc-bridge-native.sh >/dev/null +grep -F 'build_dir=${CNC_SIM_API_NATIVE_BUILD_DIR-build/api-native-test}' test-linuxcnc-api-native.sh >/dev/null +grep -F 'CNC_SIM_API_NATIVE_BUILD_DIR must not be empty' test-linuxcnc-api-native.sh >/dev/null +grep -F 'CNC_SIM_API_NATIVE_BUILD_DIR must not contain whitespace: $build_dir' test-linuxcnc-api-native.sh >/dev/null +grep -F 'CNC_SIM_API_NATIVE_BUILD_DIR must not be the filesystem root' test-linuxcnc-api-native.sh >/dev/null +grep -F 'build_jobs=${CNC_SIM_BUILD_JOBS:-8}' test-linuxcnc-api-native.sh >/dev/null +grep -F 'CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs' test-linuxcnc-api-native.sh >/dev/null +grep -F 'exec 9>"$build_dir/api-native.lock"' test-linuxcnc-api-native.sh >/dev/null +grep -F 'check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" --root-only' test-linuxcnc-api-native.sh >/dev/null +grep -F 'build-linuxcnc-native-bridge-objects.sh' test-linuxcnc-api-native.sh >/dev/null +grep -F 'api_extra_sources=()' test-linuxcnc-api-native.sh >/dev/null +grep -F 'is_bridge_source "$source"' test-linuxcnc-api-native.sh >/dev/null +grep -F 'BRIDGE_OBJECTS :=' test-linuxcnc-api-native.sh >/dev/null +grep -F 'api_signature="$build_dir/api-native.signature"' test-linuxcnc-api-native.sh >/dev/null +grep -F "printf 'BUILD_RULE_VERSION=1\n'" test-linuxcnc-api-native.sh >/dev/null +grep -F 'make --output-sync=target -j"$build_jobs" -f "$api_makefile"' test-linuxcnc-api-native.sh >/dev/null +grep -F 'core/src/gcode_backend.cpp)' test-linuxcnc-api-native.sh >/dev/null +grep -F 'source_flags+=(-DCNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND)' test-linuxcnc-api-native.sh >/dev/null +grep -F "printf ' %s' \"\${source_flags[@]}\"" test-linuxcnc-api-native.sh >/dev/null if ! awk ' /cleanup_wasm_cmake_probe_temps\(\)/ { in_cleanup = 1 } in_cleanup && index($0, "\"$build_dir\"") { found_build_dir = 1 } @@ -156,14 +237,29 @@ grep -F 'CNC_SIM_SOURCE_LINK_BUILD_DIR must not be empty' test-linuxcnc-source-l grep -F 'CNC_SIM_SOURCE_LINK_BUILD_DIR must not contain whitespace: $build_dir' test-linuxcnc-source-link.sh >/dev/null grep -F 'CNC_SIM_SOURCE_LINK_BUILD_DIR must not be the filesystem root' test-linuxcnc-source-link.sh >/dev/null grep -F 'CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs' test-linuxcnc-source-link.sh >/dev/null +if ! awk ' + index($0, "check-linuxcnc-inputs-cached.sh --cache-dir") && + index($0, "\"$manifest\"") && + !index($0, "--require-rs274-complete") { check_line = NR } + index($0, "./check-linuxcnc-switchkins-remap-table-cached.sh --cache-dir \"$preflight_cache_dir\" linuxcnc-kinematics-source-files.txt") { table_line = NR } + END { exit !(check_line && table_line && check_line < table_line) } +' test-linuxcnc-source-link.sh; then + echo "source-link probe must validate its requested manifest before switchkins table checks" >&2 + exit 1 +fi grep -F 'exec 9>"${TMPDIR:-/tmp}/cnc_sim_rs274_source_link.lock"' test-linuxcnc-source-link.sh >/dev/null grep -F 'source_link_signature="$build_dir/source-link-build.signature"' test-linuxcnc-source-link.sh >/dev/null grep -F 'if [[ ! -f "$source_link_signature" ]] || ! cmp -s "$source_link_signature" "$source_link_next_signature"; then' test-linuxcnc-source-link.sh >/dev/null +grep -F 'mv "$source_link_next_signature" "$source_link_signature"' test-linuxcnc-source-link.sh >/dev/null grep -F 'printf '\''PWD=%s\n'\'' "$PWD"' test-linuxcnc-source-link.sh >/dev/null grep -F 'printf '\''LINUXCNC_ROOT=%s\n'\'' "$(cd "$linuxcnc_root" && pwd)"' test-linuxcnc-source-link.sh >/dev/null +grep -F "printf 'BUILD_RULE_VERSION=1\n'" test-linuxcnc-source-link.sh >/dev/null grep -F 'printf '\''%s: %s\n'\'' "$build_dir/linuxcnc_${linuxcnc_index}.o" "$source"' test-linuxcnc-source-link.sh >/dev/null grep -F 'printf '\''%s: %s\n'\'' "$build_dir/project_${project_index}.o" "$source"' test-linuxcnc-source-link.sh >/dev/null grep -F 'printf '\''\t@$(CXX) $(COMMON_FLAGS) -MMD -MP -MF %s.d -c %s -o %s\n\n'\''' test-linuxcnc-source-link.sh >/dev/null +grep -F 'source_link_makefile="$build_dir/source-link.mk"' test-linuxcnc-source-link.sh >/dev/null +grep -F 'printf '\''\n-include $(DEP_FILES)\n'\''' test-linuxcnc-source-link.sh >/dev/null +grep -F 'make --output-sync=target -j"$build_jobs" -f "$source_link_makefile"' test-linuxcnc-source-link.sh >/dev/null grep -F 'build_dir=${CNC_SIM_NATIVE_BUILD_DIR-build/native-test}' test-native.sh >/dev/null grep -F 'CNC_SIM_NATIVE_BUILD_DIR must not be empty' test-native.sh >/dev/null grep -F 'CNC_SIM_NATIVE_BUILD_DIR must not contain whitespace: $build_dir' test-native.sh >/dev/null @@ -172,9 +268,14 @@ grep -F 'CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs' test-native grep -F 'exec 9>"$build_dir/native-test.lock"' test-native.sh >/dev/null grep -F 'native_signature="$build_dir/native-build.signature"' test-native.sh >/dev/null grep -F 'if [[ ! -f "$native_signature" ]] || ! cmp -s "$native_signature" "$native_next_signature"; then' test-native.sh >/dev/null +grep -F 'mv "$native_next_signature" "$native_signature"' test-native.sh >/dev/null grep -F 'printf '\''PWD=%s\n'\'' "$PWD"' test-native.sh >/dev/null +grep -F "printf 'BUILD_RULE_VERSION=1\n'" test-native.sh >/dev/null grep -F 'printf '\''%s: %s\n'\'' "${core_objects[$core_index]}" "$source"' test-native.sh >/dev/null grep -F 'printf '\''\t@$(CXX) $(SMOKE_CXXFLAGS) -MMD -MP -MF %s.d -c %s -o %s\n\n'\''' test-native.sh >/dev/null +grep -F 'native_makefile="$build_dir/native.mk"' test-native.sh >/dev/null +grep -F 'printf '\''\n-include $(DEP_FILES)\n'\''' test-native.sh >/dev/null +grep -F 'make --output-sync=target -j"$build_jobs" -f "$native_makefile"' test-native.sh >/dev/null grep -F 'build_dir=${CNC_SIM_SOURCE_SYNTAX_BUILD_DIR-build/source-syntax-test}' test-linuxcnc-source-syntax.sh >/dev/null grep -F 'CNC_SIM_SOURCE_SYNTAX_BUILD_DIR must not be empty' test-linuxcnc-source-syntax.sh >/dev/null grep -F 'CNC_SIM_SOURCE_SYNTAX_BUILD_DIR must not contain whitespace: $build_dir' test-linuxcnc-source-syntax.sh >/dev/null @@ -184,24 +285,166 @@ grep -F 'exec 9>"$build_dir/source-syntax.lock"' test-linuxcnc-source-syntax.sh grep -F 'source_list="$build_dir/linuxcnc_source_syntax_sources.txt"' test-linuxcnc-source-syntax.sh >/dev/null grep -F 'source_syntax_signature="$build_dir/source-syntax.signature"' test-linuxcnc-source-syntax.sh >/dev/null grep -F 'if [[ ! -f "$source_syntax_signature" ]] || ! cmp -s "$source_syntax_signature" "$source_syntax_next_signature"; then' test-linuxcnc-source-syntax.sh >/dev/null +grep -F 'mv "$source_syntax_next_signature" "$source_syntax_signature"' test-linuxcnc-source-syntax.sh >/dev/null grep -F 'printf '\''LINUXCNC_ROOT=%s\n'\'' "$(cd "$linuxcnc_root" && pwd)"' test-linuxcnc-source-syntax.sh >/dev/null grep -F 'printf '\''MANIFEST=%s\n'\'' "$(cd "$(dirname "$manifest")" && pwd)/$(basename "$manifest")"' test-linuxcnc-source-syntax.sh >/dev/null -grep -F 'printf '\''BUILD_JOBS=%s\n'\'' "$build_jobs"' test-linuxcnc-source-syntax.sh >/dev/null +grep -F "printf 'BUILD_RULE_VERSION=1\n'" test-linuxcnc-source-syntax.sh >/dev/null +if grep -F 'printf '\''BUILD_JOBS=%s\n'\'' "$build_jobs"' test-linuxcnc-source-syntax.sh >/dev/null; then + echo "source syntax signature must not depend on CNC_SIM_BUILD_JOBS" >&2 + exit 1 +fi grep -F 'printf '\''%s/source_%s.stamp: %s\n'\'' "$build_dir" "$source_index" "$source"' test-linuxcnc-source-syntax.sh >/dev/null grep -F 'printf '\''\t@$(CXX) $(COMMON_FLAGS) -MMD -MP -MF %s/source_%s.d %s\n'\''' test-linuxcnc-source-syntax.sh >/dev/null grep -F 'make --output-sync=target -j"$build_jobs" -f "$source_syntax_makefile"' test-linuxcnc-source-syntax.sh >/dev/null +grep -F 'build_dir=${CNC_SIM_SOURCE_OBJECTS_BUILD_DIR-build/source-objects-test}' test-linuxcnc-source-objects.sh >/dev/null +grep -F 'CNC_SIM_SOURCE_OBJECTS_BUILD_DIR must not be empty' test-linuxcnc-source-objects.sh >/dev/null +grep -F 'CNC_SIM_SOURCE_OBJECTS_BUILD_DIR must not contain whitespace: $build_dir' test-linuxcnc-source-objects.sh >/dev/null +grep -F 'CNC_SIM_SOURCE_OBJECTS_BUILD_DIR must not be the filesystem root' test-linuxcnc-source-objects.sh >/dev/null +grep -F 'CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs' test-linuxcnc-source-objects.sh >/dev/null +grep -F 'exec 9>"$build_dir/source-objects.lock"' test-linuxcnc-source-objects.sh >/dev/null +grep -F 'source_objects_signature="$build_dir/source-objects.signature"' test-linuxcnc-source-objects.sh >/dev/null +grep -F "printf 'BUILD_RULE_VERSION=1\n'" test-linuxcnc-source-objects.sh >/dev/null +grep -F 'source_objects_makefile="$build_dir/source-objects.mk"' test-linuxcnc-source-objects.sh >/dev/null +grep -F 'printf '\''\n-include $(DEP_FILES)\n'\''' test-linuxcnc-source-objects.sh >/dev/null +grep -F 'make --output-sync=target -j"$build_jobs" -f "$source_objects_makefile"' test-linuxcnc-source-objects.sh >/dev/null +grep -F 'build_dir=${CNC_SIM_RS274_NATIVE_BUILD_DIR-build/rs274-native-test}' test-linuxcnc-rs274-native.sh >/dev/null +grep -F 'CNC_SIM_RS274_NATIVE_BUILD_DIR must not be empty' test-linuxcnc-rs274-native.sh >/dev/null +grep -F 'CNC_SIM_RS274_NATIVE_BUILD_DIR must not contain whitespace: $build_dir' test-linuxcnc-rs274-native.sh >/dev/null +grep -F 'CNC_SIM_RS274_NATIVE_BUILD_DIR must not be the filesystem root' test-linuxcnc-rs274-native.sh >/dev/null +grep -F 'CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs' test-linuxcnc-rs274-native.sh >/dev/null +grep -F 'rs274_native_signature="$build_dir/rs274-native.signature"' test-linuxcnc-rs274-native.sh >/dev/null +grep -F "printf 'BUILD_RULE_VERSION=1\n'" test-linuxcnc-rs274-native.sh >/dev/null +grep -F 'rs274_native_makefile="$build_dir/rs274-native.mk"' test-linuxcnc-rs274-native.sh >/dev/null +grep -F 'printf '\''\n-include $(DEP_FILES)\n'\''' test-linuxcnc-rs274-native.sh >/dev/null +grep -F 'make --output-sync=target -j"$build_jobs" -f "$rs274_native_makefile"' test-linuxcnc-rs274-native.sh >/dev/null +for hot_signature_script in \ + build-linuxcnc-native-bridge-objects.sh \ + test-linuxcnc-api-native.sh \ + test-linuxcnc-bridge-native.sh \ + test-linuxcnc-rs274-native.sh \ + test-linuxcnc-source-link.sh \ + test-linuxcnc-source-objects.sh \ + test-linuxcnc-source-syntax.sh \ + test-native.sh; do + if grep -F "sha256sum $hot_signature_script" "$hot_signature_script" >/dev/null; then + echo "$hot_signature_script hot object signature must use BUILD_RULE_VERSION instead of the whole script hash" >&2 + exit 1 + fi +done grep -F 'build_jobs=${CNC_SIM_BUILD_JOBS:-8}' build-wasm.sh >/dev/null grep -F 'CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs' build-wasm.sh >/dev/null +grep -F 'cmake_compiler_launcher_mode=none' build-wasm.sh >/dev/null +grep -F 'cmake_compiler_launcher_args=()' build-wasm.sh >/dev/null +grep -F 'cmake_compiler_launcher_mode=ccache' build-wasm.sh >/dev/null +grep -F '"${cmake_compiler_launcher_args[@]}"' build-wasm.sh >/dev/null grep -F 'command -v ccache >/dev/null 2>&1' build-wasm.sh >/dev/null grep -F 'CMAKE_C_COMPILER_LAUNCHER=ccache' build-wasm.sh >/dev/null grep -F 'CMAKE_CXX_COMPILER_LAUNCHER=ccache' build-wasm.sh >/dev/null -grep -F 'cmake --build build/wasm --target cnc_sim_wasm_runtime_probe --parallel "$build_jobs"' build-wasm.sh >/dev/null -grep -F 'cmake --build build/wasm --target cnc_sim_wasm --parallel "$build_jobs"' build-wasm.sh >/dev/null +grep -F 'wasm_build_signature=build/wasm/wasm-build.signature' build-wasm.sh >/dev/null +grep -F 'wasm_build_next_signature=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_wasm_build.signature.XXXXXX")' build-wasm.sh >/dev/null +grep -F 'sha256sum "$manifest"' build-wasm.sh >/dev/null +grep -F 'sha256sum core/CMakeLists.txt' build-wasm.sh >/dev/null +grep -F "printf 'CONFIGURE_RULE_VERSION=1\n'" build-wasm.sh >/dev/null +grep -F 'run_parallel_wasm_probe() {' build-wasm.sh >/dev/null +grep -F 'wait_parallel_wasm_probes() {' build-wasm.sh >/dev/null +grep -F 'parallel_probe_pids+=("$!")' build-wasm.sh >/dev/null +grep -F 'CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" "$@"' build-wasm.sh >/dev/null +grep -F 'run_parallel_wasm_probe "LinuxCNC wasm tooldata shim link probe" ./test-linuxcnc-wasm-tooldata-link.sh' build-wasm.sh >/dev/null +grep -F 'run_parallel_wasm_probe "LinuxCNC wasm rs274ngc_pre object probe" ./test-linuxcnc-wasm-rs274ngc-pre-object.sh' build-wasm.sh >/dev/null +grep -F 'wait_parallel_wasm_probes' build-wasm.sh >/dev/null +grep -F 'configure_wasm=1' build-wasm.sh >/dev/null +grep -F 'elif [[ -f build/wasm/CMakeCache.txt ]]; then' build-wasm.sh >/dev/null +grep -F 'configure_wasm=0' build-wasm.sh >/dev/null +if grep -F 'rm -rf build/wasm' build-wasm.sh >/dev/null; then + echo "build-wasm configure signature changes must reconfigure build/wasm in place" >&2 + exit 1 +fi +if grep -F 'printf '\''BUILD_JOBS=%s\n'\'' "$build_jobs"' build-wasm.sh >/dev/null; then + echo "build-wasm configure signature must not depend on CNC_SIM_BUILD_JOBS" >&2 + exit 1 +fi +grep -F -- 'for required_tool in cmake emcmake emcc node; do' build-wasm.sh >/dev/null +grep -F -- 'missing_tools+=("$required_tool")' build-wasm.sh >/dev/null +grep -F 'WASM build tools are required. Missing:' build-wasm.sh >/dev/null +grep -F -- 'emcmake cmake -S core -B build/wasm \' build-wasm.sh >/dev/null +grep -F 'cmake --build build/wasm --target cnc_sim_wasm_runtime_probe cnc_sim_wasm --parallel "$build_jobs"' build-wasm.sh >/dev/null +if ! awk ' + index($0, "run_parallel_wasm_probe \"LinuxCNC wasm tooldata shim link probe\"") { parallel_start_line = NR } + index($0, "wait_parallel_wasm_probes") && parallel_start_line { wait_line = NR } + index($0, "LinuxCNC wasm rs274ngc_pre link blocker scan") { pre_link_line = NR } + END { exit !(parallel_start_line && wait_line && pre_link_line && parallel_start_line < wait_line && wait_line < pre_link_line) } +' build-wasm.sh; then + echo "build-wasm must wait for independent wasm probes before rs274ngc_pre link checks" >&2 + exit 1 +fi +if ! awk ' + index($0, "cmake --build build/wasm --target cnc_sim_wasm_runtime_probe cnc_sim_wasm --parallel \"$build_jobs\"") { + runtime_pos = index($0, "cnc_sim_wasm_runtime_probe") + wasm_pos = index($0, " cnc_sim_wasm ") + } + END { exit !(runtime_pos && wasm_pos && runtime_pos < wasm_pos) } +' build-wasm.sh; then + echo "build-wasm combined CMake build must list runtime probe before browser WASM target" >&2 + exit 1 +fi +grep -F 'mkdir -p web/public' build-wasm.sh >/dev/null +grep -F 'cp build/wasm/cnc_sim.js web/public/cnc_sim.js' build-wasm.sh >/dev/null +grep -F 'cp build/wasm/cnc_sim.wasm web/public/cnc_sim.wasm' build-wasm.sh >/dev/null +grep -F 'cmp -s build/wasm/cnc_sim.js web/public/cnc_sim.js' build-wasm.sh >/dev/null +grep -F 'cmp -s build/wasm/cnc_sim.wasm web/public/cnc_sim.wasm' build-wasm.sh >/dev/null +if [[ "$(grep -Fc 'cp build/wasm/cnc_sim.js' build-wasm.sh)" -ne 1 ]]; then + echo "build-wasm should copy cnc_sim.js exactly once" >&2 + exit 1 +fi +if [[ "$(grep -Fc 'cp build/wasm/cnc_sim.wasm' build-wasm.sh)" -ne 1 ]]; then + echo "build-wasm should copy cnc_sim.wasm exactly once" >&2 + exit 1 +fi +if ! awk ' + index($0, "mkdir -p web/public") { mkdir_line = NR } + index($0, "cp build/wasm/cnc_sim.js web/public/cnc_sim.js") { cp_line = NR } + END { exit !(mkdir_line && cp_line && mkdir_line < cp_line) } +' build-wasm.sh; then + echo "build-wasm must create web/public before copying wasm artifacts" >&2 + exit 1 +fi +if ! awk ' + index($0, "for required_tool in cmake emcmake emcc node; do") { tools_line = NR } + index($0, "emcmake cmake -S core -B build/wasm") { emcmake_line = NR } + END { exit !(tools_line && emcmake_line && tools_line < emcmake_line) } +' build-wasm.sh; then + echo "build-wasm must check required WASM tools before configuring with emcmake" >&2 + exit 1 +fi +if ! awk ' + index($0, "cp build/wasm/cnc_sim.js web/public/cnc_sim.js") { cp_js_line = NR } + index($0, "cp build/wasm/cnc_sim.wasm web/public/cnc_sim.wasm") { cp_wasm_line = NR } + index($0, "cmp -s build/wasm/cnc_sim.js web/public/cnc_sim.js") { cmp_js_line = NR } + index($0, "cmp -s build/wasm/cnc_sim.wasm web/public/cnc_sim.wasm") { cmp_wasm_line = NR } + index($0, "node test-web-wasm-node-smoke.cjs") { node_line = NR } + index($0, "./test-web-wasm-browser-smoke.sh") { browser_line = NR } + END { + exit !(cp_js_line && cp_wasm_line && cmp_js_line && cmp_wasm_line && node_line && browser_line && + cp_js_line < cmp_js_line && cp_wasm_line < cmp_wasm_line && + cmp_js_line < node_line && cmp_wasm_line < node_line && + node_line < browser_line) + } +' build-wasm.sh; then + echo "build-wasm must verify copied wasm artifacts before running the Node and browser smokes" >&2 + exit 1 +fi grep -F 'command -v ccache >/dev/null 2>&1' test-native.sh >/dev/null grep -F 'command -v ccache >/dev/null 2>&1' test-linuxcnc-source-link.sh >/dev/null grep -F 'command -v ccache >/dev/null 2>&1' test-linuxcnc-source-syntax.sh >/dev/null grep -F 'build_jobs=${CNC_SIM_BUILD_JOBS:-8}' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null grep -F 'CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null +grep -F 'cmake_compiler_launcher_args=()' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null +grep -F '"${cmake_compiler_launcher_args[@]}"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null +grep -F 'CMAKE_C_COMPILER_LAUNCHER=ccache' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null +grep -F 'CMAKE_CXX_COMPILER_LAUNCHER=ccache' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null +grep -F 'self_checks=${CNC_SIM_WASM_CMAKE_SAFE_PROBE_SELF_CHECKS:-0}' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null +grep -F 'CNC_SIM_WASM_CMAKE_SAFE_PROBE_SELF_CHECKS must be 0 or 1: $self_checks' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null +grep -F 'CNC_SIM_WASM_BLOCKERS_SELF_CHECKS must be 0 or 1: $self_checks' test-linuxcnc-wasm-blockers.sh >/dev/null grep -F 'cmake_compiler_launcher_mode=none' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null grep -F 'cmake_compiler_launcher_mode=ccache' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null grep -F 'COMPILER_LAUNCHER=%s\n' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null @@ -212,17 +455,43 @@ grep -F 'CNC_SIM_WASM_CMAKE_SAFE_PROBE_BUILD_DIR must not be the filesystem root grep -F 'build_signature="$build_dir/wasm-cmake-safe-probe.signature"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null grep -F 'build_next_signature=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_wasm_cmake_safe_probe.signature.XXXXXX")' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null grep -F 'if [[ ! -f "$build_signature" ]] || ! cmp -s "$build_signature" "$build_next_signature"; then' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null +grep -F 'mv "$build_next_signature" "$build_signature"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null grep -F 'printf '\''LINUXCNC_ROOT=%s\n'\'' "$linuxcnc_root"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null grep -F 'printf '\''MANIFEST=%s\n'\'' "$manifest"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null -grep -F 'printf '\''BUILD_JOBS=%s\n'\'' "$build_jobs"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null -grep -F 'cmake --build "$build_dir" --target linuxcnc_rs274_wasm_safe_probe_objects --parallel "$build_jobs"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null -grep -F 'cmake --build "$build_dir" --target linuxcnc_rs274_wasm_safe_probe --parallel "$build_jobs"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null -grep -F 'cmake --build "$build_dir" --target cnc_sim_wasm_runtime_probe --parallel "$build_jobs"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null +grep -F 'sha256sum "$manifest"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null +grep -F "printf 'CONFIGURE_RULE_VERSION=1\n'" test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null +if grep -F 'printf '\''BUILD_JOBS=%s\n'\'' "$build_jobs"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null; then + echo "wasm CMake probe signature must not depend on CNC_SIM_BUILD_JOBS" >&2 + exit 1 +fi +if awk ' + BEGIN { forbidden = "rm -rf " "\"$build_dir\"" } + $0 ~ "^[[:space:]]*" forbidden { found = 1 } + END { exit !found } +' test-linuxcnc-wasm-cmake-safe-probe.sh; then + echo "wasm CMake probe signature changes must reconfigure build_dir in place" >&2 + exit 1 +fi +grep -F 'cmake --build "$build_dir" --target linuxcnc_rs274_wasm_safe_probe cnc_sim_wasm_runtime_probe --parallel "$build_jobs"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null grep -F 'CNC_SIM_NATIVE_BUILD_DIR' docs/linuxcnc-source-policy.md >/dev/null grep -F 'CNC_SIM_SOURCE_LINK_BUILD_DIR' docs/linuxcnc-source-policy.md >/dev/null grep -F 'CNC_SIM_SOURCE_SYNTAX_BUILD_DIR' docs/linuxcnc-source-policy.md >/dev/null +grep -F 'CNC_SIM_SOURCE_OBJECTS_BUILD_DIR' docs/linuxcnc-source-policy.md >/dev/null +grep -F 'CNC_SIM_RS274_NATIVE_BUILD_DIR' docs/linuxcnc-source-policy.md >/dev/null grep -F 'CNC_SIM_WASM_CMAKE_SAFE_PROBE_BUILD_DIR' docs/linuxcnc-source-policy.md >/dev/null grep -F 'Build directory overrides must' docs/linuxcnc-source-policy.md >/dev/null +grep -F 'source-syntax, and wasm build paths should enable compiler caching' docs/linuxcnc-source-policy.md >/dev/null +grep -F 'manual wrapper, including the' docs/linuxcnc-source-policy.md >/dev/null +grep -F 'Persistent object/stamp signatures must not include `CNC_SIM_BUILD_JOBS`.' docs/linuxcnc-source-policy.md >/dev/null +grep -F 'signatures must include an explicit' docs/linuxcnc-source-policy.md >/dev/null +grep -F '`BUILD_RULE_VERSION` instead of hashing the whole probe script' docs/linuxcnc-source-policy.md >/dev/null +grep -F 'keyed by manifest, `core/CMakeLists.txt`, an explicit' docs/linuxcnc-source-policy.md >/dev/null +grep -F '`CONFIGURE_RULE_VERSION`, LinuxCNC root, and compiler launcher' docs/linuxcnc-source-policy.md >/dev/null +grep -F 'Reuse standalone wasm probe objects and binaries for the tooldata link,' docs/linuxcnc-source-policy.md >/dev/null +grep -F 'Reuse wasm final link probes for `rs274ngc_pre` and runtime link checks' docs/linuxcnc-source-policy.md >/dev/null +grep -F 'cached `nm --defined-only` symbol export signatures' docs/linuxcnc-source-policy.md >/dev/null +grep -F 'Cache wasm blocker analyzer output' docs/linuxcnc-source-policy.md >/dev/null +grep -F 'Persistent native, source-link, source-syntax, and wasm-safe CMake probe' docs/linuxcnc-source-policy.md >/dev/null grep -F 'copies and byte-compares `cnc_sim.js` and `cnc_sim.wasm` under' docs/linuxcnc-source-policy.md >/dev/null grep -F '`CNC_SIM_BUILD_JOBS` must be a positive integer and defaults to `8`.' docs/linuxcnc-source-policy.md >/dev/null grep -F '`test-native.sh` uses persistent `build/native-test` objects' docs/linuxcnc-source-policy.md >/dev/null @@ -232,7 +501,8 @@ grep -F '`test-linuxcnc-wasm-cmake-safe-probe.sh` uses persistent' docs/linuxcnc grep -F 'must reject empty, whitespace-containing, or filesystem-root build' docs/linuxcnc-source-policy.md >/dev/null grep -F 'entry points must serialize' docs/linuxcnc-source-policy.md >/dev/null grep -F 'shared non-concurrency-safe paths with lock files' docs/linuxcnc-source-policy.md >/dev/null -grep -F '`-MMD -MP` dependency tracking and `make --output-sync=target`' docs/linuxcnc-source-policy.md >/dev/null +grep -F 'Native, source-object, source-link, RS274 native, and syntax probes must build' docs/linuxcnc-source-policy.md >/dev/null +grep -F '`-MMD -MP` dependency tracking and' docs/linuxcnc-source-policy.md >/dev/null grep -F 'must run blocker, syntax, object, CMake, tooldata,' docs/linuxcnc-source-policy.md >/dev/null grep -F 'must check `cmake`, `emcmake`, `emcc`, and `node`' docs/linuxcnc-source-policy.md >/dev/null grep -F 'must build `cnc_sim_wasm_runtime_probe` before' docs/linuxcnc-source-policy.md >/dev/null @@ -246,12 +516,23 @@ grep -F 'must include the built LinuxCNC `lib` path and rpath' docs/linuxcnc-sou grep -F 'must surface helper failures instead of hiding them' docs/linuxcnc-source-policy.md >/dev/null grep -F 'Temporary probe reports must be cleaned on failure' docs/linuxcnc-source-policy.md >/dev/null grep -F 'Documentation-only build policy edits should still update the guardrail' docs/linuxcnc-source-policy.md >/dev/null +grep -F 'CNC_SIM_WASM_CMAKE_SAFE_PROBE_SELF_CHECKS=1' docs/linuxcnc-source-policy.md >/dev/null +grep -F 'default actual CMake, syntax, and blocker probe paths remain fast' docs/linuxcnc-source-policy.md >/dev/null grep -F 'python_include_output=$(python3.13-config --includes 2>/dev/null || python3-config --includes)' list-linuxcnc-source-common-cxxflags.sh >/dev/null if grep -F 'read -r -a python_includes <<<"$(' list-linuxcnc-source-common-cxxflags.sh >/dev/null; then echo "source common cxxflags still hides python-config failures behind read" >&2 exit 1 fi -for script in test-native.sh test-linuxcnc-source-syntax.sh test-linuxcnc-source-link.sh build-wasm.sh; do + +for script in \ + test-native.sh \ + test-linuxcnc-source-syntax.sh \ + test-linuxcnc-source-objects.sh \ + test-linuxcnc-source-link.sh \ + test-linuxcnc-rs274-native.sh \ + test-linuxcnc-bridge-native.sh \ + test-linuxcnc-api-native.sh \ + build-wasm.sh; do if CNC_SIM_BUILD_JOBS=0 "./$script" >"$invalid_build_jobs_log" 2>&1; then echo "$script accepted invalid CNC_SIM_BUILD_JOBS" >&2 exit 1 @@ -334,6 +615,78 @@ if ! grep -F "CNC_SIM_NATIVE_BUILD_DIR must not contain whitespace: build/native sed -n '1,20p' "$whitespace_build_dir_log" >&2 exit 1 fi +if CNC_SIM_BRIDGE_NATIVE_BUILD_DIR=/ ./test-linuxcnc-bridge-native.sh >"$invalid_build_dir_log" 2>&1; then + echo "test-linuxcnc-bridge-native.sh accepted filesystem root as build dir" >&2 + exit 1 +fi +if ! grep -F "CNC_SIM_BRIDGE_NATIVE_BUILD_DIR must not be the filesystem root" "$invalid_build_dir_log" >/dev/null; then + echo "test-linuxcnc-bridge-native.sh did not report filesystem-root build dir clearly" >&2 + sed -n '1,20p' "$invalid_build_dir_log" >&2 + exit 1 +fi +if CNC_SIM_BRIDGE_NATIVE_BUILD_DIR=/tmp/.. ./test-linuxcnc-bridge-native.sh >"$invalid_build_dir_log" 2>&1; then + echo "test-linuxcnc-bridge-native.sh accepted canonical filesystem root as build dir" >&2 + exit 1 +fi +if ! grep -F "CNC_SIM_BRIDGE_NATIVE_BUILD_DIR must not be the filesystem root" "$invalid_build_dir_log" >/dev/null; then + echo "test-linuxcnc-bridge-native.sh did not report canonical filesystem-root build dir clearly" >&2 + sed -n '1,20p' "$invalid_build_dir_log" >&2 + exit 1 +fi +if CNC_SIM_BRIDGE_NATIVE_BUILD_DIR= ./test-linuxcnc-bridge-native.sh >"$empty_build_dir_log" 2>&1; then + echo "test-linuxcnc-bridge-native.sh accepted empty build dir" >&2 + exit 1 +fi +if ! grep -F "CNC_SIM_BRIDGE_NATIVE_BUILD_DIR must not be empty" "$empty_build_dir_log" >/dev/null; then + echo "test-linuxcnc-bridge-native.sh did not report empty build dir clearly" >&2 + sed -n '1,20p' "$empty_build_dir_log" >&2 + exit 1 +fi +if CNC_SIM_BRIDGE_NATIVE_BUILD_DIR="build/bridge native test" ./test-linuxcnc-bridge-native.sh >"$whitespace_build_dir_log" 2>&1; then + echo "test-linuxcnc-bridge-native.sh accepted whitespace in build dir" >&2 + exit 1 +fi +if ! grep -F "CNC_SIM_BRIDGE_NATIVE_BUILD_DIR must not contain whitespace: build/bridge native test" "$whitespace_build_dir_log" >/dev/null; then + echo "test-linuxcnc-bridge-native.sh did not report whitespace build dir clearly" >&2 + sed -n '1,20p' "$whitespace_build_dir_log" >&2 + exit 1 +fi +if CNC_SIM_API_NATIVE_BUILD_DIR=/ ./test-linuxcnc-api-native.sh >"$invalid_build_dir_log" 2>&1; then + echo "test-linuxcnc-api-native.sh accepted filesystem root as build dir" >&2 + exit 1 +fi +if ! grep -F "CNC_SIM_API_NATIVE_BUILD_DIR must not be the filesystem root" "$invalid_build_dir_log" >/dev/null; then + echo "test-linuxcnc-api-native.sh did not report filesystem-root build dir clearly" >&2 + sed -n '1,20p' "$invalid_build_dir_log" >&2 + exit 1 +fi +if CNC_SIM_API_NATIVE_BUILD_DIR=/tmp/.. ./test-linuxcnc-api-native.sh >"$invalid_build_dir_log" 2>&1; then + echo "test-linuxcnc-api-native.sh accepted canonical filesystem root as build dir" >&2 + exit 1 +fi +if ! grep -F "CNC_SIM_API_NATIVE_BUILD_DIR must not be the filesystem root" "$invalid_build_dir_log" >/dev/null; then + echo "test-linuxcnc-api-native.sh did not report canonical filesystem-root build dir clearly" >&2 + sed -n '1,20p' "$invalid_build_dir_log" >&2 + exit 1 +fi +if CNC_SIM_API_NATIVE_BUILD_DIR= ./test-linuxcnc-api-native.sh >"$empty_build_dir_log" 2>&1; then + echo "test-linuxcnc-api-native.sh accepted empty build dir" >&2 + exit 1 +fi +if ! grep -F "CNC_SIM_API_NATIVE_BUILD_DIR must not be empty" "$empty_build_dir_log" >/dev/null; then + echo "test-linuxcnc-api-native.sh did not report empty build dir clearly" >&2 + sed -n '1,20p' "$empty_build_dir_log" >&2 + exit 1 +fi +if CNC_SIM_API_NATIVE_BUILD_DIR="build/api native test" ./test-linuxcnc-api-native.sh >"$whitespace_build_dir_log" 2>&1; then + echo "test-linuxcnc-api-native.sh accepted whitespace in build dir" >&2 + exit 1 +fi +if ! grep -F "CNC_SIM_API_NATIVE_BUILD_DIR must not contain whitespace: build/api native test" "$whitespace_build_dir_log" >/dev/null; then + echo "test-linuxcnc-api-native.sh did not report whitespace build dir clearly" >&2 + sed -n '1,20p' "$whitespace_build_dir_log" >&2 + exit 1 +fi if CNC_SIM_SOURCE_SYNTAX_BUILD_DIR=/ ./test-linuxcnc-source-syntax.sh >"$invalid_build_dir_log" 2>&1; then echo "test-linuxcnc-source-syntax.sh accepted filesystem root as build dir" >&2 exit 1 @@ -370,6 +723,78 @@ if ! grep -F "CNC_SIM_SOURCE_SYNTAX_BUILD_DIR must not contain whitespace: build sed -n '1,20p' "$whitespace_build_dir_log" >&2 exit 1 fi +if CNC_SIM_SOURCE_OBJECTS_BUILD_DIR=/ ./test-linuxcnc-source-objects.sh >"$invalid_build_dir_log" 2>&1; then + echo "test-linuxcnc-source-objects.sh accepted filesystem root as build dir" >&2 + exit 1 +fi +if ! grep -F "CNC_SIM_SOURCE_OBJECTS_BUILD_DIR must not be the filesystem root" "$invalid_build_dir_log" >/dev/null; then + echo "test-linuxcnc-source-objects.sh did not report filesystem-root build dir clearly" >&2 + sed -n '1,20p' "$invalid_build_dir_log" >&2 + exit 1 +fi +if CNC_SIM_SOURCE_OBJECTS_BUILD_DIR=/tmp/.. ./test-linuxcnc-source-objects.sh >"$invalid_build_dir_log" 2>&1; then + echo "test-linuxcnc-source-objects.sh accepted normalized filesystem root as build dir" >&2 + exit 1 +fi +if ! grep -F "CNC_SIM_SOURCE_OBJECTS_BUILD_DIR must not be the filesystem root" "$invalid_build_dir_log" >/dev/null; then + echo "test-linuxcnc-source-objects.sh did not report normalized filesystem-root build dir clearly" >&2 + sed -n '1,20p' "$invalid_build_dir_log" >&2 + exit 1 +fi +if CNC_SIM_SOURCE_OBJECTS_BUILD_DIR= ./test-linuxcnc-source-objects.sh >"$empty_build_dir_log" 2>&1; then + echo "test-linuxcnc-source-objects.sh accepted empty build dir" >&2 + exit 1 +fi +if ! grep -F "CNC_SIM_SOURCE_OBJECTS_BUILD_DIR must not be empty" "$empty_build_dir_log" >/dev/null; then + echo "test-linuxcnc-source-objects.sh did not report empty build dir clearly" >&2 + sed -n '1,20p' "$empty_build_dir_log" >&2 + exit 1 +fi +if CNC_SIM_SOURCE_OBJECTS_BUILD_DIR="build/source objects test" ./test-linuxcnc-source-objects.sh >"$whitespace_build_dir_log" 2>&1; then + echo "test-linuxcnc-source-objects.sh accepted whitespace in build dir" >&2 + exit 1 +fi +if ! grep -F "CNC_SIM_SOURCE_OBJECTS_BUILD_DIR must not contain whitespace: build/source objects test" "$whitespace_build_dir_log" >/dev/null; then + echo "test-linuxcnc-source-objects.sh did not report whitespace build dir clearly" >&2 + sed -n '1,20p' "$whitespace_build_dir_log" >&2 + exit 1 +fi +if CNC_SIM_RS274_NATIVE_BUILD_DIR=/ ./test-linuxcnc-rs274-native.sh >"$invalid_build_dir_log" 2>&1; then + echo "test-linuxcnc-rs274-native.sh accepted filesystem root as build dir" >&2 + exit 1 +fi +if ! grep -F "CNC_SIM_RS274_NATIVE_BUILD_DIR must not be the filesystem root" "$invalid_build_dir_log" >/dev/null; then + echo "test-linuxcnc-rs274-native.sh did not report filesystem-root build dir clearly" >&2 + sed -n '1,20p' "$invalid_build_dir_log" >&2 + exit 1 +fi +if CNC_SIM_RS274_NATIVE_BUILD_DIR=/tmp/.. ./test-linuxcnc-rs274-native.sh >"$invalid_build_dir_log" 2>&1; then + echo "test-linuxcnc-rs274-native.sh accepted normalized filesystem root as build dir" >&2 + exit 1 +fi +if ! grep -F "CNC_SIM_RS274_NATIVE_BUILD_DIR must not be the filesystem root" "$invalid_build_dir_log" >/dev/null; then + echo "test-linuxcnc-rs274-native.sh did not report normalized filesystem-root build dir clearly" >&2 + sed -n '1,20p' "$invalid_build_dir_log" >&2 + exit 1 +fi +if CNC_SIM_RS274_NATIVE_BUILD_DIR= ./test-linuxcnc-rs274-native.sh >"$empty_build_dir_log" 2>&1; then + echo "test-linuxcnc-rs274-native.sh accepted empty build dir" >&2 + exit 1 +fi +if ! grep -F "CNC_SIM_RS274_NATIVE_BUILD_DIR must not be empty" "$empty_build_dir_log" >/dev/null; then + echo "test-linuxcnc-rs274-native.sh did not report empty build dir clearly" >&2 + sed -n '1,20p' "$empty_build_dir_log" >&2 + exit 1 +fi +if CNC_SIM_RS274_NATIVE_BUILD_DIR="build/rs274 native test" ./test-linuxcnc-rs274-native.sh >"$whitespace_build_dir_log" 2>&1; then + echo "test-linuxcnc-rs274-native.sh accepted whitespace in build dir" >&2 + exit 1 +fi +if ! grep -F "CNC_SIM_RS274_NATIVE_BUILD_DIR must not contain whitespace: build/rs274 native test" "$whitespace_build_dir_log" >/dev/null; then + echo "test-linuxcnc-rs274-native.sh did not report whitespace build dir clearly" >&2 + sed -n '1,20p' "$whitespace_build_dir_log" >&2 + exit 1 +fi if CNC_SIM_SOURCE_LINK_BUILD_DIR=/ ./test-linuxcnc-source-link.sh >"$invalid_build_dir_log" 2>&1; then echo "test-linuxcnc-source-link.sh accepted filesystem root as build dir" >&2 exit 1 @@ -573,7 +998,8 @@ if ! grep -F "unknown manifest source output mode: bogus" "$unknown_output_log" exit 1 fi native_manifest_core_full=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-manifest-sources.sh "$manifest" core full) -grep -Fx "$linuxcnc_root/src/emc/rs274ngc/interp_arc.cc" <<<"$native_manifest_core_full" >/dev/null +linuxcnc_root_full=$(cd "$linuxcnc_root" && pwd) +grep -Fx "$linuxcnc_root_full/src/emc/rs274ngc/interp_arc.cc" <<<"$native_manifest_core_full" >/dev/null kinematics_manifest_config=$(./list-linuxcnc-kinematics-manifest-sources.sh linuxcnc-kinematics-source-files.txt config) grep -Fx "configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini" <<<"$kinematics_manifest_config" >/dev/null @@ -732,6 +1158,11 @@ if ! grep -F "missing manifest source: src/emc/rs274ngc/does_not_exist.cc" "$mis exit 1 fi +if [[ "$guardrails_only" == "1" ]]; then + echo "all native guardrail checks passed" + exit 0 +fi + ./test-native.sh ./test-linuxcnc-source-syntax.sh "$manifest" ./test-linuxcnc-source-objects.sh "$manifest" diff --git a/test-linuxcnc-api-native.sh b/test-linuxcnc-api-native.sh index 9f59ed2..0fc6e12 100755 --- a/test-linuxcnc-api-native.sh +++ b/test-linuxcnc-api-native.sh @@ -5,38 +5,158 @@ cd "$(dirname "$0")" linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} cxx=${CXX:-g++} -build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_native.XXXXXX") +if [[ -z ${CXX:-} ]] && command -v ccache >/dev/null 2>&1; then + cxx="ccache $cxx" +fi +build_jobs=${CNC_SIM_BUILD_JOBS:-8} +if ! [[ "$build_jobs" =~ ^[1-9][0-9]*$ ]]; then + echo "CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs" >&2 + exit 1 +fi +build_dir=${CNC_SIM_API_NATIVE_BUILD_DIR-build/api-native-test} +if [[ -z "$build_dir" ]]; then + echo "CNC_SIM_API_NATIVE_BUILD_DIR must not be empty" >&2 + exit 1 +fi +if [[ "$build_dir" =~ [[:space:]] ]]; then + echo "CNC_SIM_API_NATIVE_BUILD_DIR must not contain whitespace: $build_dir" >&2 + exit 1 +fi +mkdir -p "$build_dir" +build_dir=$(cd "$build_dir" && pwd) +if [[ "$build_dir" == "/" ]]; then + echo "CNC_SIM_API_NATIVE_BUILD_DIR must not be the filesystem root" >&2 + exit 1 +fi +exec 9>"$build_dir/api-native.lock" +flock 9 +preflight_cache_dir="$build_dir/preflight-cache" + +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" --root-only +linuxcnc_root=$(cd "$linuxcnc_root" && pwd) -trap 'rm -rf "$build_dir"' EXIT -LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only var_file="$build_dir/rs274ngc-api.var" cp "$linuxcnc_root/tests/halui/jogging/sim.var" "$var_file" -project_source_list="$build_dir/linuxcnc_rs274_api_project_sources.txt" -./list-linuxcnc-rs274-api-project-sources.sh > "$project_source_list" -mapfile -t project_sources < "$project_source_list" - common_flag_output=$( - LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-common-cxxflags.sh \ + CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" \ + LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-common-cxxflags.sh \ --no-ulapi \ --python \ --core-include \ --core-src ) mapfile -t common_flags <<<"$common_flag_output" -link_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-native-linkflags.sh) +link_flag_output=$(CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-native-linkflags.sh) mapfile -t link_flags <<<"$link_flag_output" +bridge_object_output=$(CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" LINUXCNC_ROOT="$linuxcnc_root" ./build-linuxcnc-native-bridge-objects.sh) +mapfile -t bridge_objects <<<"$bridge_object_output" +bridge_source_list="$build_dir/linuxcnc_bridge_smoke_project_sources.txt" +./list-linuxcnc-bridge-smoke-project-sources.sh > "$bridge_source_list" +mapfile -t bridge_sources < "$bridge_source_list" +project_source_list="$build_dir/linuxcnc_rs274_api_project_sources.txt" +./list-linuxcnc-rs274-api-project-sources.sh > "$project_source_list" +mapfile -t api_sources < "$project_source_list" -"$cxx" "${common_flags[@]}" \ - -DCNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND \ - "${project_sources[@]}" \ - core/tests/cnc_sim_api_linuxcnc_rs274_smoke.cpp \ - "${link_flags[@]}" \ - -lrs274 \ - -ltooldata \ - -lpython3.13 \ - -o "$build_dir/cnc_sim_api_linuxcnc_rs274_smoke" +is_bridge_source() { + local source=$1 + local candidate + for candidate in "${bridge_sources[@]}"; do + if [[ "$candidate" == "$source" ]]; then + return 0 + fi + done + return 1 +} -CNC_SIM_RS274_VAR="$var_file" "$build_dir/cnc_sim_api_linuxcnc_rs274_smoke" +objects=() +project_index=0 +api_extra_sources=() +for source in "${api_sources[@]}"; do + if is_bridge_source "$source"; then + continue + fi + api_extra_sources+=("$source") + objects+=("$build_dir/project_${project_index}.o") + project_index=$((project_index + 1)) +done +main_object="$build_dir/cnc_sim_api_linuxcnc_rs274_smoke.o" +objects+=("$main_object") +target="$build_dir/cnc_sim_api_linuxcnc_rs274_smoke" + +api_signature="$build_dir/api-native.signature" +api_next_signature="$build_dir/api-native.signature.next" +{ + printf 'CXX=%s\n' "$cxx" + printf 'PWD=%s\n' "$PWD" + printf 'LINUXCNC_ROOT=%s\n' "$linuxcnc_root" + printf 'BUILD_RULE_VERSION=1\n' + printf 'COMMON_FLAGS=' + printf ' %s' "${common_flags[@]}" + printf '\n' + printf 'LINK_FLAGS=' + printf ' %s' "${link_flags[@]}" + printf '\n' + printf 'BRIDGE_OBJECTS:\n' + printf '%s\n' "${bridge_objects[@]}" + printf 'API_EXTRA_SOURCES:\n' + printf '%s\n' "${api_extra_sources[@]}" +} > "$api_next_signature" +if [[ ! -f "$api_signature" ]] || ! cmp -s "$api_signature" "$api_next_signature"; then + rm -f "$build_dir"/project_*.o \ + "$build_dir"/*.d \ + "$main_object" \ + "$target" +fi +mv "$api_next_signature" "$api_signature" + +api_makefile="$build_dir/api-native.mk" +{ + printf 'CXX := %s\n' "$cxx" + printf 'COMMON_FLAGS :=' + printf ' %s' "${common_flags[@]}" + printf '\n' + printf 'LINK_FLAGS :=' + printf ' %s' "${link_flags[@]}" + printf '\n' + printf 'BRIDGE_OBJECTS :=' + printf ' %s' "${bridge_objects[@]}" + printf '\n' + printf 'OBJECTS :=' + printf ' %s' "${objects[@]}" + printf '\n\n' + printf 'DEP_FILES :=\n' + for object in "${objects[@]}"; do + printf 'DEP_FILES += %s.d\n' "$object" + done + printf '\n.PHONY: all\n' + printf 'all: %s\n\n' "$target" + + project_index=0 + for source in "${api_extra_sources[@]}"; do + source_flags=() + case "$source" in + core/src/gcode_backend.cpp) + source_flags+=(-DCNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND) + ;; + esac + printf '%s: %s\n' "$build_dir/project_${project_index}.o" "$source" + printf '\t@$(CXX) $(COMMON_FLAGS)' + printf ' %s' "${source_flags[@]}" + printf ' -MMD -MP -MF %s.d -c %s -o %s\n\n' \ + "$build_dir/project_${project_index}.o" "$source" "$build_dir/project_${project_index}.o" + project_index=$((project_index + 1)) + done + + printf '%s: core/tests/cnc_sim_api_linuxcnc_rs274_smoke.cpp\n' "$main_object" + printf '\t@$(CXX) $(COMMON_FLAGS) -MMD -MP -MF %s.d -c core/tests/cnc_sim_api_linuxcnc_rs274_smoke.cpp -o %s\n\n' \ + "$main_object" "$main_object" + printf '%s: $(BRIDGE_OBJECTS) $(OBJECTS)\n' "$target" + printf '\t@$(CXX) $(BRIDGE_OBJECTS) $(OBJECTS) $(LINK_FLAGS) -lrs274 -ltooldata -lpython3.13 -o %s\n\n' "$target" + printf '\n-include $(DEP_FILES)\n' +} > "$api_makefile" +make --output-sync=target -j"$build_jobs" -f "$api_makefile" + +CNC_SIM_RS274_VAR="$var_file" "$target" echo "linuxcnc rs274 API backend smoke passed" diff --git a/test-linuxcnc-bridge-native.sh b/test-linuxcnc-bridge-native.sh index 352b109..6380138 100755 --- a/test-linuxcnc-bridge-native.sh +++ b/test-linuxcnc-bridge-native.sh @@ -5,31 +5,110 @@ cd "$(dirname "$0")" linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} cxx=${CXX:-g++} -build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_native.XXXXXX") +if [[ -z ${CXX:-} ]] && command -v ccache >/dev/null 2>&1; then + cxx="ccache $cxx" +fi +build_jobs=${CNC_SIM_BUILD_JOBS:-8} +if ! [[ "$build_jobs" =~ ^[1-9][0-9]*$ ]]; then + echo "CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs" >&2 + exit 1 +fi +build_dir=${CNC_SIM_BRIDGE_NATIVE_BUILD_DIR-build/bridge-native-test} +if [[ -z "$build_dir" ]]; then + echo "CNC_SIM_BRIDGE_NATIVE_BUILD_DIR must not be empty" >&2 + exit 1 +fi +if [[ "$build_dir" =~ [[:space:]] ]]; then + echo "CNC_SIM_BRIDGE_NATIVE_BUILD_DIR must not contain whitespace: $build_dir" >&2 + exit 1 +fi +mkdir -p "$build_dir" +build_dir=$(cd "$build_dir" && pwd) +if [[ "$build_dir" == "/" ]]; then + echo "CNC_SIM_BRIDGE_NATIVE_BUILD_DIR must not be the filesystem root" >&2 + exit 1 +fi +exec 9>"$build_dir/bridge-native.lock" +flock 9 +preflight_cache_dir="$build_dir/preflight-cache" -trap 'rm -rf "$build_dir"' EXIT -LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only -project_source_list="$build_dir/linuxcnc_bridge_smoke_project_sources.txt" -./list-linuxcnc-bridge-smoke-project-sources.sh > "$project_source_list" -mapfile -t project_sources < "$project_source_list" +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" --root-only +linuxcnc_root=$(cd "$linuxcnc_root" && pwd) common_flag_output=$( - LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-common-cxxflags.sh \ + CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" \ + LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-common-cxxflags.sh \ --no-ulapi \ --core-include \ --core-src ) mapfile -t common_flags <<<"$common_flag_output" -link_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-native-linkflags.sh) +link_flag_output=$(CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-native-linkflags.sh) mapfile -t link_flags <<<"$link_flag_output" +bridge_object_output=$(CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" LINUXCNC_ROOT="$linuxcnc_root" ./build-linuxcnc-native-bridge-objects.sh) +mapfile -t bridge_objects <<<"$bridge_object_output" +project_source_list="$build_dir/linuxcnc_bridge_smoke_project_sources.txt" +./list-linuxcnc-bridge-smoke-project-sources.sh > "$project_source_list" -"$cxx" "${common_flags[@]}" \ - "${project_sources[@]}" \ - core/tests/linuxcnc_canon_bridge_smoke.cpp \ - "${link_flags[@]}" \ - -ltooldata \ - -o "$build_dir/linuxcnc_canon_bridge_smoke" +objects=() +main_object="$build_dir/linuxcnc_canon_bridge_smoke.o" +objects+=("$main_object") +target="$build_dir/linuxcnc_canon_bridge_smoke" -"$build_dir/linuxcnc_canon_bridge_smoke" +bridge_signature="$build_dir/bridge-native.signature" +bridge_next_signature="$build_dir/bridge-native.signature.next" +{ + printf 'CXX=%s\n' "$cxx" + printf 'PWD=%s\n' "$PWD" + printf 'LINUXCNC_ROOT=%s\n' "$linuxcnc_root" + printf 'BUILD_RULE_VERSION=1\n' + printf 'COMMON_FLAGS=' + printf ' %s' "${common_flags[@]}" + printf '\n' + printf 'LINK_FLAGS=' + printf ' %s' "${link_flags[@]}" + printf '\n' + printf 'BRIDGE_OBJECTS:\n' + printf '%s\n' "${bridge_objects[@]}" +} > "$bridge_next_signature" +if [[ ! -f "$bridge_signature" ]] || ! cmp -s "$bridge_signature" "$bridge_next_signature"; then + rm -f "$build_dir"/*.d \ + "$main_object" \ + "$target" +fi +mv "$bridge_next_signature" "$bridge_signature" + +bridge_makefile="$build_dir/bridge-native.mk" +{ + printf 'CXX := %s\n' "$cxx" + printf 'COMMON_FLAGS :=' + printf ' %s' "${common_flags[@]}" + printf '\n' + printf 'LINK_FLAGS :=' + printf ' %s' "${link_flags[@]}" + printf '\n' + printf 'BRIDGE_OBJECTS :=' + printf ' %s' "${bridge_objects[@]}" + printf '\n' + printf 'OBJECTS :=' + printf ' %s' "${objects[@]}" + printf '\n\n' + printf 'DEP_FILES :=\n' + for object in "${objects[@]}"; do + printf 'DEP_FILES += %s.d\n' "$object" + done + printf '\n.PHONY: all\n' + printf 'all: %s\n\n' "$target" + + printf '%s: core/tests/linuxcnc_canon_bridge_smoke.cpp\n' "$main_object" + printf '\t@$(CXX) $(COMMON_FLAGS) -MMD -MP -MF %s.d -c core/tests/linuxcnc_canon_bridge_smoke.cpp -o %s\n\n' \ + "$main_object" "$main_object" + printf '%s: $(BRIDGE_OBJECTS) $(OBJECTS)\n' "$target" + printf '\t@$(CXX) $(BRIDGE_OBJECTS) $(OBJECTS) $(LINK_FLAGS) -ltooldata -o %s\n\n' "$target" + printf '\n-include $(DEP_FILES)\n' +} > "$bridge_makefile" +make --output-sync=target -j"$build_jobs" -f "$bridge_makefile" + +"$target" echo "linuxcnc canon bridge smoke passed" diff --git a/test-linuxcnc-rs274-native.sh b/test-linuxcnc-rs274-native.sh index e619b0c..c1f0500 100755 --- a/test-linuxcnc-rs274-native.sh +++ b/test-linuxcnc-rs274-native.sh @@ -5,10 +5,33 @@ cd "$(dirname "$0")" linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} cxx=${CXX:-g++} -build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_native.XXXXXX") +if [[ -z ${CXX:-} ]] && command -v ccache >/dev/null 2>&1; then + cxx="ccache $cxx" +fi +build_jobs=${CNC_SIM_BUILD_JOBS:-8} +if ! [[ "$build_jobs" =~ ^[1-9][0-9]*$ ]]; then + echo "CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs" >&2 + exit 1 +fi +build_dir=${CNC_SIM_RS274_NATIVE_BUILD_DIR-build/rs274-native-test} +if [[ -z "$build_dir" ]]; then + echo "CNC_SIM_RS274_NATIVE_BUILD_DIR must not be empty" >&2 + exit 1 +fi +if [[ "$build_dir" =~ [[:space:]] ]]; then + echo "CNC_SIM_RS274_NATIVE_BUILD_DIR must not contain whitespace: $build_dir" >&2 + exit 1 +fi -trap 'rm -rf "$build_dir"' EXIT -LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only +mkdir -p "$build_dir" +build_dir=$(cd "$build_dir" && pwd) +if [[ "$build_dir" == "/" ]]; then + echo "CNC_SIM_RS274_NATIVE_BUILD_DIR must not be the filesystem root" >&2 + exit 1 +fi +preflight_cache_dir="$build_dir/preflight-cache" +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" --root-only +linuxcnc_root=$(cd "$linuxcnc_root" && pwd) # 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 @@ -36,13 +59,68 @@ mapfile -t common_flags <<<"$common_flag_output" link_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-native-linkflags.sh) mapfile -t link_flags <<<"$link_flag_output" -"$cxx" "${common_flags[@]}" \ - "${project_sources[@]}" \ - "${link_flags[@]}" \ - -lrs274 \ - -ltooldata \ - -lpython3.13 \ - -o "$build_dir/linuxcnc_rs274_dump" +project_objects=() +project_index=0 +for source in "${project_sources[@]}"; do + project_objects+=("$build_dir/project_${project_index}.o") + project_index=$((project_index + 1)) +done + +rs274_native_signature="$build_dir/rs274-native.signature" +rs274_native_next_signature="$build_dir/rs274-native.signature.next" +{ + printf 'CXX=%s\n' "$cxx" + printf 'PWD=%s\n' "$PWD" + printf 'LINUXCNC_ROOT=%s\n' "$linuxcnc_root" + printf 'BUILD_RULE_VERSION=1\n' + printf 'COMMON_FLAGS=' + printf ' %s' "${common_flags[@]}" + printf '\n' + printf 'LINK_FLAGS=' + printf ' %s' "${link_flags[@]}" + printf '\n' + printf 'PROJECT_SOURCES:\n' + printf '%s\n' "${project_sources[@]}" +} > "$rs274_native_next_signature" +if [[ ! -f "$rs274_native_signature" ]] || ! cmp -s "$rs274_native_signature" "$rs274_native_next_signature"; then + rm -f "$build_dir"/project_*.o \ + "$build_dir"/project_*.o.d \ + "$build_dir/linuxcnc_rs274_dump" +fi +mv "$rs274_native_next_signature" "$rs274_native_signature" + +rs274_native_makefile="$build_dir/rs274-native.mk" +{ + printf 'CXX := %s\n' "$cxx" + printf 'COMMON_FLAGS :=' + printf ' %s' "${common_flags[@]}" + printf '\n' + printf 'LINK_FLAGS :=' + printf ' %s' "${link_flags[@]}" + printf '\n\n' + printf 'PROJECT_OBJECTS :=' + printf ' %s' "${project_objects[@]}" + printf '\n\n' + printf 'DEP_FILES :=\n' + for object in "${project_objects[@]}"; do + printf 'DEP_FILES += %s.d\n' "$object" + done + printf '\n.PHONY: all\n' + printf 'all: %s\n\n' "$build_dir/linuxcnc_rs274_dump" + + project_index=0 + for source in "${project_sources[@]}"; do + printf '%s: %s\n' "${project_objects[$project_index]}" "$source" + printf '\t@$(CXX) $(COMMON_FLAGS) -MMD -MP -MF %s.d -c %s -o %s\n\n' \ + "${project_objects[$project_index]}" "$source" "${project_objects[$project_index]}" + project_index=$((project_index + 1)) + done + + printf '%s: $(PROJECT_OBJECTS)\n' "$build_dir/linuxcnc_rs274_dump" + printf '\t@$(CXX) $(PROJECT_OBJECTS) $(LINK_FLAGS) -lrs274 -ltooldata -lpython3.13 -o %s\n' "$build_dir/linuxcnc_rs274_dump" + printf '\n-include $(DEP_FILES)\n' +} > "$rs274_native_makefile" +make --output-sync=target -j"$build_jobs" -f "$rs274_native_makefile" 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" diff --git a/test-linuxcnc-source-link.sh b/test-linuxcnc-source-link.sh index 654fa09..615f98d 100755 --- a/test-linuxcnc-source-link.sh +++ b/test-linuxcnc-source-link.sh @@ -30,9 +30,10 @@ if [[ "$build_dir" == "/" ]]; then echo "CNC_SIM_SOURCE_LINK_BUILD_DIR must not be the filesystem root" >&2 exit 1 fi +preflight_cache_dir="$build_dir/preflight-cache" +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" "$manifest" -LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest" -LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-switchkins-remap-table.sh linuxcnc-kinematics-source-files.txt +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-switchkins-remap-table-cached.sh --cache-dir "$preflight_cache_dir" linuxcnc-kinematics-source-files.txt # The LinuxCNC source-linked dump path is not concurrency-safe under parallel execution. exec 9>"${TMPDIR:-/tmp}/cnc_sim_rs274_source_link.lock" flock 9 @@ -47,9 +48,9 @@ mapfile -t common_flags <<<"$common_flag_output" link_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-native-linkflags.sh) mapfile -t link_flags <<<"$link_flag_output" -source_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-manifest-sources.sh "$manifest" core full) +source_output=$(CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-manifest-sources.sh "$manifest" core full) mapfile -t linuxcnc_sources <<<"$source_output" -LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest" --require-rs274-complete +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" "$manifest" --require-rs274-complete objects=() linuxcnc_index=0 @@ -79,6 +80,7 @@ source_link_next_signature="$build_dir/source-link-build.signature.next" printf 'CXX=%s\n' "$cxx" printf 'PWD=%s\n' "$PWD" printf 'LINUXCNC_ROOT=%s\n' "$(cd "$linuxcnc_root" && pwd)" + printf 'BUILD_RULE_VERSION=1\n' printf 'COMMON_FLAGS=' printf ' %s' "${common_flags[@]}" printf '\n' diff --git a/test-linuxcnc-source-objects.sh b/test-linuxcnc-source-objects.sh index 36ad42a..5051438 100755 --- a/test-linuxcnc-source-objects.sh +++ b/test-linuxcnc-source-objects.sh @@ -5,24 +5,97 @@ cd "$(dirname "$0")" linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} cxx=${CXX:-g++} -build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_rs274_objects.XXXXXX") +if [[ -z ${CXX:-} ]] && command -v ccache >/dev/null 2>&1; then + cxx="ccache $cxx" +fi +build_jobs=${CNC_SIM_BUILD_JOBS:-8} +if ! [[ "$build_jobs" =~ ^[1-9][0-9]*$ ]]; then + echo "CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs" >&2 + exit 1 +fi +build_dir=${CNC_SIM_SOURCE_OBJECTS_BUILD_DIR-build/source-objects-test} +if [[ -z "$build_dir" ]]; then + echo "CNC_SIM_SOURCE_OBJECTS_BUILD_DIR must not be empty" >&2 + exit 1 +fi +if [[ "$build_dir" =~ [[:space:]] ]]; then + echo "CNC_SIM_SOURCE_OBJECTS_BUILD_DIR must not contain whitespace: $build_dir" >&2 + exit 1 +fi manifest=${1:-linuxcnc-rs274-source-files.txt} -trap 'rm -rf "$build_dir"' EXIT -LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest" +mkdir -p "$build_dir" +build_dir=$(cd "$build_dir" && pwd) +if [[ "$build_dir" == "/" ]]; then + echo "CNC_SIM_SOURCE_OBJECTS_BUILD_DIR must not be the filesystem root" >&2 + exit 1 +fi +exec 9>"$build_dir/source-objects.lock" +flock 9 +preflight_cache_dir="$build_dir/preflight-cache" + +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" "$manifest" +manifest=$(cd "$(dirname "$manifest")" && pwd)/$(basename "$manifest") +linuxcnc_root=$(cd "$linuxcnc_root" && pwd) common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-common-cxxflags.sh --python) mapfile -t common_flags <<<"$common_flag_output" -source_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-manifest-sources.sh "$manifest" core full) +source_output=$(CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-manifest-sources.sh "$manifest" core full) mapfile -t sources <<<"$source_output" +objects=() source_index=0 for source in "${sources[@]}"; do - obj="$build_dir/linuxcnc_${source_index}.o" - "$cxx" "${common_flags[@]}" -c "$source" -o "$obj" + objects+=("$build_dir/linuxcnc_${source_index}.o") source_index=$((source_index + 1)) done +source_objects_signature="$build_dir/source-objects.signature" +source_objects_next_signature="$build_dir/source-objects.signature.next" +{ + printf 'CXX=%s\n' "$cxx" + printf 'PWD=%s\n' "$PWD" + printf 'LINUXCNC_ROOT=%s\n' "$linuxcnc_root" + printf 'MANIFEST=%s\n' "$manifest" + printf 'BUILD_RULE_VERSION=1\n' + printf 'COMMON_FLAGS=' + printf ' %s' "${common_flags[@]}" + printf '\n' + printf 'SOURCES:\n' + printf '%s\n' "${sources[@]}" +} > "$source_objects_next_signature" +if [[ ! -f "$source_objects_signature" ]] || ! cmp -s "$source_objects_signature" "$source_objects_next_signature"; then + rm -f "$build_dir"/linuxcnc_*.o "$build_dir"/linuxcnc_*.o.d +fi +mv "$source_objects_next_signature" "$source_objects_signature" + +source_objects_makefile="$build_dir/source-objects.mk" +{ + printf 'CXX := %s\n' "$cxx" + printf 'COMMON_FLAGS :=' + printf ' %s' "${common_flags[@]}" + printf '\n\n' + printf 'OBJECTS :=' + printf ' %s' "${objects[@]}" + printf '\n\n' + printf 'DEP_FILES :=\n' + for object in "${objects[@]}"; do + printf 'DEP_FILES += %s.d\n' "$object" + done + printf '\n.PHONY: all\n' + printf 'all: $(OBJECTS)\n\n' + + source_index=0 + for source in "${sources[@]}"; do + printf '%s: %s\n' "${objects[$source_index]}" "$source" + printf '\t@$(CXX) $(COMMON_FLAGS) -MMD -MP -MF %s.d -c %s -o %s\n\n' \ + "${objects[$source_index]}" "$source" "${objects[$source_index]}" + source_index=$((source_index + 1)) + done + printf '\n-include $(DEP_FILES)\n' +} > "$source_objects_makefile" +make --output-sync=target -j"$build_jobs" -f "$source_objects_makefile" + echo "linuxcnc rs274 source object build passed (${#sources[@]} objects)" echo "built objects in $build_dir" diff --git a/test-linuxcnc-source-syntax.sh b/test-linuxcnc-source-syntax.sh index 3356e82..f7a5d2e 100755 --- a/test-linuxcnc-source-syntax.sh +++ b/test-linuxcnc-source-syntax.sh @@ -13,6 +13,11 @@ if ! [[ "$build_jobs" =~ ^[1-9][0-9]*$ ]]; then echo "CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs" >&2 exit 1 fi +self_checks=${CNC_SIM_SOURCE_SYNTAX_SELF_CHECKS:-0} +if [[ "$self_checks" != "0" && "$self_checks" != "1" ]]; then + echo "CNC_SIM_SOURCE_SYNTAX_SELF_CHECKS must be 0 or 1: $self_checks" >&2 + exit 1 +fi build_dir=${CNC_SIM_SOURCE_SYNTAX_BUILD_DIR-build/source-syntax-test} if [[ -z "$build_dir" ]]; then echo "CNC_SIM_SOURCE_SYNTAX_BUILD_DIR must not be empty" >&2 @@ -31,8 +36,9 @@ fi exec 9>"$build_dir/source-syntax.lock" flock 9 manifest=${1:-linuxcnc-rs274-source-files.txt} +preflight_cache_dir="$build_dir/preflight-cache" -LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest" +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" "$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") @@ -46,9 +52,11 @@ if awk '/^rm -f "\$missing_source_manifest" "\$missing_source_log"$/{ found = 1 echo "linuxcnc source syntax probe still relies on manual cleanup for missing-source temporaries" >&2 exit 1 fi -LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-manifest-sources.sh "$manifest" core full > "$source_list" -LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-manifest-sources.sh "$manifest" binding full >> "$source_list" -LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-manifest-sources.sh "$manifest" tooldata full >> "$source_list" +CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-manifest-sources.sh "$manifest" core full > "$source_list" +CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-manifest-sources.sh "$manifest" binding full >> "$source_list" +CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-manifest-sources.sh "$manifest" tooldata full >> "$source_list" + +if [[ "$self_checks" == "1" ]]; then 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 @@ -70,6 +78,8 @@ if ! grep -F "missing manifest source: src/emc/rs274ngc/does_not_exist.cc" "$mis exit 1 fi +fi + common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-common-cxxflags.sh --python --syntax-only) mapfile -t common_flags <<<"$common_flag_output" @@ -80,7 +90,7 @@ source_syntax_next_signature="$build_dir/source-syntax.signature.next" printf 'PWD=%s\n' "$PWD" printf 'LINUXCNC_ROOT=%s\n' "$(cd "$linuxcnc_root" && pwd)" printf 'MANIFEST=%s\n' "$(cd "$(dirname "$manifest")" && pwd)/$(basename "$manifest")" - printf 'BUILD_JOBS=%s\n' "$build_jobs" + printf 'BUILD_RULE_VERSION=1\n' printf 'COMMON_FLAGS=' printf ' %s' "${common_flags[@]}" printf '\n' diff --git a/test-linuxcnc-wasm-blockers.sh b/test-linuxcnc-wasm-blockers.sh index 2ffc998..7743553 100755 --- a/test-linuxcnc-wasm-blockers.sh +++ b/test-linuxcnc-wasm-blockers.sh @@ -4,6 +4,32 @@ set -euo pipefail cd "$(dirname "$0")" manifest=${1:-linuxcnc-rs274-wasm-source-files.txt} +self_checks=${CNC_SIM_WASM_BLOCKERS_SELF_CHECKS:-0} +if [[ "$self_checks" != "0" && "$self_checks" != "1" ]]; then + echo "CNC_SIM_WASM_BLOCKERS_SELF_CHECKS must be 0 or 1: $self_checks" >&2 + exit 1 +fi +linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} +cache_dir=${CNC_SIM_WASM_BLOCKERS_CACHE_DIR-${CNC_SIM_PREFLIGHT_CACHE_DIR:-build/wasm-blocker-cache}} +if [[ -z "$cache_dir" ]]; then + echo "CNC_SIM_WASM_BLOCKERS_CACHE_DIR must not be empty" >&2 + exit 1 +fi +if [[ "$cache_dir" =~ [[:space:]] ]]; then + echo "CNC_SIM_WASM_BLOCKERS_CACHE_DIR must not contain whitespace: $cache_dir" >&2 + exit 1 +fi +mkdir -p "$cache_dir" +cache_dir=$(cd "$cache_dir" && pwd) +if [[ "$cache_dir" == "/" ]]; then + echo "CNC_SIM_WASM_BLOCKERS_CACHE_DIR must not be the filesystem root" >&2 + exit 1 +fi +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$cache_dir/preflight" "$manifest" +manifest=$(cd "$(dirname "$manifest")" && pwd)/$(basename "$manifest") +linuxcnc_root=$(cd "$linuxcnc_root" && pwd) + +if [[ "$self_checks" == "1" ]]; then missing_manifest=${TMPDIR:-/tmp}/cnc_sim_missing_wasm_blocker_manifest.txt missing_manifest_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_wasm_blocker_manifest.XXXXXX.log") @@ -49,8 +75,6 @@ if ! grep -F "missing LinuxCNC root: $missing_root" "$missing_root_log" >/dev/nu exit 1 fi -output=$(./analyze-linuxcnc-wasm-blockers.sh "$manifest") - 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 @@ -91,6 +115,57 @@ if ! grep -F "missing blocked replacement mapping: src/emc/tooldata/tooldata_db. exit 1 fi +fi + +manifest_key=$(printf '%s' "$manifest" | sha256sum | awk '{ print $1 }') +exec 8>"$cache_dir/wasm-blockers.lock" +flock 8 +cache_signature="$cache_dir/wasm-blockers.${manifest_key}.signature" +cache_output="$cache_dir/wasm-blockers.${manifest_key}.out" +next_signature="$cache_signature.next" +write_manifest_source_stats() { + local group path note source_path + while IFS=: read -r group path note; do + case "$group" in + ""|\#*) + continue + ;; + esac + [[ -z "${path:-}" ]] && continue + source_path="$linuxcnc_root/$path" + [[ -f "$source_path" ]] || return 1 + stat -c 'SOURCE=%n:%s:%y' "$source_path" + done < "$manifest" +} +write_shim_source_hashes() { + local shim_source + while IFS= read -r shim_source; do + [[ -n "$shim_source" ]] || continue + [[ -f "$shim_source" ]] || return 1 + sha256sum "$shim_source" + done < <(./list-linuxcnc-wasm-safe-shims.sh) +} +{ + printf 'ANALYZER=' + sha256sum analyze-linuxcnc-wasm-blockers.sh + printf 'SHIM_LISTER=' + sha256sum list-linuxcnc-wasm-safe-shims.sh + printf 'MANIFEST_PATH=%s\n' "$manifest" + printf 'MANIFEST=' + sha256sum "$manifest" + printf 'LINUXCNC_ROOT=%s\n' "$linuxcnc_root" + write_manifest_source_stats + write_shim_source_hashes +} > "$next_signature" +if [[ -f "$cache_signature" && -f "$cache_output" ]] && cmp -s "$cache_signature" "$next_signature"; then + output=$(cat "$cache_output") + rm -f "$next_signature" +else + output=$(CNC_SIM_PREFLIGHT_CACHE_DIR="$cache_dir/preflight" LINUXCNC_ROOT="$linuxcnc_root" ./analyze-linuxcnc-wasm-blockers.sh "$manifest") + printf '%s\n' "$output" > "$cache_output" + mv "$next_signature" "$cache_signature" +fi + grep -F "[manifest]" <<<"$output" >/dev/null grep -F "core=25" <<<"$(sed -n '/^\[manifest\]/,/^$/p' <<<"$output")" >/dev/null grep -F "blocked=9" <<<"$(sed -n '/^\[manifest\]/,/^$/p' <<<"$output")" >/dev/null diff --git a/test-linuxcnc-wasm-cmake-safe-probe.sh b/test-linuxcnc-wasm-cmake-safe-probe.sh index dcc1ef8..7cf5b82 100755 --- a/test-linuxcnc-wasm-cmake-safe-probe.sh +++ b/test-linuxcnc-wasm-cmake-safe-probe.sh @@ -9,9 +9,19 @@ if ! [[ "$build_jobs" =~ ^[1-9][0-9]*$ ]]; then echo "CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs" >&2 exit 1 fi +self_checks=${CNC_SIM_WASM_CMAKE_SAFE_PROBE_SELF_CHECKS:-0} +if [[ "$self_checks" != "0" && "$self_checks" != "1" ]]; then + echo "CNC_SIM_WASM_CMAKE_SAFE_PROBE_SELF_CHECKS must be 0 or 1: $self_checks" >&2 + exit 1 +fi cmake_compiler_launcher_mode=none +cmake_compiler_launcher_args=() if [[ -z ${CXX:-} ]] && command -v ccache >/dev/null 2>&1; then cmake_compiler_launcher_mode=ccache + cmake_compiler_launcher_args+=( + -DCMAKE_C_COMPILER_LAUNCHER=ccache + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + ) fi build_dir=${CNC_SIM_WASM_CMAKE_SAFE_PROBE_BUILD_DIR-build/wasm-cmake-safe-probe} if [[ -z "$build_dir" ]]; then @@ -37,6 +47,15 @@ invalid_build_jobs_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_build_wasm_invalid_jobs invalid_build_dir_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_build_wasm_invalid_dir.XXXXXX.log") empty_build_dir_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_build_wasm_empty_dir.XXXXXX.log") whitespace_build_dir_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_build_wasm_whitespace_dir.XXXXXX.log") +wasm_probe_object_cache_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_wasm_probe_object_cache.XXXXXX.log") +wasm_blockers_cache_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_wasm_blockers_cache.XXXXXX.log") +wasm_link_probe_cache_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_wasm_link_probe_cache.XXXXXX.log") +wasm_standalone_probe_cache_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_wasm_standalone_probe_cache.XXXXXX.log") +wasm_source_syntax_build_dir_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_wasm_source_syntax_build_dir.XXXXXX.log") +wasm_python_c_api_build_dir_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_wasm_python_c_api_build_dir.XXXXXX.log") +wasm_interp_find_build_dir_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_wasm_interp_find_build_dir.XXXXXX.log") +wasm_tooldata_mmap_build_dir_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_wasm_tooldata_mmap_build_dir.XXXXXX.log") +wasm_tooldata_runtime_build_dir_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_wasm_tooldata_runtime_build_dir.XXXXXX.log") cleanup_wasm_cmake_probe_temps() { local temp_path @@ -48,6 +67,15 @@ cleanup_wasm_cmake_probe_temps() { "$invalid_build_dir_log" \ "$empty_build_dir_log" \ "$whitespace_build_dir_log" \ + "$wasm_probe_object_cache_log" \ + "$wasm_blockers_cache_log" \ + "$wasm_link_probe_cache_log" \ + "$wasm_standalone_probe_cache_log" \ + "$wasm_source_syntax_build_dir_log" \ + "$wasm_python_c_api_build_dir_log" \ + "$wasm_interp_find_build_dir_log" \ + "$wasm_tooldata_mmap_build_dir_log" \ + "$wasm_tooldata_runtime_build_dir_log" \ "${build_next_signature:-}" \ "${unknown_manifest_filter_log:-}" \ "${unknown_manifest_output_log:-}" \ @@ -92,9 +120,10 @@ cleanup_wasm_cmake_probe_temps() { } trap cleanup_wasm_cmake_probe_temps EXIT -LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest" manifest=$(cd "$(dirname "$manifest")" && pwd)/$(basename "$manifest") linuxcnc_root=$(cd "$linuxcnc_root" && pwd) +preflight_cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-"$build_dir/preflight-cache"} +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" "$manifest" build_signature="$build_dir/wasm-cmake-safe-probe.signature" build_next_signature=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_wasm_cmake_safe_probe.signature.XXXXXX") { @@ -102,15 +131,23 @@ build_next_signature=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_wasm_cmake_safe_probe.sig printf 'PWD=%s\n' "$PWD" printf 'LINUXCNC_ROOT=%s\n' "$linuxcnc_root" printf 'MANIFEST=%s\n' "$manifest" - printf 'BUILD_JOBS=%s\n' "$build_jobs" + printf 'MANIFEST_HASH=' + sha256sum "$manifest" printf 'COMPILER_LAUNCHER=%s\n' "$cmake_compiler_launcher_mode" + printf 'CMAKE_LISTS=' + sha256sum core/CMakeLists.txt + printf 'CONFIGURE_RULE_VERSION=1\n' } > "$build_next_signature" +configure_cmake=1 if [[ ! -f "$build_signature" ]] || ! cmp -s "$build_signature" "$build_next_signature"; then - rm -rf "$build_dir" mkdir -p "$build_dir" +elif [[ -f "$build_dir/CMakeCache.txt" ]]; then + configure_cmake=0 fi mv "$build_next_signature" "$build_signature" +if [[ "$self_checks" == "1" ]]; then + rm -f "$missing_manifest" if CNC_SIM_BUILD_WASM_SKIP_LOCK=1 ./build-wasm.sh "$missing_manifest" >"$missing_manifest_log" 2>&1; then echo "build-wasm accepted missing manifest: $missing_manifest" >&2 @@ -171,8 +208,35 @@ 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 grep -F -- './test-linuxcnc-wasm-blockers.sh "$manifest"' build-wasm.sh >/dev/null +grep -F -- 'CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" ./test-linuxcnc-wasm-blockers.sh "$manifest"' build-wasm.sh >/dev/null +grep -F 'check-linuxcnc-inputs-cached.sh "$manifest"' analyze-linuxcnc-wasm-blockers.sh >/dev/null +grep -F 'CNC_SIM_WASM_BLOCKERS_CACHE_DIR' test-linuxcnc-wasm-blockers.sh >/dev/null +grep -F 'cache_dir=${CNC_SIM_WASM_BLOCKERS_CACHE_DIR-${CNC_SIM_PREFLIGHT_CACHE_DIR:-build/wasm-blocker-cache}}' test-linuxcnc-wasm-blockers.sh >/dev/null +grep -F 'CNC_SIM_WASM_BLOCKERS_CACHE_DIR must not be empty' test-linuxcnc-wasm-blockers.sh >/dev/null +grep -F 'CNC_SIM_WASM_BLOCKERS_CACHE_DIR must not contain whitespace: $cache_dir' test-linuxcnc-wasm-blockers.sh >/dev/null +grep -F 'CNC_SIM_WASM_BLOCKERS_CACHE_DIR must not be the filesystem root' test-linuxcnc-wasm-blockers.sh >/dev/null +grep -F 'exec 8>"$cache_dir/wasm-blockers.lock"' test-linuxcnc-wasm-blockers.sh >/dev/null +grep -F 'stat -c '\''SOURCE=%n:%s:%y'\''' test-linuxcnc-wasm-blockers.sh >/dev/null +grep -F 'sha256sum analyze-linuxcnc-wasm-blockers.sh' test-linuxcnc-wasm-blockers.sh >/dev/null +if CNC_SIM_WASM_BLOCKERS_CACHE_DIR= ./test-linuxcnc-wasm-blockers.sh "$manifest" >"$wasm_blockers_cache_log" 2>&1; then + echo "wasm blocker cache accepted an empty cache directory" >&2 + exit 1 +fi +grep -F 'CNC_SIM_WASM_BLOCKERS_CACHE_DIR must not be empty' "$wasm_blockers_cache_log" >/dev/null +if CNC_SIM_WASM_BLOCKERS_CACHE_DIR='build/wasm blocker cache' ./test-linuxcnc-wasm-blockers.sh "$manifest" >"$wasm_blockers_cache_log" 2>&1; then + echo "wasm blocker cache accepted a whitespace-containing cache directory" >&2 + exit 1 +fi +grep -F 'CNC_SIM_WASM_BLOCKERS_CACHE_DIR must not contain whitespace: build/wasm blocker cache' "$wasm_blockers_cache_log" >/dev/null +if CNC_SIM_WASM_BLOCKERS_CACHE_DIR=/ ./test-linuxcnc-wasm-blockers.sh "$manifest" >"$wasm_blockers_cache_log" 2>&1; then + echo "wasm blocker cache accepted the filesystem root as its cache directory" >&2 + exit 1 +fi +grep -F 'CNC_SIM_WASM_BLOCKERS_CACHE_DIR must not be the filesystem root' "$wasm_blockers_cache_log" >/dev/null grep -F -- './test-linuxcnc-wasm-source-syntax.sh "$manifest"' build-wasm.sh >/dev/null +grep -F -- 'CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" ./test-linuxcnc-wasm-source-syntax.sh "$manifest"' build-wasm.sh >/dev/null grep -F -- './test-linuxcnc-wasm-source-objects.sh "$manifest"' build-wasm.sh >/dev/null +grep -F -- 'CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" ./test-linuxcnc-wasm-source-objects.sh "$manifest"' build-wasm.sh >/dev/null grep -F -- './test-linuxcnc-wasm-cmake-safe-probe.sh "$manifest"' build-wasm.sh >/dev/null grep -F -- './test-linuxcnc-wasm-tooldata-link.sh' build-wasm.sh >/dev/null grep -F -- './test-linuxcnc-wasm-tooldata-common-link.sh' build-wasm.sh >/dev/null @@ -183,9 +247,284 @@ grep -F -- './test-linuxcnc-wasm-interp-find-link.sh' build-wasm.sh >/dev/null grep -F -- './test-linuxcnc-wasm-python-plugin-link.sh' build-wasm.sh >/dev/null grep -F -- './test-linuxcnc-wasm-python-c-api-symbols.sh' build-wasm.sh >/dev/null grep -F -- './test-linuxcnc-wasm-rs274ngc-pre-object.sh' build-wasm.sh >/dev/null -grep -F -- './test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh "$manifest"' build-wasm.sh >/dev/null -grep -F -- './test-linuxcnc-wasm-rs274ngc-pre-link.sh "$manifest"' build-wasm.sh >/dev/null -grep -F -- './test-linuxcnc-wasm-runtime-link.sh "$manifest"' build-wasm.sh >/dev/null +grep -F -- 'CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" ./test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh "$manifest"' build-wasm.sh >/dev/null +grep -F -- 'CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" ./test-linuxcnc-wasm-rs274ngc-pre-link.sh "$manifest"' build-wasm.sh >/dev/null +grep -F -- 'CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" ./test-linuxcnc-wasm-runtime-link.sh "$manifest"' build-wasm.sh >/dev/null +test -x build-linuxcnc-wasm-probe-objects.sh +grep -F 'CNC_SIM_WASM_PROBE_OBJECT_CACHE_DIR' build-linuxcnc-wasm-probe-objects.sh >/dev/null +grep -F 'cache_dir=${CNC_SIM_WASM_PROBE_OBJECT_CACHE_DIR-build/wasm-probe-object-cache}' build-linuxcnc-wasm-probe-objects.sh >/dev/null +grep -F 'CNC_SIM_WASM_PROBE_OBJECT_CACHE_DIR must not be empty' build-linuxcnc-wasm-probe-objects.sh >/dev/null +grep -F 'CNC_SIM_WASM_PROBE_OBJECT_CACHE_DIR must not contain whitespace: $cache_dir' build-linuxcnc-wasm-probe-objects.sh >/dev/null +grep -F 'CNC_SIM_WASM_PROBE_OBJECT_CACHE_DIR must not be the filesystem root' build-linuxcnc-wasm-probe-objects.sh >/dev/null +grep -F 'source-objects|rs274ngc-pre|runtime' build-linuxcnc-wasm-probe-objects.sh >/dev/null +grep -F 'wasm probe object source list contains an empty path' build-linuxcnc-wasm-probe-objects.sh >/dev/null +grep -F 'list-cmake-set-sources.sh cnc_sim_core_sources core/' list-linuxcnc-wasm-safe-project-sources.sh >/dev/null +grep -F 'list-cmake-set-sources.sh cnc_sim_linuxcnc_rs274_backend_sources core/' list-linuxcnc-wasm-safe-project-sources.sh >/dev/null +grep -F 'build-linuxcnc-wasm-probe-objects.sh --mode source-objects "$manifest"' test-linuxcnc-wasm-source-objects.sh >/dev/null +grep -F 'build-linuxcnc-wasm-probe-objects.sh --mode rs274ngc-pre "$manifest"' test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh >/dev/null +grep -F 'build-linuxcnc-wasm-link-probe.sh --mode rs274ngc-pre "$manifest"' test-linuxcnc-wasm-rs274ngc-pre-link.sh >/dev/null +grep -F 'build-linuxcnc-wasm-link-probe.sh --mode runtime "$manifest"' test-linuxcnc-wasm-runtime-link.sh >/dev/null +grep -F 'make --output-sync=target -j"$build_jobs" -f "$makefile" >/dev/null' build-linuxcnc-wasm-probe-objects.sh >/dev/null +grep -F 'source_${object_index}.o' build-linuxcnc-wasm-probe-objects.sh >/dev/null +grep -F 'DEP_FILES += %s.d' build-linuxcnc-wasm-probe-objects.sh >/dev/null +grep -F -- '-MMD -MP -MF %s.d' build-linuxcnc-wasm-probe-objects.sh >/dev/null +if CNC_SIM_WASM_PROBE_OBJECT_CACHE_DIR= ./build-linuxcnc-wasm-probe-objects.sh --mode source-objects "$manifest" >"$wasm_probe_object_cache_log" 2>&1; then + echo "wasm probe object cache accepted an empty cache directory" >&2 + exit 1 +fi +grep -F 'CNC_SIM_WASM_PROBE_OBJECT_CACHE_DIR must not be empty' "$wasm_probe_object_cache_log" >/dev/null +if CNC_SIM_WASM_PROBE_OBJECT_CACHE_DIR='build/wasm probe object cache' ./build-linuxcnc-wasm-probe-objects.sh --mode source-objects "$manifest" >"$wasm_probe_object_cache_log" 2>&1; then + echo "wasm probe object cache accepted a whitespace-containing cache directory" >&2 + exit 1 +fi +grep -F 'CNC_SIM_WASM_PROBE_OBJECT_CACHE_DIR must not contain whitespace: build/wasm probe object cache' "$wasm_probe_object_cache_log" >/dev/null +if CNC_SIM_WASM_PROBE_OBJECT_CACHE_DIR=/ ./build-linuxcnc-wasm-probe-objects.sh --mode source-objects "$manifest" >"$wasm_probe_object_cache_log" 2>&1; then + echo "wasm probe object cache accepted the filesystem root as its cache directory" >&2 + exit 1 +fi +grep -F 'CNC_SIM_WASM_PROBE_OBJECT_CACHE_DIR must not be the filesystem root' "$wasm_probe_object_cache_log" >/dev/null +test -x build-linuxcnc-wasm-standalone-probe.sh +grep -F 'CNC_SIM_WASM_STANDALONE_PROBE_CACHE_DIR' build-linuxcnc-wasm-standalone-probe.sh >/dev/null +grep -F 'cache_dir=${CNC_SIM_WASM_STANDALONE_PROBE_CACHE_DIR-build/wasm-standalone-probe-cache}' build-linuxcnc-wasm-standalone-probe.sh >/dev/null +grep -F 'CNC_SIM_WASM_STANDALONE_PROBE_CACHE_DIR must not be empty' build-linuxcnc-wasm-standalone-probe.sh >/dev/null +grep -F 'CNC_SIM_WASM_STANDALONE_PROBE_CACHE_DIR must not contain whitespace: $cache_dir' build-linuxcnc-wasm-standalone-probe.sh >/dev/null +grep -F 'CNC_SIM_WASM_STANDALONE_PROBE_CACHE_DIR must not be the filesystem root' build-linuxcnc-wasm-standalone-probe.sh >/dev/null +grep -F 'build_jobs=${CNC_SIM_BUILD_JOBS:-8}' build-linuxcnc-wasm-standalone-probe.sh >/dev/null +grep -F 'CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs' build-linuxcnc-wasm-standalone-probe.sh >/dev/null +grep -F 'tooldata-link|tooldata-common-link|interp-base-link|python-plugin-link|rs274ngc-pre-object' build-linuxcnc-wasm-standalone-probe.sh >/dev/null +grep -F 'check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" --root-only' build-linuxcnc-wasm-standalone-probe.sh >/dev/null +grep -F 'exec 8>"$mode_dir/probe.lock"' build-linuxcnc-wasm-standalone-probe.sh >/dev/null +grep -F 'DEP_FILES += %s.d' build-linuxcnc-wasm-standalone-probe.sh >/dev/null +grep -F 'make --output-sync=target -j"$build_jobs" -f "$makefile" >/dev/null' build-linuxcnc-wasm-standalone-probe.sh >/dev/null +grep -F 'build-linuxcnc-wasm-standalone-probe.sh --mode tooldata-link' test-linuxcnc-wasm-tooldata-link.sh >/dev/null +grep -F 'build-linuxcnc-wasm-standalone-probe.sh --mode tooldata-common-link' test-linuxcnc-wasm-tooldata-common-link.sh >/dev/null +grep -F 'build-linuxcnc-wasm-standalone-probe.sh --mode interp-base-link' test-linuxcnc-wasm-interp-base-link.sh >/dev/null +grep -F 'build-linuxcnc-wasm-standalone-probe.sh --mode python-plugin-link' test-linuxcnc-wasm-python-plugin-link.sh >/dev/null +grep -F 'build-linuxcnc-wasm-standalone-probe.sh --mode rs274ngc-pre-object' test-linuxcnc-wasm-rs274ngc-pre-object.sh >/dev/null +if CNC_SIM_WASM_STANDALONE_PROBE_CACHE_DIR= ./build-linuxcnc-wasm-standalone-probe.sh --mode python-plugin-link >"$wasm_standalone_probe_cache_log" 2>&1; then + echo "wasm standalone probe cache accepted an empty cache directory" >&2 + exit 1 +fi +grep -F 'CNC_SIM_WASM_STANDALONE_PROBE_CACHE_DIR must not be empty' "$wasm_standalone_probe_cache_log" >/dev/null +if CNC_SIM_WASM_STANDALONE_PROBE_CACHE_DIR='build/wasm standalone probe cache' ./build-linuxcnc-wasm-standalone-probe.sh --mode python-plugin-link >"$wasm_standalone_probe_cache_log" 2>&1; then + echo "wasm standalone probe cache accepted a whitespace-containing cache directory" >&2 + exit 1 +fi +grep -F 'CNC_SIM_WASM_STANDALONE_PROBE_CACHE_DIR must not contain whitespace: build/wasm standalone probe cache' "$wasm_standalone_probe_cache_log" >/dev/null +if CNC_SIM_WASM_STANDALONE_PROBE_CACHE_DIR=/ ./build-linuxcnc-wasm-standalone-probe.sh --mode python-plugin-link >"$wasm_standalone_probe_cache_log" 2>&1; then + echo "wasm standalone probe cache accepted the filesystem root as its cache directory" >&2 + exit 1 +fi +grep -F 'CNC_SIM_WASM_STANDALONE_PROBE_CACHE_DIR must not be the filesystem root' "$wasm_standalone_probe_cache_log" >/dev/null +if CNC_SIM_BUILD_JOBS=0 ./build-linuxcnc-wasm-standalone-probe.sh --mode python-plugin-link >"$wasm_standalone_probe_cache_log" 2>&1; then + echo "wasm standalone probe cache accepted invalid CNC_SIM_BUILD_JOBS" >&2 + exit 1 +fi +grep -F 'CNC_SIM_BUILD_JOBS must be a positive integer: 0' "$wasm_standalone_probe_cache_log" >/dev/null +test -x build-linuxcnc-wasm-link-probe.sh +grep -F 'CNC_SIM_WASM_LINK_PROBE_CACHE_DIR' build-linuxcnc-wasm-link-probe.sh >/dev/null +grep -F 'cache_dir=${CNC_SIM_WASM_LINK_PROBE_CACHE_DIR-build/wasm-link-probe-cache}' build-linuxcnc-wasm-link-probe.sh >/dev/null +grep -F 'CNC_SIM_WASM_LINK_PROBE_CACHE_DIR must not be empty' build-linuxcnc-wasm-link-probe.sh >/dev/null +grep -F 'CNC_SIM_WASM_LINK_PROBE_CACHE_DIR must not contain whitespace: $cache_dir' build-linuxcnc-wasm-link-probe.sh >/dev/null +grep -F 'CNC_SIM_WASM_LINK_PROBE_CACHE_DIR must not be the filesystem root' build-linuxcnc-wasm-link-probe.sh >/dev/null +grep -F 'rs274ngc-pre|runtime' build-linuxcnc-wasm-link-probe.sh >/dev/null +grep -F 'build-linuxcnc-wasm-probe-objects.sh --mode "$object_mode" "$manifest"' build-linuxcnc-wasm-link-probe.sh >/dev/null +grep -F 'exec 8>"$mode_dir/link.lock"' build-linuxcnc-wasm-link-probe.sh >/dev/null +grep -F 'make --output-sync=target -j"$build_jobs" -f "$makefile" >/dev/null' build-linuxcnc-wasm-link-probe.sh >/dev/null +grep -F 'build-linuxcnc-wasm-link-probe.sh --mode rs274ngc-pre "$manifest"' test-linuxcnc-wasm-rs274ngc-pre-link.sh >/dev/null +grep -F 'build-linuxcnc-wasm-link-probe.sh --mode runtime "$manifest"' test-linuxcnc-wasm-runtime-link.sh >/dev/null +grep -F 'symbol_signature="$probe_dir/runtime.symbols.signature"' test-linuxcnc-wasm-runtime-link.sh >/dev/null +grep -F "printf 'SYMBOL_RULE_VERSION=1\n'" test-linuxcnc-wasm-runtime-link.sh >/dev/null +grep -F "stat -c 'PROBE_STAT=%n:%s:%y' \"\$probe\"" test-linuxcnc-wasm-runtime-link.sh >/dev/null +grep -F 'nm -C "$probe" > "$symbol_file"' test-linuxcnc-wasm-runtime-link.sh >/dev/null +if CNC_SIM_WASM_LINK_PROBE_CACHE_DIR= ./build-linuxcnc-wasm-link-probe.sh --mode runtime "$manifest" >"$wasm_link_probe_cache_log" 2>&1; then + echo "wasm link probe cache accepted an empty cache directory" >&2 + exit 1 +fi +grep -F 'CNC_SIM_WASM_LINK_PROBE_CACHE_DIR must not be empty' "$wasm_link_probe_cache_log" >/dev/null +if CNC_SIM_WASM_LINK_PROBE_CACHE_DIR='build/wasm link probe cache' ./build-linuxcnc-wasm-link-probe.sh --mode runtime "$manifest" >"$wasm_link_probe_cache_log" 2>&1; then + echo "wasm link probe cache accepted a whitespace-containing cache directory" >&2 + exit 1 +fi +grep -F 'CNC_SIM_WASM_LINK_PROBE_CACHE_DIR must not contain whitespace: build/wasm link probe cache' "$wasm_link_probe_cache_log" >/dev/null +if CNC_SIM_WASM_LINK_PROBE_CACHE_DIR=/ ./build-linuxcnc-wasm-link-probe.sh --mode runtime "$manifest" >"$wasm_link_probe_cache_log" 2>&1; then + echo "wasm link probe cache accepted the filesystem root as its cache directory" >&2 + exit 1 +fi +grep -F 'CNC_SIM_WASM_LINK_PROBE_CACHE_DIR must not be the filesystem root' "$wasm_link_probe_cache_log" >/dev/null +if CNC_SIM_BUILD_JOBS=0 ./build-linuxcnc-wasm-link-probe.sh --mode runtime "$manifest" >"$wasm_link_probe_cache_log" 2>&1; then + echo "wasm link probe cache accepted invalid CNC_SIM_BUILD_JOBS" >&2 + exit 1 +fi +grep -F 'CNC_SIM_BUILD_JOBS must be a positive integer: 0' "$wasm_link_probe_cache_log" >/dev/null +grep -F 'build_dir=${CNC_SIM_WASM_SOURCE_SYNTAX_BUILD_DIR-build/wasm-source-syntax-test}' test-linuxcnc-wasm-source-syntax.sh >/dev/null +grep -F 'CNC_SIM_WASM_SOURCE_SYNTAX_BUILD_DIR must not be empty' test-linuxcnc-wasm-source-syntax.sh >/dev/null +grep -F 'CNC_SIM_WASM_SOURCE_SYNTAX_BUILD_DIR must not contain whitespace: $build_dir' test-linuxcnc-wasm-source-syntax.sh >/dev/null +grep -F 'CNC_SIM_WASM_SOURCE_SYNTAX_BUILD_DIR must not be the filesystem root' test-linuxcnc-wasm-source-syntax.sh >/dev/null +grep -F 'build_jobs=${CNC_SIM_BUILD_JOBS:-8}' test-linuxcnc-wasm-source-syntax.sh >/dev/null +grep -F 'CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs' test-linuxcnc-wasm-source-syntax.sh >/dev/null +grep -F 'command -v ccache >/dev/null 2>&1' test-linuxcnc-wasm-source-syntax.sh >/dev/null +grep -F 'check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir"' test-linuxcnc-wasm-source-syntax.sh >/dev/null +grep -F 'wasm_source_syntax_signature="$build_dir/wasm-source-syntax.signature"' test-linuxcnc-wasm-source-syntax.sh >/dev/null +grep -F "printf 'BUILD_RULE_VERSION=1\n'" test-linuxcnc-wasm-source-syntax.sh >/dev/null +grep -F 'make --output-sync=target -j"$build_jobs" -f "$wasm_source_syntax_makefile"' test-linuxcnc-wasm-source-syntax.sh >/dev/null +grep -F -- '-MMD -MP -MF %s/source_%s.d' test-linuxcnc-wasm-source-syntax.sh >/dev/null +if CNC_SIM_BUILD_JOBS=0 ./test-linuxcnc-wasm-source-syntax.sh "$manifest" >"$wasm_source_syntax_build_dir_log" 2>&1; then + echo "wasm source syntax probe accepted invalid CNC_SIM_BUILD_JOBS" >&2 + exit 1 +fi +grep -F 'CNC_SIM_BUILD_JOBS must be a positive integer: 0' "$wasm_source_syntax_build_dir_log" >/dev/null +if CNC_SIM_WASM_SOURCE_SYNTAX_BUILD_DIR= ./test-linuxcnc-wasm-source-syntax.sh "$manifest" >"$wasm_source_syntax_build_dir_log" 2>&1; then + echo "wasm source syntax probe accepted an empty build directory" >&2 + exit 1 +fi +grep -F 'CNC_SIM_WASM_SOURCE_SYNTAX_BUILD_DIR must not be empty' "$wasm_source_syntax_build_dir_log" >/dev/null +if CNC_SIM_WASM_SOURCE_SYNTAX_BUILD_DIR='build/wasm source syntax test' ./test-linuxcnc-wasm-source-syntax.sh "$manifest" >"$wasm_source_syntax_build_dir_log" 2>&1; then + echo "wasm source syntax probe accepted a whitespace-containing build directory" >&2 + exit 1 +fi +grep -F 'CNC_SIM_WASM_SOURCE_SYNTAX_BUILD_DIR must not contain whitespace: build/wasm source syntax test' "$wasm_source_syntax_build_dir_log" >/dev/null +if CNC_SIM_WASM_SOURCE_SYNTAX_BUILD_DIR=/ ./test-linuxcnc-wasm-source-syntax.sh "$manifest" >"$wasm_source_syntax_build_dir_log" 2>&1; then + echo "wasm source syntax probe accepted the filesystem root as its build directory" >&2 + exit 1 +fi +grep -F 'CNC_SIM_WASM_SOURCE_SYNTAX_BUILD_DIR must not be the filesystem root' "$wasm_source_syntax_build_dir_log" >/dev/null +grep -F 'build_dir=${CNC_SIM_WASM_PYTHON_C_API_BUILD_DIR-build/wasm-python-c-api-symbols}' test-linuxcnc-wasm-python-c-api-symbols.sh >/dev/null +grep -F 'CNC_SIM_WASM_PYTHON_C_API_BUILD_DIR must not be empty' test-linuxcnc-wasm-python-c-api-symbols.sh >/dev/null +grep -F 'CNC_SIM_WASM_PYTHON_C_API_BUILD_DIR must not contain whitespace: $build_dir' test-linuxcnc-wasm-python-c-api-symbols.sh >/dev/null +grep -F 'CNC_SIM_WASM_PYTHON_C_API_BUILD_DIR must not be the filesystem root' test-linuxcnc-wasm-python-c-api-symbols.sh >/dev/null +grep -F 'build_jobs=${CNC_SIM_BUILD_JOBS:-8}' test-linuxcnc-wasm-python-c-api-symbols.sh >/dev/null +grep -F 'CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs' test-linuxcnc-wasm-python-c-api-symbols.sh >/dev/null +grep -F 'command -v ccache >/dev/null 2>&1' test-linuxcnc-wasm-python-c-api-symbols.sh >/dev/null +grep -F 'check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" --root-only' test-linuxcnc-wasm-python-c-api-symbols.sh >/dev/null +grep -F 'signature="$build_dir/python-c-api.signature"' test-linuxcnc-wasm-python-c-api-symbols.sh >/dev/null +grep -F "printf 'BUILD_RULE_VERSION=1\n'" test-linuxcnc-wasm-python-c-api-symbols.sh >/dev/null +grep -F 'source_symbols_signature="$build_dir/python-c-api.source-symbols.signature"' test-linuxcnc-wasm-python-c-api-symbols.sh >/dev/null +grep -F "printf 'SOURCE_SYMBOL_RULE_VERSION=1\n'" test-linuxcnc-wasm-python-c-api-symbols.sh >/dev/null +grep -F 'object_symbols_signature="$build_dir/python-c-api.object-symbols.signature"' test-linuxcnc-wasm-python-c-api-symbols.sh >/dev/null +grep -F "printf 'OBJECT_SYMBOL_RULE_VERSION=1\n'" test-linuxcnc-wasm-python-c-api-symbols.sh >/dev/null +grep -F 'make --output-sync=target -j"$build_jobs" -f "$makefile" >/dev/null' test-linuxcnc-wasm-python-c-api-symbols.sh >/dev/null +grep -F -- '-MMD -MP -MF %s.d' test-linuxcnc-wasm-python-c-api-symbols.sh >/dev/null +if CNC_SIM_BUILD_JOBS=0 ./test-linuxcnc-wasm-python-c-api-symbols.sh >"$wasm_python_c_api_build_dir_log" 2>&1; then + echo "wasm Python C API probe accepted invalid CNC_SIM_BUILD_JOBS" >&2 + exit 1 +fi +grep -F 'CNC_SIM_BUILD_JOBS must be a positive integer: 0' "$wasm_python_c_api_build_dir_log" >/dev/null +if CNC_SIM_WASM_PYTHON_C_API_BUILD_DIR= ./test-linuxcnc-wasm-python-c-api-symbols.sh >"$wasm_python_c_api_build_dir_log" 2>&1; then + echo "wasm Python C API probe accepted an empty build directory" >&2 + exit 1 +fi +grep -F 'CNC_SIM_WASM_PYTHON_C_API_BUILD_DIR must not be empty' "$wasm_python_c_api_build_dir_log" >/dev/null +if CNC_SIM_WASM_PYTHON_C_API_BUILD_DIR='build/wasm python c api' ./test-linuxcnc-wasm-python-c-api-symbols.sh >"$wasm_python_c_api_build_dir_log" 2>&1; then + echo "wasm Python C API probe accepted a whitespace-containing build directory" >&2 + exit 1 +fi +grep -F 'CNC_SIM_WASM_PYTHON_C_API_BUILD_DIR must not contain whitespace: build/wasm python c api' "$wasm_python_c_api_build_dir_log" >/dev/null +if CNC_SIM_WASM_PYTHON_C_API_BUILD_DIR=/ ./test-linuxcnc-wasm-python-c-api-symbols.sh >"$wasm_python_c_api_build_dir_log" 2>&1; then + echo "wasm Python C API probe accepted the filesystem root as its build directory" >&2 + exit 1 +fi +grep -F 'CNC_SIM_WASM_PYTHON_C_API_BUILD_DIR must not be the filesystem root' "$wasm_python_c_api_build_dir_log" >/dev/null +grep -F 'build_dir=${CNC_SIM_WASM_INTERP_FIND_BUILD_DIR-build/wasm-interp-find-link}' test-linuxcnc-wasm-interp-find-link.sh >/dev/null +grep -F 'CNC_SIM_WASM_INTERP_FIND_BUILD_DIR must not be empty' test-linuxcnc-wasm-interp-find-link.sh >/dev/null +grep -F 'CNC_SIM_WASM_INTERP_FIND_BUILD_DIR must not contain whitespace: $build_dir' test-linuxcnc-wasm-interp-find-link.sh >/dev/null +grep -F 'CNC_SIM_WASM_INTERP_FIND_BUILD_DIR must not be the filesystem root' test-linuxcnc-wasm-interp-find-link.sh >/dev/null +grep -F 'build_jobs=${CNC_SIM_BUILD_JOBS:-8}' test-linuxcnc-wasm-interp-find-link.sh >/dev/null +grep -F 'CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs' test-linuxcnc-wasm-interp-find-link.sh >/dev/null +grep -F 'command -v ccache >/dev/null 2>&1' test-linuxcnc-wasm-interp-find-link.sh >/dev/null +grep -F 'check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" --root-only' test-linuxcnc-wasm-interp-find-link.sh >/dev/null +grep -F 'signature="$build_dir/interp-find.signature"' test-linuxcnc-wasm-interp-find-link.sh >/dev/null +grep -F "printf 'BUILD_RULE_VERSION=1\n'" test-linuxcnc-wasm-interp-find-link.sh >/dev/null +grep -F 'make --output-sync=target -j"$build_jobs" -f "$makefile" >/dev/null' test-linuxcnc-wasm-interp-find-link.sh >/dev/null +grep -F -- '-MMD -MP -MF %s.d' test-linuxcnc-wasm-interp-find-link.sh >/dev/null +if CNC_SIM_BUILD_JOBS=0 ./test-linuxcnc-wasm-interp-find-link.sh >"$wasm_interp_find_build_dir_log" 2>&1; then + echo "wasm interp_find probe accepted invalid CNC_SIM_BUILD_JOBS" >&2 + exit 1 +fi +grep -F 'CNC_SIM_BUILD_JOBS must be a positive integer: 0' "$wasm_interp_find_build_dir_log" >/dev/null +if CNC_SIM_WASM_INTERP_FIND_BUILD_DIR= ./test-linuxcnc-wasm-interp-find-link.sh >"$wasm_interp_find_build_dir_log" 2>&1; then + echo "wasm interp_find probe accepted an empty build directory" >&2 + exit 1 +fi +grep -F 'CNC_SIM_WASM_INTERP_FIND_BUILD_DIR must not be empty' "$wasm_interp_find_build_dir_log" >/dev/null +if CNC_SIM_WASM_INTERP_FIND_BUILD_DIR='build/wasm interp find' ./test-linuxcnc-wasm-interp-find-link.sh >"$wasm_interp_find_build_dir_log" 2>&1; then + echo "wasm interp_find probe accepted a whitespace-containing build directory" >&2 + exit 1 +fi +grep -F 'CNC_SIM_WASM_INTERP_FIND_BUILD_DIR must not contain whitespace: build/wasm interp find' "$wasm_interp_find_build_dir_log" >/dev/null +if CNC_SIM_WASM_INTERP_FIND_BUILD_DIR=/ ./test-linuxcnc-wasm-interp-find-link.sh >"$wasm_interp_find_build_dir_log" 2>&1; then + echo "wasm interp_find probe accepted the filesystem root as its build directory" >&2 + exit 1 +fi +grep -F 'CNC_SIM_WASM_INTERP_FIND_BUILD_DIR must not be the filesystem root' "$wasm_interp_find_build_dir_log" >/dev/null +grep -F 'build_dir=${CNC_SIM_WASM_TOOLDATA_MMAP_SYMBOLS_BUILD_DIR-build/wasm-tooldata-mmap-symbols}' test-linuxcnc-wasm-tooldata-mmap-symbols.sh >/dev/null +grep -F 'CNC_SIM_WASM_TOOLDATA_MMAP_SYMBOLS_BUILD_DIR must not be empty' test-linuxcnc-wasm-tooldata-mmap-symbols.sh >/dev/null +grep -F 'CNC_SIM_WASM_TOOLDATA_MMAP_SYMBOLS_BUILD_DIR must not contain whitespace: $build_dir' test-linuxcnc-wasm-tooldata-mmap-symbols.sh >/dev/null +grep -F 'CNC_SIM_WASM_TOOLDATA_MMAP_SYMBOLS_BUILD_DIR must not be the filesystem root' test-linuxcnc-wasm-tooldata-mmap-symbols.sh >/dev/null +grep -F 'build_jobs=${CNC_SIM_BUILD_JOBS:-8}' test-linuxcnc-wasm-tooldata-mmap-symbols.sh >/dev/null +grep -F 'CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs' test-linuxcnc-wasm-tooldata-mmap-symbols.sh >/dev/null +grep -F 'command -v ccache >/dev/null 2>&1' test-linuxcnc-wasm-tooldata-mmap-symbols.sh >/dev/null +grep -F 'check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" --root-only' test-linuxcnc-wasm-tooldata-mmap-symbols.sh >/dev/null +grep -F 'signature="$build_dir/tooldata-mmap-symbols.signature"' test-linuxcnc-wasm-tooldata-mmap-symbols.sh >/dev/null +grep -F "printf 'BUILD_RULE_VERSION=1\n'" test-linuxcnc-wasm-tooldata-mmap-symbols.sh >/dev/null +grep -F 'symbols_signature="$build_dir/tooldata-mmap-symbols.nm.signature"' test-linuxcnc-wasm-tooldata-mmap-symbols.sh >/dev/null +grep -F "printf 'SYMBOL_RULE_VERSION=1\n'" test-linuxcnc-wasm-tooldata-mmap-symbols.sh >/dev/null +grep -F 'make --output-sync=target -j"$build_jobs" -f "$makefile" >/dev/null' test-linuxcnc-wasm-tooldata-mmap-symbols.sh >/dev/null +grep -F -- '-MMD -MP -MF %s.d' test-linuxcnc-wasm-tooldata-mmap-symbols.sh >/dev/null +if CNC_SIM_BUILD_JOBS=0 ./test-linuxcnc-wasm-tooldata-mmap-symbols.sh >"$wasm_tooldata_mmap_build_dir_log" 2>&1; then + echo "wasm tooldata_mmap symbol probe accepted invalid CNC_SIM_BUILD_JOBS" >&2 + exit 1 +fi +grep -F 'CNC_SIM_BUILD_JOBS must be a positive integer: 0' "$wasm_tooldata_mmap_build_dir_log" >/dev/null +if CNC_SIM_WASM_TOOLDATA_MMAP_SYMBOLS_BUILD_DIR= ./test-linuxcnc-wasm-tooldata-mmap-symbols.sh >"$wasm_tooldata_mmap_build_dir_log" 2>&1; then + echo "wasm tooldata_mmap symbol probe accepted an empty build directory" >&2 + exit 1 +fi +grep -F 'CNC_SIM_WASM_TOOLDATA_MMAP_SYMBOLS_BUILD_DIR must not be empty' "$wasm_tooldata_mmap_build_dir_log" >/dev/null +if CNC_SIM_WASM_TOOLDATA_MMAP_SYMBOLS_BUILD_DIR='build/wasm tooldata mmap' ./test-linuxcnc-wasm-tooldata-mmap-symbols.sh >"$wasm_tooldata_mmap_build_dir_log" 2>&1; then + echo "wasm tooldata_mmap symbol probe accepted a whitespace-containing build directory" >&2 + exit 1 +fi +grep -F 'CNC_SIM_WASM_TOOLDATA_MMAP_SYMBOLS_BUILD_DIR must not contain whitespace: build/wasm tooldata mmap' "$wasm_tooldata_mmap_build_dir_log" >/dev/null +if CNC_SIM_WASM_TOOLDATA_MMAP_SYMBOLS_BUILD_DIR=/ ./test-linuxcnc-wasm-tooldata-mmap-symbols.sh >"$wasm_tooldata_mmap_build_dir_log" 2>&1; then + echo "wasm tooldata_mmap symbol probe accepted the filesystem root as its build directory" >&2 + exit 1 +fi +grep -F 'CNC_SIM_WASM_TOOLDATA_MMAP_SYMBOLS_BUILD_DIR must not be the filesystem root' "$wasm_tooldata_mmap_build_dir_log" >/dev/null +grep -F 'build_dir=${CNC_SIM_WASM_TOOLDATA_RUNTIME_SYMBOLS_BUILD_DIR-build/wasm-tooldata-runtime-symbols}' test-linuxcnc-wasm-tooldata-runtime-symbols.sh >/dev/null +grep -F 'CNC_SIM_WASM_TOOLDATA_RUNTIME_SYMBOLS_BUILD_DIR must not be empty' test-linuxcnc-wasm-tooldata-runtime-symbols.sh >/dev/null +grep -F 'CNC_SIM_WASM_TOOLDATA_RUNTIME_SYMBOLS_BUILD_DIR must not contain whitespace: $build_dir' test-linuxcnc-wasm-tooldata-runtime-symbols.sh >/dev/null +grep -F 'CNC_SIM_WASM_TOOLDATA_RUNTIME_SYMBOLS_BUILD_DIR must not be the filesystem root' test-linuxcnc-wasm-tooldata-runtime-symbols.sh >/dev/null +grep -F 'build_jobs=${CNC_SIM_BUILD_JOBS:-8}' test-linuxcnc-wasm-tooldata-runtime-symbols.sh >/dev/null +grep -F 'CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs' test-linuxcnc-wasm-tooldata-runtime-symbols.sh >/dev/null +grep -F 'command -v ccache >/dev/null 2>&1' test-linuxcnc-wasm-tooldata-runtime-symbols.sh >/dev/null +grep -F 'check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" --root-only' test-linuxcnc-wasm-tooldata-runtime-symbols.sh >/dev/null +grep -F 'signature="$build_dir/tooldata-runtime-symbols.signature"' test-linuxcnc-wasm-tooldata-runtime-symbols.sh >/dev/null +grep -F "printf 'BUILD_RULE_VERSION=1\n'" test-linuxcnc-wasm-tooldata-runtime-symbols.sh >/dev/null +grep -F 'symbols_signature="$build_dir/tooldata-runtime-symbols.nm.signature"' test-linuxcnc-wasm-tooldata-runtime-symbols.sh >/dev/null +grep -F "printf 'SYMBOL_RULE_VERSION=1\n'" test-linuxcnc-wasm-tooldata-runtime-symbols.sh >/dev/null +grep -F 'make --output-sync=target -j"$build_jobs" -f "$makefile" >/dev/null' test-linuxcnc-wasm-tooldata-runtime-symbols.sh >/dev/null +grep -F -- '-MMD -MP -MF %s.d' test-linuxcnc-wasm-tooldata-runtime-symbols.sh >/dev/null +if CNC_SIM_BUILD_JOBS=0 ./test-linuxcnc-wasm-tooldata-runtime-symbols.sh >"$wasm_tooldata_runtime_build_dir_log" 2>&1; then + echo "wasm tooldata runtime symbol probe accepted invalid CNC_SIM_BUILD_JOBS" >&2 + exit 1 +fi +grep -F 'CNC_SIM_BUILD_JOBS must be a positive integer: 0' "$wasm_tooldata_runtime_build_dir_log" >/dev/null +if CNC_SIM_WASM_TOOLDATA_RUNTIME_SYMBOLS_BUILD_DIR= ./test-linuxcnc-wasm-tooldata-runtime-symbols.sh >"$wasm_tooldata_runtime_build_dir_log" 2>&1; then + echo "wasm tooldata runtime symbol probe accepted an empty build directory" >&2 + exit 1 +fi +grep -F 'CNC_SIM_WASM_TOOLDATA_RUNTIME_SYMBOLS_BUILD_DIR must not be empty' "$wasm_tooldata_runtime_build_dir_log" >/dev/null +if CNC_SIM_WASM_TOOLDATA_RUNTIME_SYMBOLS_BUILD_DIR='build/wasm tooldata runtime' ./test-linuxcnc-wasm-tooldata-runtime-symbols.sh >"$wasm_tooldata_runtime_build_dir_log" 2>&1; then + echo "wasm tooldata runtime symbol probe accepted a whitespace-containing build directory" >&2 + exit 1 +fi +grep -F 'CNC_SIM_WASM_TOOLDATA_RUNTIME_SYMBOLS_BUILD_DIR must not contain whitespace: build/wasm tooldata runtime' "$wasm_tooldata_runtime_build_dir_log" >/dev/null +if CNC_SIM_WASM_TOOLDATA_RUNTIME_SYMBOLS_BUILD_DIR=/ ./test-linuxcnc-wasm-tooldata-runtime-symbols.sh >"$wasm_tooldata_runtime_build_dir_log" 2>&1; then + echo "wasm tooldata runtime symbol probe accepted the filesystem root as its build directory" >&2 + exit 1 +fi +grep -F 'CNC_SIM_WASM_TOOLDATA_RUNTIME_SYMBOLS_BUILD_DIR must not be the filesystem root' "$wasm_tooldata_runtime_build_dir_log" >/dev/null if grep -F '} > "$report_file" || true' test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh >/dev/null; then echo "rs274ngc_pre blocker scan still hides report pipeline failures with || true" >&2 exit 1 @@ -203,37 +542,34 @@ if ! awk ' exit 1 fi if ! awk ' - index($0, "LINUXCNC_ROOT=\"$linuxcnc_root\" ./check-linuxcnc-inputs.sh \"$manifest\"") { check_line = NR } - index($0, "report_file=$(mktemp ") { report_line = NR } + index($0, "check-linuxcnc-inputs-cached.sh --cache-dir \"$preflight_cache_dir\" \"$manifest\"") { check_line = NR } + index($0, "cache_dir=${CNC_SIM_WASM_RS274NGC_PRE_LINK_BLOCKERS_CACHE_DIR-build/wasm-rs274ngc-pre-link-blockers}") { report_line = NR } END { exit !(check_line && report_line && check_line < report_line) } ' test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh; then - echo "rs274ngc_pre blocker scan creates its report before input validation" >&2 + echo "rs274ngc_pre blocker scan prepares its report cache before input validation" >&2 exit 1 fi +grep -F 'CNC_SIM_WASM_RS274NGC_PRE_LINK_BLOCKERS_CACHE_DIR' test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh >/dev/null +grep -F 'rs274ngc-pre-link-blockers.lock' test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh >/dev/null +grep -F "printf 'FILTER_RULE_VERSION=1\n'" test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh >/dev/null +grep -F "stat -c 'OBJECT=%n:%s:%y'" test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh >/dev/null +grep -F 'cmp -s "$signature" "$next_signature"' test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh >/dev/null for build_dir_probe_script in \ - test-linuxcnc-wasm-interp-base-link.sh \ - test-linuxcnc-wasm-interp-find-link.sh \ - test-linuxcnc-wasm-python-c-api-symbols.sh \ - test-linuxcnc-wasm-python-plugin-link.sh \ - test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh \ - test-linuxcnc-wasm-rs274ngc-pre-link.sh \ - test-linuxcnc-wasm-runtime-link.sh \ - test-linuxcnc-wasm-rs274ngc-pre-object.sh \ - test-linuxcnc-wasm-source-objects.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; do + test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh; do if ! awk ' $0 ~ /^trap .* EXIT$/ { trap_line = NR } - index($0, "./check-linuxcnc-inputs.sh") && !check_line { check_line = NR } + (index($0, "./check-linuxcnc-inputs.sh") || + index($0, "./check-linuxcnc-inputs-cached.sh") || + index($0, "./build-linuxcnc-wasm-probe-objects.sh")) && !check_line { check_line = NR } END { exit !(trap_line && check_line && trap_line < check_line) } ' "$build_dir_probe_script"; then - echo "$build_dir_probe_script installs build_dir cleanup after input validation" >&2 + echo "$build_dir_probe_script installs build_dir cleanup after failure-prone input setup" >&2 exit 1 fi done grep -F 'trap '\''rm -f "$missing_manifest" "$missing_manifest_log" "$missing_root_log" "$missing_source_manifest" "$missing_source_log" "$duplicate_manifest" "$duplicate_manifest_log" "$missing_replacement_manifest" "$missing_replacement_log"'\'' EXIT' test-linuxcnc-wasm-blockers.sh >/dev/null +grep -F 'self_checks=${CNC_SIM_WASM_BLOCKERS_SELF_CHECKS:-0}' test-linuxcnc-wasm-blockers.sh >/dev/null +grep -F 'CNC_SIM_WASM_BLOCKERS_SELF_CHECKS must be 0 or 1: $self_checks' test-linuxcnc-wasm-blockers.sh >/dev/null if grep -F 'rm -f "$missing_source_manifest" "$missing_source_log"' test-linuxcnc-wasm-blockers.sh >/dev/null; then echo "wasm blocker scan still relies on manual cleanup for missing-source temporaries" >&2 exit 1 @@ -287,6 +623,8 @@ if grep -RInF "${forbidden_common_flags_process_substitution}LINUXCNC_ROOT=\"\$l grep -RInF "${forbidden_common_flags_process_substitution}LINUXCNC_ROOT=\"\$linuxcnc_root\" ${forbidden_common_flags_helper}" test-linuxcnc-wasm-*.sh >&2 exit 1 fi +grep -F 'preflight_cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-build/linuxcnc-helper-preflight-cache}' list-linuxcnc-wasm-common-cxxflags.sh >/dev/null +grep -F 'check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" --root-only' list-linuxcnc-wasm-common-cxxflags.sh >/dev/null grep -F -- '-DCNC_SIM_LINUXCNC_WASM_SOURCE_MANIFEST="$manifest"' build-wasm.sh >/dev/null grep -F -- 'for required_tool in cmake emcmake emcc node; do' build-wasm.sh >/dev/null grep -F -- 'missing_tools+=("$required_tool")' build-wasm.sh >/dev/null @@ -294,6 +632,31 @@ grep -F "WASM build tools are required. Missing:" build-wasm.sh >/dev/null grep -F "Install/activate cmake, emsdk, and Node.js so cmake, emcmake, emcc, and node are in PATH." build-wasm.sh >/dev/null grep -F 'build_jobs=${CNC_SIM_BUILD_JOBS:-8}' build-wasm.sh >/dev/null grep -F 'CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs' build-wasm.sh >/dev/null +grep -F 'cmake_compiler_launcher_mode=none' build-wasm.sh >/dev/null +grep -F 'cmake_compiler_launcher_mode=ccache' build-wasm.sh >/dev/null +grep -F 'wasm_build_signature=build/wasm/wasm-build.signature' build-wasm.sh >/dev/null +grep -F 'wasm_build_next_signature=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_wasm_build.signature.XXXXXX")' build-wasm.sh >/dev/null +grep -F 'sha256sum "$manifest"' build-wasm.sh >/dev/null +grep -F 'sha256sum core/CMakeLists.txt' build-wasm.sh >/dev/null +grep -F "printf 'CONFIGURE_RULE_VERSION=1\n'" build-wasm.sh >/dev/null +grep -F 'run_parallel_wasm_probe() {' build-wasm.sh >/dev/null +grep -F 'wait_parallel_wasm_probes() {' build-wasm.sh >/dev/null +grep -F 'parallel_probe_pids+=("$!")' build-wasm.sh >/dev/null +grep -F 'CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" "$@"' build-wasm.sh >/dev/null +grep -F 'run_parallel_wasm_probe "LinuxCNC wasm tooldata shim link probe" ./test-linuxcnc-wasm-tooldata-link.sh' build-wasm.sh >/dev/null +grep -F 'run_parallel_wasm_probe "LinuxCNC wasm rs274ngc_pre object probe" ./test-linuxcnc-wasm-rs274ngc-pre-object.sh' build-wasm.sh >/dev/null +grep -F 'wait_parallel_wasm_probes' build-wasm.sh >/dev/null +grep -F 'configure_wasm=1' build-wasm.sh >/dev/null +grep -F 'elif [[ -f build/wasm/CMakeCache.txt ]]; then' build-wasm.sh >/dev/null +grep -F 'configure_wasm=0' build-wasm.sh >/dev/null +if grep -F 'rm -rf build/wasm' build-wasm.sh >/dev/null; then + echo "build-wasm configure signature changes must reconfigure build/wasm in place" >&2 + exit 1 +fi +if grep -F 'printf '\''BUILD_JOBS=%s\n'\'' "$build_jobs"' build-wasm.sh >/dev/null; then + echo "build-wasm configure signature must not depend on CNC_SIM_BUILD_JOBS" >&2 + exit 1 +fi grep -F 'build_jobs=${CNC_SIM_BUILD_JOBS:-8}' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null grep -F 'CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null grep -F 'build_dir=${CNC_SIM_WASM_CMAKE_SAFE_PROBE_BUILD_DIR-build/wasm-cmake-safe-probe}' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null @@ -305,7 +668,51 @@ grep -F 'build_next_signature=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_wasm_cmake_safe_ grep -F 'if [[ ! -f "$build_signature" ]] || ! cmp -s "$build_signature" "$build_next_signature"; then' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null grep -F 'printf '\''LINUXCNC_ROOT=%s\n'\'' "$linuxcnc_root"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null grep -F 'printf '\''MANIFEST=%s\n'\'' "$manifest"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null -grep -F 'printf '\''BUILD_JOBS=%s\n'\'' "$build_jobs"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null +grep -F 'sha256sum "$manifest"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null +grep -F "printf 'CONFIGURE_RULE_VERSION=1\n'" test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null +if awk ' + BEGIN { forbidden = "rm -rf " "\"$build_dir\"" } + $0 ~ "^[[:space:]]*" forbidden { found = 1 } + END { exit !found } +' test-linuxcnc-wasm-cmake-safe-probe.sh; then + echo "wasm CMake probe signature changes must reconfigure build_dir in place" >&2 + exit 1 +fi +for signature_script in \ + build-wasm.sh \ + build-linuxcnc-wasm-probe-objects.sh \ + build-linuxcnc-wasm-link-probe.sh \ + build-linuxcnc-wasm-standalone-probe.sh \ + test-linuxcnc-source-syntax.sh \ + test-linuxcnc-wasm-cmake-safe-probe.sh \ + test-linuxcnc-wasm-interp-find-link.sh \ + test-linuxcnc-wasm-python-c-api-symbols.sh \ + test-linuxcnc-wasm-source-syntax.sh \ + test-linuxcnc-wasm-tooldata-mmap-symbols.sh \ + test-linuxcnc-wasm-tooldata-runtime-symbols.sh; do + if grep -F 'printf '\''BUILD_JOBS=%s\n'\'' "$build_jobs"' "$signature_script" >/dev/null; then + echo "$signature_script signature must not depend on CNC_SIM_BUILD_JOBS" >&2 + exit 1 + fi +done +for hot_signature_script in \ + build-linuxcnc-wasm-link-probe.sh \ + build-linuxcnc-wasm-probe-objects.sh \ + build-linuxcnc-wasm-standalone-probe.sh \ + test-linuxcnc-wasm-interp-find-link.sh \ + test-linuxcnc-wasm-python-c-api-symbols.sh \ + test-linuxcnc-wasm-source-syntax.sh \ + test-linuxcnc-wasm-tooldata-mmap-symbols.sh \ + test-linuxcnc-wasm-tooldata-runtime-symbols.sh; do + if grep -F "sha256sum $hot_signature_script" "$hot_signature_script" >/dev/null; then + echo "$hot_signature_script hot object signature must use BUILD_RULE_VERSION instead of the whole script hash" >&2 + exit 1 + fi +done +if grep -F 'sha256sum build-wasm.sh' build-wasm.sh >/dev/null; then + echo "build-wasm configure signature must use CONFIGURE_RULE_VERSION instead of the whole script hash" >&2 + exit 1 +fi grep -F 'build_jobs=${CNC_SIM_BUILD_JOBS:-8}' test-native.sh >/dev/null grep -F 'CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs' test-native.sh >/dev/null grep -F 'build_dir=${CNC_SIM_NATIVE_BUILD_DIR-build/native-test}' test-native.sh >/dev/null @@ -331,11 +738,8 @@ grep -F 'printf '\''\t@$(CXX) $(COMMON_FLAGS) -MMD -MP -MF %s.d -c %s -o %s\n\n' grep -F 'printf '\''\n-include $(DEP_FILES)\n'\''' test-linuxcnc-source-link.sh >/dev/null grep -F 'make --output-sync=target -j"$build_jobs" -f "$source_link_makefile"' test-linuxcnc-source-link.sh >/dev/null grep -F -- 'emcmake cmake -S core -B build/wasm \' build-wasm.sh >/dev/null -grep -F -- 'cmake --build build/wasm --target cnc_sim_wasm_runtime_probe --parallel "$build_jobs"' build-wasm.sh >/dev/null -grep -F -- 'cmake --build build/wasm --target cnc_sim_wasm --parallel "$build_jobs"' build-wasm.sh >/dev/null -grep -F -- 'cmake --build "$build_dir" --target linuxcnc_rs274_wasm_safe_probe_objects --parallel "$build_jobs"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null -grep -F -- 'cmake --build "$build_dir" --target linuxcnc_rs274_wasm_safe_probe --parallel "$build_jobs"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null -grep -F -- 'cmake --build "$build_dir" --target cnc_sim_wasm_runtime_probe --parallel "$build_jobs"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null +grep -F -- 'cmake --build build/wasm --target cnc_sim_wasm_runtime_probe cnc_sim_wasm --parallel "$build_jobs"' build-wasm.sh >/dev/null +grep -F -- 'cmake --build "$build_dir" --target linuxcnc_rs274_wasm_safe_probe cnc_sim_wasm_runtime_probe --parallel "$build_jobs"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null grep -F -- 'mkdir -p web/public' build-wasm.sh >/dev/null grep -F -- 'cp build/wasm/cnc_sim.js web/public/cnc_sim.js' build-wasm.sh >/dev/null grep -F -- 'cp build/wasm/cnc_sim.wasm web/public/cnc_sim.wasm' build-wasm.sh >/dev/null @@ -394,6 +798,15 @@ if ! awk ' echo "build-wasm must create web/public before copying wasm artifacts" >&2 exit 1 fi +if ! awk ' + index($0, "run_parallel_wasm_probe \"LinuxCNC wasm tooldata shim link probe\"") { parallel_start_line = NR } + index($0, "wait_parallel_wasm_probes") && parallel_start_line { wait_line = NR } + index($0, "LinuxCNC wasm rs274ngc_pre link blocker scan") { pre_link_line = NR } + END { exit !(parallel_start_line && wait_line && pre_link_line && parallel_start_line < wait_line && wait_line < pre_link_line) } +' build-wasm.sh; then + echo "build-wasm must wait for independent wasm probes before rs274ngc_pre link checks" >&2 + exit 1 +fi if ! awk ' index($0, "cp build/wasm/cnc_sim.js web/public/cnc_sim.js") { cp_js_line = NR } index($0, "cp build/wasm/cnc_sim.wasm web/public/cnc_sim.wasm") { cp_wasm_line = NR } @@ -412,27 +825,28 @@ if ! awk ' exit 1 fi if ! awk ' - index($0, "cmake --build build/wasm --target cnc_sim_wasm_runtime_probe --parallel \"$build_jobs\"") { runtime_probe_line = NR } - index($0, "cmake --build build/wasm --target cnc_sim_wasm --parallel \"$build_jobs\"") && - !index($0, "cnc_sim_wasm_runtime_probe") { wasm_line = NR } - END { exit !(runtime_probe_line && wasm_line && runtime_probe_line < wasm_line) } + index($0, "cmake --build build/wasm --target cnc_sim_wasm_runtime_probe cnc_sim_wasm --parallel \"$build_jobs\"") { + runtime_pos = index($0, "cnc_sim_wasm_runtime_probe") + wasm_pos = index($0, " cnc_sim_wasm ") + } + END { exit !(runtime_pos && wasm_pos && runtime_pos < wasm_pos) } ' build-wasm.sh; then echo "build-wasm does not build the CMake runtime probe before the browser WASM target" >&2 exit 1 fi grep -F -- 'default_manifest="$PWD/linuxcnc-rs274-wasm-source-files.txt"' build-wasm.sh >/dev/null -grep -F -- 'manifest_core_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core)' build-wasm.sh >/dev/null -grep -F -- 'manifest_blocked_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" blocked)' build-wasm.sh >/dev/null +grep -F -- 'manifest_core_output=$(CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core)' build-wasm.sh >/dev/null +grep -F -- 'manifest_blocked_output=$(CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" blocked)' build-wasm.sh >/dev/null grep -F -- 'count_nonempty_lines() {' build-wasm.sh >/dev/null grep -F -- 'manifest_core_count=$(count_nonempty_lines "$manifest_core_output")' build-wasm.sh >/dev/null grep -F -- 'manifest_blocked_count=$(count_nonempty_lines "$manifest_blocked_output")' build-wasm.sh >/dev/null -grep -F -- 'LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest" --require-rs274-complete' build-wasm.sh >/dev/null +grep -F -- 'LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" "$manifest" --require-rs274-complete' build-wasm.sh >/dev/null if grep -F 'grep -c . <<<"$manifest_core_output" || true' build-wasm.sh >/dev/null; then echo "build-wasm still counts manifest output through grep -c with || true" >&2 exit 1 fi grep -F -- 'if [[ "$manifest" == "$default_manifest" ]] && [[ "$manifest_core_count" -ne 25 || "$manifest_blocked_count" -ne 9 ]]; then' build-wasm.sh >/dev/null -grep -Fx 'LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null +grep -F 'LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" "$manifest"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null ./list-linuxcnc-wasm-safe-shims.sh >/dev/null ./list-linuxcnc-wasm-safe-project-sources.sh >/dev/null ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core >/dev/null @@ -746,6 +1160,7 @@ bad_default_manifest_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_bad_default_wasm_mani linuxcnc_root_abs=$(cd "$linuxcnc_root" && pwd) cp build-wasm.sh "$bad_default_manifest_dir/build-wasm.sh" cp check-linuxcnc-inputs.sh "$bad_default_manifest_dir/check-linuxcnc-inputs.sh" +cp check-linuxcnc-inputs-cached.sh "$bad_default_manifest_dir/check-linuxcnc-inputs-cached.sh" cp list-linuxcnc-source-files.sh "$bad_default_manifest_dir/list-linuxcnc-source-files.sh" cp list-linuxcnc-manifest-sources.sh "$bad_default_manifest_dir/list-linuxcnc-manifest-sources.sh" cp list-linuxcnc-wasm-manifest-sources.sh "$bad_default_manifest_dir/list-linuxcnc-wasm-manifest-sources.sh" @@ -887,8 +1302,8 @@ manifest_core_count=0 manifest_blocked_count=0 manifest_core_sources="$build_dir/wasm_core_sources.txt" manifest_blocked_sources="$build_dir/wasm_blocked_sources.txt" -./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core > "$manifest_core_sources" -./list-linuxcnc-wasm-manifest-sources.sh "$manifest" blocked > "$manifest_blocked_sources" +CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core > "$manifest_core_sources" +CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" blocked > "$manifest_blocked_sources" manifest_core_count=$(wc -l < "$manifest_core_sources") manifest_blocked_count=$(wc -l < "$manifest_blocked_sources") @@ -988,21 +1403,24 @@ grep -F '$' core/CMakeLists.txt >/dev/null grep -F 'CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND' core/CMakeLists.txt >/dev/null grep -F '#ifndef CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE' core/src/linuxcnc_rs274_backend.cpp >/dev/null +fi + 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 +if [[ "$configure_cmake" -eq 1 ]]; then + 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_compiler_launcher_args[@]}" +fi -cmake --build "$build_dir" --target linuxcnc_rs274_wasm_safe_probe_objects --parallel "$build_jobs" -cmake --build "$build_dir" --target linuxcnc_rs274_wasm_safe_probe --parallel "$build_jobs" +cmake --build "$build_dir" --target linuxcnc_rs274_wasm_safe_probe cnc_sim_wasm_runtime_probe --parallel "$build_jobs" "$build_dir/linuxcnc_rs274_wasm_safe_probe" -cmake --build "$build_dir" --target cnc_sim_wasm_runtime_probe --parallel "$build_jobs" "$build_dir/cnc_sim_wasm_runtime_probe" echo "linuxcnc wasm-safe CMake probe target passed" diff --git a/test-linuxcnc-wasm-interp-base-link.sh b/test-linuxcnc-wasm-interp-base-link.sh index c214feb..f49561f 100755 --- a/test-linuxcnc-wasm-interp-base-link.sh +++ b/test-linuxcnc-wasm-interp-base-link.sh @@ -3,25 +3,8 @@ 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") +probe=$(./build-linuxcnc-wasm-standalone-probe.sh --mode interp-base-link) -trap 'rm -rf "$build_dir"' EXIT -LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only - -dlfcn_shim=$(./list-linuxcnc-wasm-safe-shims.sh dlfcn.cc) -interp_base_source=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-files.sh src/emc/rs274ngc/interp_base.cc) - -common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh) -mapfile -t common_flags <<<"$common_flag_output" - -"$cxx" "${common_flags[@]}" \ - "$dlfcn_shim" \ - "$interp_base_source" \ - core/wasm_shims/interp_base_probe_main.cc \ - -o "$build_dir/interp_base_probe" - -"$build_dir/interp_base_probe" +"$probe" echo "linuxcnc wasm interp_base link probe passed" diff --git a/test-linuxcnc-wasm-interp-find-link.sh b/test-linuxcnc-wasm-interp-find-link.sh index bc2ea8c..9644294 100755 --- a/test-linuxcnc-wasm-interp-find-link.sh +++ b/test-linuxcnc-wasm-interp-find-link.sh @@ -5,10 +5,35 @@ 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 [[ -z ${CXX:-} ]] && command -v ccache >/dev/null 2>&1; then + cxx="ccache $cxx" +fi +build_jobs=${CNC_SIM_BUILD_JOBS:-8} +if ! [[ "$build_jobs" =~ ^[1-9][0-9]*$ ]]; then + echo "CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs" >&2 + exit 1 +fi +build_dir=${CNC_SIM_WASM_INTERP_FIND_BUILD_DIR-build/wasm-interp-find-link} +if [[ -z "$build_dir" ]]; then + echo "CNC_SIM_WASM_INTERP_FIND_BUILD_DIR must not be empty" >&2 + exit 1 +fi +if [[ "$build_dir" =~ [[:space:]] ]]; then + echo "CNC_SIM_WASM_INTERP_FIND_BUILD_DIR must not contain whitespace: $build_dir" >&2 + exit 1 +fi +mkdir -p "$build_dir" +build_dir=$(cd "$build_dir" && pwd) +if [[ "$build_dir" == "/" ]]; then + echo "CNC_SIM_WASM_INTERP_FIND_BUILD_DIR must not be the filesystem root" >&2 + exit 1 +fi +exec 9>"$build_dir/interp-find.lock" +flock 9 -trap 'rm -rf "$build_dir"' EXIT -LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only +preflight_cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-"$build_dir/preflight-cache"} +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" --root-only +linuxcnc_root=$(cd "$linuxcnc_root" && pwd) tooldata_mmap_backend_shim=$(./list-linuxcnc-wasm-safe-shims.sh tooldata_mmap_backend.cc) tooldata_runtime_stubs_shim=$(./list-linuxcnc-wasm-safe-shims.sh tooldata_runtime_stubs.cc) @@ -23,38 +48,71 @@ interp_find_source=${linuxcnc_sources[1]} common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh --function-sections --data-sections) mapfile -t common_flags <<<"$common_flag_output" -"$cxx" "${common_flags[@]}" \ - -c "$tooldata_common_source" \ - -o "$build_dir/tooldata_common.o" -"$cxx" "${common_flags[@]}" \ - -c "$tooldata_mmap_backend_shim" \ - -o "$build_dir/tooldata_mmap_backend.o" -"$cxx" "${common_flags[@]}" \ - -c "$tooldata_runtime_stubs_shim" \ - -o "$build_dir/tooldata_runtime_stubs.o" -"$cxx" "${common_flags[@]}" \ - -c "$rtapi_compat_shim" \ - -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 "$interp_find_source" \ - -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" +sources=( + "$tooldata_common_source" + "$tooldata_mmap_backend_shim" + "$tooldata_runtime_stubs_shim" + "$rtapi_compat_shim" + core/wasm_shims/interp_find_probe_stubs.cc + "$interp_find_source" + core/wasm_shims/interp_find_probe_main.cc +) +objects=( + "$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" +) -"$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" +signature="$build_dir/interp-find.signature" +next_signature="$build_dir/interp-find.signature.next" +{ + printf 'CXX=%s\n' "$cxx" + printf 'PWD=%s\n' "$PWD" + printf 'LINUXCNC_ROOT=%s\n' "$linuxcnc_root" + printf 'BUILD_RULE_VERSION=1\n' + printf 'COMMON_FLAGS=' + printf ' %s' "${common_flags[@]}" + printf '\n' + printf 'SOURCES:\n' + printf '%s\n' "${sources[@]}" +} > "$next_signature" +if [[ ! -f "$signature" ]] || ! cmp -s "$signature" "$next_signature"; then + rm -f "$build_dir"/*.o "$build_dir"/*.d "$build_dir/interp_find_probe" +fi +mv "$next_signature" "$signature" + +makefile="$build_dir/interp-find.mk" +{ + printf 'CXX := %s\n' "$cxx" + printf 'COMMON_FLAGS :=' + printf ' %s' "${common_flags[@]}" + printf '\n\n' + printf 'OBJECTS :=' + printf ' %s' "${objects[@]}" + printf '\n\n' + printf 'DEP_FILES :=\n' + for object in "${objects[@]}"; do + printf 'DEP_FILES += %s.d\n' "$object" + done + printf '\n.PHONY: all\n' + printf 'all: %s/interp_find_probe\n\n' "$build_dir" + + object_index=0 + for source in "${sources[@]}"; do + printf '%s: %s\n' "${objects[$object_index]}" "$source" + printf '\t@$(CXX) $(COMMON_FLAGS) -MMD -MP -MF %s.d -c %s -o %s\n\n' "${objects[$object_index]}" "$source" "${objects[$object_index]}" + object_index=$((object_index + 1)) + done + + printf '%s/interp_find_probe: $(OBJECTS)\n' "$build_dir" + printf '\t@$(CXX) $(OBJECTS) -Wl,--gc-sections -o %s/interp_find_probe\n\n' "$build_dir" + printf '\n-include $(DEP_FILES)\n' +} > "$makefile" +make --output-sync=target -j"$build_jobs" -f "$makefile" >/dev/null "$build_dir/interp_find_probe" diff --git a/test-linuxcnc-wasm-python-c-api-symbols.sh b/test-linuxcnc-wasm-python-c-api-symbols.sh index 5295d30..e50c583 100755 --- a/test-linuxcnc-wasm-python-c-api-symbols.sh +++ b/test-linuxcnc-wasm-python-c-api-symbols.sh @@ -5,10 +5,35 @@ cd "$(dirname "$0")" linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} cxx=${CXX:-g++} -build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_python_c_api_symbols.XXXXXX") +if [[ -z ${CXX:-} ]] && command -v ccache >/dev/null 2>&1; then + cxx="ccache $cxx" +fi +build_jobs=${CNC_SIM_BUILD_JOBS:-8} +if ! [[ "$build_jobs" =~ ^[1-9][0-9]*$ ]]; then + echo "CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs" >&2 + exit 1 +fi +build_dir=${CNC_SIM_WASM_PYTHON_C_API_BUILD_DIR-build/wasm-python-c-api-symbols} +if [[ -z "$build_dir" ]]; then + echo "CNC_SIM_WASM_PYTHON_C_API_BUILD_DIR must not be empty" >&2 + exit 1 +fi +if [[ "$build_dir" =~ [[:space:]] ]]; then + echo "CNC_SIM_WASM_PYTHON_C_API_BUILD_DIR must not contain whitespace: $build_dir" >&2 + exit 1 +fi +mkdir -p "$build_dir" +build_dir=$(cd "$build_dir" && pwd) +if [[ "$build_dir" == "/" ]]; then + echo "CNC_SIM_WASM_PYTHON_C_API_BUILD_DIR must not be the filesystem root" >&2 + exit 1 +fi +exec 9>"$build_dir/python-c-api.lock" +flock 9 -trap 'rm -rf "$build_dir"' EXIT -LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only +preflight_cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-"$build_dir/preflight-cache"} +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" --root-only +linuxcnc_root=$(cd "$linuxcnc_root" && pwd) python_c_api_shim=$(./list-linuxcnc-wasm-safe-shims.sh python_c_api_shim.cc) runtime_shim=$(./list-linuxcnc-wasm-safe-shims.sh linuxcnc_runtime_shim.cc) @@ -33,22 +58,114 @@ python_binding_sources=("${linuxcnc_sources[@]:6}") common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh) mapfile -t common_flags <<<"$common_flag_output" -"$cxx" "${common_flags[@]}" \ - -c "$python_c_api_shim" \ - -o "$build_dir/python_c_api_shim.o" -"$cxx" "${common_flags[@]}" \ - -c "$runtime_shim" \ - -o "$build_dir/runtime_shim.o" +objects=( + "$build_dir/python_c_api_shim.o" + "$build_dir/runtime_shim.o" +) +object_sources=( + "$python_c_api_shim" + "$runtime_shim" +) +for source in "${python_binding_sources[@]}"; do + object_name=$(basename "$source" .cc) + objects+=("$build_dir/$object_name.o") + object_sources+=("$source") +done -nm --defined-only "$build_dir/python_c_api_shim.o" \ - | awk '$2 ~ /^[A-Z]$/ {print $3}' \ - | LC_ALL=C sort -u \ - > "$build_dir/shim.exports" +signature="$build_dir/python-c-api.signature" +next_signature="$build_dir/python-c-api.signature.next" +{ + printf 'CXX=%s\n' "$cxx" + printf 'PWD=%s\n' "$PWD" + printf 'LINUXCNC_ROOT=%s\n' "$linuxcnc_root" + printf 'BUILD_RULE_VERSION=1\n' + printf 'COMMON_FLAGS=' + printf ' %s' "${common_flags[@]}" + printf '\n' + printf 'SOURCES:\n' + printf '%s\n' "${object_sources[@]}" +} > "$next_signature" +if [[ ! -f "$signature" ]] || ! cmp -s "$signature" "$next_signature"; then + rm -f "$build_dir"/*.o "$build_dir"/*.d +fi +mv "$next_signature" "$signature" -rg -o 'Py[A-Za-z0-9_]+' "${linuxcnc_sources[@]}" \ - | sed 's/^.*://' \ - | LC_ALL=C sort -u \ - > "$build_dir/linuxcnc.py-tokens" +makefile="$build_dir/python-c-api.mk" +{ + printf 'CXX := %s\n' "$cxx" + printf 'COMMON_FLAGS :=' + printf ' %s' "${common_flags[@]}" + printf '\n\n' + printf 'OBJECTS :=' + printf ' %s' "${objects[@]}" + printf '\n\n' + printf 'DEP_FILES :=\n' + for object in "${objects[@]}"; do + printf 'DEP_FILES += %s.d\n' "$object" + done + printf '\n.PHONY: all\n' + printf 'all: $(OBJECTS)\n\n' + + object_index=0 + for source in "${object_sources[@]}"; do + printf '%s: %s\n' "${objects[$object_index]}" "$source" + printf '\t@$(CXX) $(COMMON_FLAGS) -MMD -MP -MF %s.d -c %s -o %s\n\n' "${objects[$object_index]}" "$source" "${objects[$object_index]}" + object_index=$((object_index + 1)) + done + printf '\n-include $(DEP_FILES)\n' +} > "$makefile" +make --output-sync=target -j"$build_jobs" -f "$makefile" >/dev/null + +source_symbols_signature="$build_dir/python-c-api.source-symbols.signature" +source_symbols_next_signature="$build_dir/python-c-api.source-symbols.signature.next" +{ + printf 'SOURCE_SYMBOL_RULE_VERSION=1\n' + printf 'LINUXCNC_SOURCES:\n' + printf '%s\n' "${linuxcnc_sources[@]}" + printf 'SOURCE_STATS:\n' + stat -c 'SOURCE=%n:%s:%y' "${linuxcnc_sources[@]}" +} > "$source_symbols_next_signature" +if [[ ! -f "$source_symbols_signature" || ! -f "$build_dir/linuxcnc.py-tokens" ]] || + ! cmp -s "$source_symbols_signature" "$source_symbols_next_signature"; then + rg -o 'Py[A-Za-z0-9_]+' "${linuxcnc_sources[@]}" \ + | sed 's/^.*://' \ + | LC_ALL=C sort -u \ + > "$build_dir/linuxcnc.py-tokens" + mv "$source_symbols_next_signature" "$source_symbols_signature" +else + rm -f "$source_symbols_next_signature" +fi + +object_symbols_signature="$build_dir/python-c-api.object-symbols.signature" +object_symbols_next_signature="$build_dir/python-c-api.object-symbols.signature.next" +{ + printf 'OBJECT_SYMBOL_RULE_VERSION=1\n' + stat -c 'OBJECT=%n:%s:%y' "${objects[@]}" +} > "$object_symbols_next_signature" +if [[ ! -f "$object_symbols_signature" || + ! -f "$build_dir/shim.exports" || + ! -f "$build_dir/object.undefined" || + ! -f "$build_dir/object.defined" ]] || + ! cmp -s "$object_symbols_signature" "$object_symbols_next_signature"; then + nm --defined-only "$build_dir/python_c_api_shim.o" \ + | awk '$2 ~ /^[A-Z]$/ {print $3}' \ + | LC_ALL=C sort -u \ + > "$build_dir/shim.exports" + + nm -u "$build_dir"/*.o \ + | awk '{print $2}' \ + | sed '/^$/d' \ + | LC_ALL=C sort -u \ + > "$build_dir/object.undefined" + nm --defined-only "$build_dir"/*.o \ + | awk '{print $3}' \ + | sed '/^$/d' \ + | LC_ALL=C sort -u \ + > "$build_dir/object.defined" + mv "$object_symbols_next_signature" "$object_symbols_signature" +else + rm -f "$object_symbols_next_signature" +fi awk ' $0 == "PyObject" { next } @@ -99,23 +216,6 @@ grep -F "Py_DecRef" core/wasm_shims/boost/python/object.hpp >/dev/null grep -F "_Py_NoneStructPtr" core/wasm_shims/boost/python/object.hpp >/dev/null grep -F 'extern "C" PyObject *PyInit_gcode(void)' core/wasm_shims/linuxcnc_runtime_shim.cc >/dev/null -for source in "${python_binding_sources[@]}"; do - object_name=$(basename "$source" .cc) - "$cxx" "${common_flags[@]}" \ - -c "$source" \ - -o "$build_dir/$object_name.o" -done - -nm -u "$build_dir"/*.o \ - | awk '{print $2}' \ - | sed '/^$/d' \ - | LC_ALL=C sort -u \ - > "$build_dir/object.undefined" -nm --defined-only "$build_dir"/*.o \ - | awk '{print $3}' \ - | sed '/^$/d' \ - | LC_ALL=C sort -u \ - > "$build_dir/object.defined" comm -23 "$build_dir/object.undefined" "$build_dir/object.defined" \ | awk '/^_?Py/ { print }' \ > "$build_dir/object.missing-python" diff --git a/test-linuxcnc-wasm-python-plugin-link.sh b/test-linuxcnc-wasm-python-plugin-link.sh index 764e2df..398b3d7 100755 --- a/test-linuxcnc-wasm-python-plugin-link.sh +++ b/test-linuxcnc-wasm-python-plugin-link.sh @@ -3,23 +3,8 @@ 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") +probe=$(./build-linuxcnc-wasm-standalone-probe.sh --mode python-plugin-link) -trap 'rm -rf "$build_dir"' EXIT -LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only - -python_plugin_shim=$(./list-linuxcnc-wasm-safe-shims.sh python_plugin.cc) - -common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh) -mapfile -t common_flags <<<"$common_flag_output" - -"$cxx" "${common_flags[@]}" \ - "$python_plugin_shim" \ - core/wasm_shims/pythonplugin/python_plugin_probe_main.cc \ - -o "$build_dir/python_plugin_probe" - -"$build_dir/python_plugin_probe" +"$probe" echo "linuxcnc wasm python_plugin link probe passed" diff --git a/test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh b/test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh index 877a9a1..5f1ec8e 100755 --- a/test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh +++ b/test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh @@ -4,13 +4,12 @@ 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") +work_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_rs274ngc_pre_link_blockers.XXXXXX") manifest=${1:-linuxcnc-rs274-wasm-source-files.txt} keep_report_file=0 cleanup_rs274ngc_pre_blocker_temps() { - rm -rf "$build_dir" + rm -rf "$work_dir" if [[ "$keep_report_file" -eq 0 && -n "${report_file:-}" ]]; then rm -f "$report_file" fi @@ -22,11 +21,11 @@ if awk '/^} > "\$report_file" \|\| true$/{ found = 1 } END { exit !found }' test exit 1 fi if ! awk ' - index($0, "LINUXCNC_ROOT=\"$linuxcnc_root\" ./check-linuxcnc-inputs.sh \"$manifest\"") { check_line = NR } - index($0, "report_file=$(mktemp ") { report_line = NR } + index($0, "check-linuxcnc-inputs-cached.sh --cache-dir \"$preflight_cache_dir\" \"$manifest\"") { check_line = NR } + index($0, "cache_dir=${CNC_SIM_WASM_RS274NGC_PRE_LINK_BLOCKERS_CACHE_DIR-build/wasm-rs274ngc-pre-link-blockers}") { report_line = NR } END { exit !(check_line && report_line && check_line < report_line) } ' test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh; then - echo "rs274ngc_pre blocker scan creates its report before input validation" >&2 + echo "rs274ngc_pre blocker scan prepares its report cache before input validation" >&2 exit 1 fi if ! awk ' @@ -38,58 +37,69 @@ if ! awk ' echo "rs274ngc_pre blocker scan does not clean failed reports before preserving successful ones" >&2 exit 1 fi -LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest" -report_file=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_rs274ngc_pre_undefined.XXXXXX.txt") +preflight_cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-build/wasm-probe-preflight-cache} +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" "$manifest" +cache_dir=${CNC_SIM_WASM_RS274NGC_PRE_LINK_BLOCKERS_CACHE_DIR-build/wasm-rs274ngc-pre-link-blockers} +if [[ -z "$cache_dir" ]]; then + echo "CNC_SIM_WASM_RS274NGC_PRE_LINK_BLOCKERS_CACHE_DIR must not be empty" >&2 + exit 1 +fi +if [[ "$cache_dir" =~ [[:space:]] ]]; then + echo "CNC_SIM_WASM_RS274NGC_PRE_LINK_BLOCKERS_CACHE_DIR must not contain whitespace: $cache_dir" >&2 + exit 1 +fi +mkdir -p "$cache_dir" +cache_dir=$(cd "$cache_dir" && pwd) +if [[ "$cache_dir" == "/" ]]; then + echo "CNC_SIM_WASM_RS274NGC_PRE_LINK_BLOCKERS_CACHE_DIR must not be the filesystem root" >&2 + exit 1 +fi +exec 8>"$cache_dir/rs274ngc-pre-link-blockers.lock" +flock 8 -common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh --profile core20-sections) -mapfile -t common_flags <<<"$common_flag_output" +object_output=$(CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" LINUXCNC_ROOT="$linuxcnc_root" ./build-linuxcnc-wasm-probe-objects.sh --mode rs274ngc-pre "$manifest") +mapfile -t objects <<<"$object_output" +manifest=$(cd "$(dirname "$manifest")" && pwd)/$(basename "$manifest") +linuxcnc_root=$(cd "$linuxcnc_root" && pwd) -shim_output=$(./list-linuxcnc-wasm-safe-shims.sh) -mapfile -t shim_sources <<<"$shim_output" +undefined_file="$work_dir/undefined.raw" +defined_file="$work_dir/defined.raw" +signature="$cache_dir/rs274ngc-pre-link-blockers.signature" +next_signature="$cache_dir/rs274ngc-pre-link-blockers.signature.next" +report_file="$cache_dir/rs274ngc-pre-link-blockers.txt" +next_report_file="$cache_dir/rs274ngc-pre-link-blockers.txt.next" +{ + printf 'PWD=%s\n' "$PWD" + printf 'LINUXCNC_ROOT=%s\n' "$linuxcnc_root" + printf 'MANIFEST=%s\n' "$manifest" + printf 'FILTER_RULE_VERSION=1\n' + printf 'OBJECTS:\n' + printf '%s\n' "${objects[@]}" + printf 'OBJECT_STATS:\n' + stat -c 'OBJECT=%n:%s:%y' "${objects[@]}" +} > "$next_signature" +if [[ -f "$signature" && -f "$report_file" ]] && cmp -s "$signature" "$next_signature"; then + keep_report_file=1 + if [[ -s "$report_file" ]]; then + echo "unexpected cached 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" + rm -f "$next_signature" + exit 0 +fi +report_file="$next_report_file" +rm -f "$next_report_file" -project_source_output=$(./list-linuxcnc-wasm-safe-project-sources.sh) -mapfile -t project_sources <<<"$project_source_output" - -source_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core full) -mapfile -t linuxcnc_sources <<<"$source_output" - -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" - source_flags=() - case "$source" in - core/src/linuxcnc_corexykins_adapter.c|core/src/linuxcnc_genserfuncs_adapter.c|core/src/linuxcnc_xyzab_tdr_kins_adapter.c) - source_flags+=(-fpermissive) - ;; - esac - "$cxx" "${common_flags[@]}" "${source_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 \ +nm -u "${objects[@]}" \ | awk '{print $2}' \ | sed '/^$/d' \ | LC_ALL=C sort -u \ > "$undefined_file" -nm --defined-only "$build_dir"/*.o \ +nm --defined-only "${objects[@]}" \ | awk '{print $3}' \ | sed '/^$/d' \ | LC_ALL=C sort -u \ @@ -151,6 +161,9 @@ if [[ -s "$report_file" ]]; then exit 1 fi +mv "$next_signature" "$signature" +mv "$next_report_file" "$cache_dir/rs274ngc-pre-link-blockers.txt" +report_file="$cache_dir/rs274ngc-pre-link-blockers.txt" echo "linuxcnc wasm rs274ngc_pre link blocker scan passed" keep_report_file=1 echo "wrote $report_file" diff --git a/test-linuxcnc-wasm-rs274ngc-pre-link.sh b/test-linuxcnc-wasm-rs274ngc-pre-link.sh index 5f0fc3f..bea2453 100755 --- a/test-linuxcnc-wasm-rs274ngc-pre-link.sh +++ b/test-linuxcnc-wasm-rs274ngc-pre-link.sh @@ -3,51 +3,11 @@ 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} -trap 'rm -rf "$build_dir"' EXIT -LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest" +preflight_cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-build/wasm-probe-preflight-cache} +probe=$(CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" ./build-linuxcnc-wasm-link-probe.sh --mode rs274ngc-pre "$manifest") -common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh --profile core20-sections) -mapfile -t common_flags <<<"$common_flag_output" - -shim_output=$(./list-linuxcnc-wasm-safe-shims.sh) -mapfile -t sources <<<"$shim_output" -project_source_output=$(./list-linuxcnc-wasm-safe-project-sources.sh) -mapfile -t project_sources <<<"$project_source_output" -sources+=("${project_sources[@]}") - -source_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core full) -mapfile -t linuxcnc_sources <<<"$source_output" -for source in "${linuxcnc_sources[@]}"; do - sources+=("$source") -done - -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" - source_flags=() - case "$source" in - core/src/linuxcnc_genserfuncs_adapter.c|core/src/linuxcnc_xyzab_tdr_kins_adapter.c) - source_flags+=(-fpermissive) - ;; - esac - "$cxx" "${common_flags[@]}" "${source_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" +"$probe" echo "linuxcnc wasm rs274ngc_pre link probe passed" diff --git a/test-linuxcnc-wasm-rs274ngc-pre-object.sh b/test-linuxcnc-wasm-rs274ngc-pre-object.sh index 465b505..ceef442 100755 --- a/test-linuxcnc-wasm-rs274ngc-pre-object.sh +++ b/test-linuxcnc-wasm-rs274ngc-pre-object.sh @@ -3,19 +3,6 @@ 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") - -trap 'rm -rf "$build_dir"' EXIT -LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only -rs274ngc_pre_source=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-files.sh src/emc/rs274ngc/rs274ngc_pre.cc) - -common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh) -mapfile -t common_flags <<<"$common_flag_output" - -"$cxx" "${common_flags[@]}" \ - -c "$rs274ngc_pre_source" \ - -o "$build_dir/rs274ngc_pre.o" +./build-linuxcnc-wasm-standalone-probe.sh --mode rs274ngc-pre-object >/dev/null echo "linuxcnc wasm rs274ngc_pre object probe passed" diff --git a/test-linuxcnc-wasm-runtime-link.sh b/test-linuxcnc-wasm-runtime-link.sh index 97cf1a3..2909028 100755 --- a/test-linuxcnc-wasm-runtime-link.sh +++ b/test-linuxcnc-wasm-runtime-link.sh @@ -3,63 +3,33 @@ set -euo pipefail cd "$(dirname "$0")" -linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} -cxx=${CXX:-g++} -build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_runtime_link.XXXXXX") manifest=${1:-linuxcnc-rs274-wasm-source-files.txt} -trap 'rm -rf "$build_dir"' EXIT -LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest" +preflight_cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-build/wasm-probe-preflight-cache} +probe=$(CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" ./build-linuxcnc-wasm-link-probe.sh --mode runtime "$manifest") +probe_dir=$(dirname "$probe") +symbol_file="$probe_dir/runtime.symbols" +symbol_signature="$probe_dir/runtime.symbols.signature" +symbol_next_signature="$probe_dir/runtime.symbols.signature.next" +{ + printf 'PROBE=%s\n' "$probe" + printf 'SYMBOL_RULE_VERSION=1\n' + stat -c 'PROBE_STAT=%n:%s:%y' "$probe" +} > "$symbol_next_signature" +if [[ ! -f "$symbol_file" || ! -f "$symbol_signature" ]] || ! cmp -s "$symbol_signature" "$symbol_next_signature"; then + nm -C "$probe" > "$symbol_file" + mv "$symbol_next_signature" "$symbol_signature" +else + rm -f "$symbol_next_signature" +fi +grep -F "cnc_sim_parse_program" "$symbol_file" >/dev/null +grep -F "parse_linuxcnc_rs274_backend" "$symbol_file" >/dev/null +grep -F "parse_gcode_with_backend" "$symbol_file" >/dev/null +grep -F "Interp::read(char const*)" "$symbol_file" >/dev/null +grep -F "Interp::init()" "$symbol_file" >/dev/null +grep -E '(^| )wordexp($| )' "$symbol_file" >/dev/null +grep -E '(^| )wordfree($| )' "$symbol_file" >/dev/null -common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh --profile core20-sections) -mapfile -t common_flags <<<"$common_flag_output" -common_flags+=(-DCNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND) - -shim_output=$(./list-linuxcnc-wasm-safe-shims.sh) -mapfile -t sources <<<"$shim_output" - -core_source_output=$(./list-cmake-set-sources.sh cnc_sim_core_sources core/) -mapfile -t core_sources <<<"$core_source_output" -sources+=("${core_sources[@]}") - -backend_source_output=$(./list-cmake-set-sources.sh cnc_sim_linuxcnc_rs274_backend_sources core/) -mapfile -t backend_sources <<<"$backend_source_output" -sources+=("${backend_sources[@]}") - -source_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core full) -mapfile -t linuxcnc_sources <<<"$source_output" -sources+=("${linuxcnc_sources[@]}") -sources+=(core/wasm_shims/cnc_sim_wasm_runtime_probe_main.cc) - -objects=() -object_index=0 -for source in "${sources[@]}"; do - obj="$build_dir/source_${object_index}.o" - source_flags=() - case "$source" in - core/src/linuxcnc_genserfuncs_adapter.c|core/src/linuxcnc_xyzab_tdr_kins_adapter.c) - source_flags+=(-fpermissive) - ;; - esac - "$cxx" "${common_flags[@]}" "${source_flags[@]}" -c "$source" -o "$obj" - objects+=("$obj") - object_index=$((object_index + 1)) -done - -"$cxx" "${objects[@]}" \ - -Wl,--gc-sections \ - -lfmt \ - -o "$build_dir/cnc_sim_wasm_runtime_probe" - -nm -C "$build_dir/cnc_sim_wasm_runtime_probe" > "$build_dir/runtime.symbols" -grep -F "cnc_sim_parse_program" "$build_dir/runtime.symbols" >/dev/null -grep -F "parse_linuxcnc_rs274_backend" "$build_dir/runtime.symbols" >/dev/null -grep -F "parse_gcode_with_backend" "$build_dir/runtime.symbols" >/dev/null -grep -F "Interp::read(char const*)" "$build_dir/runtime.symbols" >/dev/null -grep -F "Interp::init()" "$build_dir/runtime.symbols" >/dev/null -grep -E '(^| )wordexp($| )' "$build_dir/runtime.symbols" >/dev/null -grep -E '(^| )wordfree($| )' "$build_dir/runtime.symbols" >/dev/null - -"$build_dir/cnc_sim_wasm_runtime_probe" +"$probe" echo "linuxcnc wasm runtime link probe passed" diff --git a/test-linuxcnc-wasm-source-objects.sh b/test-linuxcnc-wasm-source-objects.sh index 7439653..4e48259 100755 --- a/test-linuxcnc-wasm-source-objects.sh +++ b/test-linuxcnc-wasm-source-objects.sh @@ -5,33 +5,18 @@ 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") -trap 'rm -rf "$build_dir"' EXIT -LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest" +preflight_cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-build/wasm-probe-preflight-cache} shim_output=$(./list-linuxcnc-wasm-safe-shims.sh) mapfile -t shim_sources <<<"$shim_output" -common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh --profile core20) -mapfile -t common_flags <<<"$common_flag_output" +object_output=$(CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" LINUXCNC_ROOT="$linuxcnc_root" ./build-linuxcnc-wasm-probe-objects.sh --mode source-objects "$manifest") +mapfile -t objects <<<"$object_output" +if [[ "${#objects[@]}" -lt "${#shim_sources[@]}" ]]; then + echo "unexpected wasm-safe object cache count: got ${#objects[@]} fewer than ${#shim_sources[@]} shims" >&2 + exit 1 +fi +linuxcnc_object_count=$((${#objects[@]} - ${#shim_sources[@]})) -source_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core full) -mapfile -t sources <<<"$source_output" - -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 "$source" -o "$obj" - source_index=$((source_index + 1)) -done - -echo "linuxcnc wasm-safe source object probe passed (${#sources[@]} linuxcnc objects + ${#shim_sources[@]} shims)" +echo "linuxcnc wasm-safe source object probe passed ($linuxcnc_object_count linuxcnc objects + ${#shim_sources[@]} shims)" diff --git a/test-linuxcnc-wasm-source-syntax.sh b/test-linuxcnc-wasm-source-syntax.sh index fed6c89..157f498 100755 --- a/test-linuxcnc-wasm-source-syntax.sh +++ b/test-linuxcnc-wasm-source-syntax.sh @@ -6,17 +6,97 @@ cd "$(dirname "$0")" linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} manifest=${1:-linuxcnc-rs274-wasm-source-files.txt} cxx=${CXX:-g++} +if [[ -z ${CXX:-} ]] && command -v ccache >/dev/null 2>&1; then + cxx="ccache $cxx" +fi +build_jobs=${CNC_SIM_BUILD_JOBS:-8} +if ! [[ "$build_jobs" =~ ^[1-9][0-9]*$ ]]; then + echo "CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs" >&2 + exit 1 +fi +build_dir=${CNC_SIM_WASM_SOURCE_SYNTAX_BUILD_DIR-build/wasm-source-syntax-test} +if [[ -z "$build_dir" ]]; then + echo "CNC_SIM_WASM_SOURCE_SYNTAX_BUILD_DIR must not be empty" >&2 + exit 1 +fi +if [[ "$build_dir" =~ [[:space:]] ]]; then + echo "CNC_SIM_WASM_SOURCE_SYNTAX_BUILD_DIR must not contain whitespace: $build_dir" >&2 + exit 1 +fi +mkdir -p "$build_dir" +build_dir=$(cd "$build_dir" && pwd) +if [[ "$build_dir" == "/" ]]; then + echo "CNC_SIM_WASM_SOURCE_SYNTAX_BUILD_DIR must not be the filesystem root" >&2 + exit 1 +fi +exec 9>"$build_dir/wasm-source-syntax.lock" +flock 9 +preflight_cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-"$build_dir/preflight-cache"} -LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest" +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" "$manifest" +manifest=$(cd "$(dirname "$manifest")" && pwd)/$(basename "$manifest") +linuxcnc_root=$(cd "$linuxcnc_root" && pwd) common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh --profile core20 --syntax-only) mapfile -t common_flags <<<"$common_flag_output" -source_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core full) -mapfile -t sources <<<"$source_output" +source_list="$build_dir/linuxcnc_wasm_source_syntax_sources.txt" +CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core full > "$source_list" -for source in "${sources[@]}"; do - "$cxx" "${common_flags[@]}" "$source" -done +wasm_source_syntax_signature="$build_dir/wasm-source-syntax.signature" +wasm_source_syntax_next_signature="$build_dir/wasm-source-syntax.signature.next" +{ + printf 'CXX=%s\n' "$cxx" + printf 'PWD=%s\n' "$PWD" + printf 'LINUXCNC_ROOT=%s\n' "$linuxcnc_root" + printf 'MANIFEST=%s\n' "$manifest" + printf 'BUILD_RULE_VERSION=1\n' + printf 'COMMON_FLAGS=' + printf ' %s' "${common_flags[@]}" + printf '\n' + printf 'SOURCES:\n' + cat "$source_list" +} > "$wasm_source_syntax_next_signature" +if [[ ! -f "$wasm_source_syntax_signature" ]] || ! cmp -s "$wasm_source_syntax_signature" "$wasm_source_syntax_next_signature"; then + rm -f "$build_dir"/source_*.stamp "$build_dir"/source_*.d +fi +mv "$wasm_source_syntax_next_signature" "$wasm_source_syntax_signature" -echo "linuxcnc wasm-safe source syntax probe passed (${#sources[@]} files)" +wasm_source_syntax_makefile="$build_dir/wasm-source-syntax.mk" +{ + printf 'CXX := %s\n' "$cxx" + printf 'COMMON_FLAGS :=' + printf ' %s' "${common_flags[@]}" + printf '\n\n' + printf 'STAMPS :=\n' + source_index=0 + while IFS= read -r source; do + [[ -z "$source" ]] && continue + printf 'STAMPS += %s/source_%s.stamp\n' "$build_dir" "$source_index" + source_index=$((source_index + 1)) + done < "$source_list" + printf '\n.PHONY: all\n' + printf 'all: $(STAMPS)\n\n' + + source_index=0 + while IFS= read -r source; do + [[ -z "$source" ]] && continue + printf '%s/source_%s.stamp: %s\n' "$build_dir" "$source_index" "$source" + printf '\t@$(CXX) $(COMMON_FLAGS) -MMD -MP -MF %s/source_%s.d %s\n' "$build_dir" "$source_index" "$source" + printf '\t@touch %s/source_%s.stamp\n\n' "$build_dir" "$source_index" + source_index=$((source_index + 1)) + done < "$source_list" + + printf '%s' '-include' + source_index=0 + while IFS= read -r source; do + [[ -z "$source" ]] && continue + printf ' %s/source_%s.d' "$build_dir" "$source_index" + source_index=$((source_index + 1)) + done < "$source_list" + printf '\n' +} > "$wasm_source_syntax_makefile" +make --output-sync=target -j"$build_jobs" -f "$wasm_source_syntax_makefile" + +source_count=$(grep -c . "$source_list") +echo "linuxcnc wasm-safe source syntax probe passed ($source_count files)" diff --git a/test-linuxcnc-wasm-tooldata-common-link.sh b/test-linuxcnc-wasm-tooldata-common-link.sh index 1086d24..c24a5a0 100755 --- a/test-linuxcnc-wasm-tooldata-common-link.sh +++ b/test-linuxcnc-wasm-tooldata-common-link.sh @@ -3,27 +3,8 @@ 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") +probe=$(./build-linuxcnc-wasm-standalone-probe.sh --mode tooldata-common-link) -trap 'rm -rf "$build_dir"' EXIT -LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only - -tooldata_mmap_backend_shim=$(./list-linuxcnc-wasm-safe-shims.sh tooldata_mmap_backend.cc) -tooldata_runtime_stubs_shim=$(./list-linuxcnc-wasm-safe-shims.sh tooldata_runtime_stubs.cc) -tooldata_common_source=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-files.sh src/emc/tooldata/tooldata_common.cc) - -common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh --tooldata-include) -mapfile -t common_flags <<<"$common_flag_output" - -"$cxx" "${common_flags[@]}" \ - "$tooldata_common_source" \ - "$tooldata_mmap_backend_shim" \ - "$tooldata_runtime_stubs_shim" \ - core/wasm_shims/tooldata/tooldata_common_probe_main.cc \ - -o "$build_dir/tooldata_common_probe" - -"$build_dir/tooldata_common_probe" +"$probe" echo "linuxcnc wasm tooldata_common link probe passed" diff --git a/test-linuxcnc-wasm-tooldata-link.sh b/test-linuxcnc-wasm-tooldata-link.sh index fc529b5..7ea926d 100755 --- a/test-linuxcnc-wasm-tooldata-link.sh +++ b/test-linuxcnc-wasm-tooldata-link.sh @@ -3,29 +3,10 @@ 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") +probe=$(./build-linuxcnc-wasm-standalone-probe.sh --mode tooldata-link) -trap 'rm -rf "$build_dir"' EXIT -LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only - -tooldata_mmap_backend_shim=$(./list-linuxcnc-wasm-safe-shims.sh tooldata_mmap_backend.cc) -tooldata_runtime_stubs_shim=$(./list-linuxcnc-wasm-safe-shims.sh tooldata_runtime_stubs.cc) -tooldata_common_source=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-files.sh src/emc/tooldata/tooldata_common.cc) - -common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh) -mapfile -t common_flags <<<"$common_flag_output" - -"$cxx" "${common_flags[@]}" \ - "$tooldata_common_source" \ - "$tooldata_mmap_backend_shim" \ - "$tooldata_runtime_stubs_shim" \ - core/wasm_shims/tooldata/tooldata_probe_main.cc \ - -o "$build_dir/tooldata_probe" - -"$build_dir/tooldata_probe" 2>"$build_dir/tooldata_probe.err" -grep -F "tool_mmap_user(): tool mmap not available" "$build_dir/tooldata_probe.err" >/dev/null -grep -F "tooldata_put() no tool_mmap_base" "$build_dir/tooldata_probe.err" >/dev/null +"$probe" 2>"$probe.err" +grep -F "tool_mmap_user(): tool mmap not available" "$probe.err" >/dev/null +grep -F "tooldata_put() no tool_mmap_base" "$probe.err" >/dev/null echo "linuxcnc wasm tooldata shim link probe passed" diff --git a/test-linuxcnc-wasm-tooldata-mmap-symbols.sh b/test-linuxcnc-wasm-tooldata-mmap-symbols.sh index bd8650e..3a03c3c 100755 --- a/test-linuxcnc-wasm-tooldata-mmap-symbols.sh +++ b/test-linuxcnc-wasm-tooldata-mmap-symbols.sh @@ -5,10 +5,35 @@ 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 [[ -z ${CXX:-} ]] && command -v ccache >/dev/null 2>&1; then + cxx="ccache $cxx" +fi +build_jobs=${CNC_SIM_BUILD_JOBS:-8} +if ! [[ "$build_jobs" =~ ^[1-9][0-9]*$ ]]; then + echo "CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs" >&2 + exit 1 +fi +build_dir=${CNC_SIM_WASM_TOOLDATA_MMAP_SYMBOLS_BUILD_DIR-build/wasm-tooldata-mmap-symbols} +if [[ -z "$build_dir" ]]; then + echo "CNC_SIM_WASM_TOOLDATA_MMAP_SYMBOLS_BUILD_DIR must not be empty" >&2 + exit 1 +fi +if [[ "$build_dir" =~ [[:space:]] ]]; then + echo "CNC_SIM_WASM_TOOLDATA_MMAP_SYMBOLS_BUILD_DIR must not contain whitespace: $build_dir" >&2 + exit 1 +fi +mkdir -p "$build_dir" +build_dir=$(cd "$build_dir" && pwd) +if [[ "$build_dir" == "/" ]]; then + echo "CNC_SIM_WASM_TOOLDATA_MMAP_SYMBOLS_BUILD_DIR must not be the filesystem root" >&2 + exit 1 +fi +exec 9>"$build_dir/tooldata-mmap-symbols.lock" +flock 9 -trap 'rm -rf "$build_dir"' EXIT -LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only +preflight_cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-"$build_dir/preflight-cache"} +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" --root-only +linuxcnc_root=$(cd "$linuxcnc_root" && pwd) tooldata_mmap_backend_shim=$(./list-linuxcnc-wasm-safe-shims.sh tooldata_mmap_backend.cc) tooldata_mmap_source=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-files.sh src/emc/tooldata/tooldata_mmap.cc) @@ -16,23 +41,80 @@ tooldata_mmap_source=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-fil common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh) mapfile -t common_flags <<<"$common_flag_output" -"$cxx" "${common_flags[@]}" \ - -c "$tooldata_mmap_backend_shim" \ - -o "$build_dir/tooldata_mmap_backend.o" +sources=( + "$tooldata_mmap_backend_shim" + "$tooldata_mmap_source" +) +objects=( + "$build_dir/tooldata_mmap_backend.o" + "$build_dir/linuxcnc_tooldata_mmap.o" +) -"$cxx" "${common_flags[@]}" \ - -c "$tooldata_mmap_source" \ - -o "$build_dir/linuxcnc_tooldata_mmap.o" +signature="$build_dir/tooldata-mmap-symbols.signature" +next_signature="$build_dir/tooldata-mmap-symbols.signature.next" +{ + printf 'CXX=%s\n' "$cxx" + printf 'PWD=%s\n' "$PWD" + printf 'LINUXCNC_ROOT=%s\n' "$linuxcnc_root" + printf 'BUILD_RULE_VERSION=1\n' + printf 'COMMON_FLAGS=' + printf ' %s' "${common_flags[@]}" + printf '\n' + printf 'SOURCES:\n' + printf '%s\n' "${sources[@]}" +} > "$next_signature" +if [[ ! -f "$signature" ]] || ! cmp -s "$signature" "$next_signature"; then + rm -f "$build_dir"/*.o "$build_dir"/*.d +fi +mv "$next_signature" "$signature" -nm --defined-only "$build_dir/tooldata_mmap_backend.o" \ - | awk '$2 ~ /^[A-Z]$/ {print $3}' \ - | LC_ALL=C sort -u \ - > "$build_dir/backend.exports" +makefile="$build_dir/tooldata-mmap-symbols.mk" +{ + printf 'CXX := %s\n' "$cxx" + printf 'COMMON_FLAGS :=' + printf ' %s' "${common_flags[@]}" + printf '\n\n' + printf 'OBJECTS :=' + printf ' %s' "${objects[@]}" + printf '\n\n' + printf 'DEP_FILES :=\n' + for object in "${objects[@]}"; do + printf 'DEP_FILES += %s.d\n' "$object" + done + printf '\n.PHONY: all\n' + printf 'all: $(OBJECTS)\n\n' -nm --defined-only "$build_dir/linuxcnc_tooldata_mmap.o" \ - | awk '$2 ~ /^[A-Z]$/ {print $3}' \ - | LC_ALL=C sort -u \ - > "$build_dir/linuxcnc.exports" + object_index=0 + for source in "${sources[@]}"; do + printf '%s: %s\n' "${objects[$object_index]}" "$source" + printf '\t@$(CXX) $(COMMON_FLAGS) -MMD -MP -MF %s.d -c %s -o %s\n\n' "${objects[$object_index]}" "$source" "${objects[$object_index]}" + object_index=$((object_index + 1)) + done + printf '\n-include $(DEP_FILES)\n' +} > "$makefile" +make --output-sync=target -j"$build_jobs" -f "$makefile" >/dev/null + +symbols_signature="$build_dir/tooldata-mmap-symbols.nm.signature" +symbols_next_signature="$build_dir/tooldata-mmap-symbols.nm.signature.next" +{ + printf 'SYMBOL_RULE_VERSION=1\n' + stat -c 'OBJECT=%n:%s:%y' "${objects[@]}" +} > "$symbols_next_signature" +if [[ ! -f "$symbols_signature" || ! -f "$build_dir/backend.exports" || ! -f "$build_dir/linuxcnc.exports" ]] || + ! cmp -s "$symbols_signature" "$symbols_next_signature"; then + nm --defined-only "$build_dir/tooldata_mmap_backend.o" \ + | awk '$2 ~ /^[A-Z]$/ {print $3}' \ + | LC_ALL=C sort -u \ + > "$build_dir/backend.exports" + + nm --defined-only "$build_dir/linuxcnc_tooldata_mmap.o" \ + | awk '$2 ~ /^[A-Z]$/ {print $3}' \ + | LC_ALL=C sort -u \ + > "$build_dir/linuxcnc.exports" + mv "$symbols_next_signature" "$symbols_signature" +else + rm -f "$symbols_next_signature" +fi if ! comm -23 "$build_dir/linuxcnc.exports" "$build_dir/backend.exports" > "$build_dir/missing.exports"; then exit 1 diff --git a/test-linuxcnc-wasm-tooldata-runtime-symbols.sh b/test-linuxcnc-wasm-tooldata-runtime-symbols.sh index 1b5f70e..75b8459 100755 --- a/test-linuxcnc-wasm-tooldata-runtime-symbols.sh +++ b/test-linuxcnc-wasm-tooldata-runtime-symbols.sh @@ -5,10 +5,35 @@ 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 [[ -z ${CXX:-} ]] && command -v ccache >/dev/null 2>&1; then + cxx="ccache $cxx" +fi +build_jobs=${CNC_SIM_BUILD_JOBS:-8} +if ! [[ "$build_jobs" =~ ^[1-9][0-9]*$ ]]; then + echo "CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs" >&2 + exit 1 +fi +build_dir=${CNC_SIM_WASM_TOOLDATA_RUNTIME_SYMBOLS_BUILD_DIR-build/wasm-tooldata-runtime-symbols} +if [[ -z "$build_dir" ]]; then + echo "CNC_SIM_WASM_TOOLDATA_RUNTIME_SYMBOLS_BUILD_DIR must not be empty" >&2 + exit 1 +fi +if [[ "$build_dir" =~ [[:space:]] ]]; then + echo "CNC_SIM_WASM_TOOLDATA_RUNTIME_SYMBOLS_BUILD_DIR must not contain whitespace: $build_dir" >&2 + exit 1 +fi +mkdir -p "$build_dir" +build_dir=$(cd "$build_dir" && pwd) +if [[ "$build_dir" == "/" ]]; then + echo "CNC_SIM_WASM_TOOLDATA_RUNTIME_SYMBOLS_BUILD_DIR must not be the filesystem root" >&2 + exit 1 +fi +exec 9>"$build_dir/tooldata-runtime-symbols.lock" +flock 9 -trap 'rm -rf "$build_dir"' EXIT -LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only +preflight_cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-"$build_dir/preflight-cache"} +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" --root-only +linuxcnc_root=$(cd "$linuxcnc_root" && pwd) tooldata_mmap_backend_shim=$(./list-linuxcnc-wasm-safe-shims.sh tooldata_mmap_backend.cc) tooldata_runtime_stubs_shim=$(./list-linuxcnc-wasm-safe-shims.sh tooldata_runtime_stubs.cc) @@ -22,36 +47,92 @@ tooldata_nml_source=${linuxcnc_sources[1]} common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh) mapfile -t common_flags <<<"$common_flag_output" -"$cxx" "${common_flags[@]}" \ - -c "$tooldata_runtime_stubs_shim" \ - -o "$build_dir/tooldata_runtime_stubs.o" +sources=( + "$tooldata_runtime_stubs_shim" + "$tooldata_mmap_backend_shim" + "$tooldata_db_source" + "$tooldata_nml_source" +) +objects=( + "$build_dir/tooldata_runtime_stubs.o" + "$build_dir/tooldata_mmap_backend.o" + "$build_dir/linuxcnc_tooldata_db.o" + "$build_dir/linuxcnc_tooldata_nml.o" +) -"$cxx" "${common_flags[@]}" \ - -c "$tooldata_mmap_backend_shim" \ - -o "$build_dir/tooldata_mmap_backend.o" +signature="$build_dir/tooldata-runtime-symbols.signature" +next_signature="$build_dir/tooldata-runtime-symbols.signature.next" +{ + printf 'CXX=%s\n' "$cxx" + printf 'PWD=%s\n' "$PWD" + printf 'LINUXCNC_ROOT=%s\n' "$linuxcnc_root" + printf 'BUILD_RULE_VERSION=1\n' + printf 'COMMON_FLAGS=' + printf ' %s' "${common_flags[@]}" + printf '\n' + printf 'SOURCES:\n' + printf '%s\n' "${sources[@]}" +} > "$next_signature" +if [[ ! -f "$signature" ]] || ! cmp -s "$signature" "$next_signature"; then + rm -f "$build_dir"/*.o "$build_dir"/*.d +fi +mv "$next_signature" "$signature" -"$cxx" "${common_flags[@]}" \ - -c "$tooldata_db_source" \ - -o "$build_dir/linuxcnc_tooldata_db.o" +makefile="$build_dir/tooldata-runtime-symbols.mk" +{ + printf 'CXX := %s\n' "$cxx" + printf 'COMMON_FLAGS :=' + printf ' %s' "${common_flags[@]}" + printf '\n\n' + printf 'OBJECTS :=' + printf ' %s' "${objects[@]}" + printf '\n\n' + printf 'DEP_FILES :=\n' + for object in "${objects[@]}"; do + printf 'DEP_FILES += %s.d\n' "$object" + done + printf '\n.PHONY: all\n' + printf 'all: $(OBJECTS)\n\n' -"$cxx" "${common_flags[@]}" \ - -c "$tooldata_nml_source" \ - -o "$build_dir/linuxcnc_tooldata_nml.o" + object_index=0 + for source in "${sources[@]}"; do + printf '%s: %s\n' "${objects[$object_index]}" "$source" + printf '\t@$(CXX) $(COMMON_FLAGS) -MMD -MP -MF %s.d -c %s -o %s\n\n' "${objects[$object_index]}" "$source" "${objects[$object_index]}" + object_index=$((object_index + 1)) + done + printf '\n-include $(DEP_FILES)\n' +} > "$makefile" +make --output-sync=target -j"$build_jobs" -f "$makefile" >/dev/null -nm --defined-only "$build_dir/tooldata_runtime_stubs.o" \ - | awk '$2 ~ /^[A-Z]$/ {print $3}' \ - | LC_ALL=C sort -u \ - > "$build_dir/runtime.exports" +symbols_signature="$build_dir/tooldata-runtime-symbols.nm.signature" +symbols_next_signature="$build_dir/tooldata-runtime-symbols.nm.signature.next" +{ + printf 'SYMBOL_RULE_VERSION=1\n' + stat -c 'OBJECT=%n:%s:%y' "${objects[@]}" +} > "$symbols_next_signature" +if [[ ! -f "$symbols_signature" || + ! -f "$build_dir/runtime.exports" || + ! -f "$build_dir/backend.exports" || + ! -f "$build_dir/linuxcnc-runtime.exports" ]] || + ! cmp -s "$symbols_signature" "$symbols_next_signature"; then + nm --defined-only "$build_dir/tooldata_runtime_stubs.o" \ + | awk '$2 ~ /^[A-Z]$/ {print $3}' \ + | LC_ALL=C sort -u \ + > "$build_dir/runtime.exports" -nm --defined-only "$build_dir/tooldata_mmap_backend.o" \ - | awk '$2 ~ /^[A-Z]$/ {print $3}' \ - | LC_ALL=C sort -u \ - > "$build_dir/backend.exports" + nm --defined-only "$build_dir/tooldata_mmap_backend.o" \ + | awk '$2 ~ /^[A-Z]$/ {print $3}' \ + | LC_ALL=C sort -u \ + > "$build_dir/backend.exports" -nm --defined-only "$build_dir"/linuxcnc_tooldata_*.o \ - | awk '$2 ~ /^[A-Z]$/ {print $3}' \ - | LC_ALL=C sort -u \ - > "$build_dir/linuxcnc-runtime.exports" + nm --defined-only "$build_dir"/linuxcnc_tooldata_*.o \ + | awk '$2 ~ /^[A-Z]$/ {print $3}' \ + | LC_ALL=C sort -u \ + > "$build_dir/linuxcnc-runtime.exports" + mv "$symbols_next_signature" "$symbols_signature" +else + rm -f "$symbols_next_signature" +fi comm -23 "$build_dir/linuxcnc-runtime.exports" "$build_dir/backend.exports" \ > "$build_dir/expected.exports" diff --git a/test-native.sh b/test-native.sh index cd47dfb..617aa00 100755 --- a/test-native.sh +++ b/test-native.sh @@ -29,11 +29,12 @@ if [[ "$build_dir" == "/" ]]; then fi exec 9>"$build_dir/native-test.lock" flock 9 +preflight_cache_dir="$build_dir/preflight-cache" -./check-linuxcnc-inputs.sh linuxcnc-rs274-source-files.txt --require-rs274-complete -./check-linuxcnc-inputs.sh linuxcnc-rs274-wasm-source-files.txt --require-rs274-complete -./check-linuxcnc-inputs.sh linuxcnc-kinematics-source-files.txt --require-kinematics-complete -./check-linuxcnc-switchkins-remap-table.sh linuxcnc-kinematics-source-files.txt +./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" linuxcnc-rs274-source-files.txt --require-rs274-complete +./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" linuxcnc-rs274-wasm-source-files.txt --require-rs274-complete +./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" linuxcnc-kinematics-source-files.txt --require-kinematics-complete +./check-linuxcnc-switchkins-remap-table-cached.sh --cache-dir "$preflight_cache_dir" linuxcnc-kinematics-source-files.txt grep -F '#include "linuxcnc_switchkins_remap_table.inc"' core/src/cnc_sim_api.cpp >/dev/null grep -F 'switchkins: options.switchkins' web/src/wasm-core.js >/dev/null grep -F 'remap: options.remap' web/src/wasm-core.js >/dev/null @@ -341,6 +342,7 @@ native_next_signature="$build_dir/native-build.signature.next" { printf 'CXX=%s\n' "$cxx" printf 'PWD=%s\n' "$PWD" + printf 'BUILD_RULE_VERSION=1\n' printf 'SMOKE_CXXFLAGS=' printf ' %s' "${smoke_cxxflags[@]}" printf '\n'