Skip to content

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

执行流程说明

  1. 检查 cmake、ctest、gcovr 是否存在。
  2. 配置覆盖率构建目录。
  3. 编译测试目标。
  4. 执行 CTest,从而运行 GoogleTest。
  5. 使用 gcovr 读取构建目录中的 gcov 数据。
  6. 根据 filter 和 exclude 过滤源码路径。
  7. 生成 HTML、XML、JSON、TXT 四种报告。
  8. 如果设置覆盖率门禁,低于阈值时脚本返回失败。

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 时仍要注意正则和引号。