%% # 纲要 > 主干纲要、Hint/线索/路标 # Q&A #### 已明确 #### 待明确 > 当下仍存有的疑惑 **❓<font color="#c0504d"> 有什么问题?</font>** %% # CMake 变量 > 参见[^1] ## 定义和引用变量 - **定义变量**:`set(variable value...)`。当存在多个 value 时,将作为一个 list 变量(list 变量本质上是`;`分隔的字符串列表) - **引用变量值**:`${variable}` 当在 `CMakeLists.txt` 中**引用一个变量**时: - 如果该变量在 **当前作用域中** 已通过 `set()` 命令定义,则将使用这一值; - 如果该变量在当前作用域中尚未定义,则将**使用该变量的缓存值**; - 如果该变量在即未在当前作用域中定义,也不存在缓存值,则将报错。 > [!note] 变量既可以在`CMakeLists.txt`中设置,也可以使用 `cmake` 命令时指定,二者等价,后者会覆盖前者。 > > - 在 `CMakeLists.txt`中设置值:`set(CMAKE_EXPORT_COMPILE_COMMANDS ON)` > - 使用`cmake`命令时通过 `-D` 选项设置:`cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1` > ### 选项变量 option `CMakeLists.txt` 中可通过 `option()` 命令定义一个 "**选项变量**",用作为 **==布尔值==**,可用于 `if()` 语句中。 ```cmake # 命令说明: option(<变量名> "描述信息" <默认值>) # 值可为ON或OFF. # 示例一: 默认USE_MYMATH=ON; 在cmake配置阶段,可通过`-D`选项覆盖默认值: `cmake -DUSE_MYMATH=OFF` option(USE_MYMATH "Use custom math implementation" ON) # 示例二: 指定默认构造动态库 option(BUILD_SHARED_LIBS "add_library build shared lib default" ON) ``` #### 使用示例 示例一:利用 CMake 选项变量控制 "**宏定义**",进而控制 "条件编译"。 ```cmake # 当CMake选项变量USE_MYMATH为ON时, 为MathFunctions目标定义宏"USE_MYMATH" option(USE_MYMATH "Use tutorial provided math implementation" ON) if (USE_MYMATH) target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH") # 为MathFunctions目标定义宏 "USE_MYMATH" message(STATUS "Using custom math implementation") endif() ``` ```cpp #include "MathFunctions.h" #include <cmath> // Wrap the mysqrt include in a precompiled ifdef based on USE_MYMATH #ifdef USE_MYMATH #include "mysqrt.h" #endif namespace mathfunctions { double sqrt(double x) { // If USE_MYMATH is defined, use detail::mysqrt. #ifdef USE_MYMATH return detail::mysqrt(x); #else return std::sqrt(x); #endif } } // mathfunctions end ``` --- %% ## 查看变量值 有几种方式: %% <br><br><br> # CMake 内置变量 ## 目录与路径相关变量 ###### 项目源码目录/构建目录 | 变量名 | 说明 | | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- | | CMAKE_ROOT | CMake 自身的安装目录. | | CMAKE_COMMAND | `cmake`可执行文件所在的路径. | | | | | CMAKE_SOURCE_DIR | 指向项目的**顶层源代码目录**,即包含项目**顶层 `CMakeLists.txt` 文件**的目录。 | | CMAKE_BINARY_DIR | 指向项目的**顶层二进制目录**(或称为 **==构建目录==**,即 `build/`,**CMake 运行目录**)。<br>即 CMake 生成构建系统文件(如 Makefiles 或 VS 项目文件)以及编译生成二进制文件(可执行文件或库)的顶层目录。 | | CMAKE_CURRENT_SOURCE_DIR | 指向 **当前正在处理的 `CMakeLists.txt` 文件所在的目录** | | CMAKE_CURRENT_BINARY_DIR | 指向 **当前正在构建的构建目录**。 | | | | | PROJECT_BINARY_DIR | 指向 **当前项目的构建目录**。 | | PROJECT_SOURCE_DIR | 指向 **当前项目的源代码目录**。 | > [!NOTE] `PROJECT_BINARY_DIR` 和 `PROJECT_SOURCE_DIR` 指向**当前 CMake 项目**(可能是整个工程或任何子项目)的**根目录**。 > > - 在包含**多个子项目**的构建环境中,这两个变量对于**每个子项目**来说都是唯一且不同的,分别指向各个子项目的构建目录和源代码目录。 > - 在**单个项目**的构建环境中,这两个变量就等同于 `CMAKE_SOURCE_DIR`、`CMAKE_BINARY_DIR` 。 > > [!quote] > When run in [`cmake -P`](https://cmake.org/cmake/help/latest/manual/cmake.1.html#cmdoption-cmake-P) script mode, CMake sets the variables [`CMAKE_BINARY_DIR`](https://cmake.org/cmake/help/latest/variable/CMAKE_BINARY_DIR.html#variable:CMAKE_BINARY_DIR), `CMAKE_SOURCE_DIR`, [`CMAKE_CURRENT_BINARY_DIR`](https://cmake.org/cmake/help/latest/variable/CMAKE_CURRENT_BINARY_DIR.html#variable:CMAKE_CURRENT_BINARY_DIR) and [`CMAKE_CURRENT_SOURCE_DIR`](https://cmake.org/cmake/help/latest/variable/CMAKE_CURRENT_SOURCE_DIR.html#variable:CMAKE_CURRENT_SOURCE_DIR) to the current working directory. ###### CMake 生成文件输出目录 | 变量名 | 说明 | | ------------------------------ | ----------------------------------------------------------------------------------------------------------- | | CMAKE_RUNTIME_OUTPUT_DIRECTORY | 指定**可执行文件输出路径**(该变量会用于初始化所有目标上的 RUNTIME_OUTPUT_DIRECTORY 属性) | | CMAKE_LIBRARY_OUTPUT_DIRECTORY | 指定**共享库 (`.so/.dll/.dylib`) 输出路径**(该变量用于初始化所有目标上的 LIBRARY_OUTPUT_DIRECTORY 属性。) | | CMAKE_ARCHIVE_OUTPUT_DIRECTORY | 指定**静态库 (`.a/.lib`) 输出路径**(该变量用于初始化所有目标上的 ARCHIVE_OUTPUT_DIRECTORY 属性。 | | CMAKE_INSTALL_PREFIX | `install()`命令所使用的安装路径前缀 <br>(Linux/UNIX 上默认为 `/usr/local`,Windows 上默认为 `c:/Program Files/${PROJECT_NAME}`) | > [!NOTE] 可执行文件、共享库、静态库的默认输出路径 > > 即上表变量的默认分别为 `${CMAKE_BINARY_DIR}/bin, ${CMAKE_BINARY_DIR}/lib, ${CMAKE_BINARY_DIR}/lib` > ###### CMake 搜索路径 | 变量名 | 说明 | `find_file` | `find_path` | `find_library` | `find_package` | `find_program` | `include` | | -------------------- | ---------------------------------------------------------------------------------- | ----------- | ----------- | -------------- | -------------- | -------------- | --------- | | CMAKE_FIND_ROOT_PATH | 指定 **CMake 搜索的文件系统根路径**(sysroot) <br> | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | ❌ | | CMAKE_IGNORE_PATH | 指定所有 `find_*()` 命令将**忽略**的目录列表 | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | | CMAKE_PREFIX_PATH | 指定 **额外的外部依赖项查找路径** <br>(包括库文件、头文件、可执行程序、包等) | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | | CMAKE_LIBRARY_PATH | 指定 **额外的库文件查找路径** <br>(`.so`, `.a`, `.lib` 等) | ❌ | ❌ | ✔️ | ❌ | ❌ | ❌ | | CMAKE_INCLUDE_PATH | 指定 **额外的头文件查找路径** | ❌ | ✔️ | ❌ | ❌ | ❌ | ❌ | | CMAKE_MODULE_PATH | 指定 **额外的 `.cmake` 模块查找路径** | ❌ | ❌ | ❌ | ✔️ | ❌ | ✔️ | | `<PackageName>_ROOT` | 指定 **特定依赖库/包**的根目录。 <br>该变量设置后, 对 `find_package(<PackagName>)` 命令的调用将会搜索该变量指定的路径. | | | | ✔️ | | | > [!NOTE] 上述变量设置 `find_*` 相关函数的搜索路径,均为 list 变量,路径列表值需以 "分号`;`" 分隔。 > [!NOTE] CMake 会 **==优先==** 在上述变量指定路径中进行查找,若不存在,其次再查找系统默认路径(如 `/usr/lib`, `/usr/include` 等) > [!NOTE] 关于 `CMAKE_FIND_ROOT_PATH` 变量 > > 原本 Linux 系统下 CMake 的**搜索根路径是 `/`**,其默认将在是 `/usr/lib`、`/usr/include` 等路径进行查找。 > 若设置 `CMKAE_FIND_ROOT_PATH` 为 `/opt/riscv-sysroot` 后,其**将在该路径下搜索 `/opt/riscv-sysroot/lib`、`/opt/riscv-sysroot/include` 等子目录**。 > > 通常用于**交叉编译**的场景,例如嵌入式开发:在 x86_64 PC 上编译程序,但程序要运行在 ARM/RISC-V 设备上,后者的文件系统挂载在 `/opt/riscv-sysroot` 目录下,需要使用设备中的库文件和头文件,则通过该变量**修改 CMake 搜索的系统根路径**。 > [!NOTE] 关于 `CMAKE_PREFIX_PATH` 变量 > > 该变量指定 CMake 查找**外部依赖项**(包括库文件、头文件、可执行程序、包等)的**额外目录前缀**。 > "**前缀**" 的含义是,CMake 会在这些路径下**进一步查找 `lib`、`bin`、`include` 等子目录**。 > [!example] > > ```cmake > set(CMAKE_PREFIX_PATH "/usr/local/opencv") > find_package(OpenCV REQUIRED) > include_directories(${OpenCV_INCLUDE_DIRS}) > target_link_libraries(MyApp PRIVATE ${OpenCV_LIBS}) > ``` > <br> ## 编译控制相关变量 | 变量名 | 说明 | | --------------------------------- | --------------------------------------------------------------------------- | | CMAKE_BUILD_TYPE | 指定构建类型(仅适用于 **单配置生成器**)。常见值:`Debug`、`Release`、`RelWithDebInfo`、`MinSizeRel` | | CMAKE_CONFIGURATION_TYPES | 适用于 **多配置生成器**(如 Visual Studio),可选值同 `CMAKE_BUILD_TYPE` | | | | | `CMAKE_<LANG>_STANDARD` | 指定所使用的语言标准版本。 | | `CMAKE_<LANG>_STANDARD_REQUIRED` | 为 ON 时,表示 `CMAKE_XXX_STANDARD` 变量必须被指定。 | | `CMAKE_<LANG>_EXTENSIONS` | 是否使用 **编译器扩展**(如 `-std=c++14` vs `-std=gnu++14`),默认 `ON` | | | | | `CMAKE_<LANG>_FLAGS` | 编译标志。将会传递给编译器的所有调用。 | | `CMAKE_<LANG>_FLAGS_DEBUG` | 针对 Debug 构建模式的编译标志 | | `CMAKE_<LANG>_FLAGS_RELEASE` | 针对 Release 构建模式的编译标志 | | | | | `CMAKE_EXE_LINKER_FLAGS` | 可执行文件的链接选项 | | `CMAKE_SHARED_LINKER_FLAGS` | 共享库的链接选项 | | `CMAKE_STATIC_LINKER_FLAGS` | 静态库的链接选项 | | `CMAKE_POSITION_INDEPENDENT_CODE` | 设置 `ON` 以启用 **PIC(位置无关代码)**,适用于共享库 | | | | | `CMAKE_<LANG>_OUTPUT_EXTENTION` | 为单个文件编译输出的扩展名 | > [!NOTE] CMake 构建时会组合 `CMAKE_CXX_FLAGS` 以及 "构建类型特定" 的编译选项 > > 例如,Release 配置下构建时会传递 `g++ ${CMAKE_CXX_FLAGS} {CMAKE_CXX_FLAGS_RELEASE}`。 > > - `CMAKE_CXX_FLAGS` 变量默认为空。 > - `CMAKE_CXX_FLAGS_DEBUG` 变量默认值为`-g`; > - `CMAKE_CXX_FLAGS_RELEASE` 变量默认值为 `-O3 -DNDEBUG`; > > 因此,通常可设置如下: > > ```cpp > set(CMAKE_CXX_FLAGS "-Wall -Wextra -Werror") > set(CMAKE_CXX_FLAGS_DEBUG "-g -O0") > set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG") > ``` > > [!note] `<LANG>` 表示为特定语言,例如 `C`, `CXX`。 > > ```CMake > # 指定编译器 > set(CMAKE_C_COMPILER clang) > set(CMAKE_CXX_COMPILER clang++) > > # 指定编译选项 > # 值为命令行字符串片段。因此,多个选项应该用空格分隔,带空格的选项应该加引号。 > # 对于每种语言,如果这一变量没有定义,则 CMake 将结合环境变量的值以及 CMake 对于工具链的内置默认值对该变量进行初始化并存储在缓存中. > # 如果需要为特定构建目标设置编译器选项,而不是全局地设置,应使用`target_compile_options()` > set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") # 追加添加标志, 启用额外警告信息 > set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g") # 追加标志, 启用调试信息 > set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DMY_MYCRO") # 追加标志, 定义一个宏 > # 该变量可以在用户提供的工具链文件中设置,也可以通过命令行上的-D 设置. 例如: > # cmake -DCMAKE_CXX_FLAGS="-Wall;-Wextra;-g;" <source_dir> > # cmake -DCMAKE_CXX_FLAGS="-isystem /path/to/include" <srouces_dir> > # cmake -DCMAKE_CXX_FLAGS="-Ipath/to/includes" <source_dir> > ``` ###### 编译工具链相关变量 | 变量名 | 说明 | | ----------------------- | ------------------------------------- | | `CMAKE_<LANG>_COMPILER` | 指定 **特定语言** 所用的编译器(如 `g++`、`clang++`) | | CMAKE_LINKER | 指定链接器(如 `ld`、`lld`、`gold`) | | CMAKE_AR | 指定静态库归档工具(如 `ar`) | | CMAKE_NM | 指定符号表工具(如 `nm`) | | CMAKE_RANLIB | 指定静态库索引工具(如 `ranlib`) | ###### 编译命令模版相关变量 ```cmake # -----编译命令模版相关. CMAKE_<LANG>_COMPILER_OBJECT # 定义编译单个C++源文件时使用的命令模板.包括编译器调用, 编译标志, 源文件和输出目标. # 该变量的值应当被设置为:`<编译器> <编译标志> -o <输出文件> <输入源文件>`的形式. # 这个变量通常在 CMake 的编译器配置阶段被设置,并且是根据所选编译器自动生成的, 不应由用户直接修改. # CMake 使用这个模板生成实际的构建规则,这些规则随后被构建系统(如 Makefiles、Ninja 等)使用 CMAKE_<LANG>_CREATE_SHARED_LIBRARY # 定义编译共享库(动态链接库)所用的命令模版 CMAKE_<LANG>_CREATE_SHARED_MODULE # 定义编译共享模块(通常用于插件)的命令模版 CMAKE_<LANG>_CREATE_STATIC_LIBRARY # 定义编译静态库的命令模版 ``` <br> ## 构建行为控制相关变量 | 变量名 | 说明 | | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | BUILD_SHARED_LIBS | 设置 `add_library()` **默认创建动态库/共享库**。默认为 OFF,即创建静态库。 | | CMAKE_EXPORT_COMPILE_COMMANDS | 生成过程中启用/禁用 **==编译命令输出==**。 <br>(如果为ON, 将在构建目录生成一个`compile_commands.json`文件, 该文件中包含了编译器对项目中所有可翻译单元(translation units)的精确调用(即项目中每个文件编译时所使用的精确编译器命令)) | | CMAKE_WARN_DEPRECATED | 使用**已弃用功能**时是否发出警告。默认为 ON。 <br>(运行`cmake`命令时,该选项可由 -Wdeprecated 选项启用,也可以由-Wno-deprecated 选项禁用) | | CMAKE_INCLUDE_CURRENT_DIR | 自动将当前源代码和构建目录添加到包含路径。默认为 OFF。 <br>(如果设置为ON, cmake会自动将 `CMAKE_CURRENT_SOURCE_DIR` 和 `CMAKE_CURRENT_BINARY_DIR` 添加到每个 include 路径中) | <br> ## 平台信息相关变量 | 变量 | 说明 | | ----------------------------- | ----------------------------------------------------------------------------------------------------- | | `CMAKE_HOST_SYSTEM` | 当前运行 CMake 的主机 OS 名称 <br>(该变量由`CMAKE_HOST_SYSTEM_NAME`以及 `CMAKE_HOST_SYSTEM_VERSION` 两变量的值构成,经中划线连接) | | `CMAKE_HOST_SYSTEM_NAME` | 主机 OS 名称 | | `CMAKE_HOST_SYSTEM_VERSION` | 主机 OS 版本 | | `CMAKE_HOST_SYSTEM_PROCESSOR` | 主机 CPU 架构(例如值为 `x86_64`,`aarch64` 等) | | | | | `CMAKE_SYSTEM_NAME` | 目标系统名称(如 `Linux`、`Windows`、`Darwin`) | | `CMAKE_SYSTEM_VERSION` | 目标系统版本 | | | | | `CMAKE_HOST_LINUX` | 当主机系统为 Linux 时, 该变量值为 true. | | `CMAKE_HOST_WIN32` | 当主机系统为 Windows(包括 Windows 64 以及 MYYS), 该变量值为 true. 注意, 在 Cygwin 上,该变量为 falae | <br><br><br> # CMake 缓存(Cache) > 参见[^2] CMake缓存可以被认为是一个 **配置文件 `CMakeCache.txt`** 。 **当CMake首次在一个项目上运行时,会在构建树的顶层目录中生成一个`CMakeCache.txt` 文件。** CMake 使用该文件来**存储一组全局缓存变量**(global cache variables),**这些缓存变量的值是持久的**,会在项目后续每次运行 CMake 时持续生效,直到手动清理构建目录或重新配置项目。 如果 `CMakeCache.txt` 丢失或损坏,则 CMake 会在下一次运行时重新生成,但之前的缓存信息将丢失。 CMake 缓存的目的在于: - 存储用户的选择,以便再次运行 CMake 时不需要重新输入这些信息。 > 例如,option 命令将创建一个布尔变量**并将其值存储在缓存中**,当用户再次运行 CMake 时,该变量的值将从缓存中读取。 - 在 CMake 运行之间持久地存储变量值。 > 用户可能无法看到或调整这些条目。通常,这些值是系统相关的变量,需要 CMake 编译和运行程序来确定它们的值。**一旦这些值被确定,它们被存储在缓存中,以避免每次 CMake 运行时都必须重新计算它们**。如果对计算机进行了重大更改,例如更改操作系统或切换到不同的编译器,则需要删除缓存文件。 ==注意==: - **用户不应当直接编辑修改 `CMakeCache.txt` 文件,该文件由 CMake 工具自动维护**。 > 在 CMake 中,通过 `cmake -D` 命令设置缓存初始值或是在 `CMakeLists.txt` 文件中使用 `set(<VAR> <VALUE> CACHE <TYPE> FORCE)` 等命令指定缓存值时,都会影响 `CMakeCache.txt` 文件中的内容。 > > CMake会在下次执行配置的过程中将**新设定**的缓存值写入到`CMakeCache.txt`文件(如果文件尚未存在,则会先创建)。 - 一旦一个变量位于缓存中,则其 **缓存值**(cache value)**不会被 `CMakeLists.txt` 进行修改**,例如`set(FOO ON CACHE BOOL "doc")` 这样设置 **缓存值**的命令**只在该缓存变量尚不存在时会生效**。 如果一个变量已经存在于缓存中(存在于 `CMakeCache.txt` 中),**则该命令无效,不会产生任何效果**。 但是,该**变量的值(非缓存中的值)**仍然可以通过 `set(FOO ON "doc")` 命令进行**重写**并生效。 > CMake的机制是,**缓存当中的值仅当该变量在当前作用域尚未定义时才会被使用**,如果通过不带`CACHE`关键字的`set()` 命令定义了一个普通变量,则**此处定义的变量值将被使用**,而cache中的同一变量的缓存值不会被使用。而**不带`CACHE`关键字的`set()` 命令设置的变量值不会影响 cache 中的缓存值**。 > > ```cmake > # assume that FOO is set to ON in the cache > > set(FOO OFF) > # sets foo to OFF for processing this CMakeLists file > # and subdirectories; the value in the cache stays ON > ``` - 在极少数确实需要修改**变量缓存值**的情况,需要为`set()` 命令搭配 `FORCE` 和`CACHE` 选项,`FORCE`选项将导致`set` 命令**覆盖并更改变量的缓存值**。 ```cmake # Users has previously run CMake and turned the cache value off. # The `FORCE` keywords can force the cache values to be set `ON`. # Build the vtkHybrid kit always. set(VTK_USE_HYBRID ON CACHE BOOL "doc" FORCE) ``` ### 创建缓存变量的方式 - 使用`option()` ```cmake option(USE_JPEG "include jpeg supports?")` ``` - 使用`set()` 命令并搭配 `CACHE`关键字 ```cmake set(USE_JPEG ON CACHE BOOL "include jpeg supports?") ``` - 使用`find_file()` 命令时也会将查找结果存储为一个 cache 变量 > `find_file(<VAR> name1 [path1 path2 ...] )` 命令用于查找指定文件的完整路径。 > > 如果查找成功,查找结果将保存到名为 `<VAR>` 的cache变量中(如果指定了`NO_CACHE` 则保存为普通变量);如果未找到,结果将为`<VAR>-NOTFOUND`。 ### 设置缓存初始值 > ==注意==:下列方式都会使得**下一次运行 CMake 配置过程**时 **修改 `CMakecache.txt` 中的内容**,其中方**式一的优先级最高**。 > > - 如果 `CMakeCache.txt` 文件尚不存在,则CMake会在下一次运行配置的过程中创建 `CMakeCache.txt` 文件并将**下列方式设置的缓存变量值**写入文件内。 > - 如果 `CMakeCache.txt` 文件已存在,则 CMake 会在下一次运行配置的过程中**修改该文件中的缓存变量值**。 - 方式一:使用`cmake` 命令时通过 `-D<CACHE_VAR>:TYPE=<VALUE>` **指定缓存变量的初始值**。 ```shell cmake -DBUILD_TESTING:BOOL=ON <etc..> .. ``` - 方式二:创建一个文件指定缓存变量的初始值,并在**运行`cmake` 命令时通过 `-C` 选项加载该文件** (该文件使用CMakeLists语法,内容是一系列的`set()` 命令)。 - 文件内容示例: ```cmake # Build the vtkHybrid kit. set(VTK_USE_HYBRID ON CACHE BOOL "doc string") ``` - 方式三:设置缓存变量并进行"**隐藏**(在 CMake GUI 中的 cache 编辑器中该缓存变量将不可见) 通过指定缓存变量类型为 `INTERNAL`,该缓存变量将被应用`FORCE` 并且在 CMake GUI 中的 cache 编辑器中不可见。示例: ```cmake # Build the vtkHybrid kit always and don't distract the user by showing the option. set(VTK_USE_HYBRID ON CACHE INTERNAL "doc") ``` <br><br><br> # Buffer ## 闪念 > sudden idea ## 候选资料 > Read it later # ♾️参考资料 # Footnotes [^1]: [cmake-variables(7)](https://cmake.org/cmake/help/latest/manual/cmake-variables.7.html#id1) [^2]: [CMake Cache — Mastering CMake](https://cmake.org/cmake/help/book/mastering-cmake/chapter/CMake%20Cache.html)