2020 年 8 月 31 日,是 CMake 诞生的 20 周年纪念日。CMake 风靡软件世界数载,至今仍然占据着大半市场。据估计,50% 以上 C++ 项目的构建系统都是 CMake。CMake 3.0 之后的版本,我们称为现代 CMake(类似于 C++ 11 之后的“现代” C++)。本文将讨论使用现代 CMake 的一些技巧。如需更深入了解 CMake 的历史,以及与 Mak e的比较,请查看文章 CMake 与 Make。
使用现代 CMake 最重要的技巧:如果您的项目仍在使用低于 2. 6的 CMake版本,请花一些时间和精力升级为较新的版本。经验建议使用的 CMake 版本最好在编译器版本之后。
CMake 支持的语言种类不断增加,如 CMake 3.8 增加了 C# 和 CUDA。此外,CMake 还支持 C/C++、java、Obj/C++、Swift 和 Fortran 等多种语言。
与其说这是建议,不如说是告诫。不要手动添加 -STD= C++ 11 到CMAKE_CXX_FLAGS,这是过时的做法。对于现代 CMake,请改用 CXX_STANDARD 和 CXX_STANDARD_REQUIRED 标记。
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
可以通过在顶层 CMakelists.txt 文件显式禁止内部构建,避免内部构建中与构建相关的人工产品(Artifacts)污染源目录。你可以用:
if ( ${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR} )
message( FATAL_ERROR “In-source builds not allowed! Create a build directory and run CMake from there. ” )
endif()
在 CMake 中进行推荐的外部构建时,请使用有文档化的 -S 和 -B 标记来指定源代码和构建目录。旧版本使用的是未进行文档化的-H和-B标记。
CMake 3.13+ 支持:cmake -S . -B build -G “Visual Studio 16 2019” ,推荐使用。
对于较老的 CMake 版本:cmake -H. -Bbuild -G “MSYS Makefiles”,注意标记-B 和文件名称之间没有空格。
预设设置是使用文件指定 CMake 选项集合的一种方法。如果需要支持多个平台的复杂项目,最好提供(同时也方便后续使用)CMake 预设文件。预设可以保证不同编译器工具链和包的多个配置一致。预设文件是通过传递 -C 标记来加载和使用的,建议将其放在源目录的 cmake/presets 文件夹中。
使用 linter 检查代码库,报告编译器遗漏的错误,始终是最佳选择。与程序员的 IDE 相比,在中心位置运行 linter 需要将 linting 集成到 CMake。针对 C/C++ 项目,CMake 从第3.7.2版开始支持 clang-tidy(一种静态代码分析框架)。警告将被视为 CI 生成中的错误,以检查技术债务。
代码格式有时就像是信仰,开发者各执己见,互不妥协。但是,保证格式一致始终是最妥当的做法,尤其需要多个开发人员共同协作时。在 C++ 项目中,clang 格式可以轻易地集成到 CMake。
无论你决定使用 CTest 还是 Google 测试框架,都需要确保测试是作为持续集成进程的一部分。或者可以作为构建的后期步骤(POST_BUILD),在编译时运行测试。
使用 include_ directories 并不是过时的做法,或者也可以用target_include_directores 和 target_link_libraries 替代,强化项目的设计。CMake 拥有 PRIVATE, PUBLIC 和 INTERFACE 的关键字是有其充分理由的,正确使用这些关键字是保持项目组件单向分层的关键。
可以使用宏和函数在 CMake 中创建自定义命令。宏不会给变量引入额外的作用域,但函数会。宏对于包装具有输出参数的命令,另外函数在其他情况下也非常使用。
CMake 支持本机依赖关系的可视化。使用 ZGRViewer 等程序可以很容易地查看输出点文件。
像关注生产代码一样关注 CMakeLists.txt 文件,确保对其进行了充分的注释,并遵循了所有现代 CMake 最佳实践。
下面的图表很好地解释了什么时候应该在 CMake 使用 PRIVATE,PUBLIC 和 INTERFACE.
| 依赖关系 | 描述 |
| PRIVATE | 我需要,但是依赖者不需要 |
| PUBLIC | 我和依赖者都需要 |
| INTERFACE | 我不需要,但是依赖者需要 |
Findpackage 是 CMake 或用户提供的搜索脚本,用于查找包文件。现代的包制作方会提供 <package>Config.cmake. 一旦包已安装,PackageConfig 可以向 CMake 传递其详细信息。
在 CMake 构建过程中,可以选择 pass–parallel 标记,进行并行作业。但是,并不是所有的构建生成器都可以通过并行加速构建,CMake 3.12 之后的版本才支持并行选项。当使用分布式编译来加速 CMake 构建时,例如 Incredibuild,你需要将 –parallel 标记设置成一个非常大的数字,例如 300,指示 CMake 执行多达300个并发执行的任务。Incredibuild 可以向远程闲置内核分发多达 300 个任务,从而高效加快编译速度。
其实这更像是建议而不是技巧。CMake 是大量实战测试的强大系统,因此如果您决定使用或迁移到 CMake,请不要放弃!因为对于一个新手来说,CMake 可能并不友好。CMake 是开源的,它的社区非常有用,相关的参考文件也很实用。因此,希望大家不要轻易气馁,各种资源触手可得。
CMake 是一个伟大的构建系统生成器,支持所有主流平台。Visual Studio 17 支持本机 CMake,Qt 从 Qt6.0 开始支持 CMake,这一切都说明 CMake 是一款成功的产品。希望以上的技巧能对你有所帮助。祝大家工作顺利!