231 lines
8.2 KiB
Bash
Executable file
231 lines
8.2 KiB
Bash
Executable file
#!/usr/bin/env bash
|
||
set -euo pipefail
|
||
|
||
# tidy_build.sh
|
||
# Unified developer helper for YAMS (Meson-first)
|
||
# - Mode 1 (default): Meson build + (optional) test + conveniences
|
||
# - Mode 2: clang-tidy focused run (legacy behavior via --tidy, CMake-based)
|
||
#
|
||
# This replaces the earlier single‑purpose clang‑tidy wrapper by adding:
|
||
# * Optional clean of build preset
|
||
# * Conan install (unless --no-conan)
|
||
# * Configure + build chosen preset
|
||
# * Mock embedding/provider mode (--mock) to bypass ONNX runtime
|
||
# * Test selection via -R regex
|
||
# * Summaries & colored logging
|
||
# * Retains original clang‑tidy flow via --tidy (subcommand)
|
||
#
|
||
# Usage:
|
||
# scripts/dev/tidy_build.sh [options] # build + test path
|
||
# scripts/dev/tidy_build.sh --tidy [preset] # legacy clang-tidy mode
|
||
#
|
||
# Common Options (build+test mode):
|
||
# -p, --preset NAME Build preset: yams-debug|yams-release (default: yams-debug)
|
||
# --release Shortcut for --preset yams-release
|
||
# -c, --clean Remove build/<preset> before configure
|
||
# --no-conan Skip conan install
|
||
# --no-strict Do NOT pass -Dwerror=true and highest warning level
|
||
# --strict Force -Dwerror=true and warning level 3 (default)
|
||
# --no-tests Alias of --no-test (deprecated)
|
||
# -r, --regex REGEX GTest filter (mapped to --test-args --gtest_filter=REGEX)
|
||
# --no-test Skip running tests
|
||
# --mock Enable mock embedding/provider env (YAMS_USE_MOCK_PROVIDER=1 etc.)
|
||
# -j, --jobs N Parallel build jobs override
|
||
# -- Stop parsing; rest passed to ctest
|
||
# -h, --help Show help
|
||
#
|
||
# Environment overrides:
|
||
# YAMS_TIDY_CONAN_OPTS Extra args for conan install
|
||
# YAMS_TIDY_CTEST_OPTS Extra args appended to ctest invocation
|
||
#
|
||
# Exit codes:
|
||
# 0 success (or skipped tests)
|
||
# 1 failure
|
||
# 2 usage error
|
||
|
||
color() { local c="$1"; shift; printf "\033[%sm%s\033[0m" "$c" "$*"; }
|
||
log() { echo "$(color 1; color 34 [tidy]) $*"; }
|
||
warn() { echo "$(color 33 [tidy]) $*" >&2; }
|
||
err() { echo "$(color 31 [tidy]) $*" >&2; }
|
||
|
||
# NOTE: The --tidy path remains CMake-based for now and is intended only for
|
||
# ad-hoc static analysis runs. The primary build path below uses Meson.
|
||
if [[ ${1:-} == "--tidy" ]]; then
|
||
shift || true
|
||
preset="${1:-yams-debug}"; shift || true
|
||
outdir="build/${preset}"
|
||
log "(tidy mode) preset=${preset}"
|
||
log_full="${outdir}/tidy_full.log"
|
||
log_errors="${outdir}/tidy_errors.log"
|
||
log_checks="${outdir}/tidy_checks.txt"
|
||
mkdir -p "${outdir}"
|
||
if [[ -n "${TIDY_ARGS:-}" ]]; then
|
||
cmake --preset "${preset}" -DCMAKE_UNITY_BUILD=OFF -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
||
-DCMAKE_C_CLANG_TIDY="${TIDY_ARGS}" -DCMAKE_CXX_CLANG_TIDY="${TIDY_ARGS}"
|
||
else
|
||
cmake --preset "${preset}" -DCMAKE_UNITY_BUILD=OFF -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
|
||
fi
|
||
log "Building (serial for clearer diagnostics)"
|
||
set +e
|
||
cmake --build --preset "${preset}" -j1 2>&1 | tee "${log_full}"
|
||
status=${PIPESTATUS[0]}
|
||
set -e
|
||
grep -E "^[^ ]*:[0-9]+:[0-9]+: error: " "${log_full}" > "${log_errors}" || true
|
||
grep -o "\[[^]]\+\]" "${log_errors}" | sort | uniq -c | sort -nr > "${log_checks}" || true
|
||
echo; log "Error summary (first 20):"; head -n 20 "${log_errors}" || true
|
||
echo; log "Check counts:"; cat "${log_checks}" || true
|
||
exit "${status}"
|
||
fi
|
||
|
||
preset="yams-debug"
|
||
regex=""
|
||
clean=0
|
||
jobs=""
|
||
run_conan=1
|
||
run_tests=1
|
||
enable_tests=1
|
||
mock=0
|
||
strict=1
|
||
extra_ctest=()
|
||
|
||
usage() { grep '^# ' "$0" | sed 's/^# //'; exit 2; }
|
||
|
||
suite="smoke" # default fast suite; use --full to run all
|
||
while [[ $# -gt 0 ]]; do
|
||
case "$1" in
|
||
-p|--preset) preset="$2"; shift 2;;
|
||
--release) preset="yams-release"; shift;;
|
||
-r|--regex) regex="$2"; shift 2;;
|
||
-c|--clean) clean=1; shift;;
|
||
-j|--jobs) jobs="$2"; shift 2;;
|
||
--no-conan) run_conan=0; shift;;
|
||
--no-test) run_tests=0; enable_tests=0; shift;;
|
||
--no-tests) run_tests=0; enable_tests=0; shift;;
|
||
--mock) mock=1; shift;;
|
||
--full) suite=""; shift;;
|
||
--with-lzma) export YAMS_ENABLE_LZMA=1; shift;;
|
||
--no-strict) strict=0; shift;;
|
||
--strict) strict=1; shift;;
|
||
-h|--help) usage;;
|
||
--) shift; extra_ctest+=("$@"); break;;
|
||
*) err "Unknown option: $1"; usage;;
|
||
esac
|
||
done
|
||
|
||
# Map preset to Meson build dir and build type
|
||
build_dir="build/${preset/yams-/}"
|
||
bt="Debug"; [[ $preset == *release* ]] && bt="Release"
|
||
bt_meson=$(printf '%s' "${bt}" | tr '[:upper:]' '[:lower:]')
|
||
|
||
if [[ $clean -eq 1 ]]; then
|
||
log "Cleaning ${build_dir}"
|
||
rm -rf "${build_dir}"
|
||
fi
|
||
|
||
if [[ $run_conan -eq 1 ]]; then
|
||
log "Conan install (build_type=${bt})"
|
||
conan install . -of "${build_dir}" -s build_type="${bt}" -b missing ${YAMS_TIDY_CONAN_OPTS:-}
|
||
fi
|
||
|
||
log "Configure (Meson: ${build_dir}, strict=${strict})"
|
||
# Resolve Conan Meson native toolchain file robustly across layouts.
|
||
# Preferred (current Conan 2 Meson toolchain):
|
||
# - ${build_dir}/build-${bt_meson}/conan/conan_meson_native.ini
|
||
# Historical/alternative layouts (fallbacks):
|
||
# - ${build_dir}/build-${bt_meson}/generators/conan_meson_native.ini
|
||
# - ${build_dir}/build/${bt}/generators/conan_meson_native.ini
|
||
# - ${build_dir}/build/${bt_meson}/generators/conan_meson_native.ini
|
||
# - ${build_dir}/build-${bt_meson}/${bt}/generators/conan_meson_native.ini
|
||
# Finally: search within ${build_dir}
|
||
candidate_native_inis=(
|
||
"${build_dir}/build-${bt_meson}/conan/conan_meson_native.ini"
|
||
"${build_dir}/build-${bt_meson}/generators/conan_meson_native.ini"
|
||
"${build_dir}/build/${bt}/generators/conan_meson_native.ini"
|
||
"${build_dir}/build/${bt_meson}/generators/conan_meson_native.ini"
|
||
"${build_dir}/build-${bt_meson}/${bt}/generators/conan_meson_native.ini"
|
||
)
|
||
native_ini=""
|
||
for cand in "${candidate_native_inis[@]}"; do
|
||
if [[ -f "${cand}" ]]; then
|
||
native_ini="${cand}"
|
||
break
|
||
fi
|
||
done
|
||
if [[ -z "${native_ini}" ]]; then
|
||
# Last-resort search (depth-limited for speed)
|
||
native_ini=$(find "${build_dir}" -maxdepth 5 -type f -name 'conan_meson_native.ini' 2>/dev/null | head -n1 || true)
|
||
fi
|
||
if [[ -z "${native_ini}" ]]; then
|
||
err "Could not locate conan_meson_native.ini under ${build_dir}. Did Conan install succeed?"
|
||
err "Try: conan install . -of '${build_dir}' -s build_type='${bt}' -b missing"
|
||
exit 1
|
||
fi
|
||
log "Using native file: ${native_ini}"
|
||
meson_args=("${build_dir}" "--native-file" "${native_ini}" "--buildtype=${bt_meson}")
|
||
if [[ ${enable_tests} -eq 1 ]]; then
|
||
meson_args+=("-Dbuild-tests=true")
|
||
else
|
||
meson_args+=("-Dbuild-tests=false")
|
||
fi
|
||
# LZMA enablement (Option A): default 'auto'; allow forcing on via --with-lzma
|
||
if [[ ${YAMS_ENABLE_LZMA:-0} -eq 1 ]]; then
|
||
meson_args+=("-Denable-lzma=true")
|
||
fi
|
||
mkdir -p "${build_dir}"
|
||
if [[ $strict -eq 1 ]]; then
|
||
meson_args+=("-Dwerror=true")
|
||
# Use highest Meson warning level (0-3). Project default is 2; raise to 3 here.
|
||
meson_args+=("-Dwarning_level=3")
|
||
fi
|
||
if [[ ! -d "${build_dir}/meson-private" ]]; then
|
||
meson setup "${meson_args[@]}"
|
||
else
|
||
meson setup --reconfigure "${meson_args[@]}"
|
||
fi
|
||
|
||
log "Build (Meson)"
|
||
if [[ -n $jobs ]]; then
|
||
MESON_BUILD_ARGS=("-j" "$jobs")
|
||
else
|
||
MESON_BUILD_ARGS=()
|
||
fi
|
||
meson compile -C "${build_dir}" ${MESON_BUILD_ARGS[@]:-}
|
||
|
||
if [[ $run_tests -eq 0 ]]; then
|
||
log "Skipping tests (--no-test)"
|
||
exit 0
|
||
fi
|
||
|
||
# Standardize test mode
|
||
export YAMS_TESTING=${YAMS_TESTING:-1}
|
||
if [[ $mock -eq 1 ]]; then
|
||
export YAMS_USE_MOCK_PROVIDER=1
|
||
export YAMS_SKIP_MODEL_LOADING=1
|
||
export YAMS_PLUGIN_TRUST_ALL=1
|
||
log "Mock embedding/provider mode enabled"
|
||
fi
|
||
|
||
# Map regex to gtest filter via --test-args
|
||
test_args=()
|
||
if [[ -n $regex ]]; then
|
||
test_args+=("--test-args" "--gtest_filter=${regex}")
|
||
fi
|
||
|
||
if [[ -z "${suite}" && -z "${regex}" ]]; then
|
||
log "Test suite: full"
|
||
run_cmd=(meson test -C "${build_dir}" ${test_args[@]:-} ${YAMS_TIDY_CTEST_OPTS:-} --print-errorlogs)
|
||
elif [[ -n "${suite}" && -z "${regex}" ]]; then
|
||
log "Test suite: ${suite} (default). Use --full for all or -r to filter."
|
||
run_cmd=(meson test -C "${build_dir}" --suite "${suite}" ${YAMS_TIDY_CTEST_OPTS:-} --print-errorlogs)
|
||
else
|
||
log "Test selection via regex: ${regex}"
|
||
run_cmd=(meson test -C "${build_dir}" ${test_args[@]:-} ${YAMS_TIDY_CTEST_OPTS:-} --print-errorlogs)
|
||
fi
|
||
|
||
log "Run: ${run_cmd[*]}"
|
||
if ! "${run_cmd[@]}"; then
|
||
err "Tests failed"
|
||
err "Hint: re-run single test -> meson test -C ${build_dir} --test-args --gtest_filter=<name> -v"
|
||
exit 1
|
||
fi
|
||
log "All tests passed"
|