Cmake中的条件判断if/elseif/else

2022-04-01  本文已影响0人  Domibaba

一、基本命令解析

  if语句用于判断条件是否成立,条件成立时执行对应的语句。完整的格式如下:

if(<condition>)
<commands>
elseif(<condition>) # 可选,且可重复
<commands>
else() # 可选
<commands>
endif()

二、if语句中条件(condition)的优先级

  if语句中条件(condition)的优先级从高到低如下:

1. 圆括号():括号的优先级最高
2. 一元测试命令:
3. 二元测试命令,二元操作符有的左边和右边需要提供变量:
4. 一元逻辑操作符号:
5. 二元逻辑操作符:

三、示例说明

  if语句分为基本表达式、逻辑操作、存在性判断、文件操作、变量比较、版本号比较、变量展开几大类判定,下面依次以示例来介绍:

3.1 基本表达式

# CMakeLists.txt
if(YES)
    message("YES")
endif()

if(NOTFOUND)
else()
    message("Not found")
endif()
# 命令行中执行cmake .后的输出
YES
Not found
# 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
# 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 逻辑操作

# 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
# 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
# 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 存在性检查

# 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
# 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
# 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
# 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
# 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
# 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目录)。

# 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
# 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
# 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
# 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
# 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 正则表达式比较
# 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_LESSVERSION_GREATERVERSION_EQUALVERSION_LESS_EQUALVERSION_GREATER_EQUAL五种,需要注意两点:

# 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()

附录:参考文档

  1. https://cmake.org/cmake/help/latest/command/if.html
上一篇 下一篇

猜你喜欢

热点阅读