macro

开始记录一个宏,以便稍后作为命令调用

macro(<name> [<arg1> ...])
  <commands>
endmacro()

定义一个名为 <name> 的宏,它接受名为 <arg1>, ... 的参数。在 macro 之后、匹配的 endmacro() 之前的命令列表不会被执行,直到宏被调用。

按照传统,endmacro() 命令允许一个可选的 <name> 参数。如果使用,它必须是开头 macro 命令参数的完全重复。

有关宏内部策略的行为,请参阅 cmake_policy() 命令文档。

有关 CMake 宏和 functions 之间的差异,请参阅下面的 宏 vs 函数 部分。

调用

宏调用是大小写不敏感的。一个定义为

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#} 作为额外参数传递给函数的唯一方法。

宏 vs 函数

macro 命令与 function() 命令非常相似。尽管如此,它们之间还是有一些重要的区别。

在函数中,ARGNARGCARGVARGV0ARGV1、... 是通常 CMake 意义上的真变量。在宏中,它们不是,它们是字符串替换,很像 C 预处理器对宏所做的那样。这有一些后果,如下面的 参数注意事项 部分解释的那样。

宏和函数之间的另一个区别是控制流。函数的执行是通过将控制权从调用语句转移到函数体来完成的。宏的执行方式就好像宏体被粘贴到调用语句的位置一样。这导致宏体中的 return() 不仅仅终止宏的执行;而是从宏调用的作用域返回控制权。为了避免混淆,建议完全避免在宏中使用 return()

与函数不同,CMAKE_CURRENT_FUNCTIONCMAKE_CURRENT_FUNCTION_LIST_DIRCMAKE_CURRENT_FUNCTION_LIST_FILECMAKE_CURRENT_FUNCTION_LIST_LINE 变量不会为宏设置。

参数注意事项

由于 ARGNARGCARGVARGV0 等不是变量,您将无法使用如下命令

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 命令。

参见