macro 是文本替换无局部作用域,定义用 macro/endmacro 包裹,参数空格分隔,通过 ${ARGV} 等访问;与 function 的核心区别在于变量修改影响全局作用域。

macro 定义语法和基本结构
macro 不是函数,它只是文本替换,展开时原样插入调用位置,不创建作用域。定义必须用 macro 和 endmacro 包裹,参数名直接写在 macro 后面,不用括号或类型声明。
-
macro后紧跟宏名和参数列表(如my_setup_target TARGET_NAME SRC_FILES),参数之间空格分隔 - 宏体内访问参数用
${ARGN}(所有未命名参数)、${ARGC}(总参数个数)、${ARGV0}~${ARGV9}(按序索引) - 不能用
return()提前退出,会直接终止整个 CMake 配置过程;要用if(0)块模拟跳过逻辑 - 宏内定义的变量默认是全局作用域,容易污染父作用域——这是最常踩的坑
macro 和 function 的关键区别在哪
核心就一条:macro 没有局部作用域,function 有。这意味着你在 macro 里 set(MY_VAR "foo"),调用者上下文里的 MY_VAR 就被改了;而 function 里同名 set 只影响函数内部。
- 适合 macro 的场景:重复执行一组命令、生成多个 target、拼接字符串、条件性 add_compile_definitions —— 只要不依赖干净的变量隔离
- 适合 function 的场景:需要计算中间值、校验输入、返回结果(通过
set(... PARENT_SCOPE))、封装可复用逻辑且不想副作用 - 性能上无差异,但 macro 展开后可能让
cmake --trace日志更难读,因为看不到“调用栈”
macro 调用时参数为空或含空格怎么处理
CMake 解析参数时以空白符切分,所以带空格的路径或选项必须用双引号包裹,且宏体内必须用 ${ARGV} 或 ${ARGN} 获取完整字符串,不能硬写 ${ARGV1} 假设它是单个词。
- 错误写法:
my_macro /path/with space.cpp→${ARGV0}是/path/with,${ARGV1}是space.cpp,断开了 - 正确写法:
my_macro "/path/with space.cpp",然后在宏里用list(GET ARGN 0 SRC)或直接set(SRC ${ARGN})(如果只传一个) - 检测空参数用
if(NOT DEFINED ARGV0 OR ARGV0 STREQUAL ""),因为未传参时${ARGV0}是空字符串而非未定义
macro 内部引用外部变量要注意什么
macro 展开即嵌入,所有变量名都按当前作用域解析。如果你在 macro 里写 message(${CMAKE_SOURCE_DIR}),它取的是调用点的值,不是定义点的值——这本身没问题,但容易误以为能“捕获”定义时环境。
- 不要在 macro 里依赖“定义时”的缓存变量(如
CMAKE_BUILD_TYPE),它可能在调用时已被改写 - 若需固定值,应在调用时显式传入,比如
my_macro("${CMAKE_SOURCE_DIR}" ${SOURCES}) - 调试技巧:在 macro 开头加
message(STATUS "[macro] ARGC=${ARGC}, ARGV=${ARGV}"),确认参数接收是否符合预期
macro 看似简单,但变量作用域和参数分词这两点,实际项目里出问题的概率远高于语法错误。写完务必用不同参数组合(空、含空格、多层级路径)测一遍。


















