一、基本命令解析
if
语句用于判断条件是否成立,条件成立时执行对应的语句。完整的格式如下:
if
(<condition>)
<commands>
elseif
(<condition>) # 可选,且可重复
<commands>
else()
# 可选
<commands>
endif()
- 如果
if
括号内的condition
为真,则执行if
对应的语句块; - 如果
if
括号内的condition
为假,则判断elseif
语句的condition
是否为真,为真则执行elseif
对应的语句块,注意:elseif
是可选的,并且可以出现多次; - 如果
elseif
括号内的condition
为假,则执行else()
对应的语句块,注意:else()
是可选的,else
后面有一对空括号,也可以在括号里面写condition
,但是必须与if
对应的condition
完全一致; - 最后以
endif()
结尾,注意:endif()
的括号内也可以写condition
,但是必须与if
对应的condition
完全一致;
二、if
语句中条件(condition
)的优先级
if
语句中条件(condition
)的优先级从高到低如下:
1. 圆括号()
:括号的优先级最高
2. 一元测试命令:
-
EXIST
:判断文件或者目录是否存在,存在时为真。需要提供全路径;如果文件或者目录是符号链接(例如软连接),则只有当链接的目标存在时返回真。格式为:if(EXISTS path-to-file-or-directory)
。 -
COMMAND
:如果给定的名称是命令、宏或者函数这类可被调用的对象,则返回真。格式为:if(COMMAND command-name)
。 -
DEFINED
:如果给定的变量(普通变量、缓存变量、环境变量)存在,则返回真。格式为:if(DEFINED <name>|CACHE{<name>}|ENV{<name>})
,注意缓存变量前要加CACHE
,环境变量前要加ENV
。
3. 二元测试命令,二元操作符有的左边和右边需要提供变量:
-
EQUAL
:左边两个字符串或者变量相等时为真。格式为:if(<variable|string> EQUAL <variable|string>)
。 -
LESS
:左边小于右边时为真(数值比较)。格式为:if(<variable|string> LESS <variable|string>)
。 -
LESS_EQUAL
:左边小于等于右边时为真(数值比较)。格式为:if(<variable|string> LESS_EQUAL <variable|string>)
。 -
GREATER
:左边大于右边时为真(数值比较)。格式为:if(<variable|string> GREATER <variable|string>)
。 -
GREATER_EQUAL
:左边大于等于右边时为真(数值比较)。格式为:if(<variable|string> GREATER_EQUAL <variable|string>)
。 -
STREQUAL
:左边与右边的字典顺序相等时为真(数值比较)。格式为:if(<variable|string> STREQUAL <variable|string>)
。 -
STRLESS
:左边的字典顺序小于右边的字典序时为真。格式为:if(<variable|string> STRLESS <variable|string>)
。 -
STRLESS_EQUAL
:左边的字典顺序小于等于右边的字典序时为真。格式为:if(<variable|string> STRLESS_EQUAL <variable|string>)
。 -
STRGREATER
:左边的字典顺序大于右边的字典序时为真。格式为:if(<variable|string> STRGREATER <variable|string>)
。 -
STRGREATER_EQUAL
:左边的字典顺序大于等于右边的字典序时为真。格式为:if(<variable|string> STRGREATER_EQUAL <variable|string>)
。 -
VERSION_EQUAL
:左右两边的版本号相等时为真。左右两边的版本号格式为major[.minor[.patch[.tweak]]]
,每个部分都要求是整数(省略的部分当做0),如果是非整数,从非整数的地方往后会被截断。格式为:if(<variable|string> VERSION_EQUAL <variable|string>)
。 -
VERSION_LESS
:左边版本号小于右边版本号时为真。左右两边的版本号格式为major[.minor[.patch[.tweak]]]
,每个部分都要求是整数(省略的部分当做0)),如果是非整数,从非整数的地方往后会被截断。格式为:if(<variable|string> VERSION_LESS <variable|string>)
。 -
VERSION_LESS
:左边版本号小于右边版本号时为真。左右两边的版本号格式为major[.minor[.patch[.tweak]]]
,每个部分都要求是整数(省略的部分当做0),如果是非整数,从非整数的地方往后会被截断。格式为:if(<variable|string> VERSION_LESS <variable|string>)
。 -
VERSION_LESS_EQUAL
:左边版本号小于等于右边版本号时为真。左右两边的版本号格式为major[.minor[.patch[.tweak]]]
,每个部分都要求是整数(省略的部分当做0),如果是非整数,从非整数的地方往后会被截断。格式为:if(<variable|string> VERSION_LESS_EQUAL <variable|string>)
。 -
VERSION_GREATER
:左边版本号大于右边版本号时为真。左右两边的版本号格式为major[.minor[.patch[.tweak]]]
,每个部分都要求是整数(省略的部分当做0),如果是非整数,从非整数的地方往后会被截断。格式为:if(<variable|string> VERSION_GREATER <variable|string>)
。 -
VERSION_GREATER_EQUAL
:左边版本号大于等于右边版本号时为真。左右两边的版本号格式为major[.minor[.patch[.tweak]]]
,每个部分都要求是整数(省略的部分当做0),如果是非整数,从非整数的地方往后会被截断。格式为:if(<variable|string> VERSION_GREATER_EQUAL <variable|string>)
。 -
MATCHES
:按照正则表达式去匹配,左边是待匹配的值,右边是正则表达式,能匹配为时为真。格式为:if(<variable|string> MATCHES regex)
。
4. 一元逻辑操作符号:
-
NOT
:条件不满足时为真。格式为:if(NOT <condition>)
。
5. 二元逻辑操作符:
-
AND
:左右两边条件均成立时为真。格式为:if(<cond1> AND <cond2>)
。 -
OR
:左右两边条件任一个成立时为真。格式为:if(<cond1> OR <cond2>)
。
三、示例说明
if
语句分为基本表达式、逻辑操作、存在性判断、文件操作、变量比较、版本号比较、变量展开几大类判定,下面依次以示例来介绍:
3.1 基本表达式
-
if(<constant>)
如果constant
是1
,ON
,YES
,TRUE
,Y
或非0值
时为真;
如果constant
是0
,OFF
,NO
,FALSE
,N
,IGNORE
,NOTFOUND
,空字符串
,以NOTFOUND为后缀
时为假;
注意constant
是大小写不敏感的,如果constant
不是上述值,则会被当成变量或者字符串。
# CMakeLists.txt
if(YES)
message("YES")
endif()
if(NOTFOUND)
else()
message("Not found")
endif()
# 命令行中执行cmake .后的输出
YES
Not found
-
if(<variable>)
如果给定的变量有定义,且值不是0
,OFF
,NO
,FALSE
,N
,IGNORE
,NOTFOUND
,空字符串
,以NOTFOUND为后缀
时为真;
其他情况该判断为假;
注意:宏参数和环境变量不能这样判断,例如if(ENV{var})
肯定为假;
# CMakeLists.txt
if(test)
else()
message("var test not exists")
endif()
set(test 100)
if(test)
message("var test exists and it's value is ${test}")
endif()
# 命令行中执行cmake .后的输出
var test not exists
var test exists and it's value is 100
-
if(<string>)
string
为双引号括起来的字符串,除了以下两种情况外,其他情况均为假:
当string
的值为1
,ON
,YES
,TRUE
,Y
或非0值
时为真
当CMP0054
策略设置为OLD
,则双引号括起来的字符串会当成变量解引用为变量的值(字符串对应的变量已经定义的情况下),之后再判定表达式的真假;当CMP0054
策略设置为NEW
,双引号括起来的字符串仅仅是作为字符串。
# CMakeLists.txt
set(test "YES")
set(stest "test")
cmake_policy(SET CMP0054 NEW) # 设置策略CMP0054为NEW
if("Y")
message("TRUE")
endif()
if("test") # test被当成字符串,非constant的真值,因此判定为假
else()
message("string 'test' result false")
endif()
if("${stest}") # stest变量值为test,test被当成字符串,非constant的真值,因此判定为假
else()
message("string $stest dereference value ${stest} result false")
endif()
cmake_policy(SET CMP0054 OLD) # 设置策略CMP0054为OLD
if("test") # test被当成变量,解引用后值为YES,为constant真值
message("var test dereference value ${test}, result true")
endif()
if("${stest}") # stest先解析为test,test被当成变量,解引用后值为YES,为constant真值
message("string $stest dereference value ${stest} then $test dereference value ${test} result true")
endif()
# 命令行中执行cmake .后的输出
string 'test' result false
string $stest dereference value test result false
string test dereference value YES, result true
string $stest dereference value test then $test dereference value YES result true
3.2 逻辑操作
-
if(NOT <condition>)
:逻辑非操作
# CMakeLists.txt
if(NOT IGNORE)
message("not ignore is true")
endif()
if(NOT YES)
else()
message("not YES is false")
endif()
# 命令行中执行cmake .后的输出
not ignore is true
not YES is false
-
if(<cond1> AND <cond2>)
:逻辑与操作
# CMakeLists.txt
if(ON AND Y)
message("ON AND Y is true")
endif()
if(TRUE AND FALSE)
else()
message("TRUE AND FALSE is false")
endif()
# 命令行中执行cmake .后的输出
ON AND Y is true
TRUE AND FALSE is false
-
if(<cond1> AND <cond2>)
:逻辑或操作
# CMakeLists.txt
if(IGNORE OR NOTFOUND)
else()
message("IGNORE OR NOTFOUND is false")
endif()
if(TRUE OR FALSE)
message("TRUE OR FALSE is true")
endif()
if(TRUE AND (IGNORE OR YES)) # 注意括号的优先级最高,会先计算括号内的OR逻辑的值
message("TRUE AND (IGNORE OR YES) is true")
endif()
# 命令行中执行cmake .后的输出
IGNORE OR NOTFOUND is false
TRUE OR FALSE is true
TRUE AND (IGNORE OR YES) is true
3.3 存在性检查
-
if(COMMAND command-name)
:判断是否为命令
# CMakeLists.txt
function(f)
endfunction()
if(COMMAND f)
message("f is function")
endif()
if(COMMAND message)
message("message is a command")
endif()
if(COMMAND test)
else()
message("test is not a command")
endif()
# 命令行中执行cmake .后的输出
f is function
message is a command
test is not a command
-
if(POLICY policy-id)
:判断是否为策略,策略的格式为CMP<NNNN>
# CMakeLists.txt
if(POLICY CMP0054)
message("CMP0054 is a policy")
endif()
set(cmp CMP0001)
if(POLICY ${cmp})
message("${cmp} is a policy")
endif()
set(cmp CMP_TEST)
if(POLICY ${cmp})
else()
message("${cmp} is not a policy")
endif()
# 命令行中执行cmake .后的输出
CMP0054 is a policy
CMP0001 is a policy
CMP_TEST is not a policy
-
if(TARGET target-name)
:判断是否为通过调用add_executable()、add_library()、add_custom_target()
创建的构建目标
# CMakeLists.txt
add_executable(runTest test.cpp)
add_library(libTest test.cpp)
if(TARGET runTest)
message("runTest is a target")
endif()
if(TARGET libTest)
message("libTest is a target")
endif()
if(TARGET test)
else()
message("test is not a target")
endif()
# 命令行中执行cmake .后的输出
runTest is a target
libTest is a target
test is not a target
-
if(TEST test-name)
:判断是否为通过调用add_test()
创建的测试对象
# CMakeLists.txt
add_executable(runTest test.cpp)
add_test(NAME mytest COMMAND runTest)
if(TEST mytest)
message("mytest is a test")
endif()
# 命令行中执行cmake .后的输出
mytest is a test
-
if(DEFINED <name>|CACHE{<name>}|ENV{<name>})
:判断变量、缓存变量、环境变量是否存在,DEFINED
不关注变量具体的值。有两点需要注意:一是宏参数不是变量;二是无法直接判断一个变量是不是非缓存变量,因为不论是缓存变量还是非缓存变量,只要变量存在,if(DEFINED someName)
都会返回true
,但是可以通过if(DEFINED CACHE{someName})
判断出一个缓存变量是否存在,因此判断一个非缓存变量是否存在,可以通过if(DEFINED someName AND NOT DEFINED CACHE{someName})
实现。
# CMakeLists.txt
if(DEFINED var1)
else()
message("var1 not defined")
endif()
set(var2 "NOTFOUND")
if(DEFINED var2)
message("var2 defined and it's value is ${var2}")
endif()
if(var2) # 会按照if(constant)基本表达式来判断
else()
message("var2 return false, value is ${var2}")
endif()
if(DEFINED ENV{MY_TEST_ENV_VAR})
message("ENV MY_TEST_ENV_VAR defined: $ENV{MY_TEST_ENV_VAR}")
else()
message("ENV MY_TEST_ENV_VAR not defined")
endif()
set(cacheVar "YES" CACHE INTERNAL "internal var")
if(DEFINED CACHE{cacheVar})
message("cache cacheVar defined: ${cacheVar}")
endif()
if(DEFINED cacheVar) # 无法区分普通变量或缓存变量
message("cacheVar defined: ${cacheVar}")
endif()
if(DEFINED var2 AND (NOT DEFINED CACHE{var2})) # 判断一个变量是否为普通变量
message("var2 defined and is not a cache variable")
endif()
# 在命令行中定义一个环境变量
export MY_TEST_ENV_VAR="env var"
# 命令行中执行cmake .后的输出
var1 not defined
var2 defined and it's value is NOTFOUND
var2 return false, value is NOTFOUND
ENV MY_TEST_ENV_VAR defined: env var
cache cacheVar defined: YES
cacheVar defined: YES
var2 defined and is not a cache variable
-
if(<variable|string> IN_LIST <variable>)
:判断变量是否存在列表中。
# CMakeLists.txt
set(listVar test1 test2 test3) # define list : test1;test2;test3
if(test1 IN_LIST listVar)
message("test1 in listVar")
endif()
if(test4 IN_LIST listVar)
else()
message("test4 not in listVar")
endif()
# 命令行中执行cmake .后的输出
test1 in listVar
test4 not in listVar
3.4 文件操作
文件的操作,CMake
官方文档中都要求使用全路径,if
判断的结果才是有明确定义的,相对路径可能无法识别(例如~
不会解析成home
目录)。
-
if(EXISTS path-to-file-or-directory)
:判断文件或目录是否存在。
# CMakeLists.txt
set(myfile "/tmp/testfile")
set(mydir "/tmp/testdir")
if(EXISTS ${myfile})
message("file ${myfile} exist")
endif()
if(EXISTS ${mydir})
message("directory ${mydir} exist")
endif()
if(EXISTS "/tmp/link")
message("link to target exists")
endif()
if(EXISTS "/tmp/link2")
else()
message("link2 to target2 not exists")
endif()
# 先来创建一些文件和软连接
touch /tmp/testfile
mkdir /tmp/testdir
ln -s /tmp/target /tmp/link
touch /tmp/target # link链接到target
ln -s /tmp/target2 /tmp/link2 # link2链接的文件实际不存在
# 命令行中执行cmake .后的输出
file /tmp/testfile exist
directory /tmp/testdir exist
link to target exists
link2 to target2 not exists
-
if(file1 IS_NEWER_THAN file2)
:判断文件1是否比文件2要新。注意:当有文件不存在的时候,该判断返回true
;
# CMakeLists.txt
if("/tmp/newfile" IS_NEWER_THAN "/tmp/oldfile")
message("/tmp/newfile is newer than /tmp/oldfile")
endif()
# 创建测试文件,按顺序
touch /tmp/oldfile
touch /tmp/newfile
# 命令行中执行cmake .后的输出
/tmp/newfile is newer than /tmp/oldfile
-
if(IS_DIRECTORY path-to-directory)
:判断是否为目录;
# CMakeLists.txt
if(IS_DIRECTORY "/tmp")
message("/tmp is directory")
endif()
if(IS_DIRECTORY "/tmp/no-exist-dir")
else()
message("/tmp/no-exist-dir not a directory")
endif()
if(IS_DIRECTORY "/tmp/testfile")
else()
message("/tmp/testfile not a directory")
endif()
# 先来创建文件
touch /tmp/testfile
# 命令行中执行cmake .后的输出
/tmp is directory
/tmp/no-exist-dir not a directory
/tmp/testfile not a directory
-
if(IS_SYMLINK file-name)
:判断是否为链接文件;
# CMakeLists.txt
if(IS_SYMLINK "/tmp/link")
message("/tmp/link is symbol link")
endif()
if(IS_SYMLINK "/tmp/testfile")
else()
message("/tmp/testfile not symbol link")
endif()
# 命令行中执行cmake .后的输出
/tmp/link is symbol link
/tmp/testfile not symbol link
-
if(IS_ABSOLUTE path)
:判断是否为绝对路径;注意有几个特殊的处理:1)空路径判定为false
;2)Windows
下,以盘符和冒号开头(例如C:
),或以正斜线/反斜线开头的都将判定为true
,因此C:no\base\dir
为true
,尽管盘符后的no\base\dir
是一个相对路径;3)非Windows
,例如Linux
或macOS
下,~
开头的也会判定为true
,即使路径并不存在。
# CMakeLists.txt
if(IS_ABSOLUTE "/tmp/testfile")
message("/tmp/testfile is full path")
endif()
if(IS_ABSOLUTE "~/no-file")
message("~ regard as full path")
endif()
if(IS_ABSOLUTE "./CMakeLists.txt")
else()
message("./CMakeLists.txt is not full path")
endif()
# 命令行中执行cmake .后的输出
/tmp/testfile is full path
~ regard as full path
./CMakeLists.txt is not full path
3.5 变量比较操作
比较操作可以分为三大类:1)正则表达式匹配;2)按数值大小;3)按字典序。
3.5.1 正则表达式比较
-
if(<variable|string> MATCHES regex)
:正则表达式regex
的格式可以参考这里。
# CMakeLists.txt
if("test" MATCHES .*)
message("match any")
endif()
if("t" MATCHES .)
message("match sigle char")
endif()
# 命令行中执行cmake .后的输出
match any
match sigle char
3.5.2 数值比较
数值比较有小于(LESS
)、大于(GREATER
)、等于(EQUAL
)、大于等于(GREATER_EQUAL
)、小于等于(LESS_EQUAL
)五种。需要注意:比较的两个变量是有效的数值,例如100、200这些是有效数值,"100"、"200"也是有效数值,但是"a100"、"200c"就不是有效数值。如果任意一个不是有效数值,会返回false
。
# CMakeLists.txt
if("a100" GREATER 50)
else()
message("unvalid number a100")
endif()
if("100" GREATER "50")
message("GREATER")
endif()
if("2" LESS "3")
message("LESS")
endif()
if(5 EQUAL 5)
message("EQUAL")
endif()
if(5 LESS_EQUAL 5)
message("LESS_EQUAL - actual equal")
endif()
if(5 LESS_EQUAL 6)
message("LESS_EQUAL")
endif()
if(5 GREATER_EQUAL 5)
message("GREATER_EQUAL - actual equal")
endif()
if("7" GREATER_EQUAL 6)
message("GREATER_EQUAL")
endif()
# 命令行中执行cmake .后的输出
unvalid number a100
GREATER
LESS
EQUAL
LESS_EQUAL - actual equal
LESS_EQUAL
GREATER_EQUAL - actual equal
GREATER_EQUAL
3.5.3 字典序比较
数值比较有小于(STRLESS
)、大于(STRGREATER
)、等于(STREQUAL
)、大于等于(STRGREATER_EQUAL
)、小于等于(STRLESS_EQUAL
)五种,注意不管是数还是字符串,都是按照字典序进行比较。
# CMakeLists.txt
if(21 STRLESS 3)
message("STRLESS - number")
endif()
if("21" STRLESS "3")
message("STRLESS - string")
endif()
if("abc" STREQUAL "abc")
message("STREQUAL")
endif()
if(5 STRGREATER 200000)
message("STRGREATER - number")
endif()
if("5" STRGREATER "200000")
message("STRGREATER - string")
endif()
if("abc" STRLESS_EQUAL "abc")
message("STRLESS_EQUAL - actual equal")
endif()
if("abc" STRLESS_EQUAL "b")
message("STRLESS_EQUAL")
endif()
if("abc" STRGREATER_EQUAL "abc")
message("STRGREATER_EQUAL - actual equal")
endif()
if("abc" STRGREATER_EQUAL "abb")
message("STRGREATER_EQUAL")
endif()
# 命令行中执行cmake .后的输出
STRLESS - number
STRLESS - string
STREQUAL
STRGREATER - number
STRGREATER - string
STRLESS_EQUAL - actual equal
STRLESS_EQUAL
STRGREATER_EQUAL - actual equal
STRGREATER_EQUAL
3.6 版本号比较
版本号比较也有VERSION_LESS
、VERSION_GREATER
、VERSION_EQUAL
、VERSION_LESS_EQUAL
、VERSION_GREATER_EQUAL
五种,需要注意两点:
- 版本号的格式为
major[.minor[.patch[.tweak]]]
的四段点分格式,每部分都是一个整数值; - 如果版本号不是一个有效的整数,那么会从非数字的地方到尾部全部截断。例如
1.2a22.3.4
会截断成1.2
,a
后的部分全部被丢弃。如果最开始就是非数字,那么版本号被当成0
。
# CMakeLists.txt
if(1.2.3.4 VERSION_GREATER 1.2a.3.5)
message("VERSION_GREATER - truncate")
endif()
if(1.2.3.4 VERSION_LESS 1.2.3.5)
message("VERSION_LESS")
endif()
if(1.2.3.4 VERSION_GREATER 1.1)
message("VERSION_GREATER")
endif()
if(1.2.3.4 VERSION_EQUAL 1.2.3.4)
message("VERSION_EQUAL")
endif()
if(1.2.3.4 VERSION_LESS_EQUAL 1.2.3.4)
message("VERSION_LESS_EQUAL - actual equal")
endif()
if(1.2.3.4 VERSION_LESS_EQUAL 1.2.3.5)
message("VERSION_LESS_EQUAL")
endif()
if(1.2.3.4 VERSION_GREATER_EQUAL 1.2.3.4)
message("VERSION_GREATER_EQUAL - actual equal")
endif()
if(1.2.3.4 VERSION_GREATER_EQUAL 1.2.3.2)
message("VERSION_GREATER_EQUAL")
endif()
VERSION_GREATER - truncate
VERSION_LESS
VERSION_GREATER
VERSION_EQUAL
VERSION_LESS_EQUAL - actual equal
VERSION_LESS_EQUAL
VERSION_GREATER_EQUAL - actual equal
VERSION_GREATER_EQUAL
3.7 变量展开
对于if(${var})
这种形式,很容易理解是要将变量var
的值放到if
中进行判断。但是由于在CMake
中,if
表达式出现的比${}
要早,因此早期对于if(var)
这种形式,var
到底是按照变量还是值/字符串来解析?CMake
的做法是:首先看var
是否是一个已经定义的变量,如果是,则需要将他的值替换到if
中,否则就直把var
当成一个字符串/值。
来看一个例子:
set(var "NO")
if(var)
message("true for ${var}")
else()
message("false for ${var}")
endif()
此处的var
是一个已经定义好的变量,因此if
判断转化为if("NO")
,它会按照if(<constant>)
最终的判定结果是false
,因此上面的执行结果输出false for NO
。如果没有set(var "NO")
这句定义,那么if
判断转为if(<variable>)
,因为变量不存在而判定为false
。
来看一个稍微复杂一点的例子:
set(var "NO")
set(var2 "var")
if(${var2}) # 首先会解析${var2},因此等价于if(var),进而等价于if("NO")
message("true for ${var2}")
else()
message("false for ${var2}")
endif()
在if
命令执行之前,${var2}
首先会执行,会替换成变量var2
的值,因此变成if(var)
,然后仍然会先去判断var
是否是一个已经定义好的变量。因此后续的执行过程与上个例子一样。if
判断转化为if("NO")
,它会按照if(<constant>)
最终的判定结果是false
,输出false for NO
。如果没有set(var "NO")
这句赋值,那么if
判断转为if(<variable>)
,因为变量不存在而判定为false
。当然,如果把if(${var2})
替换成if(var2)
,会转化成if("var")
,那么if
判断转为if(<string>)
,字符串结果判定为true
.
set(var "NO")
set(var2 "var")
if(var2) # var2是变量,因此等价于if("var"),此时"var"会当成字符串而不是变量
message("true for ${var2}")
else()
message("false for ${var2}")
endif()
网友评论