宏观¶
开始录制宏以供以后作为命令调用
macro(<name> [<arg1> ...])
<commands>
endmacro()
定义一个名为 <name> 的宏,它接受名为 <arg1> 的参数,...在宏之后但在匹配的 endmacro() 之前列出的命令,直到宏才执行被调用。
按照传统, endmacro() 命令接受一个可选的 <name> 参数。如果使用,它必须逐字重复开头的“宏”命令的参数。
请参阅 cmake_policy() 命令文档以了解宏中策略的行为。
请参阅下面的 宏与函数 部分了解 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} 保存最后一个预期参数之后的参数列表。引用``${ARGV#}`` 超出``${ARGC}`` 的参数有未定义的行为。检查 ${ARGC} 是否大于 # 是确保 ${ARGV#} 作为额外参数传递给函数的唯一方法。
宏与函数¶
macro 命令与 function() 命令非常相似。尽管如此,还是有一些重要的区别。
在一个函数中,ARGN、ARGC、ARGV 和 ARGV0、ARGV1 ……在通常的 CMake 意义上是真正的变量。在宏中,它们不是,它们是字符串替换,就像 C 预处理器对宏所做的一样。这会产生许多后果,如下面的 论证注意事项 部分所述。
宏和函数之间的另一个区别是控制流。通过将控制权从调用语句转移到函数体来执行函数。宏的执行就像宏主体被粘贴到调用语句的位置一样。这会导致宏主体中的 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 范围控制,您应该查看函数命令。