C++ GitHub Actions CI流水线核心是cmake+make构建、ctest运行测试、gcovr生成覆盖率报告;需在ubuntu-latest显式安装cmake/gtest、启用-g -O0和插桩标志、正确配置add_test与RPATH、指定gcovr扫描路径并避免第三方文件干扰。

直接结论:用 GitHub Actions 配置 C++ CI 流水线,核心是三步——用 cmake + make(或 ninja)构建、用 ctest 或直接运行 ./test 执行 Google Test、用 gcovr 或 llvm-cov 生成覆盖率报告并上传。 不需要自建服务器,也不必强依赖特定 IDE;但编译器选型、插桩开关、测试产物路径这三点一旦配错,覆盖率就为空或构建失败。
怎么写 .github/workflows/ci.yml 让 C++ 构建不报错
关键不是“能不能跑”,而是“在干净 Ubuntu 环境里能否复现本地行为”。GitHub Actions 默认不带 cmake、gtest 或调试符号支持,必须显式安装和启用:
- 用
ubuntu-latest(当前为 22.04),避免用ubuntu-20.04——后者默认gcc版本太低,std::format等 C++20 特性会编译失败 - 安装依赖时别只写
sudo apt install build-essential,必须加cmake、libgtest-dev(注意:这个包只装头文件和静态库,还需手动sudo cmake /usr/src/gtest && sudo make install) - 构建时一定要开
-g -O0(调试信息 + 关闭优化),否则gcov插桩数据无法映射到源码行;若用 Clang,则需额外加--coverage编译+链接标志 -
cmake调用必须指定构建目录(如build/),不能直接在源码根目录cmake .,否则git clean -ffdx类操作可能误删源码
为什么 ctest 找不到测试可执行文件或运行超时
ctest 本身不编译测试,只发现并运行已存在的测试二进制。常见断点在这里:
- 没在
CMakeLists.txt中用add_test()注册测试,或注册时用了相对路径(如add_test(NAME mytest COMMAND ./bin/mytest)),而ctest实际工作目录是构建根目录,应改用$<mytest></mytest>或绝对路径${CMAKE_BINARY_DIR}/bin/mytest - 测试程序链接了动态库但未设
RUNPATH,导致ctest运行时报error while loading shared libraries;解决方式是在target_link_libraries()后加set_target_properties(mytest PROPERTIES INSTALL_RPATH "$ORIGIN/../lib") - CI 环境默认无显示(no DISPLAY),若测试中调用了 Qt GUI 或 OpenGL 初始化,会卡住或超时;加
timeout 30s ctest -j2并在测试入口前判断if (!qgetenv("DISPLAY").isEmpty())可规避
gcovr 生成的 HTML 覆盖率报告为什么全是 0%
这不是工具问题,是插桩链断裂。覆盖率数据从生成到呈现有三个硬性依赖环节:
- 编译时没加
-fprofile-arcs -ftest-coverage(GCC)或--coverage(Clang),.gcda文件根本不会产生 - 测试运行后没触发
gcovr扫描正确目录:它默认只扫当前目录下的.gcda,但 CMake 的ctest可能在子目录(如build/test/)执行,需用--object-directory build/test/显式指定 - 源码路径不匹配:
gcovr读.gcno里的路径是绝对路径(比如/home/runner/work/myproj/myproj/src/),而 CI 工作区路径是/home/runner/work/myproj/myproj/;用--root . --keep可让 gcovr 用相对路径重映射 - 若项目含子模块或 externalproject,
gcovr默认不递归扫描;加--recursive参数,并确认子模块编译时也开了插桩
怎么把覆盖率报告自动发到 Codecov 或 Coveralls
上传本身简单,但失败常因 token 权限或环境变量泄露。安全且稳定的实践是:
- 用 GitHub Secrets 存
CODECOV_TOKEN,不要硬编码;上传命令用bash - 确保上传前已生成
coverage.xml(lcov 格式)或直接传gcovr的 XML:gcovr -r . --xml-pretty -o coverage.xml - 上传动作必须放在
ctest成功之后,且整个 job 不能设continue-on-error: true,否则即使测试失败也会传空报告 - 如果用
llvm-cov(推荐 Clang 用户),注意其export命令输出的是 JSON,Codecov 不认;得先转成 lcov:llvm-cov export -format=lcov ... > coverage.lcov,再传
最易被忽略的一点:覆盖率统计只对参与插桩的源文件有效。如果你的 src/ 下混着第三方头文件(如 spdlog、fmt)、或 CMake 把某些 .cpp 当作 OBJECT_LIBRARY 处理而跳过插桩,它们就不会出现在报告里——不是 bug,是配置意图。检查 gcovr --verbose 输出,看它实际扫描了哪些文件。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











