【CMake】cmake的3大核心:目标、属性 和 API(含大量重点函数解析)

文章目录

  • 前言
  • [一、cmake的3大核心:目标、属性 和 API](#一、cmake的3大核心:目标、属性 和 API)
    • [1. cmake的3大核心(总结表)](#1. cmake的3大核心(总结表))
    • [2. 目标的种类](#2. 目标的种类)
      • [2.1 目标主要分为两个大类:二进制目标 和 伪目标](#2.1 目标主要分为两个大类:二进制目标 和 伪目标)
      • [2.2 伪目标的用法](#2.2 伪目标的用法)
    • [3. 目标的属性](#3. 目标的属性)
      • [3.1 属性的种类 及其 作用域](#3.1 属性的种类 及其 作用域)
      • [3.2 属性的传递机制](#3.2 属性的传递机制)
    • [4. 操作目标属性的核心API](#4. 操作目标属性的核心API)
  • 二、重点函数解析
    • [1. cmake_minimum_required](#1. cmake_minimum_required)
    • [2. project](#2. project)
      • [2.1 project函数的介绍](#2.1 project函数的介绍)
      • [2.2 project() 执行后,CMake会自动创建一些变量(project()执行后,才能设置 语言的版本)](#2.2 project() 执行后,CMake会自动创建一些变量(project()执行后,才能设置 语言的版本))
    • [3. include](#3. include)
      • [3.1 include函数的介绍](#3.1 include函数的介绍)
      • [3.2 文件路径(< file >) vs 模块名(< module >)](#3.2 文件路径(< file >) vs 模块名(< module >))
      • [3.3 父目录CMakeLists.txt文件 include子目录文件后,内置变量的变化情况](#3.3 父目录CMakeLists.txt文件 include子目录文件后,内置变量的变化情况)
    • [4. add_subdirectory](#4. add_subdirectory)
      • [4.1 add_subdirectory函数的介绍](#4.1 add_subdirectory函数的介绍)
      • [4.2 父目录CMakeLists.txt文件 add_subdirectory子目录(含CMakeLists.txt)后,内置变量的变化情况](#4.2 父目录CMakeLists.txt文件 add_subdirectory子目录(含CMakeLists.txt)后,内置变量的变化情况)
    • [5. add_executable](#5. add_executable)
    • [6. add_library](#6. add_library)
    • [7. target_include_directories](#7. target_include_directories)
      • [7.1 target_include_directories函数的介绍](#7.1 target_include_directories函数的介绍)
      • [7.2 与 include_directories() 的区别](#7.2 与 include_directories() 的区别)
    • [8. target_link_libraries](#8. target_link_libraries)
    • [9. set_target_properties](#9. set_target_properties)
    • [10. file](#10. file)
      • [10.1 file函数的介绍](#10.1 file函数的介绍)
      • [10.2 文件匹配表达式语法(含示例演示)](#10.2 文件匹配表达式语法(含示例演示))

前言

include函数 与 add_subdirectory函数的对比(学习 变量知识之后补充)


一、cmake的3大核心:目标、属性 和 API

1. cmake的3大核心(总结表)

bash 复制代码
Target
│
├─ 类型
│   ├─ EXECUTABLE
│   ├─ STATIC / SHARED / MODULE
│   ├─ OBJECT / INTERFACE
│   └─ IMPORTED / ALIAS
│
├─ 属性(≈键值表)
│   ├─ Build impl ─ SOURCES COMPILE_OPTIONS ...
│   ├─ Usage reqs ─ INTERFACE_INCLUDE_DIRECTORIES ...
│   ├─ Output/Install ─ VERSION SOVERSION OUTPUT_NAME ...
│   └─ Meta ─ TYPE ALIASED_TARGET ...
│
├─ API
│   ├─ add_library / add_executable
│   ├─ target_* (compile_options, link_libraries, include_dirs)
│   ├─ set/get_target_properties
│   └─ install(TARGETS) / export
│
└─ 流程:
       配置期 → ⽬标注册 + 属性写⼊
       ⽣成期 → 属性转具化到 Make/Ninja
       构建期 → 编译 & 链接
       安装期 → 使⽤属性⽣成 cmake_install.cmake

2. 目标的种类

2.1 目标主要分为两个大类:二进制目标 和 伪目标

  • 二进制目标(Binary Targets)
类型 创建命令 产物例子 / 说明
可执行文件(Executable) add_executable(targetName sources...) 生成可直接运行的程序(如my_app.exe或my_tool)
静态库(Static Library) add_library(targetName STATIC sources...) 生成.a(Linux)或.lib(Windows)文件,代码在编译时直接嵌入可执行文件
共享库(Shared Library) add_library(targetName SHARED sources...) 生成.so(Linux)或.dll(Windows)文件,运行时动态加载
模块库(Module Library) add_library(targetName MODULE sources...) 类似共享库,但不作为依赖链接(不在target_link_libraries()右侧使用),通常作为插件动态加载(如dlopen)
对象库(Object Library) add_library(targetName OBJECT sources...) 编译源文件生成目标文件(.o/.obj),不归档或链接

  • 伪目标(Pseudo Targets): 不生成实际文件,用于构建系统逻辑组织
类型 创建命令 作用
导入目标(Imported Target) add_library(targetName [SHARED/STATIC] IMPORTED) 引用外部已存在的库(如第三方库)
别名目标(Alias Target) add_library(aliasName ALIAS targetName) (1)作用: 为目标创建 只读别名 ,简化跨目录引用(如统一接口名) (2)限制: 别名目标 不能被安装或导出
接口库(Interface Library) add_library(targetName INTERFACE) 传递编译属性(如头文件路径、宏定义),不生成二进制文件

2.2 伪目标的用法

伪目标(Pseudo Targets):不生成实际文件,用于构建系统逻辑组织

  1. 导入目标 (Imported Target)

(1) 核心作用:

引用外部已存在的库文件(如系统库或预编译的第三方库)

(2) 关键特性:

不参与构建过程

提供统一的接口链接外部库

可传递编译属性(头文件路径、链接选项等)

(3) 创建与配置:

bash 复制代码
# 基本声明
add_library(boost_system SHARED IMPORTED)

set_target_properties(boost_system PROPERTIES
  # 设置库文件路径
  IMPORTED_LOCATION /usr/lib/libboost_system.so
  
  # 设置头文件路径(自动传递给链接者)
  INTERFACE_INCLUDE_DIRECTORIES /usr/include/boost
  
  # 添加宏定义
  INTERFACE_COMPILE_DEFINITIONS BOOST_SYSTEM_DYN_LINK
)

IMPORTED_LOCATION 是一个导入目标(IMPORTED target)的属性,它用于指定 导入目标 对应的库文件在磁盘上的位置。这个属性是导入目标所特有的,并且它不会自动传播给依赖者。
IMPORTED_LOCATION 属性必须使用 绝对路径 指定导入目标对应的文件位置。

使用示例:

bash 复制代码
# 主项目中链接导入目标
add_executable(network_app main.cpp)
target_link_libraries(network_app PRIVATE boost_system)

# 使用效果:
# (1)编译时:
# 1. 自动添加头文件搜索路径:-I/usr/include/boost
# 2. 自动添加宏定义:-DBOOST_SYSTEM_DYN_LINK

# (2)链接时:
# 1. 链接 库的绝对路径:/usr/lib/libboost_system.so

  1. 别名目标 (Alias Target)

(1) 核心作用:

为目标创建不可修改的别名(只读引用)

(2) 关键特性:

简化复杂目标名的引用

统一接口(如提供命名空间)

不能安装或导出

(3) 创建方法:

bash 复制代码
# 原始库目标
add_library(complex_math_static STATIC complex.cpp)

# 创建别名(带命名空间)
add_library(math::complex ALIAS complex_math_static)

使用示例:

bash 复制代码
# 跨CMakeLists.txt统一使用别名
# 原始文件:src/CMakeLists.txt
add_library(encryption STATIC aes.cpp)
add_library(crypto::encryption ALIAS encryption)

# 其他文件:app/CMakeLists.txt
add_executable(secure_chat chat.cpp)
target_link_libraries(secure_chat PRIVATE 
  math::complex      # 来自不同目录的库
  crypto::encryption # 通过别名统一接口
)

# 优势:
# 1. 避免目标名冲突
# 2. 代码可读性强
# 3. 解耦具体实现

  1. 接口库 (Interface Library)

(1) 核心作用:

传递编译属性,不生成任何二进制文件

(2) 关键特性:

纯属性容器(头文件路径、宏定义、编译选项)

通过 INTERFACE 关键字设置传递属性

适用于头文件库和依赖聚合

(3) 典型应用场景

  • 场景1:头文件库(Header-only)
bash 复制代码
# 创建接口库
add_library(eigen INTERFACE)

# 设置头文件路径(自动传递给链接者)
target_include_directories(eigen INTERFACE
  /usr/include/eigen3
  ${PROJECT_SOURCE_DIR}/external/eigen
)

# 添加宏定义
target_compile_definitions(eigen INTERFACE 
  EIGEN_NO_DEBUG
  EIGEN_USE_BLAS
)

# 使用
add_executable(matrix_demo demo.cpp)
target_link_libraries(matrix_demo PRIVATE eigen)  # 传递头文件和宏
  • 场景2:依赖聚合(Dependency Aggregation)
bash 复制代码
# 创建聚合接口库
add_library(logging_dependencies INTERFACE)

# 聚合多个库的依赖
target_link_libraries(logging_dependencies INTERFACE
  spdlog::spdlog    # 日志库
  fmt::fmt          # 格式化库
  Boost::date_time  # 日期处理
)

# 使用
add_library(network_service network.cpp)
target_link_libraries(network_service PRIVATE 
  logging_dependencies  # 单行链接所有日志相关依赖
)

3. 目标的属性

3.1 属性的种类 及其 作用域

类别 作用域 典型场景
目标属性 (Target) 单个构建目标(库、可执行、接口库...) 控制库/可执行文件的输出行为
目录属性 (Directory) 当前源码目录 及其 子目录 统一目录下的编译标准
全局属性 (Global) 整个项目 跨模块文件收集
缓存属性 (Cache) 跨 CMake 运行(持久化) 用户配置选项
源文件属性 (Source) 单个文件 文件级编译定制
测试属性 (Test) 由 add_test() 创建的单个测试用例 测试环境配置
安装属性(Install) 由install() 生成的安装清单条目 安装路径的配置

(1) 目标属性(Target Properties)

  1. 作用域:绑定到特定构建目标(可执行文件、库)

  2. 典型属性:

  • ARCHIVE_OUTPUT_DIRECTORY
    控制静态库(.a/.lib)的输出路径:
bash 复制代码
set_target_properties(MyLib PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "lib/static")
  • LIBRARY_OUTPUT_DIRECTORY
    控制动态库(.so/.dll)的输出路径:
bash 复制代码
set_target_properties(MyLib PROPERTIES LIBRARY_OUTPUT_DIRECTORY "lib/shared")
  1. 目标属性的传递机制:
    通过 PRIVATE/PUBLIC/INTERFACE 控制依赖传播:
bash 复制代码
target_include_directories(MyLib PUBLIC include)  # 头文件路径传递给依赖者

(2) 目录属性(Directory Properties)

  1. 作用域:当前目录及子目录

  2. 典型属性:

  • INCLUDE_DIRECTORIES
    目录级头文件搜索路径:
bash 复制代码
set_property(DIRECTORY PROPERTY INCLUDE_DIRECTORIES "include/")
  • CMAKE_CXX_STANDARD
    设置目录内的 C++ 标准:
bash 复制代码
set_property(DIRECTORY PROPERTY CMAKE_CXX_STANDARD 17)

(3) 全局属性(Global Properties)

  1. 作用域:整个项目

  2. 典型属性:

  • ALL_SOURCE_FILES
    收集全项目源文件(跨模块共享):
bash 复制代码
set_property(GLOBAL PROPERTY ALL_SOURCE_FILES "")  # 初始化
set_property(GLOBAL PROPERTY ALL_SOURCE_FILES APPEND src/main.cpp)  # 追加文件
  • COMPILE_OPTIONS
    全局编译选项(慎用,可能污染):
bash 复制代码
set_property(GLOBAL PROPERTY COMPILE_OPTIONS "-Wall")

(4) 缓存变量属性(Cache Properties)

  1. 作用域: 持久化存储(跨 CMake 运行)

  2. 典型属性:

  • STRINGS
    限制缓存变量的可选值:
bash 复制代码
set(CRYPTOBACKEND "OpenSSL" CACHE STRING "Backend selector")
set_property(CACHE CRYPTOBACKEND PROPERTY STRINGS "OpenSSL" "LibTomCrypt")  # 下拉菜单选项

(5) 源文件属性(Source File Properties)

  1. 作用域:单个源文件(.cpp/.h)

  2. 典型属性:

  • COMPILE_DEFINITIONS
    文件级宏定义:
bash 复制代码
set_property(SOURCE src.cpp PROPERTY COMPILE_DEFINITIONS "USE_DEBUG=1")
  • COMPILE_FLAGS
    文件级编译选项:
bash 复制代码
set_property(SOURCE legacy.cpp PROPERTY COMPILE_FLAGS "-Wno-deprecated")

(6) 测试属性(Test Properties)

  1. 作用域:由 add_test() 创建的单个测试用例

  2. 典型属性:

bash 复制代码
add_test(NAME BatteryTest 
		 COMMAND battery_tester
)

# 设置测试属性
set_tests_properties(BatteryTest PROPERTIES
  ENVIRONMENT "TEMP_DIR=/tmp;LOG_LEVEL=debug"  # 环境变量
  TIMEOUT 120                                  # 超时时间(秒)
  LABELS "hardware;critical"                   # 测试分类标签
  COST 1.5                                     # 资源消耗权重
)

(7) 安装属性(Install Properties)

  1. 作用域:由install() 生成的安装清单条目

  2. 典型属性:

  • 目标安装属性(Target Install Properties)
    绑定到具体目标(可执行文件/库):
bash 复制代码
# 设置目标安装路径和组件
set_target_properties(MyApp PROPERTIES
  RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
  INSTALL_RPATH "$ORIGIN/../lib"  # 安装后库搜索路径
  ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/static"
)

# 安装目标时应用属性
install(TARGETS MyApp
  RUNTIME DESTINATION bin COMPONENT Runtime
  LIBRARY DESTINATION lib COMPONENT Development
)
  • 文件安装属性(File Install Properties)
bash 复制代码
# 设置文件权限和组件
install(FILES "config.ini"
  DESTINATION etc
  PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ
  COMPONENT Config
)
  • 目录安装属性(Directory Install Properties)
bash 复制代码
# 目录安装控制
install(DIRECTORY docs/
  DESTINATION share/doc
  FILE_PERMISSIONS OWNER_READ
  DIRECTORY_PERMISSIONS OWNER_READ OWNER_EXECUTE
)

3.2 属性的传递机制

关键字 对当前目标的影响 是否传播 对当前目标依赖者的影响 解释
PRIVATE ✅ 生效 ❌ 否 ❌ 不生效 只自己用
PUBLIC ✅ 生效 ✅ 是 ✅ 生效 自己-依赖者用
INTERFACE ❌ 不生效 ✅ 是 ✅ 生效 自己不用-依赖者用

4. 操作目标属性的核心API

类别 典型命令(可选关键词) 主要作用 涉及的核心属性(部分示例)
通用读 / 写接口 set_target_properties() get_target_property() 任意目标属性的设置、追加、查询(最底层API) 任何 prop_tgt
编译阶段相关 target_compile_definitions target_compile_options target_precompile_headers target_include_directories target_sources 控制源文件编译:宏定义、编译选项 、语言特性、预编译头、包含目录、源文件列表等 COMPILE_DEFINITIONS(预处理器宏定义) COMPILE_OPTIONS(编译器选项) COMPILE_FEATURES PRECOMPILE_HEADERS INCLUDE_DIRECTORIES(指定头文件搜索路径) SOURCES 等
链接 & 输出阶段相关 target_link_libraries target_link_options target_link_directories 配置目标被链接时的库、选项及搜索路径 LINK_LIBRARIES(目标直接链接的库列表) INTERFACE_LINK_LIBRARIES(目标传递给依赖者的库列表) LINK_OPTIONS(设置目标专用的链接器) INTERFACE_LINK_OPTIONS(传递链接器选项给依赖目标) LINK_DIRECTORIES(目标链接时的库搜索路径) INTERFACE_LINK_DIRECTORIES(传递库搜索路径给依赖目标)
安装 & 打包阶段相关 install(TARGETS ...) install(EXPORT ...) 生成安装规则与包,控制目标在安装树中的 布局 及其 运行时行为 RUNTIME_OUTPUT_DIRECTORY(可执行文件的输出路径) LIBRARY_OUTPUT_DIRECTORY(动态库的输出路径) ARCHIVE_OUTPUT_DIRECTORY(静态库的输出路径) EXPORT_NAME INSTALL_RPATH

二、重点函数解析

1. cmake_minimum_required

  • 函数作用:

指定项目所需的最低 CMake 版本,应该放在顶级 CMakeLists.txt 的第一行。

  • 函数形式:
c 复制代码
cmake_minimum_required(VERSION <min>[...<policy_max>] [FATAL_ERROR])
  • 参数解析:
参数 含义 示例
VERSION 关键字,表示后面跟的是版本号
< min > 必需参数,指定最低 CMake 版本(格式:major.minor[.patch],如 3.10.2) VERSION 3.5
< policy_max > 可选参数(CMake ≥3.12 支持),指定策略兼容的最高版本,需 ≥ < min > VERSION 3.10...3.15
FATAL_ERROR(可选) 历史遗留参数(CMake ≤2.4 需显式指定),现代版本中可忽略。 若指定,当 CMake 版本不满足时会终止配置并报错(CMake 2.6+ 默认为此行为,所以不用显式指定) VERSION 3.5 FATAL_ERROR
  • 举例说明:

(1) 示例一:

bash 复制代码
cmake_minimum_required(VERSION 3.10)  # 要求 CMake ≥3.10

若系统 CMake 为 3.9,则输出:

bash 复制代码
CMake Error: CMake 3.10 or higher is required. You are running version 3.9

(2) 示例二:

bash 复制代码
cmake_minimum_required(VERSION 3.10...3.15)  # 要求 CMake 版本在 3.10 至 3.15 之间

版本检查:

若当前 CMake 版本 低于 < min >(此处是3.10),直接报错终止构建。

若当前版本 高于 < policy_max >(此处是3.15),仍可构建,但策略行为按 < policy_max >(3.15) 生效。


2. project

2.1 project函数的介绍

  • 函数作用:

指定 全局唯一项目名称 ( 后续通过 ${PROJECT_NAME} 引用 )
该函数 放在顶级CMakeLists文件的第二行,子目录的CMakeLists文件 中一般无需调用。

大多数项目都只有⼀个名字,复杂项目如c++的boost 库,每一个子功能都设置一个project

  • 基本形式:
bash 复制代码
project(<PROJECT-NAME>)
  • 完整形式
bash 复制代码
project(<PROJECT-NAME>
		[VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]
		[DESCRIPTION <project-description-string>]
		[HOMEPAGE_URL <url-string>]
		[LANGUAGES <language-name>...])
  • 参数解析:
参数 作用
< PROJECT-NAME > 项目名称(如 MyProject),用于生成默认变量(如 PROJECT_NAME)
VERSION(可选) 项目版本号(如 1.0.0),会自动定义 PROJECT_VERSION 等变量
DESCRIPTION(可选) 项目描述信息(用于生成文档或包配置)
HOMEPAGE_URL(可选) 项目主页 URL
LANGUAGES(可选) 指定项目支持的语言(如 C、CXX、Fortran、ASM 等)
  • 举例说明:

(1) 示例一:

bash 复制代码
project(MyBackendService)  # 定义项目名称为"MyBackendService"

创建全局唯一项目名称 "MyBackendService"(后续通过 ${PROJECT_NAME} 引用)

如未使用 LANGUAGES 显式指定项目支持的语言:
会自动激活 C 和 CXX(C++)语言支持, 触发编译器探测(默认查找 gcc/g++ 或 clang/clang++)

(2) 示例二:

bash 复制代码
project(MyBackendService
	  VERSION 1.4.0                  # 版本管理
	  DESCRIPTION "High-performance TCP server" 
	  HOMEPAGE_URL "https://github.com/your/repo"
	  LANGUAGES CXX                  # 显式指定 C++(重要!)
)

如果编写C++代码,强烈建议指定 LANGUAGES CXX,只使用C++语言支持(禁用C语言),避免C/C++混合编译的符号冲突

2.2 project() 执行后,CMake会自动创建一些变量(project()执行后,才能设置 语言的版本)

变量 描述
PROJECT_NAME 项目名称(如 MyProject)
CMAKE_PROJECT_NAME 顶级项目名称(与 PROJECT_NAME 相同)
PROJECT_SOURCE_DIR 顶级 CMakeLists.txt 所在目录(即源文件树根目录)
PROJECT_BINARY_DIR 构建目录(如 build/)
PROJECT_VERSION 完整版本号(如 1.2.3)
PROJECT_VERSION_MAJOR 主版本号(如 1)
PROJECT_VERSION_MINOR 次版本号(如 2)
PROJECT_VERSION_PATCH 修订号(如 3)

  • 在 project() 选择了项目支持的语言之后,还能紧接着设置 语言的版本:
bash 复制代码
project(MyService 
  	  LANGUAGES CXX
)

set(CMAKE_CXX_STANDARD 20)          # 启用 C++20
set(CMAKE_CXX_STANDARD_REQUIRED ON) # 强制要求
配置组合 行为描述
CMAKE_CXX_STANDARD=20 REQUIRED=OFF(默认) 尝试使用 C++20,若编译器不支持(如仅支持 C++17),则自动回退到编译器支持的最高版本,仅输出警告。
CMAKE_CXX_STANDARD=20 REQUIRED=ON(强制要求) 严格检查编译器对 C++20 的支持,若不满足则立即报错,停止构建。

3. include

3.1 include函数的介绍

  • 函数作用:

加载并执行指定文件(.cmake)或 模块中的 CMake 代码,被包含的代码会立即在 当前作用域 执行,如同直接粘贴到 include 位置。

作用域特性:
默认全局生效,被包含文件中定义的变量、函数、宏会覆盖当前作用域的同名对象

  • 函数形式:
bash 复制代码
include(<file|module> [OPTIONAL] [RESULT_VARIABLE <var>] [NO_POLICY_SCOPE])
  • 参数解析:
参数 作用 示例
OPTIONAL(可选) 文件不存在时不报错,静默跳过(否则会触发 FATAL_ERROR) include(legacy.cmake OPTIONAL)
RESULT_VARIABLE(可选) 将查找结果存入变量:成功时为文件绝对路径,失败时为 NOTFOUND cmake include(config.cmake RESULT_VARIABLE found) if(NOT found)  message(WARNING "未找到配置文件") endif()
NO_POLICY_SCOPE(可选) 阻止被包含文件中的策略设置(如 cmake_policy)影响当前作用域 cmake include(policy.cmake NO_POLICY_SCOPE)

3.2 文件路径(< file >) vs 模块名(< module >)

类型 定义 搜索规则
文件路径 直接指向 .cmake 脚本的绝对路径 或 相对路径(如 cmake/config.cmake) (1)如果是绝对路径,直接从对应路径加载 (2)如果指定的是相对路径,则相对当前的正在执行的CMakeLists.txt 所在的目录
模块名 代表 用户自定义 或 CMake 内置的模块名(如 FindOpenSSL) 1. 在 CMAKE_MODULE_PATH 中查找 <模块名>.cmake 2. 在 CMake 安装目录的 Modules/ 下查找内置模块

关键区别:
文件路径需显式指定文件扩展名(通常为 .cmake),而模块名无需扩展名,CMake 自动补全


  • 文件路径:绝对路径 和 相对路径
  1. 相对路径

解析规则:相对于 当前处理的 CMakeLists.txt 所在目录(CMAKE_CURRENT_LIST_DIR)。

bash 复制代码
# 假设目录结构:
#   project/
#   ├── CMakeLists.txt
#   └── cmake/
#       └── config.cmake

include(cmake/config.cmake)          # ✅ 正确:相对当前 CMakeLists.txt
  1. 绝对路径

解析规则:直接使用完整路径,不依赖上下文。

bash 复制代码
include(/home/user/project/cmake/config.cmake)     # ✅ 明确指定位置
include(${PROJECT_SOURCE_DIR}/cmake/helpers.cmake) # ✅ 通过变量动态构建绝对路径

  • 模块名:模块路径的定制(修改 CMAKE_MODULE_PATH)
  1. 在 CMAKE_MODULE_PATH 中查找用户自定义的 <模块名>.cmake
bash 复制代码
# 项目结构:
#   project/
#   ├── CMakeLists.txt
#   └── cmake/
#       ├── modules/
#       │   └── FindMyLib.cmake
#       └── ProjectOptions.cmake

# 根 CMakeLists.txt
list(APPEND CMAKE_MODULE_PATH
    ${CMAKE_CURRENT_LIST_DIR}/cmake         # 添加 cmake 目录,这样可以直接 include(ProjectOptions)
    ${CMAKE_CURRENT_LIST_DIR}/cmake/modules # 添加 modules 目录,这样可以直接 include(FindMyLib)
)
include(ProjectOptions)        # 从 cmake/ 目录查找 ProjectOptions.cmake
include(FindMyLib)             # 从 cmake/modules/ 目录查找 FindMyLib.cmake
  1. 使用内置模块
bash 复制代码
include(CheckCXXCompilerFlag)  # ✅ 模块名:查找 CMake 内置的 CheckCXXCompilerFlag.cmake

cmake的更多内置模块,咨询AI

3.3 父目录CMakeLists.txt文件 include子目录文件后,内置变量的变化情况

  • 4个描述位置 的cmake内置变量说明:
变量名 含义
CMAKE_CURRENT_SOURCE_DIR 表示当前正在处理的CMakeLists.txt文件所在的源目录路径
CMAKE_CURRENT_BINARY_DIR 表示当前正在处理的CMakeLists.txt对应的构建目录路径
CMAKE_CURRENT_LIST_FILE 表示当前正在处理的列表文件(CMakeLists.txt 或 .cmake文件)的完整路径
CMAKE_CURRENT_LIST_DIR 表示当前正在处理的列表文件(CMakeLists.txt 或 .cmake文件)所在的目录路径

bash 复制代码
# 项目结构
project/                  # 源目录:/home/user/project (假设的源文件目录绝对路径)
├── CMakeLists.txt        # 父级文件
├── build/                # 构建目录(在project下创建)
└── sub/                  # 子目录
    ├── sub.cmake         # 自定义脚本
    └── CMakeLists.txt    # 子构建文件
  • 父目录CMakeLists.txt文件 include子目录文件后,内置变量的变化情况:
bash 复制代码
# 父级 CMakeLists.txt 内容( project/CMakeLists.txt  )
cmake_minimum_required(VERSION 3.10)
project(ParentProject)

message("【父级】SOURCE_DIR: ${CMAKE_CURRENT_SOURCE_DIR}")
message("【父级】BINARY_DIR: ${CMAKE_CURRENT_BINARY_DIR}")
message("【父级】LIST_FILE: ${CMAKE_CURRENT_LIST_FILE}")
message("【父级】LIST_DIR: ${CMAKE_CURRENT_LIST_DIR}")

include(sub/sub.cmake)              # 包含子目录的 .cmake 文件
include(sub/CMakeLists.txt)         # 包含子目录的 CMakeLists.txt

(1) 场景一:include(sub/sub.cmake) 时的变量变化

bash 复制代码
# sub/sub.cmake 文件内容
message(">> 【sub.cmake】SOURCE_DIR: ${CMAKE_CURRENT_SOURCE_DIR}")
message(">> 【sub.cmake】BINARY_DIR: ${CMAKE_CURRENT_BINARY_DIR}")
message(">> 【sub.cmake】LIST_FILE: ${CMAKE_CURRENT_LIST_FILE}")
message(">> 【sub.cmake】LIST_DIR: ${CMAKE_CURRENT_LIST_DIR}")

输出结果(与父级 CMakeLists.txt 的输出结果对比):

bash 复制代码
【父级】SOURCE_DIR: /home/user/project
【父级】BINARY_DIR: /home/user/project/build
【父级】LIST_FILE: /home/user/project/CMakeLists.txt
【父级】LIST_DIR: /home/user/project

>> 【sub.cmake】SOURCE_DIR: /home/user/project               # 不变
>> 【sub.cmake】BINARY_DIR: /home/user/project/build         # 不变
>> 【sub.cmake】LIST_FILE: /home/user/project/sub/sub.cmake  # 更新为当前文件
>> 【sub.cmake】LIST_DIR: /home/user/project/sub             # 更新为当前目录

(2) 场景二:include(sub/CMakeLists.txt) 时的变量变化

bash 复制代码
# sub/CMakeLists.txt
message("  [子CMake] SOURCE_DIR: ${CMAKE_CURRENT_SOURCE_DIR}")
message("  [子CMake] BINARY_DIR: ${CMAKE_CURRENT_BINARY_DIR}")
message("  [子CMake] LIST_FILE: ${CMAKE_CURRENT_LIST_FILE}")
message("  [子CMake] LIST_DIR: ${CMAKE_CURRENT_LIST_DIR}")

输出结果(与父级 CMakeLists.txt 的输出结果对比):

bash 复制代码
>> [子CMake] SOURCE_DIR: /home/user/project                    # 不变
>> [子CMake] BINARY_DIR: /home/user/project/build              # 不变
>> [子CMake] LIST_FILE: /home/user/project/sub/CMakeLists.txt  # 更新为当前文件
>> [子CMake] LIST_DIR: /home/user/project/sub                  # 更新为当前目录
变量名 进入子目录后是否变化? 说明
CMAKE_CURRENT_SOURCE_DIR 不变化 还是父目录的源代码树的目录
CMAKE_CURRENT_BINARY_DIR 不变化 还是父目录的构建树的目录
CMAKE_CURRENT_LIST_FILE ✅ 变化 变为子目录的 CMakeLists.txt 或 .cmake文件 的文件全路径
CMAKE_CURRENT_LIST_DIR ✅ 变化 变为子目录的 CMakeLists.txt 或 .cmake文件 的文件目录

  • 终极总结:

(1)include() 本质是代码插入:

不改变构建上下文 ,所有路径操作仍在 父级作用域 进行

SOURCE_DIR / BINARY_DIR 始终指向父级上下文,即使包含的是子目录的 CMakeLists.txt

(2)四个变量的黄金法则:

SOURCE_DIR / BINARY_DIR: 构建作用域的位置
LIST_FILE / LIST_DIR: 当前文件的物理位置

4. add_subdirectory

4.1 add_subdirectory函数的介绍

  • 函数作用:

递归执行子目录的 CMakeLists.txt,实现多层级项目管理

子目录的CMakeLists.txt文件中 定义的目标(库/可执行文件)在父目录中自动可见,可直接链接

  • 函数形式:
bash 复制代码
add_subdirectory(source_dir 
		[binary_dir] 
		[EXCLUDE_FROM_ALL]
)
  • 参数解析:
参数 作用 示例
source_dir 必需包含 子目录路径(相对当前 CMakeLists.txt 所处目录 或 绝对路径),需包含 CMakeLists.txt 文件 add_subdirectory(lib)
binary_dir 可选。指定子目录的构建输出路径(默认与 source_dir 同名) add_subdirectory(lib build/lib) → 输出到 build/lib 目录
EXCLUDE_FROM_ALL(不重要) 可选。排除子目录目标从默认构建(make all)。若父目标依赖子目标,仍会构建 add_subdirectory(tests EXCLUDE_FROM_ALL) → 需手动构建测试目标
  • 举例说明:
bash 复制代码
# 项目结构
project/
├── CMakeLists.txt
├── app.cpp
└── mylib/
    ├── CMakeLists.txt  # add_library(mylib ...)
    └── mylib.cpp

# 父目录 CMakeLists.txt
add_subdirectory(mylib)                # 引入子目录
add_executable(myapp app.cpp)
target_link_libraries(myapp PRIVATE mylib)  # 链接子目录库

4.2 父目录CMakeLists.txt文件 add_subdirectory子目录(含CMakeLists.txt)后,内置变量的变化情况

  • 4个描述位置 的cmake内置变量说明:
变量名 含义
CMAKE_CURRENT_SOURCE_DIR 表示当前正在处理的CMakeLists.txt文件所在的源目录路径
CMAKE_CURRENT_BINARY_DIR 表示当前正在处理的CMakeLists.txt对应的构建目录路径
CMAKE_CURRENT_LIST_FILE 表示当前正在处理的列表文件(CMakeLists.txt 或 .cmake文件)的完整路径
CMAKE_CURRENT_LIST_DIR 表示当前正在处理的列表文件(CMakeLists.txt 或 .cmake文件)所在的目录路径

  • 假设我们有以下项目结构:
bash 复制代码
# 项目结构
/root-project/
├── CMakeLists.txt          # 父目录的CMakeLists.txt
├── build/                  # 构建目录(我们在此执行cmake命令)
└── lib/
    └── CMakeLists.txt      # 子目录的CMakeLists.txt

父目录的CMakeLists.txt(/root-project/CMakeLists.txt):

bash 复制代码
cmake_minimum_required(VERSION 3.10)
project(RootProject)

message("\n=== 父目录 (开始) ===")
message("CMAKE_CURRENT_SOURCE_DIR: ${CMAKE_CURRENT_SOURCE_DIR}")
message("CMAKE_CURRENT_BINARY_DIR: ${CMAKE_CURRENT_BINARY_DIR}")
message("CMAKE_CURRENT_LIST_FILE: ${CMAKE_CURRENT_LIST_FILE}")
message("CMAKE_CURRENT_LIST_DIR: ${CMAKE_CURRENT_LIST_DIR}")

# 添加子目录
add_subdirectory(lib)

message("\n=== 父目录 (子目录处理后) ===")
message("CMAKE_CURRENT_SOURCE_DIR: ${CMAKE_CURRENT_SOURCE_DIR}")
message("CMAKE_CURRENT_BINARY_DIR: ${CMAKE_CURRENT_BINARY_DIR}")
message("CMAKE_CURRENT_LIST_FILE: ${CMAKE_CURRENT_LIST_FILE}")
message("CMAKE_CURRENT_LIST_DIR: ${CMAKE_CURRENT_LIST_DIR}")

子目录的CMakeLists.txt(/root-project/lib/CMakeLists.txt):

bash 复制代码
message("\n=== 子目录 ===")
message("CMAKE_CURRENT_SOURCE_DIR: ${CMAKE_CURRENT_SOURCE_DIR}")
message("CMAKE_CURRENT_BINARY_DIR: ${CMAKE_CURRENT_BINARY_DIR}")
message("CMAKE_CURRENT_LIST_FILE: ${CMAKE_CURRENT_LIST_FILE}")
message("CMAKE_CURRENT_LIST_DIR: ${CMAKE_CURRENT_LIST_DIR}")
  • 输出结果(从 /root-project/build 目录执行 cmake ...):
bash 复制代码
=== 父目录 (开始) ===
CMAKE_CURRENT_SOURCE_DIR: /root-project
CMAKE_CURRENT_BINARY_DIR: /root-project/build
CMAKE_CURRENT_LIST_FILE: /root-project/CMakeLists.txt   
CMAKE_CURRENT_LIST_DIR: /root-project

=== 子目录 ===
CMAKE_CURRENT_SOURCE_DIR: /root-project/lib                  # 切换到子目录源代码路径 
CMAKE_CURRENT_BINARY_DIR: /root-project/build/lib            # 切换到子目录构建路径
CMAKE_CURRENT_LIST_FILE: /root-project/lib/CMakeLists.txt    # 切换到当前正在处理的CMakeLists.txt文件 的路径
CMAKE_CURRENT_LIST_DIR: /root-project/lib                    # 切换到当前正在处理的CMakeLists.txt文件 的目录

=== 父目录 (子目录处理后) ===
CMAKE_CURRENT_SOURCE_DIR: /root-project                     
CMAKE_CURRENT_BINARY_DIR: /root-project/build
CMAKE_CURRENT_LIST_FILE: /root-project/CMakeLists.txt
CMAKE_CURRENT_LIST_DIR: /root-project
变量名 进入子目录后是否变化? 说明
CMAKE_CURRENT_SOURCE_DIR ✅ 变化 切换到子目录源代码路径
CMAKE_CURRENT_BINARY_DIR ✅ 变化 切换到子目录构建路径
CMAKE_CURRENT_LIST_FILE ✅ 变化 变为子目录的 CMakeLists.txt 文件 的文件全路径
CMAKE_CURRENT_LIST_DIR ✅ 变化 变为子目录的 CMakeLists.txt 文件 的文件目录

  • 关键点总结

(1) 作用域绑定:这些变量始终与当前正在处理的 CMakeLists.txt 文件相关联

(2) 作用域切换:当 add_subdirectory() 被调用时:

复制代码
- 构建上下文切换到子目录
- 所有四个变量更新为子目录的值
- 子目录处理完毕后,上下文返回到父目录
- 所有变量恢复为父目录的值

5. add_executable

  • 函数作用:

指示 cmake 从源代码生成一个可执行文件

  • 函数形式:
bash 复制代码
add_executable(<target_name> 
	           [source1] [source2 ...]
)
  • 参数解析:
参数 作用
target_name 可执行文件的名称(不包含扩展名,如 myapp),项目内部唯一
[source ...] 源文件列表(如 src/main.cpp)
  • 举例说明:

(1) 示例一(生成 可执行文件):

通过源文件构建可执行目标:

bash 复制代码
add_executable(my_app main.cpp utils.cpp)

可用 file(GLOB) 收集源文件:

bash 复制代码
file(GLOB APP_SOURCES "src/*.cpp")  # 收集 src 目录下所有 .cpp 文件
add_executable(app ${APP_SOURCES})

默认情况下,将在与调用命令的CMakeLists.txt的目录相对应的 build tree directory 中创建可执行文件。 可以通过 RUNTIME_OUTPUT_DIRECTORY 目标属性,去更改可执行文件的 默认输出位置

(2) 示例二(导入 可执行文件):

引用外部预编译的可执行文件(如系统工具):

bash 复制代码
add_executable(git_tool IMPORTED)
set_property(TARGET git_tool PROPERTY 
    	     IMPORTED_LOCATION "/usr/bin/git"
)

(3) 示例三(为可执行文件 创建别名):

为现有目标(可执行文件)创建 别名(只读引用):

bash 复制代码
add_executable(app_alias ALIAS my_app)  # 为 my_app 创建别名

不能通过别名 修改原目标属性(如 target_link_libraries)
别名不能 被安装或导出

6. add_library

  • 函数作用:

CMake 中定义库目标的核心命令,支持创建多种类型的库

  • 函数形式:
bash 复制代码
add_library(<name> [STATIC | SHARED | MODULE] [EXCLUDE_FROM_ALL] [<source>...])
  • 参数解析:
参数 作用 规则与示例
< name > 必需,指定库的名称(不包含前缀和后缀,如 Foo 会生成 libFoo.a)。项目中必须全局唯一
STATIC / SHARED / MODULE 可选,指定库类型(不显示指定时,会使用默认值。默认由 BUILD_SHARED_LIBS 决定) - STATIC:静态库(.a/.lib) - SHARED:动态库(.so/.dll) - MODULE:插件库(运行时动态加载)
EXCLUDE_FROM_ALL(不重要,很少用) 可选,目标不参与默认构建(如 make all)
< source >... 源文件列表 支持通配符(file(GLOB))和 生成器表达式($<...>)
  • 举例说明:

(1) 静态库(STATIC)

bash 复制代码
add_library(mylib STATIC src/file1.cpp src/file2.cpp)

(2) 共享库(SHARED)

bash 复制代码
add_library(mylib SHARED src/file1.cpp)

(3) 模块库(MODULE):类似共享库,但不直接链接,需通过 dlopen() 动态加载

bash 复制代码
add_library(mymodule MODULE src/plugin.cpp)

(4) 对象库(OBJECT):仅编译源文件为 .o/.obj,不归档或链接,供其他目标复用

bash 复制代码
add_library(myobj OBJECT src/utils.cpp)
add_executable(app $<TARGET_OBJECTS:myobj> src/main.cpp)  # 复用对象文件

(5) 接口库(INTERFACE):不生成实际文件。仅传递头文件路径/编译定义等依赖

bash 复制代码
add_library(myheader INTERFACE)
# 其他目标链接此库自动继承头文件路径
target_include_directories(myheader INTERFACE include/)

target_link_libraries(app PRIVATE myheader)

(6) 导入库(IMPORTED):不生成实际文件。引用外部预编译库

bash 复制代码
add_library(openssl SHARED IMPORTED)
set_target_properties(openssl PROPERTIES
	  IMPORTED_LOCATION "/usr/lib/libssl.so"
	  INTERFACE_INCLUDE_DIRECTORIES "/usr/include/openssl"
)

(7) 别名库(ALIAS):不生成实际文件。为目标创建只读别名(简化引用,不修改原目标)

bash 复制代码
add_library(mylib::alias ALIAS mylib)  # 通过别名链接

target_link_libraries(app PRIVATE mylib::alias)

7. target_include_directories

7.1 target_include_directories函数的介绍

  • 函数作用:

用于为特定目标(如库或可执行文件)添加头文件搜索路径的核心命令

  • 函数形式:
bash 复制代码
target_include_directories(<target>
	    [SYSTEM] 
	    [BEFORE] 
	    <INTERFACE|PUBLIC|PRIVATE> path1 [path2 ...]
	    [<INTERFACE|PUBLIC|PRIVATE> pathN ...] ...
)
  • 参数解析:
参数 作用 说明
< target > 指定需添加头文件路径的 目标(如 my_lib 或 my_app) 必须是通过 add_executable() 或 add_library() 创建的目标,不能是别名目标(ALIAS target)
SYSTEM(不重要,很少用) 可选,系统头文件标记
BEFORE(不重要,很少用) 可选,将路径插入到已有列表最前面
INTERFACE / PUBLIC / PRIVATE 属性的作用域关键字
path1 [path2 ...] 指定头文件路径 (1)绝对路径 (2)相对路径:相对于 CMAKE_CURRENT_SOURCE_DIR
  • 举例说明:
bash 复制代码
# 数学库项目
add_library(math STATIC src/vector.cpp src/matrix.cpp)

# 公开头文件目录(依赖此库的目标自动继承)
target_include_directories(math
	  PUBLIC include/math   # API头文件:用户需包含 <math/vector.h>
)

# 私有头文件目录(仅内部实现使用)
target_include_directories(math
	  PRIVATE src/internal  # 内部头文件:如 _impl_helpers.h
)

7.2 与 include_directories() 的区别

函数 作用范围 推荐度
include_directories() 全局(当前及子目录所有目标) 低(易造成路径污染)
target_include_directories() 目标级(精确控制) 高(现代 CMake 最佳实践)
  • 函数作用:

管理目标依赖关系的核心命令,通过作用域关键字精确控制依赖的传递性

一般用来:设置二进制目标的依赖库列表,相当于使用通用的set属性设置函数设置了LINK_LIBRARIES 或者 INTERFACE_LINK_LIBRARIES这个属性

  • 函数形式:
bash 复制代码
target_link_libraries(<target>
		<PRIVATE|PUBLIC|INTERFACE> <item>...
		[<PRIVATE|PUBLIC|INTERFACE> <item>...]...
)
  • 参数解析:
参数 作用 说明
< target > 指定 目标名称(如 my_lib 或 my_app) 必须是通过 add_executable() 或 add_library() 创建的目标,不能是别名目标(ALIAS target)
INTERFACE / PUBLIC / PRIVATE 属性的作用域关键字
< item >... 依赖库列表
  • 举例说明:

(1) 可执行文件目标 依赖动/静态库

bash 复制代码
# 静态库
add_library(voltage_sensor STATIC 
	  src/sensor/adc.cpp 
	  src/sensor/calibration.cpp
)
target_include_directories(voltage_sensor PUBLIC include/sensor)

# 可执行文件
add_executable(bms_monitor 
	  main.cpp 
	  src/core/processor.cpp
)
target_link_libraries(bms_monitor PRIVATE voltage_sensor)  # 链接静态库
bash 复制代码
# 动态库
add_library(balancing_algo SHARED
	  src/algorithms/neighbor.cpp
	  src/algorithms/fuzzy_logic.cpp
)
set_target_properties(balancing_algo PROPERTIES
	  VERSION 1.2
	  SOVERSION 1
)

# 可执行文件
add_executable(balancer_ctl controller.cpp)
target_link_libraries(balancer_ctl PRIVATE balancing_algo)  # 链接动态库

(2) 目标依赖接口库

bash 复制代码
# 创建接口库
add_library(eigen INTERFACE)

# 设置头文件路径(自动传递给链接者)
target_include_directories(eigen INTERFACE
  /usr/include/eigen3
  ${PROJECT_SOURCE_DIR}/external/eigen
)

# 添加宏定义
target_compile_definitions(eigen INTERFACE 
  EIGEN_NO_DEBUG
  EIGEN_USE_BLAS
)

# 使用
add_executable(matrix_demo demo.cpp)
target_link_libraries(matrix_demo PRIVATE eigen)  # 传递头文件和宏

(3) 目标依赖导入库

bash 复制代码
# 导入库 的基本声明
add_library(boost_system SHARED IMPORTED)

set_target_properties(boost_system PROPERTIES
  # 设置库文件路径
  IMPORTED_LOCATION /usr/lib/libboost_system.so
  
  # 设置头文件路径(自动传递给链接者)
  INTERFACE_INCLUDE_DIRECTORIES /usr/include/boost
  
  # 添加宏定义
  INTERFACE_COMPILE_DEFINITIONS BOOST_SYSTEM_DYN_LINK
)

# 可执行程序目标 链接导入目标
add_executable(network_app main.cpp)
target_link_libraries(network_app PRIVATE boost_system)

# 使用效果:
# (1)编译时:
# 1. 自动添加头文件搜索路径:-I/usr/include/boost
# 2. 自动添加宏定义:-DBOOST_SYSTEM_DYN_LINK

# (2)链接时:
# 1. 链接 库的绝对路径:/usr/lib/libboost_system.so

9. set_target_properties

  • 函数作用:

set_target_properties 是 CMake 中用于为特定目标(如可执行文件、静态库、动态库)设置属性的核心命令,通过属性控制目标的输出名称、版本号、目录位置等关键行为

  • 函数形式:
bash 复制代码
set_target_properties(<target1> <target2> ...
    PROPERTIES 
    <prop1> <value1>
    <prop2> <value2> ...
)
  • 举例说明:

(1) 设置头文件路径(对比 target_include_directories)

方法A:专用命令(推荐)

bash 复制代码
# 电池传感器库
add_library(battery_sensor STATIC sensor.cpp)
target_include_directories(battery_sensor 
	  INTERFACE include/sensor   # 仅传播给依赖者
	  PRIVATE src/internal       # 私有头文件
)

方法B:set_target_properties(不推荐)

bash 复制代码
add_library(battery_sensor STATIC sensor.cpp)
# 手动设置属性(需区分作用域)
set_target_properties(battery_sensor PROPERTIES
	  INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/include/sensor"  # 仅传播给依赖者
	  INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/src/internal"              # 私有路径
)

对比结论:
✅ target_include_directories:自动处理路径转换(可以设置相对路径) 和 作用域
❌ set_target_properties:需手动处理绝对路径(不能设置相对路径) 和 作用域分离

(2) 添加链接库(对比 target_link_libraries)

方法A:专用命令(推荐)

bash 复制代码
add_executable(bms_controller main.cpp)
target_link_libraries(bms_controller
	  PUBLIC battery_core      # 公共依赖
	  PRIVATE voltage_monitor     # 私有依赖
)

方法B:set_target_properties(不推荐)

bash 复制代码
add_executable(bms_controller main.cpp)
# 手动设置链接属性
set_target_properties(bms_controller PROPERTIES
       INTERFACE_LINK_LIBRARIES "battery_core"        # 仅传播给依赖者
	   LINK_LIBRARIES "battery_core;voltage_monitor"  # 私有依赖
)

对比结论:
✅ target_link_libraries:支持作用域关键字
❌ set_target_properties:无法区分作用域,需分开设置

(3) 设置链接目录(对比 target_link_directories)

方法A:专用命令(推荐)

bash 复制代码
target_link_directories(bms_firmware 
	  PRIVATE ${STM32_HAL}/lib
)

方法B:set_target_properties(不推荐)

bash 复制代码
set_target_properties(bms_firmware PROPERTIES
      LINK_DIRECTORIES "${STM32_HAL}/lib"
)

(4) set_target_properties 专属领域(不可替代)

场景1:动态库版本控制

bash 复制代码
add_library(battery_algo SHARED algo.cpp)
set_target_properties(battery_algo PROPERTIES
	  VERSION 2.3.0       # 完整版本
	  SOVERSION 2         # API版本
	  OUTPUT_NAME "bms"   # 输出文件名
)

生成:libbms.so.2.3.0 (实际) 和 libbms.so.2 (符号链接)

场景2:多配置输出(Debug/Release)

bash 复制代码
dd_executable(monitor main.cpp)
set_target_properties(monitor PROPERTIES
	  DEBUG_POSTFIX "_d"          # Debug版本后缀
	  RELEASE_POSTFIX "_release"  # Release后缀
)

生成:monitor_d (Debug) 和 monitor_release (Release)

10. file

10.1 file函数的介绍

  • 函数作用:

file(GLOB|GLOB_RECURSE) 是 CMake 中 用于模式匹配收集文件列表 的核心命令

  • 函数形式:
bash 复制代码
file(GLOB|GLOB_RECURSE <variable> 
    [LIST_DIRECTORIES true|false]
    [RELATIVE <path>] 
    [CONFIGURE_DEPENDS] 
    <globbing-expressions>...
)
  • 参数解析:
模式 作用 适用场景
GLOB 非递归匹配当前目录 顶层目录文件收集
GLOB_RECURSE 递归匹配所有子目录 深层嵌套文件收集

参数 类型 默认值 作用
< variable > 必需 - 存储匹配结果的变量名
< globbing-expressions > 必需 - 文件匹配表达式
LIST_DIRECTORIES 可选 true 是否包含匹配的目录
RELATIVE < path > 可选 - 返回相对路径(相对于指定目录)
CONFIGURE_DEPENDS 可选 - 自动检测文件变化并重新配置
  • 举例说明:
bash 复制代码
# 收集当前目录所有C++源文件
file(GLOB SRC_LISTS 
    "src/*.cpp" 
    "src/*.cxx"
)

# 添加可执行文件
add_executable(bms_controller ${SRC_LISTS})

10.2 文件匹配表达式语法(含示例演示)

支持标准通配符和高级特性:

  • 基础通配符
通配符 作用 示例
* 匹配任意字符 *.cpp → 所有C++文件
? 匹配单个字符 ?.h → a.h, b.h
[...] 字符集匹配 [abc].txt → a.txt, b.txt, c.txt
[^...] 排除字符 [^test]*.c → 排除test开头的C文件
  • 高级特性
特性 语法 说明
多模式 {*.cpp,*.h} 匹配多个模式
递归排除 **/*.cpp (GLOB_RECURSE) 匹配所有子目录的cpp文件
转义字符 \\* 匹配字面星号

(1) 多模式匹配(花括号扩展)

语法:{pattern1,pattern2,...,patternN}

作用:同时匹配多个模式,结果合并到同一个列表中

bash 复制代码
# 示例1:收集多种类型的源文件
file(GLOB SOURCES 
    "src/{*.cpp,*.cxx,*.h,*.hpp}" 
)

# 匹配结果:
#   src/main.cpp
#   src/utils.cxx
#   src/config.h
#   src/logger.hpp
bash 复制代码
# 示例2:收集多个目录中的特定文件
file(GLOB_RECURSE BMS_FILES 
    "bms/{algorithms/*.c, sensors/*.c, config/*.json}"
)

# 匹配结果:
#   bms/algorithms/soc.c
#   bms/algorithms/thermal.c
#   bms/sensors/voltage.c
#   bms/sensors/temperature.c
#   bms/config/settings.json

(2) 递归通配符(**)

语法:**

仅在 GLOB_RECURSE 模式下有效,匹配任意层级的子目录

bash 复制代码
# 示例3:递归收集所有测试文件
file(GLOB_RECURSE TEST_FILES 
    "tests/**/*_test.cpp"
)

# 匹配结果:
#   tests/unit/battery_test.cpp
#   tests/unit/sensor_test.cpp
#   tests/integration/system_test.cpp
#   tests/performance/stress_test.cpp
bash 复制代码
# 示例4:递归收集所有文档
file(GLOB_RECURSE DOCS 
    "docs/**/*.{md,pdf}"
)

# 匹配结果:
#   docs/getting_started.md
#   docs/api/reference.md
#   docs/design/bms_architecture.pdf

(3) 字符转义

语法:\\

匹配特殊字符的字面值

bash 复制代码
# 示例5:匹配带星号的文件名
file(GLOB SPECIAL_FILES 
    "config/config_\\*.json"  # 匹配字面星号
)

# 匹配文件:
#   config/config_*.json
# 不匹配:
#   config/config_v1.json
#   config/config_v2.json
bash 复制代码
# 示例6:匹配带问号的文件名
file(GLOB LOG_FILES 
    "logs/error_\\?.log"  # 匹配字面问号
)

# 匹配文件:
#   logs/error_?.log

相关推荐
feng_you_ying_li2 小时前
C++中set和map的使用
c++
郭涤生2 小时前
GPIO 基础复习
c++
小年糕是糕手2 小时前
【35天从0开始备战蓝桥杯 -- Day6】
开发语言·前端·网络·数据库·c++·蓝桥杯
星轨初途2 小时前
【C/C++底层修炼】拆解动态内存管理:四大动态内存函数、六大错误与柔性数组
c语言·开发语言·c++·经验分享·笔记·柔性数组
闻缺陷则喜何志丹2 小时前
【计算几何】和差化积及积化和差
c++·数学·计算几何
Trouvaille ~2 小时前
【项目篇】从零手写高并发服务器(九):HTTP协议支持——从TCP到应用层
linux·服务器·c++·tcp/ip·http·高并发·应用层
小此方2 小时前
Re:从零开始的 C++ STL篇(八)深度解构AVL树自平衡机制:平衡维护与旋转调整背后的严密逻辑
开发语言·数据结构·c++·算法·stl
2301_789015622 小时前
封装哈希表实现unordered_set/undered_map
c语言·数据结构·c++·算法·哈希算法