CMake 接入 clang-tidy:从开关到可维护门禁
CMake 接入 clang-tidy
clang-tidy 适合当工程的静态门禁:比编译器警告更靠近「现代 C++ 习惯」和「API 误用」。难点通常不在「能不能跑」,而在 CMake 怎么挂、配置怎么钉死、本地与 CI 怎么一致。
本文按「能开关 → 能复现 → 能长期维护」三条线整理。
1. 先弄清两种挂法
1.1 编译时自动跑(CMAKE_CXX_CLANG_TIDY)
CMake 会把 tidy 绑到每个编译命令上:改一个 .cpp,编译时顺带分析。
option(CXX_ENABLE_CLANG_TIDY "Attach clang-tidy to CXX compile" OFF)
if(CXX_ENABLE_CLANG_TIDY)
find_program(CLANG_TIDY_EXE NAMES clang-tidy REQUIRED)
set(CMAKE_CXX_CLANG_TIDY
"${CLANG_TIDY_EXE};-warnings-as-errors=*"
CACHE STRING "" FORCE)
endif()适合:小目标 / 模块化工程 / 想强制「脏代码编不过」。
代价:全量编译变慢;增量时只扫改过的 TU。
1.2 独立目标 / 脚本扫(不绑编译)
用 compile_commands.json + 自己的 clang-tidy 调用:
cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
clang-tidy -p build src/**/*.cpp适合:大仓库、只想在 PR 跑、或 tidy 与构建解耦。
2. 配置文件:把规则钉在仓库里
优先放仓库根或 cmake/ 旁的 .clang-tidy,避免每人本机「默认检查集」不一致。
最小可用示例:
Checks: >
-*,
bugprone-*,
modernize-use-nullptr,
modernize-use-override,
readability-identifier-naming,
performance-unnecessary-copy-initialization
WarningsAsErrors: ''
HeaderFilterRegex: '.*'
FormatStyle: none经验:
| 做法 | 建议 |
|---|---|
一上来开 * | 噪音爆炸,团队会关掉 tidy |
从 bugprone-* + 少量 modernize 起步 | 易落地 |
WarningsAsErrors: '*' | 适合 CI 门禁;本地可先关 |
| 第三方头文件 | 用 HeaderFilterRegex 限定自己的源码树 |
3. 与 CMake 分层基建的常见接法
现代工程常把开关放在 CompilerWarnings.cmake / ProjectSetup.cmake 一类文件里,例如:
# cmake/CompilerWarnings.cmake(示意)
function(cxx_enable_clang_tidy target)
if(NOT CXX_ENABLE_CLANG_TIDY)
return()
endif()
find_program(CLANG_TIDY_EXE NAMES clang-tidy)
if(NOT CLANG_TIDY_EXE)
message(WARNING "clang-tidy not found; skip")
return()
endif()
set_target_properties(${target} PROPERTIES
CXX_CLANG_TIDY "${CLANG_TIDY_EXE};--config-file=${CMAKE_SOURCE_DIR}/.clang-tidy")
endfunction()对 C++23 modules / import std 工程:确认 tidy 用的 clang 版本与编译器同源(Homebrew LLVM 场景尤其常见),否则会出现「编译过、tidy 解析失败」。
4. 本地工作流(推荐)
export PATH="/opt/homebrew/opt/llvm/bin:$PATH"
# 默认关闭 tidy,日常编译快
cmake -S . -B build -G Ninja
cmake --build build
# 需要门禁时显式打开
cmake -S . -B build -G Ninja -DCXX_ENABLE_CLANG_TIDY=ON
cmake --build build把「默认 OFF、CI ON」写进 README / preset,比强迫人人开 tidy 更可持续。
5. CI 门禁清单
-
CMAKE_EXPORT_COMPILE_COMMANDS=ON(若用独立扫) - 固定
clang-tidy版本(或固定 LLVM formula 大版本) -
.clang-tidy进仓库 - PR 必跑;main 上偶发全量
- 第三方目录进
.clang-tidy忽略或HeaderFilterRegex排除
6. 小结
| 目标 | 做法 |
|---|---|
| 开发时提醒 | 本机可选 CXX_ENABLE_CLANG_TIDY=ON |
| 合入质量 | CI 打开 + warnings-as-errors |
| 长期可维护 | 仓库内 .clang-tidy + 窄检查集逐步加宽 |
下一篇会写 .cmake 模块拆分与 CMakePresets.json 的使用技巧,和 tidy 开关如何一起放进 preset。