列表操作:list命令详解
列表操作,说白了就是CMake里最常用的数据容器管理。我刚开始用CMake时,总觉得列表这东西不就是个数组嘛,有啥好学的?结果项目一复杂,各种源文件列表、依赖列表、编译选项列表堆在一起,不会操作列表简直寸步难行。
今天我就把list命令的增删改查、排序去重、转字符串这些操作,掰开揉碎了讲清楚。
列表的增删改查
先说说最基本的——怎么往列表里加东西、删东西、改东西、查东西。
追加元素:APPEND
这个最简单,也最常用。我在项目中经常用它来收集源文件:
set(MY_SOURCES)
list(APPEND MY_SOURCES "main.cpp")
list(APPEND MY_SOURCES "utils.cpp" "network.cpp")
message("源文件列表: ${MY_SOURCES}")
# 输出: main.cpp;utils.cpp;network.cpp
注意,CMake列表的分隔符是分号;,不是逗号。嗯,这里容易踩坑。
插入元素:INSERT
有时候需要在指定位置插入元素,比如把某个头文件路径插到最前面:
list(INSERT MY_SOURCES 0 "config.h")
# 现在列表变成: config.h;main.cpp;utils.cpp;network.cpp
索引从0开始,这个和大多数编程语言一致。
删除元素:REMOVE_ITEM / REMOVE_AT / REMOVE_DUPLICATES
删除操作有三种,我分别说说:
- REMOVE_ITEM:按值删除,删掉所有匹配的元素
- REMOVE_AT:按索引删除,只删一个
- REMOVE_DUPLICATES:去重,后面单独讲
list(REMOVE_ITEM MY_SOURCES "utils.cpp")
# 删掉所有等于"utils.cpp"的元素
list(REMOVE_AT MY_SOURCES 1)
# 删掉索引为1的元素(第二个元素)
我曾经在项目中犯过一个错:用REMOVE_ITEM删一个不存在的值,CMake不会报错,只是静默忽略。所以删除前最好先确认元素存在。
查找元素:FIND
查找元素返回的是索引,找不到返回-1:
list(FIND MY_SOURCES "main.cpp" INDEX)
if(INDEX GREATER -1)
message("main.cpp 在索引 ${INDEX} 位置")
endif()
这个操作我经常用来做条件判断,比如某个源文件是否存在,决定要不要加特定的编译选项。
列表排序
排序用list(SORT),默认按字典序升序排列:
set(FILES "z.cpp" "a.cpp" "m.cpp")
list(SORT FILES)
message("排序后: ${FILES}")
# 输出: a.cpp;m.cpp;z.cpp
如果想降序,加个COMPARE DESCENDING:
list(SORT FILES COMPARE DESCENDING)
# 输出: z.cpp;m.cpp;a.cpp
还有个CASE选项,控制大小写是否敏感。默认是CASE SENSITIVE,如果你想要不区分大小写:
list(SORT FILES COMPARE DESCENDING CASE INSENSITIVE)
列表去重
去重用REMOVE_DUPLICATES,它会保留第一次出现的元素,删掉后续重复的:
set(LIST_WITH_DUPS "a" "b" "a" "c" "b")
list(REMOVE_DUPLICATES LIST_WITH_DUPS)
message("去重后: ${LIST_WITH_DUPS}")
# 输出: a;b;c
这个操作在收集源文件时特别有用。比如你从多个子目录递归收集源文件,可能会有重复,去重一下就能避免重复编译。
列表转换字符串
把列表转成字符串,最直接的方式就是用${}展开,但这样得到的是分号分隔的字符串。有时候我们需要自定义分隔符,比如逗号或空格。
CMake没有直接的list(JOIN)命令(3.12版本之前),但可以用foreach循环拼接:
set(MY_LIST "a" "b" "c")
set(JOINED_STR "")
foreach(item IN LISTS MY_LIST)
if(JOINED_STR)
string(APPEND JOINED_STR ", ")
endif()
string(APPEND JOINED_STR "${item}")
endforeach()
message("拼接结果: ${JOINED_STR}")
# 输出: a, b, c
CMake 3.12之后有了list(JOIN),简单多了:
list(JOIN MY_LIST ", " JOINED_STR)
message("拼接结果: ${JOINED_STR}")
# 输出: a, b, c
我个人习惯用list(JOIN),代码更简洁。如果你的CMake版本够新,建议直接用这个。
知识体系总览
下面这张图总结了列表操作的核心逻辑,我画出来方便你记忆:
实战经验总结
说了这么多,我总结几个实际项目中的经验:
- 列表长度获取:用
list(LENGTH),别傻傻地数分号 - 列表是否为空:判断
LENGTH EQUAL 0,或者直接用if(MY_LIST)——空列表在CMake里是假值 - 列表复制:直接
set(NEW_LIST ${OLD_LIST})就行,列表是值传递 - 小心空格:列表元素如果包含空格,一定要用引号包起来,否则会被当成多个元素
核心要点:列表操作是CMake构建脚本的基石。增删改查是基本功,排序去重是进阶技巧,转字符串是输出和调试的利器。把这些玩熟了,你的CMake脚本会干净很多。
好了,列表操作就讲到这里。记住,多用message()打印列表内容来调试,这是最笨也最有效的方法。我到现在写CMake脚本,还是习惯在每个关键操作后加个message()看看列表长啥样。
公众号:蓝海资料掘金营,微信deep3321