cmake函数必须用function(name)和endfunction()严格配对,参数通过argv访问,变量默认局部作用域,需parent_scope导出;宏是文本替换,作用域规则与函数不同。

function() 语法必须用 endfunction() 配对
定义一个 function 时,CMake 要求严格配对:开头是 function(name),结尾必须是 endfunction(),不能用 endfunction(name)(CMake 3.20+ 虽支持带名写法,但不推荐——它只校验名字,不解决作用域问题,反而容易掩盖拼写错误)。
常见错误现象:Parse error: function did not end with endfunction() 或函数体被跳过,往往是因为缩进里混了 endif()、endforeach() 等未闭合结构,或者复制粘贴时漏掉了 endfunction()。
-
function和endfunction必须在同一作用域层级,缩进不影响解析,但嵌套结构(如 if + function)容易导致错位 - 函数体内不能直接用
return()退出(它只跳出当前函数调用,不终止整个函数体执行),要用return()需配合条件判断并确保逻辑清晰 - 函数名区分大小写,且不能与内置命令(如
set、message)同名,否则后续调用会静默失败
参数通过 ARGV 访问,不是命名形参
CMake 的 function 不支持命名参数或默认值语法,所有传入参数统一存进 ARGV 这个列表变量,索引从 0 开始;ARGC 是参数个数;还有 ARGN(从第 N 个参数起的剩余参数),但更常用的是 ARGV 直接解包。
典型误用:写成 function(my_func path name) 并试图直接用 ${path} —— 这不会生效,CMake 会把 path 和 name 当作函数体内的局部变量名,但不会自动赋值。
- 正确做法是手动取值:
list(GET ARGV 0 src_dir)、list(GET ARGV 1 target_name) - 检查参数数量用
if(ARGC LESS 2),避免越界访问ARGV - 若需可选参数,靠
list(LENGTH ARGV len)+ 条件分支处理,没有语法糖
变量作用域:函数内 set() 默认只影响局部作用域
在 function 里用 set(var value),默认只修改函数内部的局部变量,函数返回后就失效。想把值“传出来”,必须显式指定作用域:
- 写回调用者作用域:
set(var value PARENT_SCOPE) - 写入全局缓存(常用于配置开关):
set(var value CACHE STRING "desc") - 仅在当前函数有效:
set(var value)(默认行为)
容易踩的坑:忘记加 PARENT_SCOPE,结果调用方读到空值或旧值,调试时发现 message("${my_result}") 总是空——大概率是这里漏了作用域修饰。
宏(macro)和函数(function)别混用
macro 和 function 表面相似,但作用域规则完全不同:macro 是纯文本替换,里面所有 set() 都直接影响上层作用域;function 是独立作用域。选哪个取决于你要什么语义。
比如封装一个「设置编译选项并记录日志」的逻辑:如果希望选项设置能透出到外层,用 macro 更直接;但如果要隔离中间变量、防止污染,必须用 function + PARENT_SCOPE 显式控制输出。
性能差异极小,不用纠结;但一旦项目里混用,排查变量生命周期会很吃力——尤其当某个 set() 在 macro 里生效,在 function 里却无效时,问题根源往往就在这里。
真正麻烦的是跨目录复用:function 定义后只在当前 CMakeLists.txt 有效,想多处使用,得用 include() 或 add_subdirectory() 导入定义文件,且注意 include() 的路径解析依赖 CMAKE_CURRENT_LIST_DIR,不是运行时工作目录。











