CMakeLists.txt基本语法及常用
常用变量:
变量名 | 含义 |
PROJECT_NAME | project 命令中写的项目名 |
CMAKE_VERSION | 当前使用CMake的版本 |
CMAKE_SOURCE_DIR | 工程顶层目录,即入口CMakeLists文件所在路径 |
PROJECT_SOURCE_DIR | 同CMAKE_SOURCE_DIR |
CMAKE_BINARY_DIR | 工程编译发生的目录,即执行cmake命令进行项目配置的目录,一般为build |
PROJECT_BINARY_DIR | 同CMAKE_BINARY_DIR |
CMAKE_CURRENT_SOURCE_DIR | 当前处理的CMakeLists.txt所在的路径 |
CMAKE_CURRRENT_BINARY_DIR | 当前处理的CMakeLists.txt中生成目标文件所在编译目录 |
CMAKE_CURRENT_LIST_FILE | 输出调用这个变量的CMakeLists.txt文件的完整路径 |
CMAKE_CURRENT_LIST_DIR | 当前处理的CMakeLists.txt文件所在目录的路径 |
CMAKE_INSTALL_PREFIX | 指定make install 命令执行时包安装路径 |
CMAKE_MODULE_PATH | find_package 命令搜索包路径之一,默认为空 |
0 . Set
使用set来设置变量
(1)使用语法 ${VariableName} 来访问名字为 VariableName 的变量的值(变量名区分大小写)。需要注意的是,即使在字符串中也可以使用 ${VariableName} 来访问变量的值:
set(VAR a b c)
#输出 VAR = a;b;c
message("VAR = ${VAR}")
(2)设置 CMAKE_C_FLAGS 变量和 CMAKE_CXX_FLAGS 变量等
变量名 | 含义 |
---|---|
CMAKE_BUILD_TYPE | 编译选项,Release或者Debug,如set(CMAKE_BUILD_TYPE "Release") |
CMAKE_CXX_FLAGS | 编译标志,设置C++11编译,set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") |
CMAKE_CXX_STANDARD | 也可以设置C++11编译,set(CMAKE_CXX_STANDARD 11) |
1 . ADD_DEFINITIONS
向 C/C++ 编译器添加 -D 定义,比如 在CMakeList.txt文件中添加:
ADD_DEFINITIONS(-DENABLE_DEBUG -DABC) #参数之间用空格分割。
代码中有:
#ifdef ENABLE_DEBUG
... //代码段生效
#endif
如果要添加其他的编译器开关,可以通过 CMAKE_C_FLAGS 变量和 CMAKE_CXX_FLAGS 变量设置。
2.ADD_DEPENDENCIES
定义 target 依赖的其他 target ,确保在编译本 target 之前,其他的 target 已经被构建。
ADD_DEPENDENCIES(target-name depend-target1 depend-target2 ...)
3. ADD_EXECUTABLE 、 ADD_LIBRARY 、 ADD_SUBDIRECTORY
(1)add_executable 命令
命令语法:
add_executable(<name> [WIN32] [MACOSX_BUNDLE][EXCLUDE_FROM_ALL] source1 source2 … sourceN)
命令简述:用于指定从一组源文件 source1 source2 … sourceN 编译出一个可执行文件且命名为 name
使用范例:
add_executable(Main ${DIR_SRCS})
(2)add_library 命令
命令语法:
add_library([STATIC | SHARED | MODULE] [EXCLUDE_FROM_ALL] source1source2 … sourceN)
命令简述:用于指定从一组源文件 source1 source2 … sourceN 编译出一个库文件且命名为 name
使用范例:
add_library(Lib ${DIR_SRCS})
(3) add_subdirectory 命令
命令语法:
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
命令简述:用于添加一个需要进行构建的子目录
使用范例:
add_subdirectory(Lib)
4. ADD_TEST 与 ENABLE_TESTING
ENABLE_TESTING
指令用来控制 Makefile 是否构建 test 目标,涉及工程所有目录。语法很简单,没有任何参数, ENABLE_TESTING() ,一般情况这个指令放在工程的主CMakeLists.txt 中 .
(1)ADD_TEST
ADD_TEST(testname Exename arg1 arg2 ...)
- testname 是自定义的 test 名称,
- Exename 可以是构建的目标文件也可以是外部脚本等等。
- 后面连接传递给可执行文件的参数。如果没有在同一个 CMakeLists.txt 中打开ENABLE_TESTING() 指令,任何 ADD_TEST 都是无效的。
比如我们前面的 Helloworld 栗子,可以在工程主 CMakeLists.txt 中添加
ADD_TEST(mytest ${PROJECT_BINARY_DIR}/bin/main)
ENABLE_TESTING()
生成 Makefile 后,就可以运行 make test 来执行测试了。
5. AUX_SOURCE_DIRECTORY
基本语法是:
AUX_SOURCE_DIRECTORY(dir VARIABLE)
作用是发现一个目录下所有的源代码文件并将列表存储在一个变量中,这个指令临时被用来自动构建源文件列表。因为目前 cmake 还不能自动发现新添加的源文件。
比如
AUX_SOURCE_DIRECTORY(. SRC_LIST)
ADD_EXECUTABLE(main ${SRC_LIST})
6 . CMAKE_MINIMUM_REQUIRED
其语法为
CMAKE_MINIMUM_REQUIRED(VERSION versionNumber [FATAL_ERROR])
比如
CMAKE_MINIMUM_REQUIRED(VERSION 2.8 FATAL_ERROR)
如果 cmake 版本小与 2.8 ,则出现严重错误,整个过程中止。
7. EXEC_PROGRAM
在 CMakeLists.txt 处理过程中执行命令,并不会在生成的 Makefile 中执行。具体语法为:
EXEC_PROGRAM(Executable [directory in which to run]
[ARGS <arguments to executable>]
[OUTPUT_VARIABLE <var>]
[RETURN_VALUE <var>])
用于在指定的目录运行某个程序,通过 ARGS 添加参数,如果要获取输出和返回值,可通过OUTPUT_VARIABLE 和 RETURN_VALUE 分别定义两个变量 .
这个指令可以帮助你在 CMakeLists.txt 处理过程中支持任何命令,比如根据系统情况去修改代码文件等等。
举个简单的栗子,我们要在 src 目录执行 ls 命令,并把结果和返回值存下来。
可以直接在 src/CMakeLists.txt 中添加:
EXEC_PROGRAM(ls ARGS "*.c" OUTPUT_VARIABLE LS_OUTPUT RETURN_VALUE LS_RVALUE)
IF(not LS_RVALUE)
MESSAGE(STATUS "ls result: " ${LS_OUTPUT})
ENDIF(not LS_RVALUE)
在 cmake 生成 Makefile 的过程中,就会执行 ls 命令,如果返回 0 ,则说明成功执行,
那么就输出 ls *.c 的结果。关于 IF 语句,后面的控制指令会提到。
8 . FILE 指令
文件操作指令,基本语法为 :
FILE(WRITE filename "message to write"... )
FILE(APPEND filename "message to write"... )
FILE(READ filename variable)
FILE(GLOB variable [RELATIVE path] [globbingexpressions]...)
FILE(GLOB_RECURSE variable [RELATIVE path] [globbing expressions]...)
FILE(REMOVE [directory]...)
FILE(REMOVE_RECURSE [directory]...)
FILE(MAKE_DIRECTORY [directory]...)
FILE(RELATIVE_PATH variable directory file)
FILE(TO_CMAKE_PATH path result)
FILE(TO_NATIVE_PATH path result)
(1)WRITE
WRITE 将一则信息写入文件’filename’中,如果该文件存在,它会覆盖它,如果不存在,它会创建该文件
(2)APPEND
APPEND 如同WRITE,区别在于它将信息内容追加到文件末尾。
(3)READ
READ 会读取文件的内容并将其存入到变量中。它会在给定的偏移量处开始读取最多numBytes个字节。如果指定了HEX参数,二进制数据将会被转换成十进制表示形式并存储到变量中。
(4)GLOB 遍历
1)目录的遍历
# GLOB 用于产生一个文件(目录)路径列表并保存在variable 中
# 文件路径列表中的每个文件的文件名都能匹配globbing expressions(非正则表达式,但是类似)
# 如果指定了 RELATIVE 路径,那么返回的文件路径列表中的路径为相对于 RELATIVE 的路径
file(GLOB variable [RELATIVE path][globbing expressions]...)
2) 获取当前目录下的所有的文件(目录)的路径并保存到 ALL_FILE_PATH 变量中
file(GLOB ALL_FILE_PATH ./*)
3)获取当前目录下的 .h 文件的文件名并保存到ALL_H_FILE 变量中
# 这里的变量CMAKE_CURRENT_LIST_DIR 表示正在处理的 CMakeLists.txt 文件的所在的目录的绝对路径(2.8.3 以及以后版本才支持)
file(GLOB ALL_H_FILE RELATIVE${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}/*.h)
(5)REMOVE
REMOVE 会删除指定的文件以及子目录下的文件
(6)REMOVE_RECURSE
REMOVE_RECURSE 会删除指定的文件及子目录,包括非空目录
(7)MAKE_DIRECTORY
MAKE_DIRECTORY在指定目录处创建子目录,如果它们的父目录不存在,也会创建它们的父目录
(8)RELATIVE_PATH
RELATIVE_PAT推断出指定文件相对于特定目录的路径
...
9 .INCLUDE
INCLUDE 指令,用来载入 CMakeLists.txt 文件,也用于载入预定义的 cmake 模块 .
INCLUDE(file1 [OPTIONAL])
INCLUDE(module [OPTIONAL])
OPTIONAL 参数的作用是文件不存在也不会产生错误。
你可以指定载入一个文件,如果定义的是一个模块,那么将在 CMAKE_MODULE_PATH 中搜索这个模块并载入。
载入的内容将在处理到 INCLUDE 语句是直接执行。
应用举例:
include(./common.cmake) # 指定包含文件的全路径
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) # 设置include的搜索路径
include(def) # 在搜索路径中搜索def.cmake文件
10.INSTALL(引自:Install用法)
install
方法的基础用法如下
install(TARGETS MyLib
EXPORT MyLibTargets
LIBRARY DESTINATION lib # 动态库安装路径
ARCHIVE DESTINATION lib # 静态库安装路径
RUNTIME DESTINATION bin # 可执行文件安装路径
PUBLIC_HEADER DESTINATION include # 头文件安装路径
)
LIBRARY, ARCHIVE, RUNTIME, PUBLIC_HEADER是可选的,可以根据需要进行选择。
DESTINATION后面的路径可以自行制定,根目录默认为CMAKE_INSTALL_PREFIX
,可以试用set
方法进行指定,
如果使用默认值的话,
- Unix系统的默认值为
/usr/local
, - Windows的默认值为
c:/Program Files/${PROJECT_NAME}
。 - 比如linux系统下若LIBRARY的安装路径指定为
lib
,即为/usr/local/lib
。所以要安装mymath mymathapp
我们可以这样写
# 将库文件,可执行文件,头文件安装到指定目录
install(TARGETS mymath mymathapp
EXPORT MyMathTargets
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
PUBLIC_HEADER DESTINATION include
)
他人如果使用我们编写的函数库,安装完成后,希望可以通过find_package
方法进行引用,这时我们需要怎么做呢。
1. 首先我们需要生成一个MyMathConfigVersion.cmake
的文件来声明版本信息
# 写入库的版本信息
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
MyMathConfigVersion.cmake
VERSION ${PACKAGE_VERSION}
COMPATIBILITY AnyNewerVersion # 表示该函数库向下兼容
)
其中PACKAGE_VERSION
便是我们在CMakeLists.txt
开头project(Installation VERSION 1.0)
中声明的版本号
2. 第二步我们将前面EXPORT MyMathTargets
的信息写入到MyLibTargets.cmake
文件中, 该文件存放目录为${CMAKE_INSTALL_PREFIX}/lib/cmake/MyMath
install(EXPORT MyMathTargets
FILE MyLibTargets.cmake
NAMESPACE MyMath::
DESTINATION lib/cmake/MyLib
)
3. 最后我们在源代码目录新建一个MyMathConfig.cmake.in
文件,用于获取配置过程中的变量,并寻找项目依赖包。如果不依赖外部项目的话,可以直接include MyMathTargets.cmake
文件
include(CMakeFindDependencyMacro)
# 如果想要获取Config阶段的变量,可以使用这个
# set(my-config-var @my-config-var@)
# 如果你的项目需要依赖其他的库,可以使用下面语句,用法与find_package相同
# find_dependency(MYDEP REQUIRED)
# Any extra setup
# Add the targets file
include("${CMAKE_CURRENT_LIST_DIR}/MyMathTargets.cmake")
4. 最后在CMakeLists.txt文件中,配置生成MyMathTargets.cmake
文件,并一同安装到${CMAKE_INSTALL_PREFIX}/lib/cmake/MyMath
目录中。
configure_file(MyMathConfig.cmake.in MyMathConfig.cmake @ONLY)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/MyMathConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/MyMathConfigVersion.cmake"
DESTINATION lib/cmake/MyMath
)
5. 最后我们在其他项目中,就可以使用
find_package(MyMath 1.0)
target_linked_library(otherapp MyMath::mymath)
11.FIND_
FIND_ 系列指令主要包含一下指令:
FIND_FILE(<VAR> name1 path1 path2 ...)
#VAR 变量代表找到的文件全路径,包含文件名
FIND_LIBRARY(<VAR> name1 path1 path2 ...)
#VAR 变量表示找到的库全路径,包含库文件名
FIND_PATH(<VAR> name1 path1 path2 ...)
#VAR 变量代表包含这个文件的路径。
FIND_PROGRAM(<VAR> name1 path1 path2 ...)
#VAR 变量代表包含这个程序的全路径。
FIND_PACKAGE(<name> [major.minor] [QUIET] [NO_MODULE] [[REQUIRED|COMPONENTS] [componets...]])
Find_Package()用法
如果程序中使用了外部库,事先并不知道它的头文件和链接库的位置,就要给出头文件和链接库的查找方法,并将他们链接到程序中。
FIND_PACKAGE(<name> [major.minor] [QUIET] [NO_MODULE]
[[REQUIRED|COMPONENTS] [componets...]])
1) find_package()的查找路径
find_package()命令首先会在模块路径中寻找 一个事先编译好的Find.cmake文件,而且一般官方给出了很多,不需要自己编写这是查找库的一个典型方式。
具体查找路径依次为CMake:
模块模式
- ${CMAKE_MODULE_PATH}中的所有目录。
- 模块目录 /share/cmake-x.y/Modules/ 这称为。
配置模式
- ~/.cmake/packages/或/usr/local/share/中的各个包目录中查找,寻找<库名字的大写>Config.cmake 或者 <库名字的小写>-config.cmake (比如库Opencv,它会查找/usr/local/share/OpenCV中的OpenCVConfig.cmake或opencv-config.cmake)。
2) *.cmake文件定义变量
不管使用哪一种模式,只要找到.cmake,.cmake里面都会定义下面这些变量:
<NAME>_FOUND
<NAME>_INCLUDE_DIRS or <NAME>_INCLUDES
<NAME>_LIBRARIES or <NAME>_LIBRARIES or <NAME>_LIBS
<NAME>_DEFINITIONS
注意大部分包的这些变量中的包名是全大写的,如 LIBFOO_FOUND ,有些包则使用包的实际大小写,如 LibFoo_FOUND
3)添加头文件与链接库文件
如果找到这个包,则可以通过在工程的顶层目录中的CMakeLists.txt 文件添加 include_directories(_INCLUDE_DIRS) 来包含库的头文件,添加target_link_libraries(源文件 _LIBRARIES)命令将源文件与库文件链接起来。
4) 链接OpenCV的例子
创建t4目录添加cmake目录与main.cpp与CMakeList.txt文件
创建cmake目录添加FindOpenCV.cmake文件。
CMakeList.txt
cmake_minimum_required(VERSION 2.8)
PROJECT (HELLO)
SET(SRC_LIST main.cpp)
INCLUDE_DIRECTORIES(cmake)
SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
#在${CMAKE_MODULE_PATH}中添加包含FindOpenCV.cmake目录
FIND_PACKAGE(OpenCV)
#获取OPENCV_FOUND OPENCV_INCLUDE_DIR OPENCV_LIBRARIES
INCLUDE_DIRECTORIES(${OPENCV_INCLUDE_DIR})
ADD_EXECUTABLE(hello ${SRC_LIST})
TARGET_LINK_LIBRARIES(hello ${OPENCV_LIBRARIES})
12.IF 指令
IF(expression)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ELSE(expression)
# ELSE section.
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDIF(expression)
另外一个指令是 ELSEIF
IF(expression)
# THEN section.
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
ELSEIF(expression)
# THEN section.
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
ELSEIF(expression)
# THEN section.
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
ENDIF(expression)
总体把握一个原则,凡是出现 IF 的地方一定要有对应的ENDIF。出现 ELSEIF 的地方, ENDIF 是可选的。
IF判断表达式中常用的指令:
命令名 | 变量说明 |
---|---|
NOT | True if the expression is not true |
AND | True if both expressions would be considered true individually |
OR | True if either expression would be considered true individually |
COMMAND | True if the given name is a command, macro or function that can be invoked |
POLICY | True if the given name is an existing policy |
TARGET | True if the given name is an existing logical target name such as those created by the add_executable(), add_library(), or add_custom_target() commands} |
EXISTS | True if the named file or directory exists. Behavior is well-defined only for full paths |
IS_DIRECTORY | True if the given name is a directory. Behavior is well-defined only for full paths |
IS_SYMLINK | True if the given name is a symbolic link. Behavior is well-defined only for full paths |
IS_ABSOLUTE | True if the given path is an absolute path |
MATCHES | if(<variable\|string> MATCHES regex) True if the given string or variable’s value matches the given regular expression |
LESS | True if the given string or variable’s value is a valid number and less than that on the right |
GREATER | True if the given string or variable’s value is a valid number and greater than that on the right |
EQUAL | True if the given string or variable’s value is a valid number and equal to that on the right |
STRLESS | True if the given string or variable’s value is lexicographically less than the string or variable on the right |
STRGREATER | True if the given string or variable’s value is lexicographically greater than the string or variable on the right |
STREQUAL | True if the given string or variable’s value is lexicographically equal to the string or variable on the right |
VERSION_LESS | Component-wise integer version number comparison (version format is major[.minor[.patch[.tweak]]] |
VERSION_EQUAL | Component-wise integer version number comparison (version format is major[.minor[.patch[.tweak]]]) |
VERSION_GREATER | Component-wise integer version number comparison (version format is major[.minor[.patch[.tweak]]]) |
DEFINED | True if the given variable is defined. It does not matter if the variable is true or false just if it has been |
一个小栗子,用来判断平台差异:
IF(WIN32)
MESSAGE(STATUS “This is windows.”)
# 作一些 Windows 相关的操作
ELSE(WIN32)
MESSAGE(STATUS “This is not windows”)
# 作一些非 Windows 相关的操作
ENDIF(WIN32)
这就用到了我们在“常用变量”一节提到的 CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS 开关。
可以 SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON)
这时候就可以写成 :
IF(WIN32)
ELSE()
ENDIF()
如果配合 ELSEIF 使用,可能的写法是这样 :
IF(WIN32)
#do something related to WIN32
ELSEIF(UNIX)
#do something related to UNIX
ELSEIF(APPLE)
#do something related to APPLE
ENDIF(WIN32)
13.WHILE
WHILE 指令的语法是:
WHILE(condition)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDWHILE(condition)
其真假判断条件可以参考 IF 指令。
14.FOREACH
FOREACH 指令的使用方法有三种形式:
AUX_SOURCE_DIRECTORY(. SRC_LIST)
FOREACH(F ${SRC_LIST})
MESSAGE(${F})
ENDFOREACH(F)
1) 列表
FOREACH(loop_var arg1 arg2 ...)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDFOREACH(loop_var)
像我们前面使用的 AUX_SOURCE_DIRECTORY 的栗子
AUX_SOURCE_DIRECTORY(. SRC_LIST)
FOREACH(F ${SRC_LIST})
MESSAGE(${F})
ENDFOREACH(F)
2 )范围
FOREACH(loop_var RANGE total)
ENDFOREACH(loop_var)从 0 到 total 以1为步进
举例如下:
FOREACH(VAR RANGE 10)
MESSAGE(${VAR})
ENDFOREACH(VAR)
最终得到的输出是:
0
1
2
3
4
5
6
7
8
9
10
3)范围和步进
FOREACH(loop_var RANGE start stop [step])
ENDFOREACH(loop_var)
从 start 开始到 stop 结束,以 step 为步进,
举例如下
FOREACH(A RANGE 5 15 3)
MESSAGE(${A})
ENDFOREACH(A)
最终得到的结果是:
5
8
11
14
注:整个FOREACH遇到 ENDFOREACH 指令,整个语句块才会得到真正的执行。
15.MACRO
宏定义如下:
macro(<name> [arg1 [arg2 [arg3 ...]]])
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
endmacro(<name>)
- <name><name>为函数名字
- arg1、arg2...为函数参数
举个栗子:
set(var "ABC")
macro(Moo arg)
message("arg = ${arg}")
set(arg "abc")
message("# After change the value of arg.")
message("arg = ${arg}")
endmacro()
message("=== Call macro ===")
Moo(${var})
#输出如下:
=== Call macro ===
arg = ABC
# After change the value of arg.
arg = ABC
这里的宏是做了字符串的替换
16.FUNCTION
1)函数定义如下:
function(<name> [arg1 [arg2 [arg3 ...]]])
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
endfunction(<name>)
- <name><name>为函数名字
- arg1、arg2...为函数参数
举个栗子:
set(var "ABC")
function(Foo arg)
message("arg = ${arg}")
set(arg "abc")
message("# After change the value of arg.")
message("arg = ${arg}")
endfunction()
message("=== Call function ===")
Foo(${var})
#输出为:
=== Call function ===
arg = ABC
# After change the value of arg.
arg = abc
上面的栗子和c语言的值传函数比较像
2)实现类似引用传参
set(var "abc") # 定义一个变量var,初值为abc
function(f1 arg)
set(${arg} "ABC" PARENT_SCOPE) # ${arg} == var, 于是相当于set(var "ABC" PARENT_SCOPE)
endfunction()
message("before calling f1, var = ${var}")
f1(var) # 如果写成了f1(${var})会怎样?
message("after calling f1, var = ${var}")
需要注意的两点:
- 函数调用处用变量的名字var,而不是它的值${var}
- 在函数内部,set的时候,要加上作用域PARENT_SCOPE.
3)隐含参数
name | 变量说明 |
---|---|
ARGC | 函数实参的个数 |
ARGV | 所有实参列表 |
ARGN | 所有额外实参列表, 即ARGV去掉函数声明时显示指定的实参,剩余的实参 |
ARGV0 | 函数第1个实参 |
ARGV1 | 函数第2个实参 |
ARGV2 | 函数第3个实参 |
依次类推 | 依次类推 |
使用上面表格里的几个隐含参数,通过下面这个例子可以更好的说明上面两种传递参数的方式,函数内部发生了什么。
function(print_list arg)
message("======= args count : ${ARGC} ======= ") # 实际实参个数
message("======= all args ======= ") # 打印所有参数
foreach(v IN LISTS ARGV)
message(${v})
endforeach()
message("======= all extra args ======= ") # 打印所有额外参数
foreach(v IN LISTS ARGN)
message(${v})
endforeach()
message("======= print content of ARGV0 ======= ") # 打印第一个参数里的所有内容
foreach(v IN LISTS ARGV0)
message(${v})
endforeach()
endfunction()
set(arg hello world)
message("------------ calling with qutoes -----------") # 使用引号来调用
print_list("${arg}")
message("------------ calling without qutoes -----------") # 不使用引号调用
print_list(${arg})
输出为:
------------ calling with qutoes -----------
======= args count : 1 =======
======= all args =======
hello
world
======= all extra args =======
======= print content of ARGV0 =======
hello
world
------------ calling without qutoes -----------
======= args count : 2 =======
======= all args =======
hello
world
======= all extra args =======
world
======= print content of ARGV0 =======
hello
从两个输出结果里可以看到:
1.使用引号包裹参数时
参数个数:1, 即hello world
额外参数个数: 0
打印第一个参数的内容 = 要打印的列表内容
2.不使用引号包裹参数时
参数个数:2, 分别是 hello 和 world
额外参数个数: 1, world
打印第一个参数的内容 = hello
在不使用括号包裹的情况下,因为函数只需要一个参数,列表里除了第一个元素的其它元素被当做额外的参数传给函数了,当我打印第一个参数的时候,就仅仅把列表的第一个元素打印出来了。
通过这个例子可以看到,在不使用括号来包裹列表类型的参数作为函数实参时,列表参数内部的空格(或者分号)会使得这个列表的内容被当做多个参数传递给函数。
此外,CMake里的函数支持递归调用。
参考:
https://blog.csdn.net/qq_35503971/article/details/102754613
https://www.cnblogs.com/fnlingnzb-learner/p/7202222.html
https://blog.csdn.net/zhanghm1995/article/details/80902807