(三)最小构建
前言
所有 CMake 项目起始于一个 CMakeLists.txt 文件,可视为 CMake 的工程文件,它定义了关于构建的所有:从源码到目标文件,包括测试、打包以及其他自定义任务。
继续以源文件为例来说明,CMake 定义了它自己的语言,比如变量、方法、宏、逻辑判断、循环、注释等等。
一个基本的,完整的可执行的 CMakeLists.txt 包含以下内容:
cmake_minimum_required(VERSION 3.2)
project(MyApp)
add_executable(MyExe main.cpp)
每一行包含一条内置的 CMake 命令,类似于其它语言中的函数调用,除了它们包含多个参数且不直接返回值外。参数之间以一个或多个空格分隔,也可以分隔成多行。
命令对大小写不敏感,所以 add_executable 和 ADD_EXECUTABLE、dd_Executable 一样。但现在普遍都使用小写形式。
cmake_minimum_required()
随着 CMake 持续的更新与发展以添加新的工具和功能,来适应新的平台。每次更新发布都对向后兼容要非常小心。因为使用旧版本构建的项目需要不断处理新版本所带来的行为上的变更,要求以及警告等。与其让项目去解决这些问题,CMake 提出了一种机制,这种机制提供了让项目选择按哪个版本来进行构建的能力。
实现这种机制的方式是在 CMakeLists.txt 的首行中使用 cmake_minimum_required(),确保在进行任何其他操作之前,先对项目的各项要求进行检查并确定下来。
这行命令总共做了两件事:
- 指定该项目所需最小 CMake 版本,如果使用小于该版本的 CMake 进行构建则会立即终止并报错;
- 执行相应的策略设置,以使 CMake 的行为与指定的版本相匹配。
典型的 cmake_minimum_required() 命令如下:
cmake_minimum_required(VERSION major.minor[.patch[.tweak]])
其中主版本号和次要版本号是必需的,大多数项目只需要指定这两者即可,因为新功能只会出现在新 minor 版本中。除非是修复具体的 Bug 时会需要指定 patch 版本号,而 tweak 自 3.x 以后一直没有使用过。
3.5 是新项目应考虑使用的最老版本,如果开发者在项目中使用最新版本的 CMake,任何较旧的版本都会引发弃用警告。
所以如果可以,在新项目中尽量使用较新版本的 CMake。除非该项目是作为其他项目的子项目或子模块,在这种情况下,或许更明智的做法是使用仍具备所需最低 CMake 功能的最旧版本 CMake,但如果有后续版本的特性可用,则应充分利用这些特性。这避免了强制要求其它项目或模块使用较新版本而不是其环境允许或能提供的版本。
独立的新项目如果愿意可以总是使用较新版本,但不能这样要求旧项目。使用旧版本最大的坏处是新版本的 CMake 为了鼓励使用新特性而对旧的特性给出弃用警告。比如在安装了 v4.0.2 的环境中指定了最小要求版本 3.10.0 并使用弃用的命令时。
project()
每个 CMake 项目都应包含一条 project() 命令,且在 cmake_minimum_required() 后面。通用用法如下:
project(projectName[VERSION major[.minor[.patch[.tweak]]]][LANGUAGES languageName ...]
)
项目名称(projectName) 可包含字母、数字、下划线或短线,尽管实际应用中大都只用字母、数字和下划线。项目名称是必填项,其它均为选填。
版本号(VERSION),指定当前项目版本,可选,仅在 3.0 及以后版本可以使用。
语言(LANGUAGES),指定项目开发语言,可选,包括C, CXX, Fortran, ASM, CUDA等,具体取决于 CMake 版本。多个用空格隔开。如果没有使用任何语言则指定为 NONE。如果不指定则默认为 C,C++。3.0 以前的版本不支持 LANGUAGES,但可以在项目名称后面对语言进行指定:
project(MyApp C CXX)
该命令不仅是定义了一个变量,其中最主要的一个功能是会检查指定语言的编译器是否能编译和链接成功。检查通过后 CMake 会设置一些变量和属性来控制指定语言的构建。并且这些变量和属性会被缓存(至 CMakeCache.txt)供后续使用。
有关这些检查的更多详细信息可以在构建区域内的子目录中找到,但开发人员通常只有在使用新的或不常见的编译器,或者在为交叉编译设置工具链文件时,才会需要查看这些子目录。
add_executable()
作为最小实例的最后一行命令 add_executable() 告诉 CMake 基于哪些源文件创建一个可执行文件。基本形式如下:
add_executable(targetName source1 [source2 ...])
该命令创建了一个可执行文件,该可执行文件可在 CMake 项目中以 targetName 引用。其命名规则同项目名称。当项目构建成功后,
会在构建目录中创建一个与平台相关的可执行文件。比如在 windows 中为 targetName.exe,在 linux 平台中为 targetName。
同一 CMakeLists.txt 中可以包含多条 add_executable() 命令,但 targetName 不能重复,否则会报错并高亮显示。
注释
CMake 遵从 Unix Shell 脚本的注释惯例,以 # 开头作为注释的开始。除了在引号内部,任何以 # 开头的都会视作注释。
3.0 版本后支持块注释,同 lua 语言中样式一样,以 #[==[ 开头,以 ]==] 结尾。= 可以是一个或多个,但要首尾匹配。
示例:
cmake_minimum_required(VERSION 3.2)
# We don't use the C++ compiler, so don't let project()
# test for it in case the platform doesn't have one
project(MyApp VERSION 4.7.2 LANGUAGES C)
# Primary tool for this project
add_executable(MainToolmain.cdebug.c # Optimized away for release builds
)
# Helpful diagnostic tool for development and testing
add_executable(TestTool testTool.c)
# These tools are not ready yet, disable them
#[=[
add_executable(NewTool1 tool1.c)
add_executable(NewTool2tool2.cextras.c
)
]=]
建议
确保每个 CMake 项目在其顶层的 CMakeLists.txt 文件的第一行都包含一个 cmake_minimum_required() 命令。在确定要指定的最低版本号时,请记住,较新的版本将为使用更先进的 CMake 特性提供更多的自由度。这也将意味着该项目更有可能更好地适应新的平台或操作系统版本的发布,因为这些版本必然会引入构建系统需要处理的新内容。相反,如果该项目旨在作为操作系统的一部分进行构建和分发(常见于 Linux 系统),那么最低的 CMake 版本很可能会由该相同发行版提供的 CMake 版本所决定。尽早促使对项目版本号进行思考,并尽早将版本号编号纳入项目() 命令中是很有益的。在项目的生命周期后期克服现有流程的惯性和改变版本号处理方式可能会很困难。在决定版本策略时,可以参考诸如语义版本化这样的流行做法。