missing paths where feasible, fail fast where support is not real, and sync docs with live behavior. - add vector/metadata and buffer extraction coverage - narrow storage and s3/plugin loader contracts - refresh pipeline, build, and test docs
245 lines
7.9 KiB
Bash
Executable file
245 lines
7.9 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
cd "$REPO_ROOT"
|
|
|
|
if [[ "${YAMS_SKIP_COVERAGE_HOOK:-0}" == "1" ]]; then
|
|
echo "[coverage] YAMS_SKIP_COVERAGE_HOOK=1; skipping local coverage gate" >&2
|
|
exit 0
|
|
fi
|
|
|
|
case "$(uname -s)" in
|
|
Linux | Darwin) ;;
|
|
*)
|
|
echo "[coverage] unsupported platform for tracked coverage hook; skipping" >&2
|
|
exit 0
|
|
;;
|
|
esac
|
|
|
|
for tool in bash git python3 gcovr; do
|
|
if ! command -v "$tool" >/dev/null 2>&1; then
|
|
echo "[coverage] required tool '$tool' is missing; install it or set YAMS_SKIP_COVERAGE_HOOK=1 to bypass temporarily" >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
changed_tmp="$(mktemp)"
|
|
trap 'rm -f "$changed_tmp"' EXIT
|
|
had_input=0
|
|
|
|
collect_changed() {
|
|
local range="$1"
|
|
local -a diff_args=()
|
|
if [[ -n "$range" ]]; then
|
|
diff_args+=("$range")
|
|
fi
|
|
git diff --name-only --diff-filter=ACMRT "${diff_args[@]}" |
|
|
grep -E '(\.(c|cc|cpp|cxx|h|hh|hpp)$|(^|/)(meson\.build|meson_options\.txt|setup\.sh|setup\.ps1)$)' \
|
|
>>"$changed_tmp" || true
|
|
}
|
|
|
|
while IFS=' ' read -r local_ref local_sha remote_ref remote_sha; do
|
|
had_input=1
|
|
[[ -z "${local_sha:-}" ]] && continue
|
|
if [[ "$local_sha" =~ ^0+$ ]]; then
|
|
continue
|
|
fi
|
|
if [[ -n "${remote_sha:-}" && ! "$remote_sha" =~ ^0+$ ]]; then
|
|
collect_changed "${remote_sha}..${local_sha}"
|
|
elif git rev-parse "${local_sha}^" >/dev/null 2>&1; then
|
|
collect_changed "${local_sha}^..${local_sha}"
|
|
else
|
|
collect_changed "$local_sha"
|
|
fi
|
|
done
|
|
|
|
if [[ "$had_input" == "0" ]]; then
|
|
if git rev-parse --abbrev-ref --symbolic-full-name '@{upstream}' >/dev/null 2>&1; then
|
|
upstream_ref="$(git rev-parse --abbrev-ref --symbolic-full-name '@{upstream}')"
|
|
collect_changed "${upstream_ref}..HEAD"
|
|
elif git rev-parse HEAD^ >/dev/null 2>&1; then
|
|
collect_changed 'HEAD^..HEAD'
|
|
else
|
|
collect_changed 'HEAD'
|
|
fi
|
|
fi
|
|
|
|
changed=()
|
|
while IFS= read -r path; do
|
|
[[ -n "$path" ]] && changed+=("$path")
|
|
done < <(sort -u "$changed_tmp")
|
|
|
|
if [[ ${#changed[@]} -eq 0 ]]; then
|
|
echo "[coverage] no pushed C/C++ or Meson/build-script changes; skipping" >&2
|
|
exit 0
|
|
fi
|
|
|
|
echo "[coverage] running local coverage gate for ${#changed[@]} changed file(s)" >&2
|
|
printf ' %s\n' "${changed[@]}" >&2
|
|
|
|
BUILD_DIR="${YAMS_COVERAGE_BUILD_DIR:-build/coverage}"
|
|
JOBS="${YAMS_COVERAGE_HOOK_JOBS:-4}"
|
|
TEST_ARGS=(--suite unit --print-errorlogs --timeout-multiplier 2)
|
|
if [[ "$BUILD_DIR" != "build/coverage" ]]; then
|
|
echo "[coverage] custom build dir '$BUILD_DIR' selected; warm cache comparisons only make sense when reusing the same build dir because gcov metadata stays build-dir specific" >&2
|
|
fi
|
|
if [[ "${YAMS_COVERAGE_INCLUDE_INTEGRATION:-0}" == "1" ]]; then
|
|
echo "[coverage] including integration suite (excluding isolated smoke)" >&2
|
|
TEST_ARGS+=(--suite integration --no-suite isolated)
|
|
else
|
|
echo "[coverage] unit-only fast path; set YAMS_COVERAGE_INCLUDE_INTEGRATION=1 for integration coverage" >&2
|
|
fi
|
|
|
|
export YAMS_BUILD_DIR="$BUILD_DIR"
|
|
export YAMS_ENABLE_MOBILE_BINDINGS=false
|
|
export YAMS_EXTRA_MESON_FLAGS="${YAMS_COVERAGE_MESON_FLAGS:--Dbuild-tests=true -Denable-onnx=disabled -Denable-bench-tests=false -Dbuild-benchmarks=false}"
|
|
# Keep the tracked coverage lane sanitizer-free. setup.sh preserves ENABLE_TSAN /
|
|
# ENABLE_ASAN from the parent shell, and reusing a builddir configured with TSAN
|
|
# or ASAN can destabilize gcov/gcda writeout on macOS.
|
|
export ENABLE_TSAN=false
|
|
export ENABLE_ASAN=false
|
|
if [[ -z "${SOURCE_DATE_EPOCH:-}" ]]; then
|
|
if source_date_epoch="$(git log -1 --format=%ct HEAD 2>/dev/null)" && [[ -n "$source_date_epoch" ]]; then
|
|
export SOURCE_DATE_EPOCH="$source_date_epoch"
|
|
echo "[coverage] using SOURCE_DATE_EPOCH=$SOURCE_DATE_EPOCH for reproducible version metadata" >&2
|
|
fi
|
|
fi
|
|
|
|
CCACHE_BIN="$(command -v ccache || true)"
|
|
C_COMPILER="$(command -v cc || true)"
|
|
CPP_COMPILER="$(command -v c++ || true)"
|
|
if command -v clang >/dev/null 2>&1 && command -v clang++ >/dev/null 2>&1; then
|
|
C_COMPILER="$(command -v clang)"
|
|
CPP_COMPILER="$(command -v clang++)"
|
|
fi
|
|
|
|
mkdir -p "$BUILD_DIR"
|
|
CCACHE_STATS_FILE="$BUILD_DIR/ccache-stats.txt"
|
|
GCOVR_SUMMARY_FILE="$BUILD_DIR/gcovr-summary.txt"
|
|
if [[ -n "$CCACHE_BIN" ]]; then
|
|
export CCACHE_BASEDIR="${CCACHE_BASEDIR:-$REPO_ROOT}"
|
|
export CCACHE_NOHASHDIR="${CCACHE_NOHASHDIR:-1}"
|
|
echo "[coverage] ccache path normalization enabled (base_dir=$CCACHE_BASEDIR, hash_dir=off)" >&2
|
|
fi
|
|
|
|
FAST_LINKER=""
|
|
case "$(uname -s)" in
|
|
Darwin)
|
|
if command -v ld.lld >/dev/null 2>&1; then
|
|
FAST_LINKER="lld"
|
|
fi
|
|
;;
|
|
*)
|
|
if command -v mold >/dev/null 2>&1; then
|
|
FAST_LINKER="mold"
|
|
elif command -v ld.lld >/dev/null 2>&1; then
|
|
FAST_LINKER="lld"
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
LOCAL_NATIVE_FILE="$BUILD_DIR/local-coverage-native.ini"
|
|
if [[ -d "$BUILD_DIR" && (-n "$CCACHE_BIN" || -n "$FAST_LINKER") ]]; then
|
|
toolchain_mismatch=0
|
|
if [[ ! -f "$BUILD_DIR/build.ninja" ]]; then
|
|
toolchain_mismatch=1
|
|
else
|
|
if [[ -n "$CCACHE_BIN" ]] && ! rg -q -- '(^|[[:space:]/])ccache([[:space:]]|$)' "$BUILD_DIR/build.ninja"; then
|
|
toolchain_mismatch=1
|
|
fi
|
|
if [[ -n "$FAST_LINKER" ]] && ! rg -q -- "-fuse-ld=$FAST_LINKER" "$BUILD_DIR/build.ninja"; then
|
|
toolchain_mismatch=1
|
|
fi
|
|
fi
|
|
if [[ "$toolchain_mismatch" == "1" ]]; then
|
|
echo "[coverage] removing stale build dir so Meson can apply compiler/linker overrides at initial setup" >&2
|
|
rm -rf "$BUILD_DIR"
|
|
fi
|
|
fi
|
|
|
|
INTRO_BUILDOPTIONS_JSON="$BUILD_DIR/meson-info/intro-buildoptions.json"
|
|
if [[ -f "$INTRO_BUILDOPTIONS_JSON" ]]; then
|
|
profile_mismatch="$({
|
|
python3 - "$INTRO_BUILDOPTIONS_JSON" <<'PY'
|
|
import json
|
|
import sys
|
|
|
|
with open(sys.argv[1], 'r', encoding='utf-8') as fh:
|
|
options = json.load(fh)
|
|
|
|
values = {}
|
|
for option in options:
|
|
values.setdefault(option.get('name'), []).append(option.get('value'))
|
|
|
|
issues = []
|
|
if any(value not in (None, '', 'none', []) for value in values.get('b_sanitize', [])):
|
|
issues.append(f"b_sanitize={values.get('b_sanitize')}")
|
|
if any(value in (True, 'true') for value in values.get('enable-tsan', [])):
|
|
issues.append('enable-tsan=true')
|
|
if any(value in (True, 'true') for value in values.get('enable-asan', [])):
|
|
issues.append('enable-asan=true')
|
|
coverage_values = values.get('b_coverage', [])
|
|
if coverage_values and not any(value in (True, 'true') for value in coverage_values):
|
|
issues.append(f"b_coverage={coverage_values}")
|
|
|
|
print('; '.join(issues))
|
|
PY
|
|
} || true)"
|
|
if [[ -n "$profile_mismatch" ]]; then
|
|
echo "[coverage] removing stale build dir due to coverage profile drift ($profile_mismatch)" >&2
|
|
rm -rf "$BUILD_DIR"
|
|
fi
|
|
fi
|
|
|
|
if [[ -n "$CCACHE_BIN" || -n "$FAST_LINKER" ]]; then
|
|
mkdir -p "$BUILD_DIR"
|
|
{
|
|
echo "[binaries]"
|
|
if [[ -n "$CCACHE_BIN" ]]; then
|
|
printf "c = ['%s', '%s']\n" "$CCACHE_BIN" "$C_COMPILER"
|
|
printf "cpp = ['%s', '%s']\n" "$CCACHE_BIN" "$CPP_COMPILER"
|
|
else
|
|
printf "c = '%s'\n" "$C_COMPILER"
|
|
printf "cpp = '%s'\n" "$CPP_COMPILER"
|
|
fi
|
|
if [[ -n "$FAST_LINKER" ]]; then
|
|
echo "[built-in options]"
|
|
printf "c_link_args = ['-fuse-ld=%s']\n" "$FAST_LINKER"
|
|
printf "cpp_link_args = ['-fuse-ld=%s']\n" "$FAST_LINKER"
|
|
fi
|
|
} >"$LOCAL_NATIVE_FILE"
|
|
export YAMS_EXTRA_MESON_NATIVE_FILES="$LOCAL_NATIVE_FILE"
|
|
echo "[coverage] applying compiler/linker override via initial Meson setup ($LOCAL_NATIVE_FILE)" >&2
|
|
fi
|
|
|
|
./setup.sh Debug --coverage
|
|
|
|
MESON_BIN="meson"
|
|
if [[ -x "$BUILD_DIR/mesonw" ]]; then
|
|
MESON_BIN="$BUILD_DIR/mesonw"
|
|
fi
|
|
|
|
if [[ -n "$CCACHE_BIN" ]]; then
|
|
ccache -z >/dev/null 2>&1 || true
|
|
fi
|
|
"$MESON_BIN" compile -C "$BUILD_DIR" -j"$JOBS"
|
|
if command -v ccache >/dev/null 2>&1; then
|
|
echo "[coverage] ccache stats after compile:" >&2
|
|
ccache -s | tee "$CCACHE_STATS_FILE" >&2 || true
|
|
fi
|
|
# Coverage counters are build-dir local state. Clear stale *.gcda files before the
|
|
# test run so changed sources do not try to merge against incompatible counter
|
|
# layouts from an older build.
|
|
find "$BUILD_DIR" -name '*.gcda' -delete
|
|
"$MESON_BIN" test -C "$BUILD_DIR" "${TEST_ARGS[@]}"
|
|
|
|
gcovr \
|
|
--root "$REPO_ROOT" \
|
|
--object-directory "$BUILD_DIR" \
|
|
--exclude 'tests/.*' \
|
|
--exclude 'third_party/.*' \
|
|
--merge-mode-functions merge-use-line-min \
|
|
--txt-summary | tee "$GCOVR_SUMMARY_FILE"
|
|
|
|
echo "[coverage] local coverage gate passed" >&2
|