macro¶
开始录制一个宏,以便稍后作为命令调用
macro(<name> [<arg1> ...])
<commands>
endmacro()
定义一个名为 <name> 的宏,它接收名为 <arg1> 等参数。在 macro 之后、匹配的 endmacro() 之前的命令,直到宏被调用时才会被执行。
按照惯例,endmacro() 命令允许一个可选的 <name> 参数。如果使用了该参数,它必须与起始 macro 命令的参数完全一致。
有关宏内部策略的行为,请参阅 cmake_policy() 命令文档。
请参阅下方的 宏与函数 (Macro vs Function) 一节,了解 CMake 宏与 函数 (functions) 之间的区别。
调用¶
宏的调用不区分大小写。定义为
macro(foo)
<commands>
endmacro()
的宏可以通过以下任意方式调用
foo()
Foo()
FOO()
cmake_language(CALL foo)
等等。但是,强烈建议保持宏定义中选择的大小写格式。通常宏名使用全小写。
3.18 版本新增:cmake_language(CALL ...) 命令也可用于调用宏。
参数¶
当调用一个宏时,首先通过将形式参数(${arg1} 等)替换为传入的实际参数,来修改宏中记录的所有命令。然后,所有修改后的命令将作为普通命令执行。
除了引用形式参数外,您还可以引用值 ${ARGC}(将被设置为传入宏的参数个数),以及 ${ARGV0}, ${ARGV1}, ${ARGV2} 等(它们包含传入参数的实际值)。这有助于创建具有可选参数的宏。
此外,${ARGV} 保存了传递给宏的所有参数列表,而 ${ARGN} 保存了最后一个预期参数之后的所有参数列表。引用超过 ${ARGC} 的 ${ARGV#} 参数会导致未定义的行为。检查 ${ARGC} 是否大于 # 是确保 ${ARGV#} 已作为额外参数传递给函数的唯一方法。
宏与函数¶
macro 命令与 function() 命令非常相似。尽管如此,它们之间仍存在一些重要差异。
在函数中,ARGN, ARGC, ARGV 以及 ARGV0, ARGV1 等都是 CMake 常规意义上的真正变量。而在宏中,它们不是变量,而是类似于 C 预处理器进行宏替换那样的字符串替换。正如下方 参数注意事项 (Argument Caveats) 一节所述,这带来了一系列后果。
宏和函数的另一个区别是控制流。函数的执行是将控制权从调用语句转移到函数体。宏的执行则像是将宏体直接粘贴在调用语句的位置。这意味着宏体内的 return() 不仅仅终止宏的执行;相反,它会从宏调用所在的作用域返回控制权。为了避免混淆,建议完全避免在宏中使用 return()。
与函数不同,变量 CMAKE_CURRENT_FUNCTION, CMAKE_CURRENT_FUNCTION_LIST_DIR, CMAKE_CURRENT_FUNCTION_LIST_FILE, 以及 CMAKE_CURRENT_FUNCTION_LIST_LINE 在宏中不会被设置。
参数注意事项¶
由于 ARGN, ARGC, ARGV, ARGV0 等不是变量,您将无法使用类似以下的命令
if(ARGV1) # ARGV1 is not a variable
if(DEFINED ARGV2) # ARGV2 is not a variable
if(ARGC GREATER 2) # ARGC is not a variable
foreach(loop_var IN LISTS ARGN) # ARGN is not a variable
在第一种情况下,您可以使用 if(${ARGV1})。在第二和第三种情况下,检查是否向宏传递了可选变量的正确方法是使用 if(${ARGC} GREATER 2)。在最后一种情况下,您可以使用 foreach(loop_var ${ARGN}),但这会跳过空参数。如果您需要包含它们,可以使用
set(list_var "${ARGN}")
foreach(loop_var IN LISTS list_var)
请注意,如果您在调用宏的作用域中有一个同名变量,使用未引用的名称将使用现有变量,而不是参数。例如
macro(bar)
foreach(arg IN LISTS ARGN)
<commands>
endforeach()
endmacro()
function(foo)
bar(x y z)
endfunction()
foo(a b c)
它将遍历 a;b;c 而不是预期的 x;y;z。如果您需要真正的 CMake 变量和/或更好的 CMake 作用域控制,请查看 function 命令。