条件控制
一、概览
if(<condition>)
<commands>
elseif(<condition>) # optional block, can be repeated
<commands>
else() # optional block
<commands>
endif()
else和endif中可不带参数,如果要带则需要与if中的条件参数一样。
二、优先级
- 括号
- 一元测试如EXISTS、COMMAND和DEFINED
- 二元测试如EQUAL、LESS等
- 一元操作符NOT
- 二元操作符AND和OR
三、基础表达式
1.常量
if(<constant>)
1, ON, YES, TRUE, Y, 或者非0数字(包括浮点数)表示TRUE。
0, OFF, NO, FALSE, N, IGNORE, NOTFOUND, 空字符串,以-NOTFOUND结尾的常量表示FALSE。
常量大小写不敏感。
#constant 大小写不敏感
##constant true
if(1 AND ON AND YES AND TRUE AND Y AND 0.1 )#AND ""
message("constant true")
endif()
##constant false
if(NOT(0 OR OFF OR NO OR fALSE OR N OR IGNORE OR "" OR NOTFOUND))
#以及以-NOTFOUND结尾的常量
message("constant false")
endif()
2.变量
if(<variable>)
如果变量的值不是表示false的常量都为TRUE,否则都是false,包括未定义。
注意:宏参数不是变量。
注意:环境变量不能通过此检测,即都是false。
#variable
set(VAR "1")
#变量在条件中是直接使用var的,而使用${VAR}实际上走的是constant逻辑
#两者在这里没有什么明显区别,但是在macro中使用就有区别了,macro中,使用参数为false,而使用$则根据参数值确定
#其值只要不是const fasle,且定义了,都是true
if(VAR)
message("variable ")
endif()
if(${VAR})
message("variable value" )
endif()
#注意宏参数不是变量
macro(test mvar)
if(${mvar})
message("macro argument" ${mvar})
endif()
endmacro(test mvar)
test(${VAR})
#env varaiable不能被检测。
set(ENV{CXXFLAGS} "test")
message("-------->" $ENV{CXXFLAGS})
if(ENV{CXXFLAGS})
message("variable env " )
endif()
#print:
#variable
#variable value
#macro argument1
#-------->test
3.字符串
if(<string>)
带引号的字符串除了以下的场景外都是false:
- 字符串值是为true的常量值。
- CMP0054没被设置为NEW,且字符串值为变量名。
#string
#除非等于constant true或者cmp0054为new,并且字符串值为变量名,否则都是false
set(VAR "111")
if(NOT "test")
message("string" )
endif()
cmake_policy(SET CMP0054 OLD)
if("VAR")
message("string var" )
endif()
#print:
#string
#string var
四、逻辑操作符
NOT、AND、OR
五、存在检测
1.COMMAND
if(COMMAND <command-name>)
存在名字为command-name的命令、函数、宏则为true,
2.POLICY
if(POLICY <policy-id>)
如果存在policy-id则为true,形式如CMP0054
3.TARGET
if(TARGET <target-name>)
已经存在通过add_executable(), add_library(), or add_custom_target()创建的目标名则为true
4.TEST
if(TEST <test-name>)
已经存在通过add_test()创建的测试名则返回true。
5.DEFINED
if(DEFINED <name>|CACHE{<name>}|ENV{<name>})
如果定义了变量、缓存变量、环境变量name,则返回true。注意name和CACHE{name}是不一样的。
6.IN_LIST
if(<variable|string> IN_LIST <variable>)
判断变量或字符串是否在variable中。
六、文件操作
1.EXISTS
if(EXISTS <path-to-file-or-directory>)
文件或目录是否存在。注意需要传入全路径,如~/开始的目录则不回被扩展为家目录并作为相对目录(即找不到)。
如果路径为符号链接,那么结果由实际目标确定,即如果目标不存在则返回false,否则返回true
字符串为空则为false
2.IS_NEWER_THAN
if(<file1> IS_NEWER_THAN <file2>)
判断file1是否比file2新
3.IS_DIRECTORY
if(IS_DIRECTORY <path>)
判断是否是目录,要求全路径。
4.IS_SYMLINK
if(IS_SYMLINK <path>)
判断是否是软链接,要求全路径。
5.IS_ABSOLUTE
if(IS_ABSOLUTE <path>)
判断是否是绝对路径。注意一些特殊场景:
- 空路径为false
- 在Windows主机上,任何以驱动器号和冒号(例如C:)、正斜杠或反斜杠开头的路径都将计算为true。这意味着像C:no\base\dir这样的路径将计算为true,即使路径的非驱动器部分是相对的。
- 在非windows主机上,任何以波浪号(~)开头的路径都计算为true
七、比较
1.正则
if(<variable|string> MATCHES <regex>)
如果变量或字符串符合正则regex规则,则为true
2.数值判断
if(<variable|string> Comparisons <variable|string>)
Comparisons包括LESS、GREATER、EQUAL、LESS_EQUAL、GREATER_EQUAL。
只有当指定变量或字符串的值为合法数字,并且前者小于、大于、等于、小于等于、大于等于时为true。
3.字符串比较
if(<variable|string> strComparisons <variable|string>)¶
Comparisons包括STRLESS、STRGREATER、STREQUAL、STRLESS_EQUAL、STRGREATER_EQUAL。
只有左侧变量或者字符串值的字母顺序小于、、大于、等于、小于等于、大于等于右侧时为true。
八、版本比较
if(<variable|string> version_Comparisons <variable|string>)¶
Comparisons包括VERSION_LESS、VERSION_GREATER、VERSION_EQUAL、VERSION_LESS_EQUAL、VERSION_GREATER_EQUAL。
只有左侧变量或者字符串的版本小于、大于、等于、小于等于、大于等于右侧时为true。
九、路径比较
if(<variable|string> PATH_EQUAL <variable|string>)
只有左侧路径等于右侧时为true。
比较时,是按路径的逐个部分进行比较,多个分隔符会被合并,斜杠和反斜杠两种分隔符也不会区分。
# comparison is TRUE
if ("/a//b/c" PATH_EQUAL "/a/b/c")
...
endif()
# comparison is TRUE
if ("/a//b/c" PATH_EQUAL "/a\\b/c")
message("path equal" )
endif()
# comparison is FALSE
if ("/a//b/c" STREQUAL "/a/b/c")
...
endif()
十、变量扩展
if命令在CMake的历史中很早就被编写出来了,早于${}变量求值语法,为了方便,它求值由其参数命名的变量,如上面的签名所示。注意,在if命令接收参数之前,使用${}进行正常的变量求值。因此下面的代码
set(var1 OFF)
set(var2 "var1")
if(${var2})
等同于
if(var1)
并且会以if的变量形式处理,即判断var1变量的值,为OFF,因此为false。
而如果我们移除${}
if(var2)
则条件为true,因为var2的变量值不是为false的常量值。
循环一(foreach)
遍历列表中的所有值,并执行一系列命令
一、items
foreach(<loop_var> <items>)
<commands>
endforeach()
items是以分号或者空格分隔的列表。
#items:;或者空格分隔的列表,不要加引号,否则认为是一个
foreach(it a;b;c;d) #"a;b;c;d" print:string list:abcd
message("string list:" ${it})
endforeach(it "a;b;c;d")
#print
#string list:a
#string list:b
#string list:c
#string list:d
二、range
foreach(<loop_var> RANGE <stop>)
从0开始到stop的数字(包含stop),stop是非负整数
foreach(<loop_var> RANGE <start> <stop> [<step>])
指定遍历的起始数字、结束数字以及步长。
foreach(it RANGE 5)
message("range 5:"${it})
endforeach(it RANGE 5)
foreach(it RANGE 5 12 2)
message("5-12,step 2:"${it})
endforeach()
print:
range 5:0
range 5:1
range 5:2
range 5:3
range 5:4
range 5:5
5-12,step 2:5
5-12,step 2:7
5-12,step 2:9
5-12,step 2:11
三、IN
foreach(<loop_var> IN [LISTS [<lists>]] [ITEMS [<items>]])
可以通过LISTS或ITEMS将多个列表合并遍历。
set(A 0;1)
set(B 2 3)
set(C "4 5")
set(D 6;7 8)
set(E "")
foreach(X IN LISTS A B C D E)
message(STATUS "X=${X}")
endforeach()
print:
-- X=0
-- X=1
-- X=2
-- X=3
-- X=4 5
-- X=6
-- X=7
-- X=8
四、IN ZIP_LISTS
foreach(<loop_var>... IN ZIP_LISTS <lists>)
将多个list压缩,类似于多个map。
list(APPEND English one two three four)
list(APPEND Bahasa satu dua tiga)
foreach(num IN ZIP_LISTS English Bahasa)
message(STATUS "num_0=${num_0}, num_1=${num_1}")
endforeach()
foreach(en ba IN ZIP_LISTS English Bahasa)
message(STATUS "en=${en}, ba=${ba}")
endforeach()
print:
-- num_0=one, num_1=satu
-- num_0=two, num_1=dua
-- num_0=three, num_1=tiga
-- num_0=four, num_1=
-- en=one, ba=satu
-- en=two, ba=dua
-- en=three, ba=tiga
-- en=four, ba=
遍历次数为最长的那个列表长度。
循环二(while)
while(<condition>)
<commands>
endwhile()
condition与if相同,为true时重复执行command,可以使用break和continue命令。