#!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)" VENDOR_DIR="$ROOT_DIR/vendor/linuxcnc" BUILD_DIR="$ROOT_DIR/build/native" WRAP_DIR="$ROOT_DIR/runtime/core/linuxcnc_wrap" SHIM_DIR="$ROOT_DIR/runtime/core/shims" INCLUDE_DIR="$ROOT_DIR/runtime/core/include" MINIMAL_GCODE_FIXTURE="$ROOT_DIR/tests/fixtures/gcode/minimal_linear.ngc" NAMEDPARAM_INI_FIXTURE="$ROOT_DIR/tests/fixtures/ini/namedparams.ini" CXX="${CXX:-g++}" mkdir -p "$BUILD_DIR" COMMON_FLAGS=( -std=c++20 -O2 -D_GNU_SOURCE -DM_PI=3.14159265358979323846 -DOBJECT_FWD_DWA2002724_HPP -I"$WRAP_DIR" -I"$SHIM_DIR" -I"$INCLUDE_DIR" -I"$VENDOR_DIR/src" -I"$VENDOR_DIR/src/rtapi" -I"$VENDOR_DIR/src/emc" -I"$VENDOR_DIR/src/emc/nml_intf" -I"$VENDOR_DIR/src/emc/motion" -I"$VENDOR_DIR/src/libnml/posemath" ) COMMON_INI_FLAGS=( "${COMMON_FLAGS[@]}" -I"$VENDOR_DIR/src/emc/ini" ) MINIMAL_FLAGS=( "${COMMON_INI_FLAGS[@]}" -ffunction-sections -fdata-sections -DUNIT_TEST -DLINUXCNC_STANDALONE_USE_RS274_PRE_STATE ) MINIMAL_LINK_FLAGS=( -Wl,--gc-sections ) write_command_file() { local file="$1" shift printf '%q\n' "$@" > "$file" } command_matches() { local file="$1" shift local tmp tmp="$(mktemp)" write_command_file "$tmp" "$@" if [[ -f "$file" ]] && cmp -s "$tmp" "$file"; then rm -f "$tmp" return 0 fi rm -f "$tmp" return 1 } depfile_newer_than() { local depfile="$1" local output="$2" [[ -f "$depfile" ]] || return 0 local deps deps="$(tr '\\\n' ' ' < "$depfile" | sed -e 's/^[^:]*://')" local dep for dep in $deps; do if [[ -f "$dep" && "$dep" -nt "$output" ]]; then return 0 fi done return 1 } object_needs_rebuild() { local obj="$1" local depfile="$2" local cmdfile="$3" shift 3 [[ -f "$obj" ]] || return 0 command_matches "$cmdfile" "$@" || return 0 depfile_newer_than "$depfile" "$obj" && return 0 return 1 } compile_object() { local target="$1" local source="$2" local obj="$3" shift 3 local stdout_log="$BUILD_DIR/$target.stdout.log" local stderr_log="$BUILD_DIR/$target.stderr.log" local depfile="$obj.d" local cmdfile="$obj.cmd" local cmd=("$CXX" "$@" -MMD -MP -MF "$depfile" -c "$source" -o "$obj") mkdir -p "$(dirname "$obj")" if ! object_needs_rebuild "$obj" "$depfile" "$cmdfile" "${cmd[@]}"; then printf 'up to date: %s\n' "$source" >> "$stdout_log" return 0 fi printf 'compile: %s\n' "$source" >> "$stdout_log" if "${cmd[@]}" >> "$stdout_log" 2>> "$stderr_log"; then write_command_file "$cmdfile" "${cmd[@]}" return 0 fi return 1 } link_needs_rebuild() { local output="$1" local cmdfile="$2" shift 2 [[ -f "$output" ]] || return 0 command_matches "$cmdfile" "$@" || return 0 local arg for arg in "$@"; do if [[ -f "$arg" && "$arg" -nt "$output" ]]; then return 0 fi done return 1 } link_binary() { local target="$1" local output="$2" shift 2 local stdout_log="$BUILD_DIR/$target.stdout.log" local stderr_log="$BUILD_DIR/$target.stderr.log" local cmdfile="$output.cmd" local cmd=("$CXX" "$@" -o "$output") if ! link_needs_rebuild "$output" "$cmdfile" "${cmd[@]}"; then printf 'up to date: %s\n' "$output" >> "$stdout_log" return 0 fi printf 'link: %s\n' "$output" >> "$stdout_log" if "${cmd[@]}" >> "$stdout_log" 2>> "$stderr_log"; then write_command_file "$cmdfile" "${cmd[@]}" return 0 fi return 1 } compile_target_objects() { local target="$1" local flags_name="$2" local sources_name="$3" local objs_name="$4" local -n flags="$flags_name" local -n sources="$sources_name" local -n objs="$objs_name" local obj_dir="$BUILD_DIR/obj/$target" local status=0 objs=() local source for source in "${sources[@]}"; do local base base="$(basename "$source")" local obj="$obj_dir/${base%.*}.o" objs+=("$obj") if ! compile_object "$target" "$source" "$obj" "${flags[@]}"; then status=1 fi done return "$status" } build_binary_target() { local target="$1" local output="$2" local flags_name="$3" local sources_name="$4" local link_flags_name="$5" shift 5 local stdout_log="$BUILD_DIR/$target.stdout.log" local stderr_log="$BUILD_DIR/$target.stderr.log" : > "$stdout_log" : > "$stderr_log" local target_objs=() local status=0 if ! compile_target_objects "$target" "$flags_name" "$sources_name" target_objs; then status=1 fi if [[ "$status" -eq 0 ]]; then local -n link_flags="$link_flags_name" if ! link_binary "$target" "$output" "${link_flags[@]}" "${target_objs[@]}" "$@"; then status=1 fi fi echo "$status" > "$BUILD_DIR/$target.exitcode" return "$status" } build_object_target() { local target="$1" local output="$2" local source="$3" local flags_name="$4" local stdout_log="$BUILD_DIR/$target.stdout.log" local stderr_log="$BUILD_DIR/$target.stderr.log" : > "$stdout_log" : > "$stderr_log" local -n flags="$flags_name" local status=0 if ! compile_object "$target" "$source" "$output" "${flags[@]}"; then status=1 fi echo "$status" > "$BUILD_DIR/$target.exitcode" return "$status" } NO_LINK_FLAGS=() INI_PROBE_SOURCES=( "$VENDOR_DIR/src/emc/ini/inifile.cc" "$WRAP_DIR/linuxcnc_ini_probe.cpp" ) STATE_PROBE_SOURCES=( "$WRAP_DIR/linuxcnc_interp_state_probe.cpp" ) INTERP_CORE_SOURCES=( "$VENDOR_DIR/src/emc/rs274ngc/modal_state.cc" "$VENDOR_DIR/src/emc/rs274ngc/interp_array.cc" "$VENDOR_DIR/src/emc/rs274ngc/interp_internal.cc" "$VENDOR_DIR/src/emc/rs274ngc/interp_read.cc" "$VENDOR_DIR/src/emc/rs274ngc/interp_check.cc" "$VENDOR_DIR/src/emc/rs274ngc/interp_arc.cc" "$VENDOR_DIR/src/emc/rs274ngc/interp_inverse.cc" "$VENDOR_DIR/src/emc/rs274ngc/interp_execute.cc" "$VENDOR_DIR/src/emc/rs274ngc/interp_convert.cc" "$VENDOR_DIR/src/emc/rs274ngc/interp_cycles.cc" "$VENDOR_DIR/src/emc/rs274ngc/interp_g7x.cc" "$VENDOR_DIR/src/emc/rs274ngc/interp_queue.cc" "$VENDOR_DIR/src/emc/rs274ngc/interp_find.cc" "$VENDOR_DIR/src/emc/rs274ngc/interp_namedparams.cc" "$VENDOR_DIR/src/emc/rs274ngc/interp_write.cc" "$VENDOR_DIR/src/emc/rs274ngc/interp_o_word.cc" "$VENDOR_DIR/src/emc/rs274ngc/rs274ngc_pre.cc" "$WRAP_DIR/linuxcnc_hal_adapter.cpp" "$WRAP_DIR/linuxcnc_interp_edge_stubs.cpp" "$WRAP_DIR/linuxcnc_runtime_state_stubs.cpp" "$WRAP_DIR/linuxcnc_tool_adapter.cpp" "$WRAP_DIR/linuxcnc_interp_minimal_runtime.cpp" "$VENDOR_DIR/src/emc/ini/inifile.cc" ) NAMEDPARAM_SOURCES=( "${INTERP_CORE_SOURCES[@]}" "$WRAP_DIR/linuxcnc_namedparam_harness.cpp" ) MINIMAL_SOURCES=( "${INTERP_CORE_SOURCES[@]}" "$WRAP_DIR/linuxcnc_interp_minimal_harness.cpp" ) PARAMETER_FILE_SOURCES=( "${INTERP_CORE_SOURCES[@]}" "$WRAP_DIR/linuxcnc_parameter_file_harness.cpp" ) build_binary_target \ linuxcnc_ini_probe \ "$BUILD_DIR/linuxcnc_ini_probe" \ COMMON_FLAGS \ INI_PROBE_SOURCES \ NO_LINK_FLAGS \ -lfmt build_binary_target \ linuxcnc_interp_state_probe \ "$BUILD_DIR/linuxcnc_interp_state_probe" \ COMMON_FLAGS \ STATE_PROBE_SOURCES \ NO_LINK_FLAGS build_binary_target \ linuxcnc_namedparam_harness \ "$BUILD_DIR/linuxcnc_namedparam_harness" \ MINIMAL_FLAGS \ NAMEDPARAM_SOURCES \ MINIMAL_LINK_FLAGS \ -lfmt if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_namedparam_harness.exitcode")" == "0" ]]; then set +e "$BUILD_DIR/linuxcnc_namedparam_harness" "$NAMEDPARAM_INI_FIXTURE" \ >"$BUILD_DIR/linuxcnc_namedparam_harness.run.stdout.log" \ 2>"$BUILD_DIR/linuxcnc_namedparam_harness.run.stderr.log" NAMEDPARAM_RUN_RC=$? set -e echo "$NAMEDPARAM_RUN_RC" > "$BUILD_DIR/linuxcnc_namedparam_harness.run.exitcode" else rm -f \ "$BUILD_DIR/linuxcnc_namedparam_harness.run.exitcode" \ "$BUILD_DIR/linuxcnc_namedparam_harness.run.stdout.log" \ "$BUILD_DIR/linuxcnc_namedparam_harness.run.stderr.log" fi build_binary_target \ linuxcnc_interp_minimal_harness \ "$BUILD_DIR/linuxcnc_interp_minimal_harness" \ MINIMAL_FLAGS \ MINIMAL_SOURCES \ MINIMAL_LINK_FLAGS \ -lfmt if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_interp_minimal_harness.exitcode")" == "0" ]]; then set +e "$BUILD_DIR/linuxcnc_interp_minimal_harness" "$MINIMAL_GCODE_FIXTURE" \ >"$BUILD_DIR/linuxcnc_interp_minimal_harness.run.stdout.log" \ 2>"$BUILD_DIR/linuxcnc_interp_minimal_harness.run.stderr.log" INTERP_MIN_RUN_RC=$? set -e echo "$INTERP_MIN_RUN_RC" > "$BUILD_DIR/linuxcnc_interp_minimal_harness.run.exitcode" else rm -f \ "$BUILD_DIR/linuxcnc_interp_minimal_harness.run.exitcode" \ "$BUILD_DIR/linuxcnc_interp_minimal_harness.run.stdout.log" \ "$BUILD_DIR/linuxcnc_interp_minimal_harness.run.stderr.log" fi build_binary_target \ linuxcnc_parameter_file_harness \ "$BUILD_DIR/linuxcnc_parameter_file_harness" \ MINIMAL_FLAGS \ PARAMETER_FILE_SOURCES \ MINIMAL_LINK_FLAGS \ -lfmt if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_parameter_file_harness.exitcode")" == "0" ]]; then set +e "$BUILD_DIR/linuxcnc_parameter_file_harness" \ >"$BUILD_DIR/linuxcnc_parameter_file_harness.run.stdout.log" \ 2>"$BUILD_DIR/linuxcnc_parameter_file_harness.run.stderr.log" PARAMETER_FILE_RUN_RC=$? set -e echo "$PARAMETER_FILE_RUN_RC" > "$BUILD_DIR/linuxcnc_parameter_file_harness.run.exitcode" else rm -f \ "$BUILD_DIR/linuxcnc_parameter_file_harness.run.exitcode" \ "$BUILD_DIR/linuxcnc_parameter_file_harness.run.stdout.log" \ "$BUILD_DIR/linuxcnc_parameter_file_harness.run.stderr.log" fi build_object_target \ linuxcnc_rs274_compile_probe \ "$BUILD_DIR/linuxcnc_rs274_compile_probe.o" \ "$WRAP_DIR/linuxcnc_rs274_compile_probe.cpp" \ COMMON_FLAGS build_object_target \ linuxcnc_interp_convert_source_probe \ "$BUILD_DIR/linuxcnc_interp_convert_source_probe.o" \ "$VENDOR_DIR/src/emc/rs274ngc/interp_convert.cc" \ COMMON_FLAGS echo "native probes complete"