基本常识

动态库链接

在 Windows 上,动态库使用 .dll 后缀,但编译器无法直接链接 .dll 文件。通常,我们链接 .dll 对应的 .lib 文件,提供编译时的库信息。 在 Linux 上,直接链接.so既可。 运行时动态库搜索路径

运行时动态库搜索路径

  • Windows:动态库必须位于系统的搜索路径中。一般通过环境变量 PATH 指定,或者直接放在可执行文件所在目录。
  • Linux:运行时动态库一般通过环境变量 LD_LIBRARY_PATH 查找,确保动态库在执行时可被找到。

生成器

CMake支持多种生成器来支持不同的构建系统,默认的生成器是(cmake --help 输出中带*标记的generator) 可以设置环境变量CMAKE_GENERATOR来改变默认生成器(CMake 3.15+))。

缓存变量

CMake 支持缓存变量,它们存储在构建目录中的 CMakeCache.txt 文件中。调用 CMake 时使用 -D 参数传递的变量会自动作为缓存变量保存。
缓存变量与普通变量的区别:

  • 普通变量: 定义在 CMakeLists.txt 中,具有动态作用域,仅在当前 CMakeLists.txt 或作用域中有效。
  • 缓存变量: 全局可见,存在于整个 CMake 配置过程中,即使 CMakeLists.txt 的变量被修改,缓存变量的值不会自动更新,除非强制修改或清除缓存。

创建缓存变量的方式:

  • 通过命令行 -D 参数:cmake -DCMAKE_BUILD_TYPE=Release
  • 使用 option 命令创建布尔缓存变量option(ENABLE_FEATURE_A "feature_a" ON)
  • 使用 set 命令指定缓存类型:set(VAR "value" CACHE STRING "注释")

    注意: 缓存变量修改必须使用set的带FORCE版本set(VAR "value" CACHE STRING "注释" FORCE)

CMake配置的多种方式

  1. 使用当前工作目录作为构建树,使用 作为源代码目录。
cmake [options] <path-to-source> # 不推荐, cmake .
  1. 使用 作为构建树,并从其 CMakeCache.txt 文件加载源树的路径。
cmake [options] <path-to-existing-build> # 加载已经构建了的缓存,刷新CMakeLists的修改, cmake build/
  1. 使用 作为构建树,使用 作为源树(推荐)。

    如果仅给出一种类型的路径,则当前工作目录 (cwd) 将用于另一种路径。例如:

cmake [options] -S <path-to-source> -B <path-to-build> # 无歧义, cmake -S . -B build/

CMake标识符规则

CMake命令不去分大小写。 CMake 保留以下标识符(无论是大写、小写或混合大小写):

  • begin with CMAKE_
  • begin with_CMAKE_
  • begin with _

CMakeLists.txt 内文件路径的查找规则

路径分隔符统一以/分隔,cmake会自动处理。\需转义,即\\

  • 绝对路径:直接按照文件系统中的实际路径查找,无需依赖当前目录()。
  • 相对路径
    • 一般情况下:相对路径是相对于当前 CMakeLists.txt 文件所在的目录。例如,add_executable()add_library() 等命令使用的文件路径,都会以当前 CMakeLists.txt 的目录作为基准。
    • 特殊情况:当使用 include(script.cmake)find_package() 等命令时,路径会相对于调用这些命令的 CMakeLists.txt 文件所在的目录,而不是被包含的脚本文件。

示例

假设目录结构如下:

project/
├── CMakeLists.txt
├── src/
│   ├── CMakeLists.txt
│   └── main.cpp
├── scripts/
│   └── setup.cmake

示例代码

顶层的CMakeLists.txt 中的相对路径

cmake_minimum_required(VERSION 3.20)
project(DemoProject)

add_subdirectory(src) # project/
include(scripts/setup.cmake) # project/

src/CMakeLists.txt 中的相对路径

add_executable(app main.cpp) # project/src/

scripts/setup.cmake 文件的相对路径

message("正在处理的cmake文件路径: ${CMAKE_CURRENT_LIST_DIR}") # 输出: project/scripts
message("当前所在的CMakeLists文件路径: ${CMAKE_CURRENT_SOURCE_DIR}") # 输出: project

CMake 变量细则

CMake 的变量用来存储值,但所有值都是字符串类型。即使看起来像数字或布尔值,CMake 都会以字符串的形式存储它们。

set(VAR1 "Hello, World!")   # 设置变量 VAR1 的值为字符串 "Hello, World!"
set(VAR2 42)                # 虽然看起来是数字,但 CMake 将 "42" 存储为字符串
set(VAR3 true)              # 同样,"true" 也是字符串
  1. 变量具有动态Block Scope(作用域)。
  2. 每个变量“set”或“unset”都会在当前作用域中创建一个绑定,Block Scope结束删除绑定,每次创建作用域会复制父作用域的变量绑定。这意味着函数内部设置的变量不会渗透到父作用域中。

以下命令可以创建新的Block Scope:

  • block()
  • function()
  • add_subdirectory()

set命令语法:

set(<variable> <value>... [PARENT_SCOPE])

CMake set 命令提供了一个关于作用域的选项,PARENT_SCOPE 将变量设置到父作用域中,而不是当前作用域中。

另外,CMake缓存变量是具有全局作用域的,除非指定了FORCE选项,否则CACHE选项不会设置CACHE中已存在的变量,即set(VAR "value" CACHE STRING "注释" FORCE)

与大多编程语言不同,CMake 的循环(if)和控制流(foreach、while)没有自己的作用域。