现代 CMake 准则

2020/2/13 code tool

原文地址 (opens new window)

# 准备开始

有关 CMake 的用户级简绍,请观看 Jason Turner 撰写的 C++ Weekly 第 78 集,CMake 简介 (opens new window)。 LLVM 的 CMake 入门 (opens new window) 对 CMake 语法进行了很好的高级介绍。现在去阅读吧。

之后,观看 Mathieu Ropert 在 CppCon 2017 上的演讲,使用现代 CMake 模式来实现良好的模块化设计 (opens new window)(幻灯片 (opens new window))。 它彻底解释了什么是现代 CMake,以及为什么它比“老派” CMake 好得多。 本次演讲中的模块化设计思想基于 John Lakos 撰写的 《大规模 C++ 软件设计》 (opens new window) 一书。 下一个更详细介绍现代 CMake 的视频是 Daniel Pfeifer 在 C++Now 2017 上发表的 Effective CMake (opens new window) (幻灯片 (opens new window))。

这段文字受 Mathieu Ropert 和 Daniel Pfeifer 的演讲影响很大。

如果您对 CMake 的历史和内部体系结构感兴趣,请参阅 《开源应用程序的体系结构》 (opens new window) 一书中的 CMake (opens new window) 文章。

# 概论

# 至少使用 CMake 3.0.0 版本

现代 CMake 仅从 3.0.0 版本开始可用。

# 将 CMake 代码视为生产代码

CMake 是代码。因此,它应该是整洁的。对 CMakeLists.txt 和模块使用与其余代码库相同的原则。

# 全局定义项目属性

例如,一个项目可能使用一组通用的编译器警告。在顶层 CMakeLists.txt 文件中全局定义此类属性可防止因依赖对象使用更严格的编译器选项从而导致依赖对象的公共标头造成依赖对象无法编译的情况。通过全局定义此类项目属性,可以更轻松地管理项目以及所有相关对象。

这些命令在目录级别上运行。在该级别定义的所有目标都继承这些属性。这增加了隐藏依赖性的机会。更好地直接对目标进行操作。

# 放弃 CMAKE_CXX_FLAGS

不同的编译器使用不同的命令行参数格式。将来会停止在 CMAKE_CXX_FLAGS 中通过 -std=c++14 设置 C++ 标准,因为这些要求也在其他标准(如 C++17)中得到了满足,并且旧编译器上的编译器选项也不相同。因此,最好告诉 CMake 编译功能,以便找出适合的编译器选项。

# 不要滥用使用要求

例如,不要将 -Wall 添加到 target_compile_optionsPUBLICINTERFACE 部分,因为不需要构建依赖目标。

# Modules

# 使用声明出口目标的现代查找模块

从 CMake 3.4 开始,越来越多的查找模块导出可以通过 target_link_libraries 使用的目标。

# 使用外部软件包的导出目标

不要退一步使用由外部程序包定义的变量的旧 CMake 样式。而是通过 target_link_libraries 使用导出的目标。

# 将查找模块用于不支持客户端使用 CMake 的第三方库

CMake 为第三方库提供了一组查找模块。例如,Boost 不支持 CMake。相反,CMake 提供了一个查找模块以在 CMake 中使用 Boost 。

# 如果库不支持客户端使用 CMake,请将其作为错误报告给第三方库作者。如果该库是一个开源项目,请考虑发送补丁

CMake 主导了整个行业。如果图书馆作者不支持 CMake,那就是一个问题。

# 为不支持客户端使用 CMake 的第三方库编写查找模块

可以改装一个查找模块,以将目标正确导出到不支持 CMake 的外部程序包中。

# 如果您是库作者,请导出库的接口

有关如何执行此操作的信息,请参阅 Daniel Pfeifer 的 C++Now 2017 演讲 Effective CMake(slide 24ff)。请记住要导出正确的信息。使用 BUILD_INTERFACEINSTALL_INTERFACE 生成器表达式作为过滤器。

# 项目

# 避免在项目命令的参数中使用自定义变量

保持简洁。不要引入不必要的自定义变量。执行 add_library(a b.h b.cpp) 以代替 add_library(a ${MY_HEADERS} ${MY_SOURCES})

# 不要在项目中使用 file(GLOB)

CMake 是构建系统生成器,而不是构建系统。生成构建系统时,它将 GLOB 表达式评估为文件列表。然后,构建系统在该文件列表上运行。因此,生成系统无法检测到文件系统中的某些更改。

CMake 不能仅将 GLOB 表达式转发到构建系统,以便在构建时对表达式进行求值。CMake 希望成为受支持的构建系统的共同点。并非所有的构建系统都支持此功能,因此 CMake 也不支持。

# 将特定于 CI 的设置放在 CTest 脚本中,而不在项目中

它只是使事情变得简单。有关更多信息,请参见通过 CTest 脚本的 Dashboard Client。

# 遵循测试名称的命名约定

通过 CTest 运行测试时,这可以简化正则表达式的过滤。

# 目标和属性

# 考虑目标和属性

通过根据目标定义属性(即,编译定义,编译选项,编译功能,包括目录和库依赖项),它可以帮助开发人员在目标级别上推理系统。开发人员无需为了推理一个目标而了解整个系统。构建系统处理传递性。

Last Updated: 2023-10-29T08:26:04.000Z