CMake的foreach是展开式宏,不支持break/continue、无迭代器、不检查变量存在;常用形式为foreach(item IN ITEMS a b c),必须配对endforeach(),IN ITEMS不可省略或误写;还支持IN LIST(遍历变量)和IN RANGE(生成数字序列),但均不支持嵌套循环。

foreach 语法结构和基本写法
CMake 的 foreach 不是传统编程语言里的 for-each,它本质是展开式宏,没有迭代器、不支持 break/continue,也不检查变量是否存在——写错就报错或静默跳过。
最常用形式是遍历列表:
foreach(item IN ITEMS apple banana cherry)
message(STATUS "Got: ${item}")
endforeach()注意必须配对 endforeach(),且 IN ITEMS 是显式关键字,不能省略或写成 in 或 IN_LIST(那是另一种用法)。
-
IN ITEMS后面直接跟字面值,空格分隔,不加引号(除非含空格,此时需整体加引号并用list(APPEND ...)更稳妥) - 变量名
item是任意的,但作用域仅限于循环体内;循环外访问会得到未定义值 - 如果列表为空,
foreach块完全不执行,不会报错
IN LIST 和 IN RANGE 怎么用
除了 IN ITEMS,还有两个更安全的变体:IN LIST 从已定义变量取值,IN RANGE 生成数字序列。
比如遍历一个已声明的列表变量:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
set(fruits "apple;banana;cherry")
foreach(fruit IN LIST fruits)
message(STATUS "Fruit: ${fruit}")
endforeach()IN RANGE 支持起始、终止、步长(可选):
foreach(i IN RANGE 0 4 2) # 生成 0 2 4
message(STATUS "i = ${i}")
endforeach()-
IN LIST要求变量已存在且为 list 类型(即用分号分隔),否则 CMake 报错Variable not defined -
IN RANGE终止值是包含的(闭区间),不是 Python 风格的左闭右开 - 所有三种形式都**不支持嵌套循环**,也不能在循环内修改正在遍历的 list 变量
常见错误:变量未展开、拼写错误、作用域陷阱
最常踩的坑是以为 foreach 会自动展开变量,结果写了 foreach(x IN ITEMS ${my_list}) 却没效果——因为 ${my_list} 展开后若为空或含空格,CMake 就当做一个字符串处理,而不是多个 item。
- 错误写法:
foreach(x IN ITEMS ${undefined_var})→ 静默跳过,不报错也不提示 - 错误写法:
foreach(x IN ITEMS "${fruits}")→ 把整个"apple;banana;cherry"当作单个字符串,只循环一次 - 错误写法:
foreach(x ${fruits})(漏掉IN ITEMS)→ CMake 解析失败,报错Parse error - 循环内用
set(x new_value)不影响下一次迭代,因为每次迭代都是独立作用域
替代方案:什么时候不该用 foreach
如果你需要条件跳出、多次修改同一变量、或依赖前次迭代结果,foreach 就不合适。CMake 没有原生 while,但可以用 while + math + break() 模拟。
更常见的需求其实是“对每个源文件添加编译定义”,这时应该优先用 target_compile_definitions() 或 source_group(),而不是手写循环。
- 大量字符串拼接?用
list(JOIN ...)或string(CONCAT ...)更高效 - 需要过滤或转换?先用
list(FILTER ...)或list(TRANSFORM ...)预处理,再foreach - 跨目录递归遍历?
file(GLOB_RECURSE)+foreach可行,但要注意路径中含空格时要用list而非裸字符串
真正麻烦的是混合使用 set() 和 foreach 修改同一变量——CMake 的变量作用域规则会让结果难以预测,这种时候最好拆成函数或用 macro 封装逻辑。

















