← 返回文章页
工程

一个 CMake 项目模板真正有用的地方

模板的价值不在于把目录堆满,而在于让新项目从第一天开始就有清楚的构建边界、依赖入口和验证路径。

我以前对项目模板的理解比较简单:建好 srcincludetests,再放一个能跑的 CMakeLists.txt,就算完成。后来真正维护几个 C++ 项目之后,才发现模板的价值不在“看起来像工程”,而在它能不能降低后续修改的心理成本。

一个好的 CMake 模板,应该让你在新项目开始时少做无意义选择,同时保留足够清晰的扩展空间。

模板首先要定义边界

最重要的边界,是库代码和可执行入口之间的边界。

我更倾向于把核心逻辑放进一个明确的 library target,再让命令行程序、测试程序、示例程序去链接它。这样做有几个好处:

  • 核心逻辑可以被测试直接调用;
  • CLI 入口不会把业务代码和参数解析缠在一起;
  • 后续如果要加 GUI、服务端或 Python 绑定,不需要重写底层结构;
  • 构建目标之间的依赖关系更清楚。

CMake 里最容易失控的地方,就是所有东西都写进一个全局变量和一个巨大 target。刚开始很快,后面每加一个依赖都像在碰运气。

依赖入口要克制

模板不应该预装一堆“可能会用到”的库。 我更喜欢只保留一个清晰的依赖接入位置,比如 cmake/Dependencies.cmake,然后在 README 里写明:项目依赖统一从这里进入,不要散落到每个子目录。

这不是形式主义。依赖一旦散开,后续排查构建问题会非常痛苦:谁引入了 OpenCV,谁设置了编译宏,谁改变了 warning 级别,都会变得不透明。

模板的克制感很重要。默认越简单,项目越容易长期维护。

选项要服务真实工作流

我会给模板保留少量构建选项,例如:

option(PROJECT_BUILD_TESTS "Build tests" ON)
option(PROJECT_BUILD_EXAMPLES "Build examples" ON)
option(PROJECT_ENABLE_WARNINGS "Enable warnings" ON)

这些选项的目标不是显得专业,而是服务真实场景:

  • 本地开发时需要测试;
  • CI 里需要稳定检查;
  • 打包或嵌入其他项目时可能只需要 library;
  • 示例程序可以帮助验证接口,但不应该强制参与所有构建。

好的模板应该让这些选择变得显式,而不是靠改文件来切换。

测试入口必须早一点出现

很多项目不是没有测试能力,而是最开始没有留入口。等代码已经长大,再补测试结构,成本就会明显上升。

所以即使第一版只有一个 smoke test,我也愿意把 tests/ctest 跑通。这个动作会提醒自己:项目不是只能编译,还应该能被验证。

对于医学图像或图形工具类项目,测试不一定全部是复杂算法结果。最开始可以先覆盖这些内容:

  • 文件能否正确读取;
  • 参数解析是否稳定;
  • 核心函数对边界输入是否有合理行为;
  • 一段最小流程能否跑完;
  • 输出目录和日志是否按预期生成。

这些测试不华丽,但能防止很多低级回退。

README 也是模板的一部分

模板不是只给机器看的,README 也不是装饰。 一个可用的工程模板,应该在 README 里回答最基本的几个问题:

  1. 如何配置项目?
  2. 如何构建?
  3. 如何运行测试?
  4. 如何添加新的源文件?
  5. 项目约定哪些目录职责?

如果这些问题没有写清楚,那么模板只是把文件摆好了,还没有真正降低使用门槛。

最后

一个 CMake 项目模板真正有用的地方,是让项目从第一天开始就有边界、有入口、有验证路径。它不需要复杂,但需要稳定。

我现在判断模板好不好,不看它塞了多少功能,而看它能不能让我在三个月后重新打开项目时,仍然知道应该从哪里改、从哪里测、从哪里接新依赖。

模板的专业感,最终体现在长期维护时的从容感。