条件判断
基本语法
条件判断的基本语法如下:
if(<condition>)
<commands>
elseif(<condition>)
<commands>
else()
<commands>
endif()
if(<condition>):检查条件是否满足。如果满足,则执行随后的命令直到遇到elseif、else或endif。elseif(<condition>):可选。如果前面的if或elseif的条件不满足,将检查这里的条件。可以有多个elseif块。else():可选。如果所有的if和elseif条件都不满足,则执行else块中的命令。endif():结束条件判断块。
在CMake中,基本表达式用于if语句中,以决定是否执行特定的代码块。这些表达式可以是常量、变量或字符串,CMake会根据这些表达式的值来判断条件为真(True)或假(False)。
判定表达式
当表达式的值为以下之一时,条件被认为是真(True):
- 数值
1:代表真。 - 字符串
ON:明确表示启用或真。 - 字符串
YES:同样表示肯定或真。 - 字符串
TRUE:布尔真值。 - 字符串
Y:简短的肯定回答,表示真。 - 非零数值:在大多数编程语言中,非零值通常被解释为真。
- 非空字符串:任何非空的字符串都被视为真,除了下面将要提到的特定假值字符串外。
当表达式的值为以下之一时,条件被认为是假(False):
- 数值
0:代表假。 - 字符串
OFF:明确表示禁用或假。 - 字符串
NO:表示否定或假。 - 字符串
FALSE:布尔假值。 - 字符串
N:简短的否定回答,表示假。 - 字符串
IGNORE:有时用于特定的设置中,解释为假。 - 字符串
NOTFOUND:特别在查找库或程序时,如果未找到,这个值表示假。 - 空字符串:表示没有值,解释为假。
# 示例:变量设置为非零值
set(MY_VAR 42)
if(MY_VAR)
message("MY_VAR is true")
endif()
# 示例:变量设置为假值字符串
set(MY_VAR "FALSE")
if(NOT MY_VAR)
message("MY_VAR is false")
endif()
# 示例:使用未定义的变量
if(UNDEFINED_VAR)
message("This will not be printed")
else()
message("UNDEFINED_VAR is considered false")
endif()
逻辑判断
逻辑操作符NOT、AND、OR允许你根据一个或多个条件来执行特定的代码块。这些操作符的行为与大多数编程语言中的逻辑操作符类似,非常直观。
NOT
NOT操作符用于取反一个条件的结果。如果原条件为真(True),则NOT后的结果为假(False);如果原条件为假(False),则NOT后的结果为真(True)。
set(VAR1 ON)
if(NOT VAR1)
message("VAR1 is false")
else()
message("VAR1 is true")
endif()
在这个例子中,因为VAR1被设置为ON(即真),NOT VAR1的结果为假,所以执行else分支,输出"VAR1 is true"。
AND
AND操作符用于检查两个或更多条件是否都为真。只有当所有条件都为真时,整个AND表达式的结果才为真(True);否则为假(False)。
set(VAR1 ON)
set(VAR2 OFF)
if(VAR1 AND VAR2)
message("Both VAR1 and VAR2 are true")
else()
message("At least one of VAR1 or VAR2 is false")
endif()
在这个例子中,因为VAR1为真而VAR2为假,整个条件表达式的结果为假,所以执行else分支。
OR
OR操作符用于检查两个或更多条件中至少有一个是否为真。如果至少有一个条件为真,整个OR表达式的结果就为真(True);只有当所有条件都为假时,结果才为假(False)。
set(VAR1 ON)
set(VAR2 OFF)
if(VAR1 OR VAR2)
message("At least one of VAR1 or VAR2 is true")
else()
message("Both VAR1 and VAR2 are false")
endif()
在这个例子中,因为至少有一个条件(VAR1)为真,所以整个条件表达式的结果为真,执行if分支。
比较
数值比较
数值比较用于比较两个变量或字符串代表的数值,包括:
LESS:检查左侧的数值是否小于右侧的数值。GREATER:检查左侧的数值是否大于右侧的数值。EQUAL:检查两个数值是否相等。LESS_EQUAL:检查左侧的数值是否小于或等于右侧的数值。GREATER_EQUAL:检查左侧的数值是否大于或等于右侧的数值。
set(NUM1 10)
set(NUM2 20)
if(NUM1 LESS NUM2)
message("NUM1 is less than NUM2")
endif()
字符串比较
字符串比较根据字符串的字典顺序来比较两个变量或字符串的大小,包括:
STRLESS:如果左侧字符串在字典顺序上小于右侧,返回True。STRGREATER:如果左侧字符串在字典顺序上大于右侧,返回True。STREQUAL:如果两个字符串相等,返回True。STRLESS_EQUAL:如果左侧字符串小于等于右侧,返回True。STRGREATER_EQUAL:如果左侧字符串大于等于右侧,返回True。
set(STR1 "apple")
set(STR2 "banana")
if(STR1 STRLESS STR2)
message("apple comes before banana")
endif()
文件判断
判断文件或目录是否存在
- EXISTS 操作用于检查指定路径的文件或目录是否存在。如果路径存在,条件为真(True),否则为假(False)。
if(EXISTS "${PROJECT_SOURCE_DIR}/myfile.txt")
message("myfile.txt exists.")
else()
message("myfile.txt does not exist.")
endif()
判断是否为目录
- IS_DIRECTORY 用于检查给定的路径是否是一个目录。这要求提供的路径是绝对路径。
if(IS_DIRECTORY "${PROJECT_SOURCE_DIR}/mydir")
message("mydir is a directory.")
else()
message("mydir is not a directory.")
endif()
判断是否为软链接
- IS_SYMLINK 操作用于检查指定的文件名是否是一个软链接。这同样要求文件名对应的路径是绝对路径。
if(IS_SYMLINK "/path/to/mylink")
message("mylink is a symlink.")
else()
message("mylink is not a symlink.")
endif()
判断是否为绝对路径
- IS_ABSOLUTE 用于检查给定的路径是否是绝对路径。在Linux上,绝对路径以根目录(
/)开始;在Windows上,它以盘符开始(如C:/)。
if(IS_ABSOLUTE "/usr/local/bin")
message("This is an absolute path.")
else()
message("This is not an absolute path.")
endif()
其他
判断元素是否在列表中
CMake 3.3及更高版本支持IN_LIST查询,这允许开发者检查一个变量或字符串是否在一个给定的列表中。
set(MY_LIST apple banana cherry)
set(MY_ITEM apple)
if(MY_ITEM IN_LIST MY_LIST)
message("${MY_ITEM} is in the list.")
else()
message("${MY_ITEM} is not in the list.")
endif()
比较两个路径是否相等
CMake 3.24及更高版本引入了PATH_EQUAL,这对于比较两个路径是否相等非常有用,特别是在路径可能包含多余的分隔符时。PATH_EQUAL会在比较前标准化路径,从而忽略多余的分隔符。
if("/path/to//directory" PATH_EQUAL "/path/to/directory")
message("Paths are equal.")
else()
message("Paths are not equal.")
endif()
与STREQUAL相比,PATH_EQUAL在处理路径时更加智能,能够识别并处理路径中的冗余分隔符。这在跨平台开发中特别有用,因为不同操作系统的路径分隔符习惯可能不同。
以下示例演示了PATH_EQUAL和STREQUAL在处理包含多余分隔符的路径时的行为差异:
cmake_minimum_required(VERSION 3.26)
project(test)
if("/home//user///directory" PATH_EQUAL "/home/user/directory")
message("Paths are equal using PATH_EQUAL.")
else()
message("Paths are not equal using PATH_EQUAL.")
endif()
if("/home//user///directory" STREQUAL "/home/user/directory")
message("Paths are equal using STREQUAL.")
else()
message("Paths are not equal using STREQUAL.")
endif()
输出结果将展示PATH_EQUAL成功地忽略了路径中的多余分隔符,而STREQUAL则没有。
循环
有两种循环方式,分别是foreach和while。
foreach
使用foreach可以执行重复的任务,如设置变量、打印信息、或者根据列表中的每个项目执行特定的命令。
基本的foreach循环语法如下:
foreach(loop_var IN ITEMS item1 item2 ... itemN)
# 执行的命令
endforeach()
loop_var是循环变量,在每次循环中,它被设置为当前项的值。ITEMS后面跟着的是一系列要遍历的项目。
遍历数值范围
- 单一终止值:
foreach(loop_var RANGE stop)
# 执行的命令
endforeach()
- 这里
loop_var从0遍历到stop,包括stop。
- 指定起始、终止值(可选步长):
foreach(loop_var RANGE start stop [step])
# 执行的命令
endforeach()
start:遍历开始的值。stop:遍历结束的值。step:(可选)遍历的步长,默认为1。
示例
当需要遍历一个从0开始到指定终止值的整数序列时,可以使用RANGE关键字。
foreach(item RANGE 10)
message(STATUS "当前遍历的值为: ${item}")
endforeach()
这将输出从0到10的数值,包括10。
示例2
如果需要从特定的起始值开始遍历,到特定的终止值结束,并且还可以指定步长(默认为1),可以使用增强版的RANGE。
foreach(item RANGE 10 30 2)
message(STATUS "当前遍历的值为: ${item}")
endforeach()
这将从10开始,到30结束,步长为2,输出10, 12, 14, ..., 30。
遍历列表
foreach还可以遍历一个或多个列表,通过IN LISTS或ITEMS关键字。
- 遍历单一或多个列表:
foreach(loop_var IN LISTS list1 [list2 ...])
# 执行的命令
endforeach()
IN LISTS后面可以指定一个或多个列表变量。
- 遍历一系列项目:
foreach(loop_var IN ITEMS item1 [item2 ...])
# 执行的命令
endforeach()
IN ITEMS后面跟着要直接遍历的项目序列。
示例:遍历列表或多个列表
- 使用
IN LISTS直接遍历一个或多个列表变量:
set(WORD a b c d)
set(NAME ace sabo luffy)
foreach(item IN LISTS WORD NAME)
message(STATUS "当前遍历的值为: ${item}")
endforeach()
- 使用
IN ITEMS遍历一个或多个通过变量展开的列表:
set(WORD a b c "d e f")
set(NAME ace sabo luffy)
foreach(item IN ITEMS ${WORD} ${NAME})
message(STATUS "当前遍历的值为: ${item}")
endforeach()
遍历多个列表(CMake 3.17及以上)
foreach(loop_var1 loop_var2 ... IN ZIP_LISTS list1 list2 ...)
# 执行的命令
endforeach()
IN ZIP_LISTS允许同时遍历多个列表,loop_var1、loop_var2等变量分别对应每个列表中当前位置的元素。
示例:ZIP_LISTS
在CMake 3.17及以上版本中,foreach命令引入了IN ZIP_LISTS,这允许你同时遍历多个列表,并在每次迭代中从每个列表中取出相应位置的元素。
list(APPEND WORD hello world "hello world")
list(APPEND NAME ace sabo luffy zoro sanji)
foreach(item1 item2 IN ZIP_LISTS WORD NAME)
message(STATUS "当前遍历的值为: item1 = ${item1}, item2=${item2}")
endforeach()
这种方式特别适用于需要同时处理多个相关列表的情况,如同时遍历文件名列表和对应的目标名列表。
while
与foreach循环相比,while循环更加灵活,因为它允许你在每次循环结束时基于复杂的逻辑来更新循环条件。这使得while循环特别适合于处理那些在循环开始前不能确定迭代次数的情况。
基本语法:
while(<condition>)
<commands>
endwhile()
<condition>:这是循环继续执行的条件。只要条件评估为真(即,非0或非空字符串,或特定的CMake真值,如ON、TRUE等),循环就会继续。<commands>:在每次循环迭代中执行的命令集。
示例:遍历列表并逐个移除元素
在你提供的示例中,while循环用于遍历一个列表,并在每次迭代中从列表的头部移除一个元素,直到列表为空:
cmake_minimum_required(VERSION 3.5)
project(test)
# 创建一个列表
set(NAME luffy sanji zoro nami robin)
# 获取列表长度
list(LENGTH NAME LEN)
# 循环直到列表为空
while(${LEN} GREATER 0)
message(STATUS "names = ${NAME}")
# 从列表头部移除一个元素
list(POP_FRONT NAME)
# 更新列表长度
list(LENGTH NAME LEN)
endwhile()
每次循环迭代都会更新列表NAME,并重新计算其长度LEN。循环继续执行直到LEN为0,即列表中不再有元素。