gcovr 覆盖率报告通用 Bat 与 Shell 脚本
本文记录 gcovr 生成覆盖率报告的通用脚本模板,分别提供 Shell 和 Windows Bat 版本。脚本适合 CMake、CTest、GoogleTest 项目,路径和关键参数都做成变量,便于在不同工程中复用。
gcovr 可以直接生成文本、HTML、XML、Cobertura 等报告,CI 集成通常比 lcov + genhtml 更简单。
Shell 脚本
#!/usr/bin/env bash
set -euo pipefail
# =========================
# User configurable options
# =========================
PROJECT_ROOT="${PROJECT_ROOT:-$(pwd)}"
BUILD_DIR="${BUILD_DIR:-${PROJECT_ROOT}/build-coverage}"
REPORT_DIR="${REPORT_DIR:-${PROJECT_ROOT}/coverage-gcovr}"
CMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE:-Debug}"
COVERAGE_OPTION_NAME="${COVERAGE_OPTION_NAME:-ENABLE_COVERAGE}"
COVERAGE_OPTION_VALUE="${COVERAGE_OPTION_VALUE:-ON}"
CMAKE_BIN="${CMAKE_BIN:-cmake}"
CTEST_BIN="${CTEST_BIN:-ctest}"
GCOVR_BIN="${GCOVR_BIN:-gcovr}"
GCOV_TOOL="${GCOV_TOOL:-gcov}"
CTEST_LABEL="${CTEST_LABEL:-}"
CTEST_PARALLEL="${CTEST_PARALLEL:-}"
HTML_REPORT="${HTML_REPORT:-${REPORT_DIR}/coverage.html}"
XML_REPORT="${XML_REPORT:-${REPORT_DIR}/coverage.xml}"
JSON_REPORT="${JSON_REPORT:-${REPORT_DIR}/coverage.json}"
TXT_REPORT="${TXT_REPORT:-${REPORT_DIR}/coverage.txt}"
ENABLE_BRANCH_COVERAGE="${ENABLE_BRANCH_COVERAGE:-ON}"
FAIL_UNDER_LINE="${FAIL_UNDER_LINE:-}"
FAIL_UNDER_BRANCH="${FAIL_UNDER_BRANCH:-}"
GCOVR_FILTERS=(
"${PROJECT_ROOT}/src/.*"
"${PROJECT_ROOT}/include/.*"
)
GCOVR_EXCLUDES=(
".*/tests/.*"
".*/test/.*"
".*/third_party/.*"
".*/_deps/.*"
".*/CMakeFiles/.*"
)
# =========================
# Internal helpers
# =========================
require_command() {
if ! command -v "$1" >/dev/null 2>&1; then
echo "error: command not found: $1" >&2
exit 1
fi
}
run_ctest() {
local args=(--test-dir "${BUILD_DIR}" --output-on-failure)
if [[ -n "${CTEST_LABEL}" ]]; then
args+=(-L "${CTEST_LABEL}")
fi
if [[ -n "${CTEST_PARALLEL}" ]]; then
args+=(-j "${CTEST_PARALLEL}")
fi
"${CTEST_BIN}" "${args[@]}"
}
gcovr_args=(
--root "${PROJECT_ROOT}"
"${BUILD_DIR}"
--print-summary
--gcov-executable "${GCOV_TOOL}"
)
for pattern in "${GCOVR_FILTERS[@]}"; do
gcovr_args+=(--filter "${pattern}")
done
for pattern in "${GCOVR_EXCLUDES[@]}"; do
gcovr_args+=(--exclude "${pattern}")
done
if [[ "${ENABLE_BRANCH_COVERAGE}" == "ON" ]]; then
gcovr_args+=(--branches)
fi
if [[ -n "${FAIL_UNDER_LINE}" ]]; then
gcovr_args+=(--fail-under-line "${FAIL_UNDER_LINE}")
fi
if [[ -n "${FAIL_UNDER_BRANCH}" ]]; then
gcovr_args+=(--fail-under-branch "${FAIL_UNDER_BRANCH}")
fi
# =========================
# Main flow
# =========================
require_command "${CMAKE_BIN}"
require_command "${CTEST_BIN}"
require_command "${GCOVR_BIN}"
mkdir -p "${REPORT_DIR}"
"${CMAKE_BIN}" -S "${PROJECT_ROOT}" -B "${BUILD_DIR}" \
-DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" \
-D"${COVERAGE_OPTION_NAME}"="${COVERAGE_OPTION_VALUE}"
"${CMAKE_BIN}" --build "${BUILD_DIR}" --parallel
run_ctest
"${GCOVR_BIN}" "${gcovr_args[@]}" \
--html --html-details \
--output "${HTML_REPORT}"
"${GCOVR_BIN}" "${gcovr_args[@]}" \
--xml-pretty \
--output "${XML_REPORT}"
"${GCOVR_BIN}" "${gcovr_args[@]}" \
--json \
--output "${JSON_REPORT}"
"${GCOVR_BIN}" "${gcovr_args[@]}" \
--txt \
--output "${TXT_REPORT}"
echo "gcovr html report: ${HTML_REPORT}"
echo "gcovr xml report : ${XML_REPORT}"
echo "gcovr json report: ${JSON_REPORT}"
echo "gcovr txt report : ${TXT_REPORT}"
Bat 脚本
@echo off
setlocal EnableExtensions EnableDelayedExpansion
rem =========================
rem User configurable options
rem =========================
if "%PROJECT_ROOT%"=="" set "PROJECT_ROOT=%CD%"
if "%BUILD_DIR%"=="" set "BUILD_DIR=%PROJECT_ROOT%\build-coverage"
if "%REPORT_DIR%"=="" set "REPORT_DIR=%PROJECT_ROOT%\coverage-gcovr"
if "%CMAKE_BUILD_TYPE%"=="" set "CMAKE_BUILD_TYPE=Debug"
if "%COVERAGE_OPTION_NAME%"=="" set "COVERAGE_OPTION_NAME=ENABLE_COVERAGE"
if "%COVERAGE_OPTION_VALUE%"=="" set "COVERAGE_OPTION_VALUE=ON"
if "%CMAKE_BIN%"=="" set "CMAKE_BIN=cmake"
if "%CTEST_BIN%"=="" set "CTEST_BIN=ctest"
if "%GCOVR_BIN%"=="" set "GCOVR_BIN=gcovr"
if "%GCOV_TOOL%"=="" set "GCOV_TOOL=gcov"
if "%HTML_REPORT%"=="" set "HTML_REPORT=%REPORT_DIR%\coverage.html"
if "%XML_REPORT%"=="" set "XML_REPORT=%REPORT_DIR%\coverage.xml"
if "%JSON_REPORT%"=="" set "JSON_REPORT=%REPORT_DIR%\coverage.json"
if "%TXT_REPORT%"=="" set "TXT_REPORT=%REPORT_DIR%\coverage.txt"
if "%ENABLE_BRANCH_COVERAGE%"=="" set "ENABLE_BRANCH_COVERAGE=ON"
rem Optional:
rem set "CTEST_LABEL=unit"
rem set "CTEST_PARALLEL=8"
rem set "FAIL_UNDER_LINE=80"
rem set "FAIL_UNDER_BRANCH=70"
rem set "GCOV_TOOL=C:\msys64\mingw64\bin\gcov.exe"
rem gcovr filters and excludes. Adjust these patterns for your project.
set "GCOVR_FILTER_1=%PROJECT_ROOT:\=/%/src/.*"
set "GCOVR_FILTER_2=%PROJECT_ROOT:\=/%/include/.*"
set "GCOVR_EXCLUDE_1=.*/tests/.*"
set "GCOVR_EXCLUDE_2=.*/test/.*"
set "GCOVR_EXCLUDE_3=.*/third_party/.*"
set "GCOVR_EXCLUDE_4=.*/_deps/.*"
set "GCOVR_EXCLUDE_5=.*/CMakeFiles/.*"
where "%CMAKE_BIN%" >nul 2>nul
if errorlevel 1 (
echo error: cmake not found
exit /b 1
)
where "%CTEST_BIN%" >nul 2>nul
if errorlevel 1 (
echo error: ctest not found
exit /b 1
)
where "%GCOVR_BIN%" >nul 2>nul
if errorlevel 1 (
echo error: gcovr not found
exit /b 1
)
where "%GCOV_TOOL%" >nul 2>nul
if errorlevel 1 (
echo error: gcov tool not found
exit /b 1
)
if not exist "%REPORT_DIR%" mkdir "%REPORT_DIR%"
"%CMAKE_BIN%" -S "%PROJECT_ROOT%" -B "%BUILD_DIR%" ^
-DCMAKE_BUILD_TYPE="%CMAKE_BUILD_TYPE%" ^
-D%COVERAGE_OPTION_NAME%="%COVERAGE_OPTION_VALUE%"
if errorlevel 1 exit /b 1
"%CMAKE_BIN%" --build "%BUILD_DIR%" --parallel
if errorlevel 1 exit /b 1
set "CTEST_ARGS=--test-dir "%BUILD_DIR%" --output-on-failure"
if not "%CTEST_LABEL%"=="" set "CTEST_ARGS=%CTEST_ARGS% -L "%CTEST_LABEL%""
if not "%CTEST_PARALLEL%"=="" set "CTEST_ARGS=%CTEST_ARGS% -j "%CTEST_PARALLEL%""
"%CTEST_BIN%" %CTEST_ARGS%
if errorlevel 1 exit /b 1
set "GCOVR_ARGS=--root "%PROJECT_ROOT%" "%BUILD_DIR%" --print-summary --gcov-executable "%GCOV_TOOL%""
set "GCOVR_ARGS=%GCOVR_ARGS% --filter "%GCOVR_FILTER_1%" --filter "%GCOVR_FILTER_2%""
set "GCOVR_ARGS=%GCOVR_ARGS% --exclude "%GCOVR_EXCLUDE_1%" --exclude "%GCOVR_EXCLUDE_2%" --exclude "%GCOVR_EXCLUDE_3%" --exclude "%GCOVR_EXCLUDE_4%" --exclude "%GCOVR_EXCLUDE_5%""
if /I "%ENABLE_BRANCH_COVERAGE%"=="ON" set "GCOVR_ARGS=%GCOVR_ARGS% --branches"
if not "%FAIL_UNDER_LINE%"=="" set "GCOVR_ARGS=%GCOVR_ARGS% --fail-under-line "%FAIL_UNDER_LINE%""
if not "%FAIL_UNDER_BRANCH%"=="" set "GCOVR_ARGS=%GCOVR_ARGS% --fail-under-branch "%FAIL_UNDER_BRANCH%""
"%GCOVR_BIN%" %GCOVR_ARGS% --html --html-details --output "%HTML_REPORT%"
if errorlevel 1 exit /b 1
"%GCOVR_BIN%" %GCOVR_ARGS% --xml-pretty --output "%XML_REPORT%"
if errorlevel 1 exit /b 1
"%GCOVR_BIN%" %GCOVR_ARGS% --json --output "%JSON_REPORT%"
if errorlevel 1 exit /b 1
"%GCOVR_BIN%" %GCOVR_ARGS% --txt --output "%TXT_REPORT%"
if errorlevel 1 exit /b 1
echo gcovr html report: %HTML_REPORT%
echo gcovr xml report : %XML_REPORT%
echo gcovr json report: %JSON_REPORT%
echo gcovr txt report : %TXT_REPORT%
endlocal
变量说明
| 变量 | 默认值 | 说明 |
|---|---|---|
PROJECT_ROOT |
当前目录 | 工程根目录 |
BUILD_DIR |
build-coverage |
覆盖率构建目录 |
REPORT_DIR |
coverage-gcovr |
gcovr 报告输出目录 |
CMAKE_BUILD_TYPE |
Debug |
CMake 构建类型 |
COVERAGE_OPTION_NAME |
ENABLE_COVERAGE |
工程覆盖率 CMake 选项名 |
COVERAGE_OPTION_VALUE |
ON |
工程覆盖率 CMake 选项值 |
CTEST_LABEL |
空 | 只执行指定 CTest 标签 |
CTEST_PARALLEL |
空 | CTest 并行数 |
ENABLE_BRANCH_COVERAGE |
ON |
是否生成分支覆盖率 |
FAIL_UNDER_LINE |
空 | 行覆盖率门禁 |
FAIL_UNDER_BRANCH |
空 | 分支覆盖率门禁 |
GCOV_TOOL |
gcov |
gcovr 调用的 gcov 工具,应与编译器工具链匹配 |
GCOVR_FILTERS |
src、include |
Shell 脚本中保留的源码路径 |
GCOVR_EXCLUDES |
测试、第三方、构建目录 | Shell 脚本中排除的路径 |
Shell 使用方式
chmod +x coverage_gcovr.sh
./coverage_gcovr.sh
指定覆盖率门禁:
FAIL_UNDER_LINE=80 FAIL_UNDER_BRANCH=70 ./coverage_gcovr.sh
只运行单元测试标签:
CTEST_LABEL=unit ./coverage_gcovr.sh
指定 gcov 工具:
GCOV_TOOL=gcc-13-gcov ./coverage_gcovr.sh
GCOV_TOOL=/opt/toolchain/bin/arm-none-eabi-gcov ./coverage_gcovr.sh
GCOV_TOOL="llvm-cov gcov" ./coverage_gcovr.sh
Bat 使用方式
保存为 coverage_gcovr.bat 后运行:
coverage_gcovr.bat
指定变量运行:
set PROJECT_ROOT=D:\project\demo
set BUILD_DIR=D:\project\demo\build-coverage
set FAIL_UNDER_LINE=80
set FAIL_UNDER_BRANCH=70
set GCOV_TOOL=C:\msys64\mingw64\bin\gcov.exe
coverage_gcovr.bat
执行流程说明
- 检查
cmake、ctest、gcovr是否存在。 - 配置覆盖率构建目录。
- 编译测试目标。
- 执行 CTest,从而运行 GoogleTest。
- 使用 gcovr 读取构建目录中的 gcov 数据。
- 根据 filter 和 exclude 过滤源码路径。
- 生成 HTML、XML、JSON、TXT 四种报告。
- 如果设置覆盖率门禁,低于阈值时脚本返回失败。
gcovr 报告选择
| 报告 | 参数 | 常见用途 |
|---|---|---|
| HTML | --html --html-details |
本地查看源码覆盖细节 |
| XML | --xml-pretty |
CI 平台解析,常见为 Cobertura 风格 |
| JSON | --json |
自定义脚本二次处理 |
| TXT | --txt |
终端或日志中快速查看 |
Windows 注意事项
- Bat 中的 gcovr 过滤正则建议使用
/,因此脚本把%PROJECT_ROOT%中的\转换为/。 - MSVC 自带编译器不生成 gcov 数据。gcovr 通常配合 GCC、MinGW 或 Clang gcov 兼容数据使用。
- 如果使用 Ninja、MinGW、MSYS2,应确认
gcov、gcovr、编译器来自同一套工具链。 - 如果存在多个 GCC、MinGW 或交叉工具链版本,应设置
GCOV_TOOL。 - 如果路径中包含空格,脚本中的变量引用已经尽量加了引号,但自定义 filter 时仍要注意正则和引号。