目录
1.简介
2.常用命令
2.1.APPEND:向列表末尾添加元素
2.2.PREPEND:向列表开头添加元素
2.3.INSERT:在指定索引处插入元素
2.4.REMOVE_ITEM:删除列表中指定值的元素
2.5.REMOVE_AT:删除指定索引处的元素
2.6.REMOVE_DUPLICATES:移除列表中的重复元素
2.7.LENGTH:获取列表长度(元素个数)
2.8.GET:获取列表中指定索引的元素
2.9.JOIN:将列表元素用指定分隔符连接成字符串
2.10.REVERSE:反转列表顺序
2.11.SORT:对列表进行排序(按字母顺序)
2.12.CLEAR:清空列表中的所有元素
2.13.CONCAT:合并多个列表为一个列表(等价于多次 APPEND)
2.14.TRANSFORM: 列表元素变换
2.15.FILTER(CMake 3.6+): 过滤元素
3.注意事项
4.set 命令和 list 命令异同
5.总结
相关链接
1.简介
在 CMake 中,list命令用于操作列表(Lists),这是 CMake 中一种重要的数据结构(用于存储多个值,如源文件路径、编译选项、宏定义等)。list命令支持多种子命令,用于实现列表的创建、修改、查询和处理等操作。
list(COMMAND [arguments...])其中,COMMAND是子命令(如APPEND、REMOVE_ITEM等),arguments是对应子命令的参数。
2.常用命令
2.1.APPEND:向列表末尾添加元素
list(APPEND my_list "item1" "item2") # 添加多个元素 # 结果:my_list = ["item1", "item2"]2.2.PREPEND:向列表开头添加元素
list(PREPEND my_list "item0") # 结果:my_list = ["item0", "item1", "item2"]2.3.INSERT:在指定索引处插入元素
list(INSERT my_list 1 "item0.5") # 在索引 1 处插入元素 # 结果:my_list = ["item0", "item0.5", "item1", "item2"]2.4.REMOVE_ITEM:删除列表中指定值的元素
list(REMOVE_ITEM my_list "item0.5") # 删除值为 "item0.5" 的元素 # 结果:my_list = ["item0", "item1", "item2"]2.5.REMOVE_AT:删除指定索引处的元素
list(REMOVE_AT my_list 0) # 删除索引 0 处的元素 # 结果:my_list = ["item1", "item2"]2.6.REMOVE_DUPLICATES:移除列表中的重复元素
list(APPEND my_list "item1") # 添加重复元素 # 此时 my_list = ["item1", "item2", "item1"] list(REMOVE_DUPLICATES my_list) # 结果:my_list = ["item1", "item2"]2.7.LENGTH:获取列表长度(元素个数)
list(LENGTH my_list list_length) message(STATUS "列表长度:${list_length}") # 输出:列表长度:22.8.GET:获取列表中指定索引的元素
list(GET my_list 0 first_item) # 获取索引 0 处的元素 message(STATUS "第一个元素:${first_item}") # 输出:第一个元素:item12.9.JOIN:将列表元素用指定分隔符连接成字符串
list(JOIN my_list ";" joined_string) message(STATUS "连接后的字符串:${joined_string}") # 输出:item1;item22.10.REVERSE:反转列表顺序
list(REVERSE my_list) # 结果:my_list = ["item2", "item1"]2.11.SORT:对列表进行排序(按字母顺序)
list(APPEND my_list "item3" "item0") # 此时 my_list = ["item2", "item1", "item3", "item0"] list(SORT my_list) # 结果:my_list = ["item0", "item1", "item2", "item3"]2.12.CLEAR:清空列表中的所有元素
list(CLEAR temp_files) # 清空临时文件列表2.13.CONCAT:合并多个列表为一个列表(等价于多次APPEND)
list(CONCAT all_files src_files test_files) # 合并源文件和测试文件列表2.14.TRANSFORM: 列表元素变换
在 CMake 3.12 及以上版本中,list(TRANSFORM ...)用于对列表中的每个元素执行转换操作(如添加前缀、替换字符串、调用函数等),并将结果存储到原列表或新变量中。这是处理列表元素的强大工具,尤其适合批量处理文件路径、编译选项等场景。
list(TRANSFORM <list> <ACTION> ... [OUTPUT_VARIABLE <output>])<list>:要操作的列表变量名。<ACTION>:转换动作,支持以下几种:PREPEND <prefix>:为每个元素添加前缀。APPEND <suffix>:为每个元素添加后缀。REPLACE <match> <replace>:替换元素中的匹配字符串。STRIP:去除元素首尾的空格。TOUPPER/TOLOWER:转换为大写 / 小写。GENEX_STRIP:移除生成器表达式(如$<CONFIG:Debug>)。
OUTPUT_VARIABLE <output>(可选):将结果存储到新变量,而非覆盖原列表。
常用场景示例
1.添加前缀 / 后缀(最常用)
set(SOURCES "main.cpp" "utils.cpp") # 为每个源文件添加 "src/" 前缀(等效于 "src/main.cpp" "src/utils.cpp") list(TRANSFORM SOURCES PREPEND "src/") # 为每个源文件添加 "_test" 后缀(等效于 "main_test.cpp" "utils_test.cpp") list(TRANSFORM SOURCES APPEND "_test.cpp")2.替换字符串
set(PATHS "/old/path/file1" "/old/path/file2") # 将 "/old/path/" 替换为 "/new/path/" list(TRANSFORM PATHS REPLACE "/old/path/" "/new/path/")3.大小写转换
set(FLAGS "Debug" "Release") # 转换为大写(等效于 "DEBUG" "RELEASE") list(TRANSFORM FLAGS TOUPPER) # 转换为小写(等效于 "debug" "release") list(TRANSFORM FLAGS TOLOWER)4.输出到新变量(保留原列表)
set(INPUT "file1.txt" "file2.txt") # 将结果存入 NEW_INPUT,原 INPUT 不变 list(TRANSFORM INPUT PREPEND "data/" OUTPUT_VARIABLE NEW_INPUT) # NEW_INPUT 为 "data/file1.txt" "data/file2.txt"5.结合生成器表达式
生成器表达式可在构建时动态计算值,例如:
set(SOURCES "main.cpp" "utils.cpp") # 为每个源文件添加基于配置的编译定义(如 Debug 模式下定义 DEBUG=1) list(TRANSFORM SOURCES APPEND "$<$<CONFIG:Debug>:-DDEBUG=1>")2.15.FILTER(CMake 3.6+): 过滤元素
list(FILTER <list> <MODE> REGEX <regex>)对列表进行过滤,原地修改原列表变量,只保留或者剔除符合正则的元素。
参数说明
<list>:变量名(不是直接写列表值!),是要被修改的列表变量<MODE>二选一:INCLUDE:保留匹配正则的元素,删掉不匹配的EXCLUDE:剔除匹配正则的元素,删掉匹配的(你要的删除包含指定字符串就是这个)
REGEX <regex>:CMake 正则表达式
⚠️ 重点:是正则匹配,不是简单子串匹配!
基础示例:
set(LST "foo.txt;bar.cpp;foo.h;test.log") # 保留带 foo 的(INCLUDE) list(FILTER LST INCLUDE REGEX "foo") # LST = foo.txt;foo.h set(LST "foo.txt;bar.cpp;foo.h;test.log") # 删除带 foo 的(EXCLUDE) list(FILTER LST EXCLUDE REGEX "foo") # LST = bar.cpp;test.log匹配规则(CMake 正则语法,很容易踩坑)
CMake 使用的是POSIX ERE(扩展正则)
"abc":只要元素里面任意位置包含 abc就匹配(等价于 .abc.)✅ 也就是你想要的 “包含字符串”"^abc":以 abc 开头"abc$":以 abc 结尾"^abc$":整项严格等于 abc
不需要手动前后加
.*,REGEX "abc"自动是包含匹配,这是很多人不知道的。
元字符需要转义
. + * ? [ ] ( ) \ { }都是正则元字符,如果你想字面匹配这些字符,必须转义:
表格
| 要匹配字面 | 正则写法 |
|---|---|
.so | \\.so |
+ | \\+ |
* | \\* |
[ | \\[ |
示例:删除包含.so的条目
set(LST "liba.so;main.cpp;libb.a;test.so") list(FILTER LST EXCLUDE REGEX "\\.so") # 结果:main.cpp;libb.aCMake 字符串里
\本身也要转义,所以写两个反斜杠\\.
大小写
CMake 的 REGEX默认区分大小写
set(LST "Abc;abc;ABC") list(FILTER LST EXCLUDE REGEX "abc") # 只会删掉小写 abc,Abc、ABC 保留CMake 原生
list(FILTER)没有不区分大小写开关!如果需要忽略大小写,list(FILTER)做不到,只能用foreach + string(TOLOWER) + string(FIND)方案。
边界坑点(高频踩坑)
1.不会新建变量,原地修改!
# ❌ 错误写法:不能直接写列表字面量 list(FILTER "a;b;c" EXCLUDE REGEX "a") # ✅ 正确:必须先 set 到变量 set(lst a;b;c) list(FILTER lst EXCLUDE REGEX "a")2.空列表、空匹配结果
如果所有元素都被 EXCLUDE 过滤掉,最后变量变成空列表,不会报错:
set(lst "1;2;3") list(FILTER lst EXCLUDE REGEX ".") # 全部匹配,全部删掉 message(STATUS ">>${lst}<<") # >> <<3.列表元素带分号;
CMake 列表分隔符就是;,如果你的元素内部含有;,list本身就已经拆分错乱,不适合用 list (FILTER)。
4.和 string (FIND 的区别)
list(FILTER REGEX):正则,适合简单包含、开头结尾、多模式;要注意转义string(FIND):纯字面子串查找,没有正则,遇到. * +不用转义,适合不确定内容、带大量特殊符号的场景(兼容所有 CMake 版本)
纯字面删除模板(无正则,无转义烦恼,CMake 3.0 就能用):
macro(list_remove_contains LIST_VAR SUB_STR) set(_new "") foreach(_item IN LISTS ${LIST_VAR}) string(FIND "${_item}" "${SUB_STR}" _pos) if(_pos EQUAL -1) list(APPEND _new "${_item}") endif() endforeach() set(${LIST_VAR} "${_new}") endmacro()多条件过滤示例
删除包含foo或者包含log的项,正则|代表或:
set(LST "foo.txt;bar.log;test.cpp;hello.h") list(FILTER LST EXCLUDE REGEX "foo|log") # 结果 test.cpp;hello.h只保留以.cpp或者.h结尾的:
list(FILTER LST INCLUDE REGEX "\\.(cpp|h)$")版本替代方案小结
- CMake ≥3.6:优先
list(FILTER),简洁一行搞定 - CMake <3.6 / 需要忽略大小写 / 子串有大量特殊符号:foreach + string (FIND)
set(my_list "abc123;def456;abc789;xyz000") set(new_list "") foreach(item IN LISTS my_list) string(FIND "${item}" "abc" pos) if(NOT pos EQUAL -1) # 包含abc,跳过(即删除) continue() endif() list(APPEND new_list "${item}") endforeach() set(my_list "${new_list}") message("${my_list}")快速速查表
# 保留包含 xxx list(FILTER var INCLUDE REGEX "xxx") # 删除包含 xxx list(FILTER var EXCLUDE REGEX "xxx") # 保留以 xxx 开头 list(FILTER var INCLUDE REGEX "^xxx") # 保留以 xxx 结尾 list(FILTER var INCLUDE REGEX "xxx$") # 删除 .so 文件 list(FILTER var EXCLUDE REGEX "\\.so$")3.注意事项
- CMake中的list是通过空格分隔的字符串表示的,因此要注意元素中是否包含空格或特殊字符。
- 列表索引从0开始(类似 C/C++ 数组)。
list命令修改的是当前作用域内的列表变量,若需在父作用域或全局作用域生效,需配合PARENT_SCOPE或CACHE使用。- 在使用list命令时,要确保指定的list变量已经存在,否则CMake会报错。
- CMake的list命令是大小写敏感的,因此在指定元素或索引时要保持一致。
- 列表常用于
add_executable、add_library等命令中传递源文件列表,例如:
list(APPEND SOURCES "file1.cpp" "file2.cpp") add_executable(my_app ${SOURCES})4.set命令和list命令异同
在 CMake 中,set命令和list命令的核心区别主要体现在功能定位和操作对象上,具体可以从以下几个方面区分:
1.功能用途:通用赋值 vs 列表专项操作
set命令的核心是设置变量,可以为变量赋予任意类型的值(字符串、数字、列表、布尔值等),甚至能操作缓存变量(CACHE选项)或环境变量(PARENT_SCOPE等作用域)。
简单来说,只要是 “给某个变量赋值” 的场景,无论是普通数据还是复杂结构,都可以用set,例如:
set(APP_NAME "MyProject") # 赋值字符串 set(SOURCE_FILES main.cpp util.cpp) # 赋值列表(本质是用空格分隔的字符串) set(DEBUG ON CACHE BOOL "Debug mode") # 设置缓存变量list命令的核心是对列表进行专项操作,只能作用于 “列表类型” 的数据(CMake 中列表本质是用空格分隔的字符串,但list命令会将其按列表逻辑处理),支持对列表进行元素添加、删除、查找、排序、拆分、合并等操作,必须通过子命令指定具体操作(如APPEND、REMOVE_ITEM、FIND等)。
例如,对已有的列表变量进行修改:
list(APPEND SOURCE_FILES module.cpp) # 向列表追加元素 list(REMOVE_ITEM SOURCE_FILES util.cpp) # 从列表删除指定元素 list(SORT SOURCE_FILES) # 对列表元素排序2.操作对象:变量赋值 vs 列表修改
set可以创建新变量或覆盖已有变量,赋值时直接给定值,无需关注值的类型是否为列表(即使是列表,也是作为一个整体赋值给变量)。
比如,用set定义列表时,本质是将多个元素用空格拼接成字符串,存储到变量中:
set(MY_LIST item1 item2 item3) # 变量 MY_LIST 的值是 "item1 item2 item3"(列表形式的字符串)list必须基于已有的变量(且通常假设该变量存储的是列表),通过子命令对列表的结构进行修改(增、删、改、查元素),不会直接创建变量,而是操作已有变量中的列表数据。
例如,必须先通过set定义一个列表变量,再用list对其元素进行操作:
set(MY_LIST item1 item2) # 先用 set 定义列表变量 list(INSERT MY_LIST 1 item0) # 用 list 在索引 1 处插入元素,结果 MY_LIST 变为 "item1 item0 item2"3.常用场景:基础变量管理 vs 复杂列表处理
set的使用场景更通用,包括:- 定义项目基本信息(如项目名称、版本);
- 收集源文件、头文件路径(以列表形式赋值,但本质是变量赋值);
- 设置编译选项(如
CMAKE_CXX_FLAGS)、缓存变量(用于用户配置)等。
list的使用场景更聚焦于列表的动态操作,例如:- 向已有源文件列表中追加特定平台的文件(
list(APPEND ...)); - 从列表中删除不需要的元素(如排除测试文件);
- 对列表进行排序、去重、查找特定元素(
list(SORT/REMOVE_DUPLICATES/FIND ...)); - 拆分或合并列表(如
list(SPLIT ...)按分隔符拆字符串为列表,list(JOIN ...)用分隔符合并列表为字符串)。
- 向已有源文件列表中追加特定平台的文件(
总之,set命令和list命令可以简单理解为:
set是 “万能赋值器”,负责给变量 “存值”,值可以是任何形式(单个值或列表),甚至能创建或修改变量的作用域(如缓存、父作用域);list是 “列表处理器”,负责对 “已经存了列表值的变量” 进行 “元素级别的操作”,必须依赖已有变量,且需要通过子命令指定具体的列表操作逻辑。
5.总结
| 子命令 | 功能描述 | 示例(列表名为my_list) |
|---|---|---|
APPEND | 向末尾添加元素 | list(APPEND my_list "a" "b") |
PREPEND | 向开头添加元素 | list(PREPEND my_list "x") |
INSERT | 在指定索引插入元素 | list(INSERT my_list 1 "mid") |
REMOVE_ITEM | 删除指定值的元素 | list(REMOVE_ITEM my_list "b") |
REMOVE_AT | 删除指定索引的元素 | list(REMOVE_AT my_list 0) |
REMOVE_DUPLICATES | 去重 | list(REMOVE_DUPLICATES my_list) |
LENGTH | 获取列表长度 | list(LENGTH my_list len) |
GET | 获取指定索引的元素 | list(GET my_list 2 val) |
JOIN | 连接元素为字符串 | list(JOIN my_list ";" str) |
REVERSE | 反转列表顺序 | list(REVERSE my_list) |
SORT | 按字母序排序 | list(SORT my_list) |
| CLEAR | 清除列表 | list(CLEAR temp_files) |
| CONCAT | 合并多个列表为一个列表 | list(CONCAT all_files src_files test_files) |
| TRANSFORM | 列表元素转换 | list(TRANSFORM SOURCES PREPEND "src/") |
通过这些子命令,你可以灵活地操作 CMake 列表,实现源文件管理、编译选项配置、跨平台条件处理等复杂逻辑。
相关链接
- CMake 官网 CMake - Upgrade Your Software Build System
- CMake 官方文档:CMake Tutorial — CMake 4.0.2 Documentation
- CMake 源码:GitHub - Kitware/CMake: Mirror of CMake upstream repository
- CMake 源码: CMake · GitLab
- 中文版基础介绍:CMake 入门实战 | HaHack
- wiki:Home · Wiki · CMake / Community · GitLab