Files
wasm-simulator/check-linuxcnc-inputs.sh

1359 lines
58 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
# LinuxCNC source basis: manifest validation checks LinuxCNC-root relative
# sources and completeness against src/emc/rs274ngc, src/emc/kinematics,
# src/libnml/posemath, and configs/sim without interpreting CNC behavior.
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
manifest=${1:-}
require_complete=${2:-}
allowed_manifest_groups=(
asset
binding
blocked
blocked-header
component
config
core
generated
header
metadata
remap
tooldata
)
contains_manifest_group() {
local candidate=$1
local group
for group in "${allowed_manifest_groups[@]}"; do
[[ "$group" == "$candidate" ]] && return 0
done
return 1
}
if [[ "$manifest" == "--root-only" ]]; then
manifest=
fi
if [[ "$manifest" == "--require-rs274-complete" || "$manifest" == "--require-kinematics-complete" ]]; then
echo "usage: $0 [manifest] [--require-rs274-complete|--require-kinematics-complete]" >&2
exit 1
fi
case "$require_complete" in
""|--require-rs274-complete|--require-kinematics-complete)
;;
*)
echo "unknown check-linuxcnc-inputs option: $require_complete" >&2
exit 1
;;
esac
if [[ -n "$manifest" && ! -f "$manifest" ]]; then
echo "missing manifest: $manifest" >&2
exit 1
fi
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
if [[ -n "$manifest" ]]; then
seen_manifest_sources=()
manifest_line_number=0
while IFS= read -r manifest_line || [[ -n "$manifest_line" ]]; do
manifest_line_number=$((manifest_line_number + 1))
case "$manifest_line" in
""|\#*)
continue
;;
esac
IFS=: read -r group path note extra <<<"$manifest_line"
if [[ -z "$group" || -z "$path" || -z "$note" || -n "${extra:-}" ]]; then
echo "bad manifest line $manifest_line_number: $manifest_line" >&2
exit 1
fi
if ! contains_manifest_group "$group"; then
echo "unknown manifest group: $group" >&2
exit 1
fi
if [[ "$path" = /* || "$path" == .. || "$path" == ../* || "$path" == */.. || "$path" == */../* ]]; then
echo "manifest source must be LinuxCNC-root relative: $path" >&2
exit 1
fi
if printf '%s\n' "${seen_manifest_sources[@]}" | grep -Fx -- "$path" >/dev/null; then
echo "duplicate manifest source: $path" >&2
exit 1
fi
if [[ ! -f "$linuxcnc_root/$path" ]]; then
echo "missing manifest source: $path" >&2
exit 1
fi
seen_manifest_sources+=("$path")
done < "$manifest"
fi
if [[ -n "$manifest" && "$require_complete" == "--require-rs274-complete" ]]; then
rs274_dir="$linuxcnc_root/src/emc/rs274ngc"
if [[ ! -d "$rs274_dir" ]]; then
echo "missing LinuxCNC rs274ngc source directory: $rs274_dir" >&2
exit 1
fi
mapfile -t rs274_sources < <(
find "$rs274_dir" -maxdepth 1 -type f \
| sed "s#^$linuxcnc_root/##" \
| sort
)
mapfile -t manifest_rs274_sources < <(
awk -F: '
$1 !~ /^($|#)/ && $2 ~ /^src\/emc\/rs274ngc\// { print $2 }
' "$manifest" | sort -u
)
missing_rs274_sources=()
for source in "${rs274_sources[@]}"; do
if ! printf '%s\n' "${manifest_rs274_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
missing_rs274_sources+=("$source")
fi
done
if ((${#missing_rs274_sources[@]} > 0)); then
echo "manifest does not cover all LinuxCNC rs274ngc sources: $manifest" >&2
printf 'missing rs274ngc source: %s\n' "${missing_rs274_sources[@]}" >&2
exit 1
fi
fi
if [[ -n "$manifest" && "$require_complete" == "--require-kinematics-complete" ]]; then
kinematics_dir="$linuxcnc_root/src/emc/kinematics"
if [[ ! -d "$kinematics_dir" ]]; then
echo "missing LinuxCNC kinematics source directory: $kinematics_dir" >&2
exit 1
fi
mapfile -t kinematics_sources < <(
find "$kinematics_dir" -maxdepth 1 -type f \
| sed "s#^$linuxcnc_root/##" \
| sort
)
mapfile -t manifest_kinematics_sources < <(
awk -F: '
$1 !~ /^($|#)/ && $2 ~ /^src\/emc\/kinematics\// { print $2 }
' "$manifest" | sort -u
)
missing_kinematics_sources=()
for source in "${kinematics_sources[@]}"; do
if ! printf '%s\n' "${manifest_kinematics_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
missing_kinematics_sources+=("$source")
fi
done
if ((${#missing_kinematics_sources[@]} > 0)); then
echo "manifest does not cover all LinuxCNC kinematics sources: $manifest" >&2
printf 'missing kinematics source: %s\n' "${missing_kinematics_sources[@]}" >&2
exit 1
fi
mapfile -t kinematics_header_sources < <(
find "$kinematics_dir" -maxdepth 1 -type f -name '*.h' \
| sed "s#^$linuxcnc_root/##" \
| sort
)
mapfile -t manifest_kinematics_header_sources < <(
awk -F: '
$1 !~ /^($|#)/ && $1 == "header" && $2 ~ /^src\/emc\/kinematics\/.*\.h$/ { print $2 }
' "$manifest" | sort -u
)
mapfile -t kinematics_metadata_sources < <(
find "$kinematics_dir" -maxdepth 1 -type f \
\( -name 'Submakefile' -o -name 'meson.build' \) \
| sed "s#^$linuxcnc_root/##" \
| sort
)
mapfile -t manifest_kinematics_metadata_sources < <(
awk -F: '
$1 !~ /^($|#)/ && $1 == "metadata" && $2 ~ /^src\/emc\/kinematics\/(Submakefile|meson\.build)$/ { print $2 }
' "$manifest" | sort -u
)
mapfile -t kinematics_asset_sources < <(
find "$kinematics_dir" -maxdepth 1 -type f -name '*.fig' \
| sed "s#^$linuxcnc_root/##" \
| sort
)
mapfile -t manifest_kinematics_asset_sources < <(
awk -F: '
$1 !~ /^($|#)/ && $1 == "asset" && $2 ~ /^src\/emc\/kinematics\/.*\.fig$/ { print $2 }
' "$manifest" | sort -u
)
missing_kinematics_header_sources=()
for source in "${kinematics_header_sources[@]}"; do
if ! printf '%s\n' "${manifest_kinematics_header_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
missing_kinematics_header_sources+=("$source")
fi
done
if ((${#missing_kinematics_header_sources[@]} > 0)); then
echo "manifest does not group all LinuxCNC kinematics headers as header sources: $manifest" >&2
printf 'missing kinematics header source: %s\n' "${missing_kinematics_header_sources[@]}" >&2
exit 1
fi
missing_kinematics_metadata_sources=()
for source in "${kinematics_metadata_sources[@]}"; do
if ! printf '%s\n' "${manifest_kinematics_metadata_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
missing_kinematics_metadata_sources+=("$source")
fi
done
if ((${#missing_kinematics_metadata_sources[@]} > 0)); then
echo "manifest does not group all LinuxCNC kinematics build metadata as metadata sources: $manifest" >&2
printf 'missing kinematics metadata source: %s\n' "${missing_kinematics_metadata_sources[@]}" >&2
exit 1
fi
missing_kinematics_asset_sources=()
for source in "${kinematics_asset_sources[@]}"; do
if ! printf '%s\n' "${manifest_kinematics_asset_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
missing_kinematics_asset_sources+=("$source")
fi
done
if ((${#missing_kinematics_asset_sources[@]} > 0)); then
echo "manifest does not group all LinuxCNC kinematics reference assets as asset sources: $manifest" >&2
printf 'missing kinematics asset source: %s\n' "${missing_kinematics_asset_sources[@]}" >&2
exit 1
fi
posemath_dir="$linuxcnc_root/src/libnml/posemath"
if [[ ! -d "$posemath_dir" ]]; then
echo "missing LinuxCNC posemath source directory: $posemath_dir" >&2
exit 1
fi
mapfile -t posemath_core_sources < <(
find "$posemath_dir" -maxdepth 1 -type f \
\( -name '*.c' -o -name '*.cc' \) \
| sed "s#^$linuxcnc_root/##" \
| sort
)
mapfile -t manifest_posemath_core_sources < <(
awk -F: '
$1 !~ /^($|#)/ && $1 == "core" && $2 ~ /^src\/libnml\/posemath\/.*\.(c|cc)$/ { print $2 }
' "$manifest" | sort -u
)
mapfile -t posemath_header_sources < <(
find "$posemath_dir" -maxdepth 1 -type f -name '*.h' \
| sed "s#^$linuxcnc_root/##" \
| sort
)
mapfile -t manifest_posemath_header_sources < <(
awk -F: '
$1 !~ /^($|#)/ && $1 == "header" && $2 ~ /^src\/libnml\/posemath\/.*\.h$/ { print $2 }
' "$manifest" | sort -u
)
mapfile -t posemath_metadata_sources < <(
find "$posemath_dir" -maxdepth 1 -type f \
\( -name 'Submakefile' -o -name 'meson.build' \) \
| sed "s#^$linuxcnc_root/##" \
| sort
)
mapfile -t manifest_posemath_metadata_sources < <(
awk -F: '
$1 !~ /^($|#)/ && $1 == "metadata" && $2 ~ /^src\/libnml\/posemath\/(Submakefile|meson\.build)$/ { print $2 }
' "$manifest" | sort -u
)
missing_posemath_core_sources=()
for source in "${posemath_core_sources[@]}"; do
if ! printf '%s\n' "${manifest_posemath_core_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
missing_posemath_core_sources+=("$source")
fi
done
if ((${#missing_posemath_core_sources[@]} > 0)); then
echo "manifest does not group all LinuxCNC posemath C/C++ sources as core sources: $manifest" >&2
printf 'missing posemath core source: %s\n' "${missing_posemath_core_sources[@]}" >&2
exit 1
fi
missing_posemath_header_sources=()
for source in "${posemath_header_sources[@]}"; do
if ! printf '%s\n' "${manifest_posemath_header_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
missing_posemath_header_sources+=("$source")
fi
done
if ((${#missing_posemath_header_sources[@]} > 0)); then
echo "manifest does not group all LinuxCNC posemath headers as header sources: $manifest" >&2
printf 'missing posemath header source: %s\n' "${missing_posemath_header_sources[@]}" >&2
exit 1
fi
missing_posemath_metadata_sources=()
for source in "${posemath_metadata_sources[@]}"; do
if ! printf '%s\n' "${manifest_posemath_metadata_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
missing_posemath_metadata_sources+=("$source")
fi
done
if ((${#missing_posemath_metadata_sources[@]} > 0)); then
echo "manifest does not group all LinuxCNC posemath build metadata as metadata sources: $manifest" >&2
printf 'missing posemath metadata source: %s\n' "${missing_posemath_metadata_sources[@]}" >&2
exit 1
fi
mapfile -t m428_m430_remap_sources < <(
find "$linuxcnc_root/configs" -path '*/remap_subs/*' -type f \
\( -name '428remap.ngc' -o -name '429remap.ngc' -o -name '430remap.ngc' \) \
| sed "s#^$linuxcnc_root/##" \
| sort
)
mapfile -t manifest_m428_m430_remap_sources < <(
awk -F: '
$1 !~ /^($|#)/ && $2 ~ /\/remap_subs\/(428|429|430)remap\.ngc$/ { print $2 }
' "$manifest" | sort -u
)
missing_m428_m430_remap_sources=()
for source in "${m428_m430_remap_sources[@]}"; do
if ! printf '%s\n' "${manifest_m428_m430_remap_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
missing_m428_m430_remap_sources+=("$source")
fi
done
if ((${#missing_m428_m430_remap_sources[@]} > 0)); then
echo "manifest does not cover all LinuxCNC M428/M429/M430 remap sources: $manifest" >&2
printf 'missing M428/M429/M430 remap source: %s\n' "${missing_m428_m430_remap_sources[@]}" >&2
exit 1
fi
mismatched_m428_m430_remap_notes=()
for source in "${m428_m430_remap_sources[@]}"; do
kinstype=$(sed -n 's/^[[:space:]]*#<kinstype>[[:space:]]*=[[:space:]]*\([0-9][0-9]*\).*/\1/p' "$linuxcnc_root/$source" | head -n 1)
manifest_note=$(awk -F: -v path="$source" '
$1 !~ /^($|#)/ && $2 == path { print $3; exit }
' "$manifest")
if [[ -z "$kinstype" || "$manifest_note" != *"kinstype $kinstype"* ]]; then
mismatched_m428_m430_remap_notes+=("$source")
fi
done
if ((${#mismatched_m428_m430_remap_notes[@]} > 0)); then
echo "manifest M428/M429/M430 remap notes do not match LinuxCNC #<kinstype> assignments: $manifest" >&2
printf 'mismatched M428/M429/M430 remap note: %s\n' "${mismatched_m428_m430_remap_notes[@]}" >&2
exit 1
fi
mapfile -t m428_m430_ini_sources < <(
find "$linuxcnc_root/configs" -name '*.ini' -type f -print0 \
| while IFS= read -r -d '' source; do
if grep -Eiq 'REMAP[[:space:]]*=[[:space:]]*M(428|429|430)([^0-9]|$)' "$source"; then
printf '%s\n' "${source#"$linuxcnc_root/"}"
fi
done \
| sort
)
mapfile -t manifest_m428_m430_ini_sources < <(
awk -F: '
$1 !~ /^($|#)/ && $1 == "config" && $2 ~ /\.ini$/ { print $2 }
' "$manifest" | sort -u
)
missing_m428_m430_ini_sources=()
for source in "${m428_m430_ini_sources[@]}"; do
if ! printf '%s\n' "${manifest_m428_m430_ini_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
missing_m428_m430_ini_sources+=("$source")
fi
done
if ((${#missing_m428_m430_ini_sources[@]} > 0)); then
echo "manifest does not cover all LinuxCNC INI files that declare M428/M429/M430 remaps: $manifest" >&2
printf 'missing M428/M429/M430 INI source: %s\n' "${missing_m428_m430_ini_sources[@]}" >&2
exit 1
fi
mapfile -t manifest_component_sources < <(
awk -F: '
$1 !~ /^($|#)/ && $1 == "component" { print $2 }
' "$manifest" | sort -u
)
mapfile -t manifest_generated_sources < <(
awk -F: '
$1 !~ /^($|#)/ && $1 == "generated" { print $2 }
' "$manifest" | sort -u
)
mapfile -t manifest_metadata_sources < <(
awk -F: '
$1 !~ /^($|#)/ && $1 == "metadata" { print $2 }
' "$manifest" | sort -u
)
m428_m430_kinematics_component_sources=()
m428_m430_kinematics_generated_sources=()
for source in "${manifest_m428_m430_ini_sources[@]}"; do
while IFS= read -r kinematics_name; do
[[ -n "$kinematics_name" ]] || continue
component_source="src/hal/components/$kinematics_name.comp"
generated_source="src/objects/hal/components/$kinematics_name.c"
if [[ -f "$linuxcnc_root/$component_source" ]]; then
m428_m430_kinematics_component_sources+=("$component_source")
fi
if [[ -f "$linuxcnc_root/$generated_source" ]]; then
m428_m430_kinematics_generated_sources+=("$generated_source")
fi
done < <(
sed -n 's/^[[:space:]]*KINEMATICS[[:space:]]*=[[:space:]]*\([^[:space:]#;]*\).*/\1/Ip' "$linuxcnc_root/$source"
)
done
mapfile -t m428_m430_kinematics_component_sources < <(
printf '%s\n' "${m428_m430_kinematics_component_sources[@]}" | sed '/^$/d' | sort -u
)
mapfile -t m428_m430_kinematics_generated_sources < <(
printf '%s\n' "${m428_m430_kinematics_generated_sources[@]}" | sed '/^$/d' | sort -u
)
missing_m428_m430_kinematics_component_sources=()
for source in "${m428_m430_kinematics_component_sources[@]}"; do
if ! printf '%s\n' "${manifest_component_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
missing_m428_m430_kinematics_component_sources+=("$source")
fi
done
if ((${#missing_m428_m430_kinematics_component_sources[@]} > 0)); then
echo "manifest does not cover all LinuxCNC component sources referenced by M428/M429/M430 INI KINEMATICS entries: $manifest" >&2
printf 'missing M428/M429/M430 KINEMATICS component source: %s\n' "${missing_m428_m430_kinematics_component_sources[@]}" >&2
exit 1
fi
missing_m428_m430_kinematics_generated_sources=()
for source in "${m428_m430_kinematics_generated_sources[@]}"; do
if ! printf '%s\n' "${manifest_generated_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
missing_m428_m430_kinematics_generated_sources+=("$source")
fi
done
if ((${#missing_m428_m430_kinematics_generated_sources[@]} > 0)); then
echo "manifest does not cover all LinuxCNC generated component sources referenced by M428/M429/M430 INI KINEMATICS entries: $manifest" >&2
printf 'missing M428/M429/M430 KINEMATICS generated source: %s\n' "${missing_m428_m430_kinematics_generated_sources[@]}" >&2
exit 1
fi
generated_component_metadata_sources=()
for source in "${manifest_generated_sources[@]}"; do
case "$source" in
src/objects/hal/components/*.c)
metadata_source=${source%.c}.mak
if [[ -f "$linuxcnc_root/$metadata_source" ]]; then
generated_component_metadata_sources+=("$metadata_source")
fi
;;
esac
done
mapfile -t generated_component_metadata_sources < <(
printf '%s\n' "${generated_component_metadata_sources[@]}" | sed '/^$/d' | sort -u
)
missing_generated_component_metadata_sources=()
for source in "${generated_component_metadata_sources[@]}"; do
if ! printf '%s\n' "${manifest_metadata_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
missing_generated_component_metadata_sources+=("$source")
fi
done
if ((${#missing_generated_component_metadata_sources[@]} > 0)); then
echo "manifest does not cover all LinuxCNC generated component make metadata adjacent to generated kinematics sources: $manifest" >&2
printf 'missing generated component make metadata source: %s\n' "${missing_generated_component_metadata_sources[@]}" >&2
exit 1
fi
missing_m428_m430_ini_remap_sources=()
unresolved_m428_m430_ini_remaps=()
for source in "${manifest_m428_m430_ini_sources[@]}"; do
config="$linuxcnc_root/$source"
config_dir=$(cd "$(dirname "$config")" && pwd)
mapfile -t subroutine_paths < <(
sed -n 's/^[[:space:]]*SUBROUTINE_PATH[[:space:]]*=[[:space:]]*//Ip' "$config" |
sed 's/[[:space:]]*[#;].*$//; s/^[[:space:]]*//; s/[[:space:]]*$//'
)
if ((${#subroutine_paths[@]} == 0)); then
subroutine_paths=(".")
fi
for mcode in 428 429 430; do
ngc_name=$(sed -n "s/.*REMAP[[:space:]]*=[[:space:]]*M$mcode[[:space:]][^#;]*ngc[[:space:]]*=[[:space:]]*\\([^[:space:]#;]*\\).*/\\1/Ip" "$config" |
head -n 1)
[[ -n "$ngc_name" ]] || continue
[[ "$ngc_name" == *.ngc ]] || ngc_name="$ngc_name.ngc"
resolved_source=
for subroutine_path in "${subroutine_paths[@]}"; do
[[ -n "$subroutine_path" ]] || continue
IFS=: read -ra path_parts <<<"$subroutine_path"
for path_part in "${path_parts[@]}"; do
[[ -n "$path_part" ]] || continue
if [[ "$path_part" = /* ]]; then
candidate_dir=$path_part
else
candidate_dir="$config_dir/$path_part"
fi
if [[ -f "$candidate_dir/$ngc_name" ]]; then
resolved_abs=$(cd "$(dirname "$candidate_dir/$ngc_name")" && pwd)/$(basename "$candidate_dir/$ngc_name")
resolved_source=${resolved_abs#"$linuxcnc_root/"}
break 2
fi
done
done
if [[ -z "$resolved_source" ]]; then
unresolved_m428_m430_ini_remaps+=("$source M$mcode ngc=$ngc_name")
elif ! printf '%s\n' "${manifest_m428_m430_remap_sources[@]}" | grep -Fx -- "$resolved_source" >/dev/null; then
missing_m428_m430_ini_remap_sources+=("$resolved_source")
fi
done
done
if ((${#unresolved_m428_m430_ini_remaps[@]} > 0)); then
echo "LinuxCNC M428/M429/M430 INI remap sources could not be resolved through SUBROUTINE_PATH: $manifest" >&2
printf 'unresolved M428/M429/M430 INI remap: %s\n' "${unresolved_m428_m430_ini_remaps[@]}" >&2
exit 1
fi
if ((${#missing_m428_m430_ini_remap_sources[@]} > 0)); then
echo "manifest does not cover all LinuxCNC M428/M429/M430 remap sources referenced by INI REMAP entries: $manifest" >&2
printf 'missing INI-referenced M428/M429/M430 remap source: %s\n' "${missing_m428_m430_ini_remap_sources[@]}" >&2
exit 1
fi
mapfile -t manifest_config_hal_sources < <(
awk -F: '
$1 !~ /^($|#)/ && $1 == "config" && $2 ~ /\.hal$/ { print $2 }
' "$manifest" | sort -u
)
unresolved_m428_m430_config_hal_sources=()
mapfile -t m428_m430_config_hal_sources < <(
for source in "${manifest_m428_m430_ini_sources[@]}"; do
config_dir=$(cd "$(dirname "$linuxcnc_root/$source")" && pwd)
sed -n 's/^[[:space:]]*\(HALFILE\|POSTGUI_HALFILE\)[[:space:]]*=[[:space:]]*//Ip' "$linuxcnc_root/$source" |
sed 's/[[:space:]]*[#;].*$//; s/^[[:space:]]*//; s/[[:space:]]*$//' |
while IFS= read -r hal_source; do
[[ -n "$hal_source" && "$hal_source" != LIB:* ]] || continue
if [[ "$hal_source" = /* ]]; then
candidate=$hal_source
else
candidate="$config_dir/$hal_source"
fi
if [[ -f "$candidate" ]]; then
candidate_abs=$(cd "$(dirname "$candidate")" && pwd)/$(basename "$candidate")
printf '%s\n' "${candidate_abs#"$linuxcnc_root/"}"
else
printf '%s HALFILE=%s\n' "$source" "$hal_source" >&2
fi
done
done | sort -u
)
mapfile -t unresolved_m428_m430_config_hal_sources < <(
for source in "${manifest_m428_m430_ini_sources[@]}"; do
config_dir=$(cd "$(dirname "$linuxcnc_root/$source")" && pwd)
sed -n 's/^[[:space:]]*\(HALFILE\|POSTGUI_HALFILE\)[[:space:]]*=[[:space:]]*//Ip' "$linuxcnc_root/$source" |
sed 's/[[:space:]]*[#;].*$//; s/^[[:space:]]*//; s/[[:space:]]*$//' |
while IFS= read -r hal_source; do
[[ -n "$hal_source" && "$hal_source" != LIB:* ]] || continue
if [[ "$hal_source" = /* ]]; then
candidate=$hal_source
else
candidate="$config_dir/$hal_source"
fi
[[ -f "$candidate" ]] || printf '%s HALFILE=%s\n' "$source" "$hal_source"
done
done | sort -u
)
if ((${#unresolved_m428_m430_config_hal_sources[@]} > 0)); then
echo "LinuxCNC M428/M429/M430 INI HALFILE/POSTGUI_HALFILE sources could not be resolved: $manifest" >&2
printf 'unresolved M428/M429/M430 HAL source: %s\n' "${unresolved_m428_m430_config_hal_sources[@]}" >&2
exit 1
fi
missing_m428_m430_config_hal_sources=()
for source in "${m428_m430_config_hal_sources[@]}"; do
if ! printf '%s\n' "${manifest_config_hal_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
missing_m428_m430_config_hal_sources+=("$source")
fi
done
if ((${#missing_m428_m430_config_hal_sources[@]} > 0)); then
echo "manifest does not cover all LinuxCNC HALFILE/POSTGUI_HALFILE sources referenced by M428/M429/M430 INI files: $manifest" >&2
printf 'missing M428/M429/M430 HAL source: %s\n' "${missing_m428_m430_config_hal_sources[@]}" >&2
exit 1
fi
mapfile -t manifest_asset_sources < <(
awk -F: '
$1 !~ /^($|#)/ && $1 == "asset" { print $2 }
' "$manifest" | sort -u
)
mapfile -t manifest_tooldata_sources < <(
awk -F: '
$1 !~ /^($|#)/ && $1 == "tooldata" { print $2 }
' "$manifest" | sort -u
)
required_delta_asset_sources=(
configs/sim/axis/ldelta.ini
configs/sim/axis/ldelta_demo.ini
configs/sim/axis/ldelta_demo.txt
configs/sim/axis/ldelta_demo_es.txt
configs/sim/axis/rdelta.ini
lib/hallib/sim_ldelta.hal
lib/hallib/sim_rdelta.hal
)
missing_delta_asset_sources=()
for source in "${required_delta_asset_sources[@]}"; do
if ! printf '%s\n' "${manifest_asset_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
missing_delta_asset_sources+=("$source")
fi
done
if ((${#missing_delta_asset_sources[@]} > 0)); then
echo "manifest does not cover LinuxCNC delta kinematics INI/HAL demo sources as asset sources: $manifest" >&2
printf 'missing delta kinematics asset source: %s\n' "${missing_delta_asset_sources[@]}" >&2
exit 1
fi
required_corexy_asset_sources=(
configs/sim/axis/corexy/corexy_by_kins.ini
configs/sim/axis/corexy/corexy_by_hal.ini
configs/sim/axis/corexy/corexy_base.inc
configs/sim/axis/corexy/3joint_xyz.hal
configs/sim/axis/corexy/corexy_by_hal.hal
configs/sim/axis/corexy/corexy.halshow
configs/sim/axis/corexy/README
configs/sim/axis/corexy/corexy_by_kins.txt
configs/sim/axis/corexy/corexy_by_hal.txt
)
missing_corexy_asset_sources=()
for source in "${required_corexy_asset_sources[@]}"; do
if ! printf '%s\n' "${manifest_asset_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
missing_corexy_asset_sources+=("$source")
fi
done
if ((${#missing_corexy_asset_sources[@]} > 0)); then
echo "manifest does not cover LinuxCNC CoreXY kinematics INI/HAL/demo sources as asset sources: $manifest" >&2
printf 'missing CoreXY kinematics asset source: %s\n' "${missing_corexy_asset_sources[@]}" >&2
exit 1
fi
required_corexy_component_source=src/hal/components/corexy_by_hal.comp
if ! printf '%s\n' "${manifest_component_sources[@]}" | grep -Fx -- "$required_corexy_component_source" >/dev/null; then
echo "manifest does not cover LinuxCNC CoreXY HAL component source: $manifest" >&2
printf 'missing CoreXY component source: %s\n' "$required_corexy_component_source" >&2
exit 1
fi
required_corexy_generated_source=src/objects/hal/components/corexy_by_hal.c
if ! printf '%s\n' "${manifest_generated_sources[@]}" | grep -Fx -- "$required_corexy_generated_source" >/dev/null; then
echo "manifest does not cover LinuxCNC generated CoreXY HAL component source: $manifest" >&2
printf 'missing generated CoreXY component source: %s\n' "$required_corexy_generated_source" >&2
exit 1
fi
required_corexy_metadata_sources=(
src/objects/hal/components/corexy_by_hal.mak
src/objects/man/man9/corexy_by_hal.9.adoc
)
missing_corexy_metadata_sources=()
for source in "${required_corexy_metadata_sources[@]}"; do
if ! printf '%s\n' "${manifest_metadata_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
missing_corexy_metadata_sources+=("$source")
fi
done
if ((${#missing_corexy_metadata_sources[@]} > 0)); then
echo "manifest does not cover LinuxCNC CoreXY HAL component metadata sources: $manifest" >&2
printf 'missing CoreXY metadata source: %s\n' "${missing_corexy_metadata_sources[@]}" >&2
exit 1
fi
required_rose_engine_asset_sources=(
configs/sim/axis/rose_engine/rose_engine.ini
configs/sim/axis/rose_engine/rose_engine.halshow
configs/sim/axis/rose_engine/rcone.ngc
configs/sim/axis/rose_engine/rcone_demo.ngc
configs/sim/axis/rose_engine/README
)
missing_rose_engine_asset_sources=()
for source in "${required_rose_engine_asset_sources[@]}"; do
if ! printf '%s\n' "${manifest_asset_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
missing_rose_engine_asset_sources+=("$source")
fi
done
if ((${#missing_rose_engine_asset_sources[@]} > 0)); then
echo "manifest does not cover LinuxCNC rose engine kinematics INI/NGC/demo sources as asset sources: $manifest" >&2
printf 'missing rose engine kinematics asset source: %s\n' "${missing_rose_engine_asset_sources[@]}" >&2
exit 1
fi
required_rosekins_metadata_source=docs/src/man/man9/rosekins.9.adoc
if ! printf '%s\n' "${manifest_metadata_sources[@]}" | grep -Fx -- "$required_rosekins_metadata_source" >/dev/null; then
echo "manifest does not cover LinuxCNC rosekins manpage source: $manifest" >&2
printf 'missing rosekins metadata source: %s\n' "$required_rosekins_metadata_source" >&2
exit 1
fi
required_tripod_asset_sources=(
configs/sim/tklinuxcnc/tripod.ini
lib/hallib/tripodsim.hal
configs/sim/tklinuxcnc/README
)
missing_tripod_asset_sources=()
for source in "${required_tripod_asset_sources[@]}"; do
if ! printf '%s\n' "${manifest_asset_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
missing_tripod_asset_sources+=("$source")
fi
done
if ((${#missing_tripod_asset_sources[@]} > 0)); then
echo "manifest does not cover LinuxCNC tripod kinematics INI/HAL sources as asset sources: $manifest" >&2
printf 'missing tripod kinematics asset source: %s\n' "${missing_tripod_asset_sources[@]}" >&2
exit 1
fi
required_tripod_metadata_source=docs/man/man9/tripodkins.9
if ! printf '%s\n' "${manifest_metadata_sources[@]}" | grep -Fx -- "$required_tripod_metadata_source" >/dev/null; then
echo "manifest does not cover LinuxCNC tripodkins manpage source: $manifest" >&2
printf 'missing tripodkins metadata source: %s\n' "$required_tripod_metadata_source" >&2
exit 1
fi
required_tklinuxcnc_trivkins_asset_sources=(
configs/sim/tklinuxcnc/trivkins/xz.ini
configs/sim/tklinuxcnc/trivkins/xz_both.ini
configs/sim/tklinuxcnc/trivkins/xzbase.inc
configs/sim/tklinuxcnc/trivkins/xz.tbl
configs/sim/tklinuxcnc/trivkins/README
)
missing_tklinuxcnc_trivkins_asset_sources=()
for source in "${required_tklinuxcnc_trivkins_asset_sources[@]}"; do
if ! printf '%s\n' "${manifest_asset_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
missing_tklinuxcnc_trivkins_asset_sources+=("$source")
fi
done
if ((${#missing_tklinuxcnc_trivkins_asset_sources[@]} > 0)); then
echo "manifest does not cover LinuxCNC TkLinuxCNC trivkins XZ INI/include/tool/demo sources as asset sources: $manifest" >&2
printf 'missing TkLinuxCNC trivkins asset source: %s\n' "${missing_tklinuxcnc_trivkins_asset_sources[@]}" >&2
exit 1
fi
required_axis_ja_xz_trivkins_asset_sources=(
configs/sim/axis/ja_tests/xz/xz.ini
configs/sim/axis/ja_tests/xz/xz_both.ini
configs/sim/axis/ja_tests/xz/xz_both_auto.ini
configs/sim/axis/ja_tests/xz/xzbase.inc
configs/sim/axis/ja_tests/xz/xz.tbl
configs/sim/axis/ja_tests/xz/README
)
missing_axis_ja_xz_trivkins_asset_sources=()
for source in "${required_axis_ja_xz_trivkins_asset_sources[@]}"; do
if ! printf '%s\n' "${manifest_asset_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
missing_axis_ja_xz_trivkins_asset_sources+=("$source")
fi
done
if ((${#missing_axis_ja_xz_trivkins_asset_sources[@]} > 0)); then
echo "manifest does not cover LinuxCNC AXIS joint-axis trivkins XZ INI/include/tool/demo sources as asset sources: $manifest" >&2
printf 'missing AXIS joint-axis trivkins asset source: %s\n' "${missing_axis_ja_xz_trivkins_asset_sources[@]}" >&2
exit 1
fi
required_axis_ja_xyzb_locking_indexer_asset_sources=(
configs/sim/axis/ja_tests/xyzb_locking_indexer/xyzb.ini
configs/sim/axis/ja_tests/xyzb_locking_indexer/xyyzb.ini
configs/sim/axis/ja_tests/xyzb_locking_indexer/xyzb.hal
configs/sim/axis/ja_tests/xyzb_locking_indexer/xyzb.halshow
configs/sim/axis/ja_tests/xyzb_locking_indexer/README
)
missing_axis_ja_xyzb_locking_indexer_asset_sources=()
for source in "${required_axis_ja_xyzb_locking_indexer_asset_sources[@]}"; do
if ! printf '%s\n' "${manifest_asset_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
missing_axis_ja_xyzb_locking_indexer_asset_sources+=("$source")
fi
done
if ((${#missing_axis_ja_xyzb_locking_indexer_asset_sources[@]} > 0)); then
echo "manifest does not cover LinuxCNC AXIS joint-axis trivkins XYZB locking indexer INI/HAL/demo sources as asset sources: $manifest" >&2
printf 'missing AXIS joint-axis XYZB locking indexer asset source: %s\n' "${missing_axis_ja_xyzb_locking_indexer_asset_sources[@]}" >&2
exit 1
fi
required_scorbot_asset_sources=(
configs/by_machine/scorbot-er-3/scorbot-er-3.ini
configs/by_machine/scorbot-er-3/scorbot-er-3.hal
configs/by_machine/scorbot-er-3/pyvcp.hal
configs/by_machine/scorbot-er-3/gripper.xml
src/hal/user_comps/scorbot-er-3.py
)
missing_scorbot_asset_sources=()
for source in "${required_scorbot_asset_sources[@]}"; do
if ! printf '%s\n' "${manifest_asset_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
missing_scorbot_asset_sources+=("$source")
fi
done
if ((${#missing_scorbot_asset_sources[@]} > 0)); then
echo "manifest does not cover LinuxCNC Scorbot ER-3 kinematics INI/HAL/PYVCP/user component sources as asset sources: $manifest" >&2
printf 'missing Scorbot ER-3 asset source: %s\n' "${missing_scorbot_asset_sources[@]}" >&2
exit 1
fi
required_scorbot_metadata_sources=(
src/hal/user_comps/Submakefile
docs/src/man/man1/scorbot-er-3.1.adoc
)
missing_scorbot_metadata_sources=()
for source in "${required_scorbot_metadata_sources[@]}"; do
if ! printf '%s\n' "${manifest_metadata_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
missing_scorbot_metadata_sources+=("$source")
fi
done
if ((${#missing_scorbot_metadata_sources[@]} > 0)); then
echo "manifest does not cover LinuxCNC Scorbot ER-3 user component metadata sources: $manifest" >&2
printf 'missing Scorbot ER-3 metadata source: %s\n' "${missing_scorbot_metadata_sources[@]}" >&2
exit 1
fi
required_gantry_asset_sources=(
configs/sim/axis/gantry/gantry_mm.ini
configs/sim/axis/gantry/gantry_mm.hal
configs/sim/axis/gantry/gantry_mm.tbl
configs/sim/axis/gantry/README
)
missing_gantry_asset_sources=()
for source in "${required_gantry_asset_sources[@]}"; do
if ! printf '%s\n' "${manifest_asset_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
missing_gantry_asset_sources+=("$source")
fi
done
if ((${#missing_gantry_asset_sources[@]} > 0)); then
echo "manifest does not cover LinuxCNC gantry metric INI/HAL/tool sources as asset sources: $manifest" >&2
printf 'missing gantry metric asset source: %s\n' "${missing_gantry_asset_sources[@]}" >&2
exit 1
fi
required_gantry_component_source=src/hal/components/gantry.comp
if ! printf '%s\n' "${manifest_component_sources[@]}" | grep -Fx -- "$required_gantry_component_source" >/dev/null; then
echo "manifest does not cover LinuxCNC gantry HAL component source: $manifest" >&2
printf 'missing gantry component source: %s\n' "$required_gantry_component_source" >&2
exit 1
fi
required_gantry_generated_source=src/objects/hal/components/gantry.c
if ! printf '%s\n' "${manifest_generated_sources[@]}" | grep -Fx -- "$required_gantry_generated_source" >/dev/null; then
echo "manifest does not cover LinuxCNC generated gantry HAL component source: $manifest" >&2
printf 'missing generated gantry component source: %s\n' "$required_gantry_generated_source" >&2
exit 1
fi
required_gantry_metadata_sources=(
src/objects/hal/components/gantry.mak
src/objects/man/man9/gantry.9.adoc
)
missing_gantry_metadata_sources=()
for source in "${required_gantry_metadata_sources[@]}"; do
if ! printf '%s\n' "${manifest_metadata_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
missing_gantry_metadata_sources+=("$source")
fi
done
if ((${#missing_gantry_metadata_sources[@]} > 0)); then
echo "manifest does not cover LinuxCNC gantry HAL component metadata sources: $manifest" >&2
printf 'missing gantry metadata source: %s\n' "${missing_gantry_metadata_sources[@]}" >&2
exit 1
fi
resolve_vismach_loadusr_source() {
local command_name=$1
case "$command_name" in
*/*|*\\*|*.py)
return 0
;;
esac
local vismach_source="src/hal/user_comps/vismach/$command_name.py"
if [[ -f "$linuxcnc_root/$vismach_source" ]]; then
printf '%s\n' "$vismach_source"
fi
}
m428_m430_vismach_loadusr_sources=()
for source in "${manifest_m428_m430_ini_sources[@]}"; do
while IFS= read -r command_name; do
[[ -n "$command_name" ]] || continue
while IFS= read -r vismach_source; do
[[ -n "$vismach_source" ]] && m428_m430_vismach_loadusr_sources+=("$vismach_source")
done < <(resolve_vismach_loadusr_source "$command_name")
done < <(
awk '
BEGIN { IGNORECASE = 1 }
/^[[:space:]]*HALCMD[[:space:]]*=/ && /loadusr[[:space:]]/ {
for (i = 1; i <= NF; i++) {
if ($i == "-W" && (i + 1) <= NF) {
value = $(i + 1)
sub(/[[:space:]#;].*/, "", value)
if (value != "") print value
}
}
}
' "$linuxcnc_root/$source"
)
done
for source in "${m428_m430_config_hal_sources[@]}"; do
while IFS= read -r command_name; do
[[ -n "$command_name" ]] || continue
while IFS= read -r vismach_source; do
[[ -n "$vismach_source" ]] && m428_m430_vismach_loadusr_sources+=("$vismach_source")
done < <(resolve_vismach_loadusr_source "$command_name")
done < <(
awk '
/^[[:space:]]*loadusr[[:space:]]/ {
for (i = 1; i <= NF; i++) {
if ($i == "-W" && (i + 1) <= NF) {
value = $(i + 1)
sub(/[[:space:]#;].*/, "", value)
if (value != "") print value
}
}
}
' "$linuxcnc_root/$source"
)
done
mapfile -t m428_m430_vismach_loadusr_sources < <(
printf '%s\n' "${m428_m430_vismach_loadusr_sources[@]}" | sed '/^$/d' | sort -u
)
missing_m428_m430_vismach_loadusr_sources=()
for source in "${m428_m430_vismach_loadusr_sources[@]}"; do
if ! printf '%s\n' "${manifest_asset_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
missing_m428_m430_vismach_loadusr_sources+=("$source")
fi
done
if ((${#missing_m428_m430_vismach_loadusr_sources[@]} > 0)); then
echo "manifest does not cover all LinuxCNC vismach user component sources referenced by M428/M429/M430 INI/HAL loadusr commands as asset sources: $manifest" >&2
printf 'missing M428/M429/M430 vismach loadusr source: %s\n' "${missing_m428_m430_vismach_loadusr_sources[@]}" >&2
exit 1
fi
if ((${#m428_m430_vismach_loadusr_sources[@]} > 0)); then
vismach_submakefile_source=src/hal/user_comps/vismach/Submakefile
if [[ -f "$linuxcnc_root/$vismach_submakefile_source" ]] &&
! printf '%s\n' "${manifest_metadata_sources[@]}" | grep -Fx -- "$vismach_submakefile_source" >/dev/null; then
echo "manifest does not cover LinuxCNC vismach user component build metadata as metadata sources: $manifest" >&2
printf 'missing vismach user component metadata source: %s\n' "$vismach_submakefile_source" >&2
exit 1
fi
fi
m428_m430_vismach_man_sources=()
for source in "${m428_m430_vismach_loadusr_sources[@]}"; do
command_name=$(basename "$source" .py)
man_source="docs/src/man/man1/$command_name.1.adoc"
if [[ -f "$linuxcnc_root/$man_source" ]]; then
m428_m430_vismach_man_sources+=("$man_source")
fi
done
mapfile -t m428_m430_vismach_man_sources < <(
printf '%s\n' "${m428_m430_vismach_man_sources[@]}" | sed '/^$/d' | sort -u
)
missing_m428_m430_vismach_man_sources=()
for source in "${m428_m430_vismach_man_sources[@]}"; do
if ! printf '%s\n' "${manifest_metadata_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
missing_m428_m430_vismach_man_sources+=("$source")
fi
done
if ((${#missing_m428_m430_vismach_man_sources[@]} > 0)); then
echo "manifest does not cover LinuxCNC vismach user component manpage sources as metadata sources: $manifest" >&2
printf 'missing vismach user component manpage source: %s\n' "${missing_m428_m430_vismach_man_sources[@]}" >&2
exit 1
fi
qtvcp_panel_sources=()
for source in "${manifest_m428_m430_ini_sources[@]}"; do
if grep -Eiq '^[[:space:]]*EMBED_TAB_COMMAND[[:space:]]*=[[:space:]]*qtvcp[[:space:]]+vismach_scara([[:space:]#;]|$)' "$linuxcnc_root/$source"; then
qtvcp_panel_sources+=(
share/qtvcp/panels/vismach_scara/vismach_scara.ui
share/qtvcp/panels/vismach_scara/vismach_scara_handler.py
lib/python/qtvcp/lib/qt_vismach/README.txt
lib/python/qtvcp/lib/qt_vismach/__init__.py
lib/python/qtvcp/lib/qt_vismach/primitives.py
lib/python/qtvcp/lib/qt_vismach/qt_vismach.py
lib/python/qtvcp/lib/qt_vismach/scara.py
)
fi
done
mapfile -t qtvcp_panel_sources < <(
printf '%s\n' "${qtvcp_panel_sources[@]}" | sed '/^$/d' | sort -u
)
missing_qtvcp_panel_sources=()
for source in "${qtvcp_panel_sources[@]}"; do
if [[ ! -f "$linuxcnc_root/$source" ]]; then
missing_qtvcp_panel_sources+=("$source")
elif ! printf '%s\n' "${manifest_asset_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
missing_qtvcp_panel_sources+=("$source")
fi
done
if ((${#missing_qtvcp_panel_sources[@]} > 0)); then
echo "manifest does not cover all LinuxCNC QtVCP vismach_scara sources referenced by M428/M429/M430 INI EMBED_TAB_COMMAND entries as asset sources: $manifest" >&2
printf 'missing M428/M429/M430 QtVCP panel source: %s\n' "${missing_qtvcp_panel_sources[@]}" >&2
exit 1
fi
m428_m430_config_asset_sources=()
unresolved_m428_m430_config_asset_sources=()
for source in "${manifest_m428_m430_ini_sources[@]}"; do
config_dir=$(cd "$(dirname "$linuxcnc_root/$source")" && pwd)
while IFS= read -r config_asset; do
key=${config_asset%%=*}
asset_source=${config_asset#*=}
[[ -n "$asset_source" ]] || continue
if [[ "$asset_source" = /* ]]; then
candidate=$asset_source
else
candidate="$config_dir/$asset_source"
fi
if [[ -f "$candidate" ]]; then
candidate_abs=$(cd "$(dirname "$candidate")" && pwd)/$(basename "$candidate")
case "$candidate_abs" in
"$linuxcnc_root"/*)
m428_m430_config_asset_sources+=("${candidate_abs#"$linuxcnc_root/"}")
;;
*)
unresolved_m428_m430_config_asset_sources+=("$source $key=$asset_source")
;;
esac
else
unresolved_m428_m430_config_asset_sources+=("$source $key=$asset_source")
fi
done < <(
awk -F= '
BEGIN { IGNORECASE = 1 }
/^[[:space:]]*(PYVCP|OPEN_FILE)[[:space:]]*=/ {
key = $1
value = $0
sub(/^[^=]*=/, "", value)
sub(/[[:space:]]*[#;].*$/, "", value)
gsub(/^[[:space:]]+|[[:space:]]+$/, "", key)
gsub(/^[[:space:]]+|[[:space:]]+$/, "", value)
gsub(/^"|"$/, "", value)
if (value != "") {
print toupper(key) "=" value
}
}
' "$linuxcnc_root/$source"
)
done
mapfile -t m428_m430_config_asset_sources < <(
printf '%s\n' "${m428_m430_config_asset_sources[@]}" | sed '/^$/d' | sort -u
)
mapfile -t unresolved_m428_m430_config_asset_sources < <(
printf '%s\n' "${unresolved_m428_m430_config_asset_sources[@]}" | sed '/^$/d' | sort -u
)
if ((${#unresolved_m428_m430_config_asset_sources[@]} > 0)); then
echo "LinuxCNC M428/M429/M430 INI PYVCP/OPEN_FILE assets could not be resolved: $manifest" >&2
printf 'unresolved M428/M429/M430 config asset: %s\n' "${unresolved_m428_m430_config_asset_sources[@]}" >&2
exit 1
fi
missing_m428_m430_config_asset_sources=()
for source in "${m428_m430_config_asset_sources[@]}"; do
if ! printf '%s\n' "${manifest_asset_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
missing_m428_m430_config_asset_sources+=("$source")
fi
done
if ((${#missing_m428_m430_config_asset_sources[@]} > 0)); then
echo "manifest does not cover all LinuxCNC PYVCP/OPEN_FILE assets referenced by M428/M429/M430 INI files as asset sources: $manifest" >&2
printf 'missing M428/M429/M430 config asset source: %s\n' "${missing_m428_m430_config_asset_sources[@]}" >&2
exit 1
fi
m428_m430_config_readme_sources=()
for source in "${manifest_m428_m430_ini_sources[@]}"; do
config_dir=$(cd "$(dirname "$linuxcnc_root/$source")" && pwd)
readme_source="$config_dir/README"
if [[ -f "$readme_source" ]]; then
readme_abs=$(cd "$(dirname "$readme_source")" && pwd)/$(basename "$readme_source")
m428_m430_config_readme_sources+=("${readme_abs#"$linuxcnc_root/"}")
fi
done
mapfile -t m428_m430_config_readme_sources < <(
printf '%s\n' "${m428_m430_config_readme_sources[@]}" | sed '/^$/d' | sort -u
)
missing_m428_m430_config_readme_sources=()
for source in "${m428_m430_config_readme_sources[@]}"; do
if ! printf '%s\n' "${manifest_asset_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
missing_m428_m430_config_readme_sources+=("$source")
fi
done
if ((${#missing_m428_m430_config_readme_sources[@]} > 0)); then
echo "manifest does not cover LinuxCNC README sources adjacent to M428/M429/M430 INI files as asset sources: $manifest" >&2
printf 'missing M428/M429/M430 README source: %s\n' "${missing_m428_m430_config_readme_sources[@]}" >&2
exit 1
fi
m428_m430_config_tooldata_sources=()
unresolved_m428_m430_config_tooldata_sources=()
for source in "${manifest_m428_m430_ini_sources[@]}"; do
config_dir=$(cd "$(dirname "$linuxcnc_root/$source")" && pwd)
while IFS= read -r tool_table; do
[[ -n "$tool_table" ]] || continue
if [[ "$tool_table" = /* ]]; then
candidate=$tool_table
else
candidate="$config_dir/$tool_table"
fi
if [[ -f "$candidate" ]]; then
candidate_abs=$(cd "$(dirname "$candidate")" && pwd)/$(basename "$candidate")
case "$candidate_abs" in
"$linuxcnc_root"/*)
m428_m430_config_tooldata_sources+=("${candidate_abs#"$linuxcnc_root/"}")
;;
*)
unresolved_m428_m430_config_tooldata_sources+=("$source TOOL_TABLE=$tool_table")
;;
esac
else
unresolved_m428_m430_config_tooldata_sources+=("$source TOOL_TABLE=$tool_table")
fi
done < <(
awk -F= '
BEGIN { IGNORECASE = 1 }
/^[[:space:]]*TOOL_TABLE[[:space:]]*=/ {
value = $0
sub(/^[^=]*=/, "", value)
sub(/[[:space:]]*[#;].*$/, "", value)
gsub(/^[[:space:]]+|[[:space:]]+$/, "", value)
gsub(/^"|"$/, "", value)
if (value != "") print value
}
' "$linuxcnc_root/$source"
)
done
mapfile -t m428_m430_config_tooldata_sources < <(
printf '%s\n' "${m428_m430_config_tooldata_sources[@]}" | sed '/^$/d' | sort -u
)
mapfile -t unresolved_m428_m430_config_tooldata_sources < <(
printf '%s\n' "${unresolved_m428_m430_config_tooldata_sources[@]}" | sed '/^$/d' | sort -u
)
missing_m428_m430_config_tooldata_sources=()
for source in "${m428_m430_config_tooldata_sources[@]}"; do
if ! printf '%s\n' "${manifest_tooldata_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
missing_m428_m430_config_tooldata_sources+=("$source")
fi
done
if ((${#missing_m428_m430_config_tooldata_sources[@]} > 0)); then
echo "manifest does not cover all resolvable LinuxCNC TOOL_TABLE files referenced by M428/M429/M430 INI files as tooldata sources: $manifest" >&2
printf 'missing M428/M429/M430 tool table source: %s\n' "${missing_m428_m430_config_tooldata_sources[@]}" >&2
exit 1
fi
mapfile -t manifest_twp_ini_sources < <(
awk -F: '
$1 !~ /^($|#)/ && $1 == "config" && $2 ~ /table-rotary_spindle-rotary-nutating\/.*_twp\/.*\.ini$/ { print $2 }
' "$manifest" | sort -u
)
twp_config_asset_sources=()
unresolved_twp_config_asset_sources=()
resolve_twp_config_asset() {
local config_dir=$1
local source=$2
local key=$3
local asset_source=$4
local candidate
if [[ "$asset_source" = /* ]]; then
candidate=$asset_source
else
candidate="$config_dir/$asset_source"
fi
if [[ -f "$candidate" ]]; then
local candidate_abs
candidate_abs=$(cd "$(dirname "$candidate")" && pwd)/$(basename "$candidate")
case "$candidate_abs" in
"$linuxcnc_root"/*)
twp_config_asset_sources+=("${candidate_abs#"$linuxcnc_root/"}")
;;
*)
unresolved_twp_config_asset_sources+=("$source $key=$asset_source")
;;
esac
else
unresolved_twp_config_asset_sources+=("$source $key=$asset_source")
fi
}
resolve_twp_subroutine_asset() {
local config_dir=$1
local source=$2
local key=$3
local ngc_name=$4
shift 4
local subroutine_paths=("$@")
[[ "$ngc_name" == *.ngc ]] || ngc_name="$ngc_name.ngc"
local resolved_source=
local subroutine_path path_part candidate_dir candidate resolved_abs
for subroutine_path in "${subroutine_paths[@]}"; do
[[ -n "$subroutine_path" ]] || continue
IFS=: read -ra path_parts <<<"$subroutine_path"
for path_part in "${path_parts[@]}"; do
[[ -n "$path_part" ]] || continue
if [[ "$path_part" = /* ]]; then
candidate_dir=$path_part
else
candidate_dir="$config_dir/$path_part"
fi
candidate="$candidate_dir/$ngc_name"
if [[ -f "$candidate" ]]; then
resolved_abs=$(cd "$(dirname "$candidate")" && pwd)/$(basename "$candidate")
resolved_source=${resolved_abs#"$linuxcnc_root/"}
break 2
fi
done
done
if [[ -n "$resolved_source" ]]; then
twp_config_asset_sources+=("$resolved_source")
else
unresolved_twp_config_asset_sources+=("$source $key=$ngc_name")
fi
}
for source in "${manifest_twp_ini_sources[@]}"; do
config="$linuxcnc_root/$source"
config_dir=$(cd "$(dirname "$config")" && pwd)
mapfile -t subroutine_paths < <(
sed -n 's/^[[:space:]]*SUBROUTINE_PATH[[:space:]]*=[[:space:]]*//Ip' "$config" |
sed 's/[[:space:]]*[#;].*$//; s/^[[:space:]]*//; s/[[:space:]]*$//'
)
if ((${#subroutine_paths[@]} == 0)); then
subroutine_paths=(".")
fi
while IFS='|' read -r remap_code ngc_name; do
[[ -n "$remap_code" && -n "$ngc_name" ]] || continue
case "$remap_code" in
M428|m428|M429|m429|M430|m430)
continue
;;
esac
resolve_twp_subroutine_asset "$config_dir" "$source" "REMAP $remap_code ngc" "$ngc_name" "${subroutine_paths[@]}"
done < <(
awk '
BEGIN { IGNORECASE = 1 }
/^[[:space:]]*REMAP[[:space:]]*=/ && /[[:space:]]ngc[[:space:]]*=/ {
code = $0
ngc = $0
sub(/.*REMAP[[:space:]]*=[[:space:]]*/, "", code)
sub(/[[:space:]].*/, "", code)
sub(/.*[[:space:]]ngc[[:space:]]*=[[:space:]]*/, "", ngc)
sub(/[[:space:]#;].*/, "", ngc)
print code "|" ngc
}
' "$config"
)
while IFS= read -r abort_name; do
[[ -n "$abort_name" ]] || continue
resolve_twp_subroutine_asset "$config_dir" "$source" "ON_ABORT_COMMAND" "$abort_name" "${subroutine_paths[@]}"
done < <(
sed -n 's/^[[:space:]]*ON_ABORT_COMMAND[[:space:]]*=[[:space:]]*o[[:space:]]*<\([^>]*\)>.*/\1/Ip' "$config"
)
while IFS= read -r twp_asset; do
[[ -n "$twp_asset" ]] || continue
resolve_twp_config_asset "$config_dir" "$source" "TWP asset" "$twp_asset"
done < <(
awk -F= '
BEGIN { IGNORECASE = 1 }
/^[[:space:]]*TOPLEVEL[[:space:]]*=/ {
value = $0
sub(/^[^=]*=/, "", value)
sub(/[[:space:]]*[#;].*$/, "", value)
gsub(/^[[:space:]]+|[[:space:]]+$/, "", value)
if (value != "") print value
}
/^[[:space:]]*EMBED_TAB_COMMAND[[:space:]]*=/ && /pyvcp[[:space:]]/ {
for (i = 1; i <= NF; i++) {
if ($i == "pyvcp" && (i + 1) <= NF) {
value = $(i + 1)
sub(/[[:space:]#;].*$/, "", value)
if (value != "") print value
}
}
}
/^[[:space:]]*TOOL_TABLE[[:space:]]*=/ {
next
}
/^[[:space:]]*HALCMD[[:space:]]*=/ && /loadusr/ {
for (i = 1; i <= NF; i++) {
if ($i ~ /^\.\.\/.*\.py$/) print $i
}
}
' "$config"
)
mapfile -t twp_loaded_python_sources < <(
awk '
BEGIN { IGNORECASE = 1 }
/^[[:space:]]*TOPLEVEL[[:space:]]*=/ {
value = $0
sub(/^[^=]*=/, "", value)
sub(/[[:space:]]*[#;].*$/, "", value)
gsub(/^[[:space:]]+|[[:space:]]+$/, "", value)
if (value != "") print value
}
/^[[:space:]]*HALCMD[[:space:]]*=/ && /loadusr/ {
for (i = 1; i <= NF; i++) {
if ($i ~ /^\.\.\/.*\.py$/) print $i
}
}
' "$config"
)
for twp_python_source in "${twp_loaded_python_sources[@]}"; do
python_candidate="$config_dir/$twp_python_source"
if [[ ! -f "$python_candidate" ]]; then
continue
fi
python_abs=$(cd "$(dirname "$python_candidate")" && pwd)/$(basename "$python_candidate")
python_dir=$(dirname "$python_abs")
if grep -Eq '^[[:space:]]*import[[:space:]]+remap([[:space:]]|$)' "$python_abs"; then
resolve_twp_config_asset "$python_dir" "$source" "python import" "remap.py"
fi
if grep -Eq '^[[:space:]]*from[[:space:]]+util[[:space:]]+import([[:space:]]|$)' "$python_abs"; then
resolve_twp_config_asset "$python_dir" "$source" "python import" "util.py"
fi
if grep -Eq '^[[:space:]]*from[[:space:]]+twp_vismach[[:space:]]+import([[:space:]]|$)' "$python_abs"; then
resolve_twp_config_asset "$python_dir" "$source" "python import" "twp_vismach.py"
fi
if grep -F 'work_piece_1.stl' "$python_abs" >/dev/null; then
resolve_twp_config_asset "$config_dir" "$source" "vismach STL" "work_piece_1.stl"
fi
done
done
mapfile -t twp_config_asset_sources < <(
printf '%s\n' "${twp_config_asset_sources[@]}" | sed '/^$/d' | sort -u
)
mapfile -t unresolved_twp_config_asset_sources < <(
printf '%s\n' "${unresolved_twp_config_asset_sources[@]}" | sed '/^$/d' | sort -u
)
if ((${#unresolved_twp_config_asset_sources[@]} > 0)); then
echo "LinuxCNC TWP INI source assets could not be resolved: $manifest" >&2
printf 'unresolved TWP config asset: %s\n' "${unresolved_twp_config_asset_sources[@]}" >&2
exit 1
fi
missing_twp_config_asset_sources=()
for source in "${twp_config_asset_sources[@]}"; do
if ! printf '%s\n' "${manifest_asset_sources[@]}" | grep -Fx -- "$source" >/dev/null; then
missing_twp_config_asset_sources+=("$source")
fi
done
if ((${#missing_twp_config_asset_sources[@]} > 0)); then
echo "manifest does not cover all LinuxCNC TWP INI source assets as asset sources: $manifest" >&2
printf 'missing TWP config asset source: %s\n' "${missing_twp_config_asset_sources[@]}" >&2
exit 1
fi
fi