- 一个标准的 CMakeLists.txt 模板
CMAKE_BUILD_TYPE构建的类型,调试模式还是发布模式- 各种构建模式在编译器选项上的区别
- 小技巧:设定一个变量的默认值
- project:初始化项目信息,并把当前 CMakeLists.txt 所在位置作为根目录
- 和子模块的关系:
PROJECT_x_DIR和CMAKE_CURRENT_x_DIR的区别 - 其他相关变量
- 子模块里也可以用 project 命令,将当前目录作为一个独立的子项目
- project 的初始化:
LANGUAGES字段 - 常见问题:
LANGUAGES中没有启用 C 语言,但是却用到了 C 语言 - 设置 C++ 标准:
CMAKE_CXX_STANDARD变量 - 常见误区:小彭老师,我手动添加
-std=c++17行不行? project的初始化:VERSION字段- 一些没什么用,但 CMake 官方不知为何就是提供了的项目字段……
- 项目名的另一大作用:会设置另外
<项目名>_SOURCE_DIR等变量 - 小技巧:CMake 的
${}表达式可以嵌套 cmake_minimum_required指定最低所需的 CMake 版本- CMake常见变量——Project和CMake相关信息
一个标准的 CMakeLists.txt 模板

CMAKE_BUILD_TYPE 构建的类型,调试模式还是发布模式
CMAKE_BUILD_TYPE 是 CMake 中一个特殊的变量,用于控制构建类型,他的值可以是:
Debug调试模式,完全不优化,生成调试信息,方便调试程序Release发布模式,优化程度最高,性能最佳,但是编译比 Debug 慢MinSizeRel最小体积发布,生成的文件比 Release 更小,不完全优化,减少二进制体积RelWithDebInfo带调试信息发布,生成的文件比 Release 更大,因为带有调试的符号信息- 默认情况下
CMAKE_BUILD_TYPE为空字符串,这时相当于Debug。

各种构建模式在编译器选项上的区别
在Release模式下,追求的是程序的最佳性能表现,在此情况下,编译器会对程序做最大的代码优化以达到最快运行速度。另一方面,由于代码优化后不与源代码一致,此模式下一般会丢失大量的调试信息。
1. Debug: -O0 -g
2. Release: -O3 -DNDEBUG
3. MinSizeRel: -Os -DNDEBUG
4. RelWithDebInfo: -O2 -g -DNDEBUG
此外,注意定义了 NDEBUG 宏会使 assert 被去除掉。
小技巧:设定一个变量的默认值
如何让 CMAKE_BUILD_TYPE 在用户没有指定的时候为 Release,指定的时候保持用户指定的值不变呢。就是说 CMake 默认情况下 CMAKE_BUILD_TYPE 是一个空字符串。因此这里通过if (NOT CMAKE_BUILD_TYPE) 判断是否为空,如果空则自动设为 Release 模式。
大多数 CMakeLists.txt 的开头都会有这样三行,为的是让默认的构建类型为发布模式(高度优化)而不是默认的调试模式(不会优化)。我们稍后会详细捋一遍类似于 CMAKE_BUILD_TYPE 这样的东西。绝大多数 CMakeLists.txt 开头都会有的部分,可以说是“标准模板”了。

project:初始化项目信息,并把当前 CMakeLists.txt 所在位置作为根目录
这里初始化了一个名称为 hellocmake 的项目,为什么需要项目名?对于 MSVC,他会在 build 里生成 hellocmake.sln 作为“IDE 眼中的项目”。

CMAKE_CURRENT_SOURCE_DIR 表示当前源码目录的位置,例如 ~/hellocmake。CMAKE_CURRENT_BINARY_DIR 表示当前输出目录的位置,例如 ~/hellocmake/build

和子模块的关系:PROJECT_x_DIR 和 CMAKE_CURRENT_x_DIR 的区别
PROJECT_SOURCE_DIR 表示最近一次调用 project 的 CMakeLists.txt 所在的源码目录。CMAKE_CURRENT_SOURCE_DIR 表示当前 CMakeLists.txt 所在的源码目录。CMAKE_SOURCE_DIR表示最为外层 CMakeLists.txt 的源码根目录。
利用 PROJECT_SOURCE_DIR 可以实现从子模块里直接获得项目最外层目录的路径。不建议用 CMAKE_SOURCE_DIR,那样会让你的项目无法被人作为子模块使用。
其他相关变量
PROJECT_SOURCE_DIR:当前项目源码路径(存放main.cpp的地方)PROJECT_BINARY_DIR:当前项目输出路径(存放main.exe的地方)CMAKE_SOURCE_DIR:根项目源码路径(存放main.cpp的地方)CMAKE_BINARY_DIR:根项目输出路径(存放main.exe的地方)PROJECT_IS_TOP_LEVEL:BOOL类型,表示当前项目是否是(最顶层的)根项目PROJECT_NAME:当前项目名CMAKE_PROJECT_NAME:根项目的项目名
详见:https://cmake.org/cmake/help/latest/command/project.html
子模块里也可以用 project 命令,将当前目录作为一个独立的子项目
这样一来 PROJECT_SOURCE_DIR 就会是子模块的源码目录而不是外层了。这时候 CMake 会认为这个子模块是个独立的项目,会额外做一些初始化。他的构建目录 PROJECT_BINARY_DIR 也会变成 build/<源码相对路径>。这样在 MSVC 上也会看见 build/mylib/mylib.vcxproj 的生成。
project 的初始化:LANGUAGES 字段
project(项目名 LANGUAGES 使用的语言列表…) 指定了该项目使用了哪些编程语言。目前支持的语言包括:
- C:C语言CXX:
- C++语言
- ASM:汇编语言
- Fortran:老年人的编程语言
- CUDA:英伟达的 CUDA(3.8 版本新增)
- OBJC:苹果的 Objective-C(3.16 版本新增)
- OBJCXX:苹果的 Objective-C++(3.16 版本新增)
- ISPC:一种因特尔的自动 SIMD 编程语言(3.18 版本新增)
如果不指定 LANGUAGES,默认为 C 和 CXX。
常见问题:LANGUAGES 中没有启用 C 语言,但是却用到了 C 语言

解决:改成 project(项目名 LANGUAGES C CXX) 即可

也可以先设置 LANGUAGES NONE,之后再调用 enable_language(CXX)
这样可以把 enable_language 放到 if 语句里,从而只有某些选项开启才启用某语言之类的
设置 C++ 标准:CMAKE_CXX_STANDARD 变量
CMAKE_CXX_STANDARD 是一个整数,表示要用的 C++ 标准。
- 比如需要 C++17 那就设为 17,需要 C++23 就设为 23。
CMAKE_CXX_STANDARD_REQUIRED 是 BOOL 类型,可以为 ON 或 OFF,默认 OFF。
- 他表示是否一定要支持你指定的 C++ 标准:如果为 OFF 则 CMake 检测到编译器不支持 C++17 时不报错,而是默默调低到 C++14 给你用;为 ON 则发现不支持报错,更安全。
CMAKE_CXX_EXTENSIONS 也是 BOOL 类型,默认为 ON。
- 设为 ON 表示启用 GCC 特有的一些扩展功能;OFF 则关闭 GCC 的扩展功能,只使用标准的 C++。
- 要兼容其他编译器(如 MSVC)的项目,都会设为 OFF 防止不小心用了 GCC 才有的特性。此外,最好是在 project 指令前设置
CMAKE_CXX_STANDARD这一系列变量,这样 CMake 可以在 project 函数里对编译器进行一些检测,看看他能不能支持 C++17 的特性。

常见误区:小彭老师,我手动添加 -std=c++17 行不行?
- 请勿直接修改
CMAKE_CXX_FLAGS来添加-std=c++17(你在百度 CSDN 学到的用法)。请使用 CMake 帮你封装好的CMAKE_CXX_STANDARD(从业人员告诉你的正确用法)。 - 为什么百度不对:你 GCC 用户手动指定了
-std=c++17,让 MSVC 的用户怎么办?此外 CMake 已经自动根据CMAKE_CXX_STANDARD的默认值 11 添加-std=c++11选项了,你再添加个-std=c++17选项不就冲突了吗?所以请用CMAKE_CXX_STANDARD。

project 的初始化:VERSION 字段
project(项目名 VERSION x.y.z) 可以把当前项目的版本号设定为 x.y.z。之后可以通过 PROJECT_VERSION 来获取当前项目的版本号。
PROJECT_VERSION_MAJOR获取x(主版本号)。PROJECT_VERSION_MINOR获取y(次版本号)。PROJECT_VERSION_PATCH获取z(补丁版本号)。


一些没什么用,但 CMake 官方不知为何就是提供了的项目字段……


项目名的另一大作用:会设置另外 <项目名>_SOURCE_DIR 等变量


小技巧:CMake 的 ${} 表达式可以嵌套
因为 ${PROJECT_NAME}求值的结果是 hellocmake所以 ${${PROJECT_NAME}_VERSION} 相当于 ${hellocmake_VERSION}进一步求值的结果也就是刚刚指定的 0.2.3 了
cmake_minimum_required 指定最低所需的 CMake 版本
假如你写的 CMakeLists.txt 包含了 3.15 版本才有的特性,如果用户在老版本上使用,就会出现各种奇怪的错误。因此最好在第一行加个 cmake_minimum_required(VERSION 3.15)表示本 CMakeLists.txt 至少需要 CMake 版本 3.15 以上才能运行。如果用户的 CMake 版本小于 3.15,会出现“CMake 版本不足”的提示


假设我现在构建一个需要版本 3.99 的 CMake 项目
- 会正常报错提示版本过低,而不是等到某处用到 3.99 版本才有的特性时才出错


可以通过 cmake --version 查看当前版本

也可以通过 CMAKE_VERSION 这个变量来获得当前 CMake 版本号。CMAKE_MINIMUM_REQUIRED_VERSION获取cmake_minimum_required中指定的“最小所需版本号”。

注意:cmake_minimum_required 不仅是“最小所需版本”
虽然名字叫 minimum_required,实际上不光是 >= 3.15 就不出错这么简单。根据你指定的不同的版本号,还会决定接下来一系列 CMake 指令的行为。此外,你还可以通过 3.15...3.20 来表示最高版本不超过 3.20。这会对 cmake_policy 有所影响,稍后再提。

CMake常见变量——Project和CMake相关信息
https://blog.csdn.net/fuyajun01/article/details/8891749
CMAKE_AR:静态库的归档工具的名称。这个变量指定了用于创建档案或静态库的程序的名称。CMAKE_ARGC: 在脚本模式下传给CMake的命令行参数的个数。当运行在-P脚本模式下,CMake设置该变量为命令行参数的个数。CMAKE_ARGV0: 在脚本模式下传给CMake的命令行的第一个参数。CMAKE_BINARY_DIR: 构建树的最外层路径,是当前CMake构建树的最外层的全路径。对于在源码中构建的情况,它与CMAKE_SOURCE_DIR的值一样。CMAKE_BUILD_TOOL: 执行构建过程的工具。该变量设置为CMake构建时输出所需的程序。对于VS 6,CMAKE_BUILD_TOOL设置为msdev, 对于Unix,它被设置为make 或 gmake。 对于VS 7, 它被设置为devenv, 对于Nmake构建文件,它的值为nmake。CMAKE_CACHEFILE_DIR: 该变量设置为包含CMakeCache.txt文件的目录的全路径。通常与CMAKE_BINARY_DIR的值一样。CMAKE_CACHE_MAJOR_VERSION: 用于创建CMakeCache.txt文件的CMake的主版本号。只有当CMake运行于在由一个不同版本的CMake创建的cache文件时,这个变量的值才会不同。CMAKE_CACHE_MINOR_VERSION: 用于创建CMakeCache.txt文件的CMake的次版本号。只有当CMake运行于在由一个不同版本的CMake创建的cache文件时,这个变量的值才会不同。CMAKE_CACHE_PATCH_VERSION: 用于创建CMakeCache.txt文件的CMake的补丁版本号。只有当CMake运行于在由一个不同版本的CMake创建的cache文件时,这个变量的值才会不同。CMAKE_CFG_INTDIR: 构建时对每个配置的输出子目录的引用。对于在一个构建树中支持多个配置的本地构建系统(例如Visual Studio或Xcode),该值就是对指定单个配置输出子目录的一个构建时变量的引用。对于Makefile产生器,该变量解析为”.”。因为在此构建树,只有一个配置。示例的值 如下:$(IntDir) = Visual Studio 6$(OutDir) = Visual Studio 7, 8, 9$(Configuration) = Visual Studio 10$(CONFIGURATION) = Xcode. = Make-based tools
由于这些值由本地构建系统解析,该变量只适合用于命令行下,这些命令在构建时被解析。使用例子如下:
add_executable(mytool mytool.c)add_custom_command(OUTPUT out.txtCOMMAND${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/mytool${CMAKE_CURRENT_SOURCE_DIR}/in.txt out.txtDEPENDS mytool in.txt)add_custom_target(drive ALL DEPENDSout.txt)
CMAKE_COMMAND: 指向cmake可执行程序的全路径。CMAKE_CROSSCOMPILING: 当前CMake是否是交叉编译。CMAKE_CTEST_COMMAND: 指向ctest可执行程序的全路径。CMAKE_CURRENT_BINARY_DIR: 当前正在处理的构建目录。每个由add_subdirectory添加的目录将会在构建树中创建一个构建目录。对于直接在源码目录中编译的情况,当前正在处理的构建目录就是当前源码所在的目录。CMAKE_CURRENT_LIST_DIR: 当前处理的CMakeLists.txt文件所在的目录。CMAKE_CURRENT_LIST_FILE: 当前处理的CMakeLists.txt文件的全路径。CMAKE_CURRENT_LIST_LINE: 当前处理的CMakeLists.txt文件的行号。CMAKE_CURRENT_SOURCE_DIR: 当前处理的源码路径。CMAKE_DL_LIBS: 包含dlopen和dlclose的库的名称。CMAKE_EDIT_COMMAND:cmake-gui或ccmake的全路径。CMAKE_EXECUTABLE_SUFFIX: 该平台上可执行程序的后缀。CMAKE_EXTRA_GENERATOR: 额外的用于构建工程的产生器。当使用Eclispse, CodeBlocks或KDevelop产生器时,CMake会产生Makefile文件以及相应的工程文件(CMAKE_GENERATOR)。该IDE工程文件产生器存储于CMAKE_EXTRA_GENERATOR(如:”Eclipse CDT4”)CMAKE_EXTRA_SHARED_LIBRARY_SUFFIXES: 共享库额外的后缀名。这些共享库不是CMAKE_SHARED_LIBRARY_SUFFIX。在分析某个目标链接的库时,CMake使用该变量识别外部共享库文件。CMAKE_GENERATOR: 构建工程的产生器。它将产生构建文件 (e.g. “Unix Makefiles”, “Visual Studio 6”, etc.)CMAKE_HOME_DIRECTORY: 源码树的顶级目录的路径。CMAKE_IMPORT_LIBRARY_PREFIX: 链接的引入库的前缀。CMAKE_IMPORT_LIBRARY_PREFIX_<LANG> overrides this for language <LANG>.CMAKE_IMPORT_LIBRARY_SUFFIX: 链接的引入库的后缀。CMAKE_IMPORT_LIBRARY_SUFFIX_<LANG> overrides this for language <LANG>.CMAKE_LINK_LIBRARY_SUFFIX: 链接的库的后缀。如Windows下是.lib。CMAKE_MAJOR_VERSION: CMake的主版本号。CMAKE_MAKE_PROGRAM: 该变量主要是为了向后兼容。参见:CMAKE_BUILD_TOOL.CMAKE_MINOR_VERSION: CMake的次版本号。CMAKE_PARENT_LIST_FILE: 正在处理的CMakeLists.txt文件的父CMakelists.txt文件所在的路径。CMAKE_PATCH_VERSION: CMake的补丁版本。CMAKE_PROJECT_NAME: 当前工程的名字。CMAKE_RANLIB: 静态库的随机化工具的名称。CMAKE_ROOT: cmake的安装目录。CMAKE_SCRIPT_MODE_FILE: 脚本模式下正在处理的脚本文件。CMAKE_SHARED_LIBRARY_PREFIX: 链接的共享库的前缀。TCMAKE_SHARED_LIBRARY_PREFIX_<LANG> overrides this for language <LANG>.CMAKE_SHARED_LIBRARY_SUFFIX:链接的共享库的后缀。CMAKE_SHARED_LIBRARY_SUFFIX_<LANG> overrides this for language <LANG>.CMAKE_SHARED_MODULE_PREFIX:链接的可加载模块的前缀。CMAKE_SHARED_MODULE_PREFIX_<LANG> overrides this for language <LANG>.CMAKE_SHARED_MODULE_SUFFIX: 链接的可加载模块的后缀。CMAKE_SHARED_MODULE_SUFFIX_<LANG> overrides this for language <LANG>.CMAKE_SIZEOF_VOID_P: void指针的大小。CMAKE_SKIP_RPATH: 如果为真,将不添加运行时路径信息。默认情况下是如果平台支持运行时信息,将会添加运行时信息到可执行程序当中。这样从构建树中运行程序将很容易。为了在安装过程中忽略掉RPATH,使用CMAKE_SKIP_INSTALL_RPATH。CMAKE_SOURCE_DIR: 源码树的最顶级目录。当在源码中编译时,与CMAKE_BINARY_DIR的值一致。CMAKE_STANDARD_LIBRARIES: 链接到每个可执行程序和共享库的标准库。它包含一系列库。CMAKE_STATIC_LIBRARY_PREFIX:链接的静态库的前缀。CMAKE_STATIC_LIBRARY_PREFIX_<LANG> overrides this for language <LANG>.CMAKE_STATIC_LIBRARY_SUFFIX: 链接的静态库的后缀。CMAKE_STATIC_LIBRARY_SUFFIX_<LANG> overrides this for language <LANG>.CMAKE_TWEAK_VERSION: CMake的tweak版本。CMAKE_USING_VC_FREE_TOOLS: 当使用了免费的Visual tools时为真。通常当编译器是Visual Studio免费工具时,该变量设为真。CMAKE_VERBOSE_MAKEFILE: 当开启时创建verbose的构建文件。默认为false.当设置为真时,CMake会产生verbose构建文件,它会显示使用到的每个命令行。CMAKE_VERSION: CMake的完全版本号。格式:major.minor.patch[.tweak[-id]]。PROJECT_BINARY_DIR: 工程的构建目录。PROJECT_NAME: 工程名。PROJECT_SOURCE_DIR: 当前工程的顶级目录。[Project name]_BINARY_DIR: 对应工程的顶级构建目录。[Project name]_SOURCE_DIR: 对应工程的源码目录。

