欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

cmake 使用记录

程序员文章站 2022-07-15 16:44:41
...

CMake中宏和函数的参数ARGV ARGC ARGN

cmake中的宏(macro)和函数(function)都支持动态参数
变量ARGC记录传入的参数个数
变量ARGV0,ARGV1,…顺序代表传入的参数
变量ARGV则是一个包含所有传入参数的list
变量ARGN也是一个包含传入参数的list,但不是所有参数,而是指macro/function声明的参数之后的所有传入参数
#定义一个宏,显式声明了两个参数hello,world

macro(argn_test hello world)
	MESSAGE(STATUS ARGV=${ARGV})
	MESSAGE(STATUS ARGN=${ARGN})
	MESSAGE(STATUS ARGV0=${ARGV0})
	MESSAGE(STATUS ARGV1=${ARGV1})
	MESSAGE(STATUS ARGV2=${ARGV2})
	MESSAGE(STATUS ARGV3=${ARGV3})
endmacro()

调用宏时传入4个参数

argn_test(TOM JERRY SUSAN BERN)

==== output ====

-- ARGV=TOMJERRYSUSANBERN
-- ARGN=SUSANBERN
-- ARGV0=TOM
-- ARGV1=JERRY
-- ARGV2=SUSAN
-- ARGV3=BERN

CMake变量以及变量的引用

CMake中的变量无需声明,并且没有类型概念,这一点类似于python;变量可以认为都是全局的,哪怕在一个宏中定义的变量,也可以在宏的外面被访问到;所有的变量都是一个列表变量,下文在举例时会详细说明这一点;CMake对于变量是大小写敏感的。
在CMake中,有两种引用方式:对于变量值的引用,和直接引用这个变量本身,使用方式分别是:${varName} 和 varName。
(感觉像是指针)

CMake的宏与函数

同大多数脚本语言一样,CMake中也有宏和函数的概念,关键字分别为"macro"和"function",具体用法如下:

# 宏

macro( [arg1 [arg2 [arg3 ...]]])
     COMMAND1(ARGS ...)
     COMMAND2(ARGS ...)
     ...
endmacro()
# 函数

function( [arg1 [arg2 [arg3 ...]]])
     COMMAND1(ARGS ...)
     COMMAND2(ARGS ...)
     ...
endfunction()

以简单的求和函数为例,我们来看宏的一个示例:

macro(sum outvar)
     set(_args ${ARGN})
     set(result 0)
     foreach(_var ${_args})
         math(EXPR result "result+{_var}")
     endforeach()
     set(outvar{result})
endmacro()
sum(addResult 1 2 3 4 5)
message("Result is :${addResult}")

上面是一段求和宏定义,我们来解读一下代码:"${ARGN}“是CMake中的一个变量,指代宏中传入的多余参数。因为我们这个宏sum中只定义了一个 参数"outvar”,其余需要求和的数字都是不定形式传入的,所以需要先将多余的参数传入一个单独的变量中。当然,在这个示例中,第一行代码显得多余, 因为似乎没必要将额外参数单独放在一个变量中,但是建议这么做。对上面这个宏再进一步加强:如果我们想限制这个宏中传入的参数数目(尽管在这个宏中实际上 是不必要的),那么可以将宏改写一下:

macro(sum outvar)
     set(_args ${ARGN})
     list(LENGTH _args argLength)
     if(NOT argLength LESS 4)        # 限制不能超过4个数字
         message(FATAL_ERROR "to much args!")
     endif()
     set(result 0)
     foreach(_var ${ARGN})
         math(EXPR result "result+{_var}")
     endforeach()
     set(outvar{result})
endmacro()
sum(addResult 1 2 3 4 5)
message("Result is :${addResult}")

而CMake中的函数(“function”)与宏唯一的区别就在于,函数不能像宏那样将计算结果传出来(也不是完全不能,只是复杂一些),并且函数中的变量是局部的,而宏中的变量在外面也可以被访问到,请看下例:

macro(macroTest)
     set(test1 "aaa")
endmacro()

function(funTest)
     set(test2 "bbb")
endfunction()

macroTest()
message("${test1}")

funTest()
message("${test2}")

==== output ====
aaa

运行这段代码后,只会打印出一条信息"aaa",由此可以看到宏与函数的区别

cmake函数参数解析 cmake_parse_arguments

如果我们向一个function传递list作为参数,在function中,形参会变成如下状况:

set(SRC)
list(APPEND SRC a.cpp b.cpp)
list(APPEND SRC c.cpp d.cpp)
 
function(tst_arguments src_list)
	message("src_list = "${src_list})
endfunction()
 
message("SRC = "${SRC})
tst_arguments(${SRC})
 
==== output ====
SRC = a.cppb.cppc.cppd.cpp
src_list = a.cpp

很奇怪的是,这里的${SRC}在function外是完整的4个元素,而在function却只剩下了头一个元素(可能跟list的定长有关)。如果我们要传给function以n个源文件组成的list,这样显然不行。
一种简单的解决方法是使用ARGV,ARGC配合,他们的含义如同C/C++中main的argv和argc,分别代表参数和参数个数,使用如下方法解析参数:

function(tst_arguments src_list)
	message("ARGC = "${ARGC})
	message("ARGV = "${ARGV})
 
        set(INDEX 0)
    
	while(INDEX LESS ${ARGC})
		message("ARG = "${ARGV${INDEX}})
		math(EXPR INDEX "${INDEX} + 1")
	endwhile()
endfunction()
 
tst_arguments(${SRC})

==== output ====
ARGC = 4
ARGV = a.cppb.cppc.cppd.cpp
ARG = a.cpp
ARG = b.cpp
ARG = c.cpp
ARG = d.cpp 

最方便使用的是cmake的cmake_parse_arguments来解析函数参数,它有点像解析一个map键值对,首先看下它的函数原型:

#必须包含这个cmake文件才能使用
include (CMakeParseArguments) 

cmake_parse_arguments (<prefix> <options> <one_value_keywords> <multi_value_keywords> args...)

首先,prefix是一个前缀,等会儿在引用参数的时候会提到,是一个列表,里面可以包含一些你感兴趣的KeyWord,随后可以通过它来看看你所需要的KeyWord是否被设置。<one_value_keywords>是一个单值参数的KeyWord列表。<multi_value_keywords>是一个多值参数的KeyWord列表(如list),下面举个例子,看看如何使用它们,首先定义所需要的函数,由于参数是由cmake_parse_arguments 来解析的,所以在函数声明中就不需要定义参数了:

function(tst_arguments)
  CMAKE_PARSE_ARGUMENTS( TEST "" "NAME;COMMAND;BASELINE" "ARGSLIST" ${ARGN}  )
 
  message("TEST_DEFAULT_ARGS is ${TEST_DEFAULT_ARGS} from ${ARGN}")
  message("TEST_NAME is ${TEST_NAME}")
  message("TEST_COMMAND is ${TEST_COMMAND}")
  message("TEST_ARGSLIST is ${TEST_ARGSLIST}")
  message("TEST_BASELINE is ${TEST_BASELINE}")
 
endfunction(tst_arguments)

这里的前缀是TEST,<one_value_keywords>我们设置单值参数的KeyWord(NAME;COMMAND;BASELINE),这将在随后的函数调用中注明KeyWord和Value的关系,<multi_value_keywords>我们设置多值参数的KeyWord(“ARGSLIST”),调用函数:

tst_arguments(  NAME testiso
    COMMAND  "RunMe"
    ARGSLIST  ${SRC}
    BASELINE   "/home/sakaue/iWork" )
 
==== output ====
TEST_DEFAULT_ARGS is  from NAME;testiso;COMMAND;RunMe;ARGSLIST;a.cpp;b.cpp;c.cpp;d.cpp;BASELINE;/home/sakaue/iWork
TEST_NAME is testiso
TEST_COMMAND is RunMe
TEST_ARGSLIST is a.cpp;b.cpp;c.cpp;d.cpp
TEST_BASELINE is /home/sakaue/iWork

可以看见,这里调用时的参数传递如同map一样<NAME ,testiso_${datafile} >,<COMMAND , “RunMe”>,<ARGSLIST , ${SRC}>等等,在函数中,使用 前缀+KeyWord 来调用Value,这样比自己解析参数方便许多,而且也不会在还有list参数时和其他类型函数混在一起的情况。


参考

相关标签: CMake