CMake循环在生成构建系统时展开而非运行时执行,常见陷阱包括:列表需显式构造、${SOURCES}不可加引号、IN LISTS与IN ITEMS语义不同、RANGE为闭区间、ZIP_LISTS以最短为准、break/continue仅限当前循环层。

foreach 循环的基本写法和常见陷阱
最常写的 foreach 形式是遍历变量列表,但必须注意:CMake 不会自动把空格分隔的字符串当列表,得先用 set() 显式构造,或确保变量本身是 list 类型。
-
set(SOURCES main.cpp util.cpp test.cpp)是合法 list;但set(SOURCES "main.cpp util.cpp")是单个字符串,foreach只会迭代一次,值为整个字符串 - 正确写法:
foreach(src ${SOURCES})—— 注意${SOURCES}要带花括号,且不能加引号 - 错误写法:
foreach(src "${SOURCES}")或foreach(src "main.cpp;util.cpp"),都会让循环只跑 1 次 -
endforeach()后面加变量名(如endforeach(src))是允许的,但纯属冗余;CMake 3.19+ 已默认忽略它,不推荐写
IN LISTS 和 IN ITEMS 的区别必须分清
这是最容易混淆的点:两者语法相似,行为完全不同。
-
foreach(x IN LISTS mylist):mylist是一个已定义的 list 变量,CMake 会展开它的真实元素 -
foreach(x IN ITEMS a b c):a、b、c是字面值;若写成IN ITEMS ${var},则${var}会被先展开再拆成项(等价于${var}直接放在 foreach 后) - 混用时顺序固定:
foreach(x IN LISTS list1 list2 ITEMS foo bar)——LISTS必须在前,ITEMS在后,不能颠倒 - 如果
list1是空 list,它会被跳过;但ITEMS ""会迭代一次空字符串
RANGE 和 ZIP_LISTS 的实用场景
不用硬编码索引,但得清楚它们的边界行为。
-
foreach(i RANGE 0 3)迭代0;1;2;3(闭区间),共 4 次;RANGE 0 5 2得到0;2;4 -
foreach(a IN ZIP_LISTS list1 list2)是并行迭代,长度不同时以最短为准;想补缺省值?CMake 没内置机制,得自己用list(LENGTH)+list(APPEND)对齐 -
ZIP_LISTS中每个 list 元素数必须一致,否则静默截断——调试时用message(STATUS "list1: ${list1}")先确认长度
break() 和 continue() 的作用域限制
它们只对最近一层 foreach 或 while 生效,且不能跨 block() / function() 边界。
-
break()会立刻退出当前循环,后续命令不再执行;continue()跳过本次剩余命令,进入下一次迭代 - 在
function()内部调用break()会报错:break() called in a function scope—— CMake 不允许从函数里跳出外层循环 - 想实现“条件跳过”,优先用
if()包裹逻辑,而不是依赖continue();后者在嵌套循环里容易误判层级
foreach 块在 configure 阶段就被完全展开成静态指令,一旦生成构建文件,循环就消失了。这意味着:调试靠 message() 打印中间状态,而不是设断点;出问题时,先检查 cmake -D CMAKE_VERBOSE_MAKEFILE=ON .. 输出的原始指令,而不是猜逻辑。


















