36、代码格式化:集成clang-format,添加自定义target

代码格式化这件事,说实话,我以前也觉得可有可无。直到有一次团队合并代码,Git 上密密麻麻全是空格和缩进的差异,真正改动的逻辑反而被淹没了。嗯,从那以后,我就把 clang-format 集成到了 CMake 里,作为每个项目的标配。

这一讲,我们就来聊聊怎么在 CMake 中集成 clang-format,并且添加一个自定义 target,让你可以一键格式化所有代码。

为什么要在 CMake 里做这件事?

你可能会想:「我 IDE 里不是有格式化插件吗?手动按一下快捷键不就行了?」

没错,个人开发没问题。但团队协作时,每个人的 IDE 配置可能不一样。有人用 4 空格缩进,有人用 Tab,还有人喜欢大括号换行风格。结果就是——代码风格五花八门。

我的做法是:在 CMake 里加一个 format 的 target,所有人都能通过 make formatcmake --build . --target format 来统一格式化。谁都不用记快捷键,也不用担心配置不一致。

准备工作:安装 clang-format

首先,你得确保系统里有 clang-format。不同平台安装方式略有不同:

平台 安装命令
Ubuntu/Debian sudo apt install clang-format
macOS brew install clang-format
Windows 通过 LLVM 安装包或 choco install llvm

安装完后,可以用 clang-format --version 验证一下。我个人习惯用 clang-format 14 或更高版本,因为对 C++20 的支持更好。

第一步:准备 .clang-format 配置文件

clang-format 的行为由 .clang-format 文件控制。你可以放在项目根目录,这样所有子目录都会继承。

这里给出一份我常用的配置,适合大多数 C++ 项目:

# .clang-format
BasedOnStyle: Google
IndentWidth: 4
UseTab: Never
ColumnLimit: 100
AllowShortFunctionsOnASingleLine: None
BreakBeforeBraces: Attach
NamespaceIndentation: All
PointerAlignment: Left

简单解释一下:

  • BasedOnStyle: Google —— 以 Google 风格为基础,省得从头配
  • IndentWidth: 4 —— 我个人喜欢 4 空格缩进,比 2 空格更清晰
  • ColumnLimit: 100 —— 100 字符换行,兼顾宽屏和可读性
  • BreakBeforeBraces: Attach —— 大括号不换行,节省行数

当然,你可以根据团队规范调整。关键是要把这个文件提交到 Git 仓库,让所有人都用同一套规则。

第二步:在 CMakeLists.txt 中集成 clang-format

核心思路是:用 add_custom_target 创建一个 target,然后调用 clang-format 命令格式化所有源文件。

来看一个完整的示例:

# 查找 clang-format 可执行文件
find_program(CLANG_FORMAT clang-format)

if(CLANG_FORMAT)
    # 收集所有需要格式化的源文件
    file(GLOB_RECURSE ALL_SOURCE_FILES
        ${CMAKE_SOURCE_DIR}/src/*.cpp
        ${CMAKE_SOURCE_DIR}/src/*.h
        ${CMAKE_SOURCE_DIR}/include/*.h
        ${CMAKE_SOURCE_DIR}/tests/*.cpp
        ${CMAKE_SOURCE_DIR}/tests/*.h
    )

    # 添加自定义 target:format
    add_custom_target(format
        COMMAND ${CLANG_FORMAT}
            -i
            -style=file
            ${ALL_SOURCE_FILES}
        COMMENT "Formatting all source files with clang-format..."
        WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
    )

    # 添加自定义 target:check-format(只检查不修改)
    add_custom_target(check-format
        COMMAND ${CLANG_FORMAT}
            --dry-run
            --Werror
            -style=file
            ${ALL_SOURCE_FILES}
        COMMENT "Checking code formatting..."
        WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
    )
else()
    message(WARNING "clang-format not found. Skipping format targets.")
endif()

这里我做了两个 target:

  • format —— 直接格式化所有源文件(-i 表示原地修改)
  • check-format —— 只检查,不修改。如果格式不对,返回非零退出码,CI 里可以用这个来卡点
小提示:file(GLOB_RECURSE) 收集文件时,注意不要包含第三方库的目录。可以在 GLOB 时排除掉 third_partybuild 目录,或者单独指定源文件路径。

第三步:使用自定义 target

配置好之后,用法很简单:

# 格式化代码
cmake --build . --target format

# 只检查格式
cmake --build . --target check-format

如果你用的是 Makefile 生成器,也可以直接:

make format
make check-format

嗯,就是这么简单。团队里每个人都能用同一行命令格式化代码,再也不用为缩进吵架了。

进阶:在 CI 中自动检查格式

我曾经在一个项目里吃过亏——有人提交了格式混乱的代码,review 时没发现,合并后整个文件 diff 变得惨不忍睹。后来我就在 CI 里加了一步:

# GitHub Actions 示例
- name: Check code formatting
  run: cmake --build build --target check-format

如果格式不对,CI 直接报红,开发者必须修复后才能合并。这招很管用,建议你也试试。

知识体系图

下面这张图帮你理清整个流程:

CMake 集成 clang-format 流程 1. 安装 clang-format apt / brew / choco 2. 编写 .clang-format 定义代码风格规则 3. 配置 CMake add_custom_target format target 直接格式化代码 check-format CI 检查用 5. 团队统一执行 cmake --build . --target format

避坑指南

注意: 使用 file(GLOB_RECURSE) 时,如果项目很大,每次 CMake 重新配置都会扫描所有文件,速度会变慢。我的建议是:如果源文件目录结构固定,可以手动列出关键目录,或者用 file(GLOB) 配合 list(APPEND) 分批次添加。

另外,clang-format--style=file 选项会从当前目录向上查找 .clang-format 文件。如果你把配置文件放在项目根目录,而 WORKING_DIRECTORY 也设成了根目录,那就没问题。否则 clang-format 可能找不到配置文件,使用默认风格——那效果就完全不一样了。

总结

这一讲的内容其实不复杂,但很实用。核心就三步:

  1. 安装 clang-format
  2. 编写 .clang-format 配置文件
  3. 在 CMake 中用 add_custom_target 创建 format 和 check-format 两个 target

我个人觉得,代码格式化这件事,越早集成到项目里越好。等代码写到几万行再回头格式化,那改动量就太大了,review 起来也痛苦。不如一开始就养成习惯,每次提交前跑一下 make format,干净利落。

好了,这一讲就到这里。如果你在集成过程中遇到什么问题,欢迎交流。


公众号:蓝海资料掘金营,微信 deep3321