本篇文章不是新手入门教学文章,主要是记录笔者个人的学习笔记
参考文章 :
- CMake中的分支与判断(if语句)
- CMake中的条件判断if/elseif/else
CMake入门(二)
- 一、CMake中的逻辑判断
- 二、 while循环
- 三、CMake中的foreach循环
- 四、数学表达式
- 五、系统信息查询
- 六、函数定义
- 1. 函数定义
- 2. 函数定义会创建一个作用域
- 3. 参数含关键字的函数
- 七、宏定义
- 八、路径操作
- 九、文件操作
一、CMake中的逻辑判断
与大多数的编程语言一样,CMake中也是通过if
语句用于判断条件是否成立。
完整的格式如下:
if(<condition>)<commands>
elseif(<condition>) # 可选,且可重复<commands>
else() # 可选<commands>
endif()
注意要点:
- 注意:
else()
是可选的,else后面必须有一对空括号。 - 条件判断最后要以
endif()
结尾,endif()的括号内也可以写condition,但是必须与if对应的condition完全一致; - CMake中在
if()
、while()
或foreach()
等语句内部定义的变量并不会创建一个新的作用域, 所以如果在if()
、while()
或foreach()
语句块内定义变量,在这些语句块外部,也同样可以访问语句块内的变量。 if/elseif
的特点:条件判断中变量不需要加${}
,cmake会自动尝试根据变量名求值。具体示例可以参考这篇文章 CMake中的分支与判断(if语句)
基础用法
-
常量判断
1
,ON
,YES
,TRUE
,Y
为真,0
,OFF
,NO
,FALSE
,N
,IGNORE
,NOTFOUND
假,不区分大小写。如果不是这些常量,则会被当作变量或字符串对待。if(YES) # 把以上值填入括号测试message(STATUS 真) else()message(STATUS 假) endif()
-
变量判断
普通变量和环境变量都用这种方式。如果变量值不是为假的常量则为真。值为上述为假的或未定义则为假
set(VAR_1 hello world)if(VAR_1) # 变量用做判断时不用加${}message(STATUS 真) else()message(STATUS 假) endif()
-
引号内字符串
除了引号内为上述为真的值,其他都为假
if("HELLO")message(STATUS 真) else()message(STATUS 假) endif()
-
逻辑运算
与
if(<cond1> AND <cond2>)
或
if(<cond1> OR <cond2>)
非
if(NOT <condition>)
-
其他常用判断
-
if(TARGET target-name)
判断一个目标是否存在(由add_executable()
,add_library()
,add_custom_target()
创建) -
if(DEFINED <name>|CACHE{<name>}|ENV{<name>})
判断一个变量是否已定义 -
if(<variable|string> IN_LIST <variable>)
判断给定元素是否在列表中。列表中各项可用空格或分号隔开set(M_LIST hello;world;and;not)if("hello" IN_LIST M_LIST)message(STATUS 真) else()message(STATUS 假) endif()
-
if(EXISTS path-to-file-or-directory)
判断文件或路径是否存在 -
if(<variable|string> MATCHES regex)
判断能否匹配上正则
-
关于CMake中的逻辑判断,更详细的介绍可以参考这篇文章:CMake中的条件判断if/elseif/else
二、 while循环
CMake中的while
命令用于在条件为true
时评估(evaluate
)一组命令,其格式如下:
while(<condition>)<commands>
endwhile()
while
中的与if命令中的具有相同语法和相同的逻辑进行评估。
- 命令
break
和continue
提供了从正常控制流中退出的方法。
下面是一段代码演示了CMake
中的while
的使用:
# CMake中的while循环set(i 0)
while (i LESS 10)# 实现自增math(EXPR i "${i} + 1")# 不等于5就打印数据if(NOT i EQUAL 5)message(STATUS "i = ${i}")else()continue()endif() # 大于7 就退出if(i GREATER 7)break()endif()
endwhile()
三、CMake中的foreach循环
CMake中的foreach循环,类似于C++中的范围for
, 或者是Python
中的for
循环,其基本格式如下:
foreach(<loop_var> <item_list>)<commands>
endforeach()
loop_var
用来接收列表中每一项的变量item_list
需要循环的列表,里面每一项用空格或者分号隔开
可以用continue()
结束本次循环,用break()
终止循环
- 普通迭代遍历
# 打印每一个子项
set(item_list aa bbb ccc dddd)
# 下面这两种写法结果与上面的保持一致
# set(item_list aa;bbb;ccc;dddd)
# set(item_list "aa;bbb;ccc;dddd")
foreach(var ${item_list})message(STATUS ${var})
endforeach()# 结果
-- aa
-- bbb
-- ccc
-- dddd
- 指定范围进行遍历
格式如下:
foreach(<loop_var> RANGE <stop>)
从0循环到stop
指定的数,可以为负数
# 打印0-5
foreach(NUM RANGE 5)message(STATUS ${NUM})
endforeach()# 结果
-- 0
-- 1
-- 2
-- 3
-- 4
-- 5
指定起始值和结束值以及步长
foreach(<loop_var> RANGE <start> <stop> [<step>])
从start
指定的数循环到stop
指定的数,默认步长为1,也可以指定步长。
# 打印0- -10之间的偶数
foreach(NUM RANGE 0 -10 -2)message(STATUS ${NUM})
endforeach()# 结果
-- 0
-- -2
-- -4
-- -6
-- -8
-- -10
- 遍历多个列表
基本格式如下:
foreach(<loop_var> IN [LISTS [<lists>]] [ITEMS [<items>]])
LISTS
后面可以跟一个或多个用分号或空格隔开的列表,会分别循环取出每个列表中的每一项ITEMS
后面可以放上多项内容,循环也会取出每一项
# 示例
set(A 0;1;3)
set(B 2 3)
set(C "4 5")
set(D 6;7 8)
set(E "")
foreach(X IN LISTS A B C D E ITEMS ${A})message(STATUS "X=${X}")
endforeach()# 结果
-- X=0
-- X=1
-- X=3
-- X=2
-- X=3
-- X=4 5
-- X=6
-- X=7
-- X=8
-- X=0
-- X=1
-- X=3
- 一次获取多个结果
foreach(<loop_var>... IN ZIP_LISTS <lists>)
# 示例 使用一个变量进行接收
list(APPEND English one two three four)
list(APPEND Bahasa satu dua tiga)
list(APPEND Chinese 一 二 三 四 五 六)foreach(num IN ZIP_LISTS English Bahasa Chinese)message(STATUS "num_0=${num_0}, num_1=${num_1}, num_2=${num_2}")
endforeach()# 结果
-- num_0=one, num_1=satu, num_2=一
-- num_0=two, num_1=dua, num_2=二
-- num_0=three, num_1=tiga, num_2=三
-- num_0=four, num_1=, num_2=四
-- num_0=, num_1=, num_2=五
-- num_0=, num_1=, num_2=六
# 使用多个变量进行接收
list(APPEND English one two three four)
list(APPEND Bahasa satu dua tiga)
list(APPEND Chinese 一 二 三 四 五 六)foreach(en ba ch IN ZIP_LISTS English Bahasa Chinese)message(STATUS "en=${en}, ba=${ba}, ch=${ch}")
endforeach()
四、数学表达式
在CMake中要进行数学运算我们不能向其他编程语言一样使用数学符号+ - * /
,而是要使用下面的指令,然后在表达式中填写我们要计算的表达式。
math(EXPR <variable> "<expression>" [OUTPUT_FORMAT <format>])
EXPR
: 关键字variable
:运算结果expression
:数学表达式format
:输出格式,值为DECIMAL
表示十进制(默认),HEXADECIMAL
为十六进制
# 示例
math(EXPR result "6 * (5 + 7)" OUTPUT_FORMAT DECIMAL)
message(STATUS ${result})# 结果
-- 72
五、系统信息查询
在CMake中我么可以通过下面的指令来查询一些系统信息
cmake_host_system_information(RESULT <variable> QUERY <key> ...)
variable
存放查询结果key
需要查询的信息,可以为多项,空格隔开
其中key
可以是下面的参数:
OS_NAME
操作系统OS_VERSION
操作系统的版本OS_PLATFORM
操作系统的平台架构(x86 , amd64 , x86_64)OS_RELEASE
操作系统的发布版本(Professional , Educational)PROCESSOR_NAME
处理器的名称PROCESSOR_DESCRIPTION
处理器的描述信息
更多的参数信息可以官方文档
六、函数定义
在 cmake
中我们也可以定义函数,cmake 提供了 function()
命令用于定义一个函数,使用方法如下所示:
1. 函数定义
# 用法
function(<name> [arg1 [arg2 [arg3 ...]]])<commands>...
endfunction(<name>)
name
函数名arg
参数
# 示例
function(func arg1 arg2)message(STATUS ${arg1})message(STATUS ${arg2})
endfunction()func(1 2)# 结果
-- 1
-- 2
在CMake中对于一个函数的参数,可以传递多于该函数要求的参数个数,但是不能少于。
然后在函数中可以使用下面的内置变量进行访问:
ARGC
传入该函数的参数个数。ARGV
传入该函数的参数形成的列表ARGN
未命名的参数形成的列表ARGV<number>
访问ARGV列表中对应下标中的元素
# 示例
function(func arg1 arg2)message(STATUS "arg1=${arg1}")message(STATUS "arg2=${arg2}")message(STATUS "ARGC=${ARGC}")message(STATUS "ARGV=${ARGV}")message(STATUS "ARGN=${ARGN}")message(STATUS "ARGV0=${ARGV0}")message(STATUS "ARGV1=${ARGV1}")
endfunction()func(1 2 3 4 5)# 结果-- arg1=1
-- arg2=2
-- ARGC=5
-- ARGV=1;2;3;4;5
-- ARGN=3;4;5
-- ARGV0=1
-- ARGV1=2
2. 函数定义会创建一个作用域
没有指定了变量的PARENT_SCOPE
时,在函数内部修改变量值在外部无法访问。
下面是一个示例,关于PARENT_SCOPE
的使用
set(num 100)
function(func arg1)set(num 11 PARENT_SCOPE)message(STATUS "arg1=${arg1}")
endfunction()func(100)message(STATUS "num=${num}")# 结果
-- arg1=100
-- num=11
3. 参数含关键字的函数
cmake
中的一些函数target_link_library
中的PUBLIC | PRIVATE | INTERFACE
即为关键字,如果需要在自定义函数中使用类似的关键字,需要用到cmake_parse_arguments
,其语法格式如下:
cmake_parse_arguments(<prefix> <options> <one_value_keywords> <multi_value_keywords> <args>...)
prefix
:前缀,可以用prefix_<KEYWORD>
的形式访问对应的参数值options
:不接收数据的关键字列表one_value_keywords
:接收一项数据的关键字列表multi_value_keywords
:接收多项数据关键字列表args
:函数的参数,可以直接放${ARGV}
或${ARGN}
# 1. 关键字传参 不接收数据的关键字列表
function(func)cmake_parse_arguments("CPA" "NO_VAL" "" "" ${ARGV})message(STATUS "NO_VAL=${CPA_NO_VAL}")
endfunction()func()
func(NO_VAL)# 结果
-- NO_VAL=FALSE
-- NO_VAL=TRUE
# 2.关键字传参 接收一个数据的关键字列表
function(func)cmake_parse_arguments("CPA" "" "ONE_VAL" "" ${ARGV})message(STATUS "ONE_VAL=${CPA_ONE_VAL}")
endfunction()func(ONE_VAL hello)# 结果
-- ONE_VAL=hello
# 3.关键字传参 接收多个数据的关键字列表
function(func)cmake_parse_arguments("CPA" "" "" "MULTI_VAL" ${ARGV})message(STATUS "MULTI_VAL=${CPA_MULTI_VAL}")
endfunction()func(MULTI_VAL hello world 1 2 3)# 结果
-- MULTI_VAL=hello;world;1;2;3
七、宏定义
CMake中宏指令定义与用法类似函数的定义与用法,区别类似于C语言中宏函数定义与普通函数定义。
基本格式:
macro(<name> [<arg1> ...])<commands>
endmacro()
宏定义中可变参数与关键字参数用法与函数相同。
宏定义与函数不同点:
- 宏定义不会创建新的使作用域
- 函数中所有参数、包括
ARGC
、ARGV
等都是变量,但宏定义中这些参数不是变量,因此不能用类似if(DEFINED ...)
的形式来检测宏定义中对应的参数是否已定义 - 宏定义的调用类似于把宏的内容直接复制到调用的地方
# macro宏
set(num 100)
macro(macroFun arg)math(EXPR num "${num} - ${arg}")
endmacro()macroFun(20)
message(STATUS "num=${num}")# 结果
-- num=80
由于宏只是进行文本替换,所以我们可以在宏体内直接修改变量的值
八、路径操作
详细介绍及例子见官方文档
在CMake
中要对路径进行操作可以使用下面的指令,基本格式如下:
cmake_path(GET <path-var> FILENAME <out-var>)cmake_path(GET <path-var> EXTENSION [LAST_ONLY] <out-var>)cmake_path(GET <path-var> STEM [LAST_ONLY] <out-var>)cmake_path(GET <path-var> PARENT_PATH <out-var>)
FILENAME
获取文件名EXTENSION
获取扩展名STEM
获取文件名,不带后缀PARENT_PATH
获取父级路径
# 示例
set(path1 "${CMAKE_CURRENT_SOURCE_DIR}/commands.cmake")
cmake_path(GET path1 FILENAME result1)
cmake_path(GET path1 EXTENSION result2)
cmake_path(GET path1 STEM result3)
cmake_path(GET path1 PARENT_PATH result4)message(STATUS "path1=${path1}")
message(STATUS "result=${result1}")
message(STATUS "result=${result2}")
message(STATUS "result=${result3}")
message(STATUS "result=${result4}")# 结果
-- path1=D:/Complete/commands.cmake
-- result=commands.cmake
-- result=.cmake
-- result=commands
-- result=D:/Complete
将相对路径转化为一个普通路径
cmake_path(NORMAL_PATH <path-var> [OUTPUT_VARIABLE <out-var>])
# 示例
set(path1 "${CMAKE_CURRENT_SOURCE_DIR}/../")
cmake_path(NORMAL_PATH path1 OUTPUT_VARIABLE result1)message(STATUS "path1=${path1}")
message(STATUS "result=${result1}")# 结果
-- path1=D:/Complete/../
-- result=D:/
九、文件操作
- 文件读取
file(READ <filename> <out-var> [...])
- filename 要读取的文件名
- 读取到的内容存放的变量
# 示例
file(READ "License.txt" content)
message(STATUS ${content})# 结果
-- This is the open source License.txt file introduced in
CMake/Tutorial/Step9...
- 文件写入
file({WRITE | APPEND} <filename> <content>...)
# 示例
file(WRITE "a.txt" "hello world") # 文件不存在会自动创建