118 lines
3.6 KiB
Bash
118 lines
3.6 KiB
Bash
#!/usr/bin/env bash
|
|
#
|
|
# xo-cmake-llvmcov-harness:
|
|
# clang/llvm source-based coverage driver.
|
|
# Parallel to xo-cmake-lcov-harness (gcov path).
|
|
#
|
|
# Pipeline:
|
|
# 1. run ctest with LLVM_PROFILE_FILE pointing into ${builddir}/ccov/raw
|
|
# 2. merge the produced .profraw files to ${outputstem}.profdata
|
|
# 3. read ${builddir}/ccov/utest-binaries.list (written by xo_umbrella_coverage_config)
|
|
# 4. emit browsable HTML under ${builddir}/ccov/html
|
|
# 5. print summary table to stdout
|
|
#
|
|
|
|
set -euo pipefail
|
|
|
|
srcdir=${1:-}
|
|
builddir=${2:-}
|
|
outputstem=${3:-}
|
|
|
|
if [[ -z "${srcdir}" || -z "${builddir}" ]]; then
|
|
echo "usage: $0 srcdir builddir [outputstem]" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ -z "${outputstem}" ]]; then
|
|
outputstem=${builddir}/ccov/out
|
|
fi
|
|
|
|
llvm_profdata=@LLVM_PROFDATA_EXECUTABLE@
|
|
llvm_cov=@LLVM_COV_EXECUTABLE@
|
|
|
|
for kv in "llvm-profdata:${llvm_profdata}" "llvm-cov:${llvm_cov}"; do
|
|
name=${kv%%:*}
|
|
path=${kv#*:}
|
|
if [[ -z "${path}" || "${path}" == *NOTFOUND ]]; then
|
|
echo "xo-cmake-llvmcov-harness: ${name} not found during xo-cmake build/install" >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
manifest=${builddir}/ccov/utest-binaries.list
|
|
if [[ ! -f "${manifest}" ]]; then
|
|
echo "xo-cmake-llvmcov-harness: missing ${manifest}" >&2
|
|
echo "xo-cmake-llvmcov-harness: expected xo_umbrella_coverage_config() to produce it" >&2
|
|
exit 1
|
|
fi
|
|
|
|
rawdir=${builddir}/ccov/raw
|
|
htmldir=${builddir}/ccov/html
|
|
mkdir -p "${rawdir}" "${htmldir}"
|
|
|
|
# clear prior raws so we don't mix stale data from a past run
|
|
rm -f "${rawdir}"/*.profraw
|
|
|
|
# 1. run tests. LLVM_PROFILE_FILE pattern: %h=hostname %m=binary-signature %p=pid
|
|
# keeps output unique across parallel tests and across binaries.
|
|
#
|
|
# tolerate ctest failures: a crashing/asserting utest still emits .profraw
|
|
# before dying, and we want a coverage report even when some tests are broken.
|
|
# Surface the exit code at the end.
|
|
ctest_rc=0
|
|
(cd "${builddir}" && \
|
|
LLVM_PROFILE_FILE="${rawdir}/%h-%m-%p.profraw" \
|
|
ctest --output-on-failure) || ctest_rc=$?
|
|
|
|
# 2. merge .profraw -> .profdata
|
|
shopt -s nullglob
|
|
raws=("${rawdir}"/*.profraw)
|
|
if [[ ${#raws[@]} -eq 0 ]]; then
|
|
echo "xo-cmake-llvmcov-harness: no .profraw produced - was the build instrumented?" >&2
|
|
exit 1
|
|
fi
|
|
"${llvm_profdata}" merge -sparse "${raws[@]}" -o "${outputstem}.profdata"
|
|
|
|
# 3. read binary list. drop blank lines and anything not executable
|
|
# (target may be in manifest but unbuilt if 'ninja ccov' was invoked
|
|
# before a full build; defensive).
|
|
readarray -t all_bins < "${manifest}"
|
|
bins=()
|
|
for b in "${all_bins[@]}"; do
|
|
[[ -n "${b}" && -x "${b}" ]] && bins+=("${b}")
|
|
done
|
|
if [[ ${#bins[@]} -eq 0 ]]; then
|
|
echo "xo-cmake-llvmcov-harness: no executable utests found in manifest" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# llvm-cov takes the first binary positionally; the rest via -object.
|
|
first=${bins[0]}
|
|
objargs=()
|
|
if [[ ${#bins[@]} -gt 1 ]]; then
|
|
for b in "${bins[@]:1}"; do objargs+=(-object "${b}"); done
|
|
fi
|
|
|
|
# exclude test code and toolchain headers from the report
|
|
ignore_re='/utest/|/nix/store/'
|
|
|
|
# 4. HTML
|
|
"${llvm_cov}" show \
|
|
-instr-profile="${outputstem}.profdata" \
|
|
-format=html \
|
|
-output-dir="${htmldir}" \
|
|
-show-line-counts-or-regions \
|
|
-ignore-filename-regex="${ignore_re}" \
|
|
"${first}" "${objargs[@]}"
|
|
|
|
# 5. summary to stdout
|
|
"${llvm_cov}" report \
|
|
-instr-profile="${outputstem}.profdata" \
|
|
-ignore-filename-regex="${ignore_re}" \
|
|
"${first}" "${objargs[@]}"
|
|
|
|
echo
|
|
echo "xo-cmake-llvmcov-harness: HTML report -> ${htmldir}/index.html"
|
|
if [[ ${ctest_rc} -ne 0 ]]; then
|
|
echo "xo-cmake-llvmcov-harness: WARNING ctest exited ${ctest_rc} - coverage is from successful tests only" >&2
|
|
fi
|