参考原文链接:https://zhuanlan.zhihu.com/p/653282782
在前文已经学习了CMake作为一门语言的最基本内容,接下来是进一步的内容
- 流程控制(条件语句,循环语句)
- 函数和宏
- cmake脚本/模块(不是CMakeLists,而是.cmake文件)
- string字符串处理
流程控制
条件语句:if语句
最完整的if语法结构如下
if(<condition>)<commands>
elseif(<condition>) # optional block, can be repeated<commands>
else() # optional block<commands>
endif()
其中的elseif和else都是可选的,例如
if(WIN32)message(STATUS "Now is windows")
elseif(APPLE)message(STATUS "Now is Apple systens.")
elseif(UNIX)message(STATUS "Now is UNIX-like OS's.")
endif()
正如前文中提到的,在if后面的变量,不需要使用${Var}
的形式获取Var的值,而是直接使用Var。
条件语法
在if中条件,也就是if(P)
中的命题P可以实现丰富的功能,因此值得详细讨论
{%note info%} 因为if语句出现的太早了,导致if(P)
的语法看起来非常奇怪: 尝试对一个变量名称自动求值,if(${P})
。 如果希望处理一个可能是变量名的字符串,建议使用双引号if("${P}")
,这会抑制if的自动求值。总之要么用if(P)
要么用if("${P}")
{%endnote%}
基本变量
P可以是最基本的常量,字符串或者变量名。
if(<constant>)
: P是有意义的常量
1, ON, YES, TRUE, Y
$\Rightarrow$ True- 非零的数,甚至浮点数 $\Rightarrow$ True
0, OFF, NO, FALSE, N, IGNORE, NOTFOUND, *-NOTFOUND
$\Rightarrow$ False- 空字符串 $\Rightarrow$ False
- 这里的布尔变量不区分大小写,例如True,true,TRUE等都是可以的
- 其它情形会被视作一个变量或一个字符串进行处理
if(<variable>)
: P是一个变量的名称(而非变量的值)
- 变量已定义,并且变量的值不是上述False常量的情形 $\Rightarrow$ True
- 变量已定义,但是变量的值是上述False常量的情形 $\Rightarrow$ False
- 变量未定义 $\Rightarrow$ False
- 上述规则对宏结构不使用,对环境变量也不使用(环境变量的名称总是得到False)
if(<string>)
: P是字符串
- 可以被解析为True常量的字符串 $\Rightarrow$ True
- 通常情形下,其它的字符串 $\Rightarrow$ False
常见的例如
set(A "") # 空字符串
...if(A)# 如果仍然是空字符串
else()# 如果被改动,被定义并解析为True
endif()
逻辑判断
P可以是一些简单的逻辑判断
# 取反运算
if(NOT <condition>)# 与运算
if(<cond1> AND <cond2>)# 或运算
if(<cond1> OR <cond2>)
可以支持基于与或非的复杂组合,例如
if((condition1) AND (condition2 OR (condition3)))
注意:
- 可以使用小括号改变计算优先级
- NOT的优先级比AND/OR更高
- 对于AND、OR计算顺序从左到右,并且不使用任何短路操作:
x AND y
在x不正确时仍然解析y
存在性判断
if(COMMAND command-name)
: 判断这个command-name是否属于命令、可调用的宏或者函数的名称,则返回Trueif(TARGET target-name)
: 判断这个target是否已经被add_executable(), add_library(), add_custom_target()
这类命令创建,即使target不在当前目录下if(DEFINED <name>|CACHE{<name>}|ENV{<name>})
: 判断这个变量是否已定义if(<variable|string> IN_LIST <variable>)
: 判断这个变量或字符串是否在列表中,见下文的列表操作
常见的比较
- 数字的比较
# 小于
if(<variable|string> LESS <variable|string>)
# 大于
if(<variable|string> GREATER <variable|string>)
# 等于
if(<variable|string> EQUAL <variable|string>)
# 小于或等于
if(<variable|string> LESS_EQUAL <variable|string>)
# 大于或等于
if(<variable|string> GREATER_EQUAL <variable|string>)
- 字符串的比较(字典序)
if(<variable|string> STRLESS <variable|string>)
if(<variable|string> STRGREATER <variable|string>)
if(<variable|string> STREQUAL <variable|string>)
if(<variable|string> STRLESS_EQUAL <variable|string>)
if(<variable|string> STRGREATER_EQUAL <variable|string>)
- 版本号比较(版本号格式为
major[.minor[.patch[.tweak]]]
,省略的部分被视为零)
if(<variable|string> VERSION_LESS <variable|string>)
if(<variable|string> VERSION_GREATER <variable|string>)
if(<variable|string> VERSION_EQUAL <variable|string>)
if(<variable|string> VERSION_LESS_EQUAL <variable|string>)
if(<variable|string> VERSION_GREATER_EQUAL <variable|string>)
路径与文件判断
比较简略,因为目录和文件需要考虑平台和格式等,细节比较多,用到再查文档吧
# 完整路径是否存在,这里~开头的还不行
if(EXISTS path-to-file-or-directory)# 两个完整路径下的文件比较时间戳
if(file1 IS_NEWER_THAN file2)# 完整路径是否是一个目录
if(IS_DIRECTORY path-to-directory)# 完整路径是不是绝对路径
if(IS_ABSOLUTE path)
# 对于windows,要求路径以盘符开始
# 对于linux,要求路径以~开始
# 空路径视作false
循环语句
在 CMake 中,循环语句用于在构建过程中重复执行一系列命令或操作。
目前,CMake 支持两种主要的循环语句:foreach 和 while。
foreach循环
基本用法
foreach循环最基本的用法就是遍历一个列表中的所有变量,此时需要使用${Var}
先解析列表的值,例如
set(A 1;2;3;4)foreach(X ${A})message("X=${X}")
endforeach()
# 1,2,3,4
由于我们已经把列表解析了,所以下面的用法也是一样的
foreach(x a b c)message("x=${x}")
endforeach()
# a b c
更本质地说,foreach基本用法中,cmake会把第一个位置的字符串定义为循环变量,把剩下的字符串数组视作迭代的列表。
等差数列遍历
foreach支持基于等差数列的遍历,可以使用foreach(... RANGE ...)
命令, 其中range n
包括从0到n的自然数序列,range a b c
可以控制起点终点和步长,语法为
foreach(<loop_var> RANGE <stop>)
foreach(<loop_var> RANGE <start> <stop> [<step>])
例如
foreach(X RANGE 10)message("X=${X}")
endforeach()
# 0,1,...,10foreach(X RANGE 0 10 2)message(STATUS "X=${X}")
endforeach()
# 0,2,4,8,10
遍历多个列表
foreach可以同时在多个列表中遍历,需要使用关键词foreach(... IN ZIP_LISTS ...)
,总的迭代次数以最长的列表为准,此时较短的列表对应取空值。
方式一,要求循环变量和列表的个数一致,此时每一个循环变量对应在一个列表中遍历,互不干扰
foreach(<loop_vars> IN ZIP_LISTS <lists>)
例如
foreach(en ba IN ZIP_LISTS English Bahasa)message("en=${en}, ba=${ba}")
endforeach()# en=one, ba=satu
# en=two, ba=dua
# en=three, ba=tiga
# en=four, ba=
方式二,通过单个变量loop_var
的多个分量loop_var_N
实现
foreach(<loop_var> IN ZIP_LISTS <lists>)
这里只用一个loop_var,来承接对后面多个列表的遍历,它通过分量loop_var_N变量记录对应列表的当前项,例如
list(APPEND English one two three four)
list(APPEND Bahasa satu dua tiga)foreach(num IN ZIP_LISTS English Bahasa)message("num_0=${num_0}, num_1=${num_1}")
endforeach()# num_0=one, num_1=satu
# num_0=two, num_1=dua
# num_0=three, num_1=tiga
# num_0=four, num_1=
while循环
和其它语言一样,cmake支持使用break()
命令提前终止循环,使用continue()
命令可用于立即开始下一次迭代。
cmake还支持while循环,但是感觉用处不大,因为cmake不需要很复杂的循环逻辑,foreach提供基本的遍历已经足够了。
【1】基本语法
while(<condition>)<commands>
endwhile()
【2】参数含义
<condition> 是一个 CMake 表达式,只有<condition>在为真(非空)时,循环体才会执行。
在循环体内,可以使用 break() 来提前跳出循环。
【3】示例
set(i 0)
while(i LESS <n>)# ...math(EXPR i "${i} + 1")
endwhile()
其他:break() 与 continue()
此外,与绝大多数编程语言一样,CMake也可以通过 break() 跳出循环,通过 continue() 结束本次循环并继续下次循环。
函数
函数的语法结构如下:定义名为<name>
的函数,该函数接收名为<arg1>...
的参数,<commands>
表示函数定义的功能,在调用函数之前不会被执行。
function(<name> [<arg1> ...])<commands>
endfunction()
可以使用<name>(...)
调用执行函数
- 函数体的内部存在一个独立的作用域,在内部定义和使用的变量默认都是"局部"的,不会影响到函数体外
- 调用函数时,对函数名称的大小写不敏感,
Foo()
,FOO()
,foo()
效果一样,建议使用全小写 - 函数的参数并没有形式和个数的要求,可以传入任意多的参数
- 函数会把传入的参数视作普通的字符串,把它作为指定名称的参数变量的值,而不是视作传入一个变量进行复制
- 函数执行时,会首先对函数体内部使用到的函数参数进行一轮替换,然后逐个执行命令
cmake语法设计非常混乱,最基本的对于函数的参数都有很多种调用的方法,这些方法可以混合使用,不会互相冲突。
参数方式(一)
第一种方式,使用cmake默认提供的函数参数代词(注意,它们不是通常的变量,因为在调用函数时会直接全部替换掉)
- ARGC: 实际传入的参数的个数,与函数定义时预期的参数个数无关
- ARGV: 实际传入的参数全体(以列表的形式)
- ARGV#: 实际传入的第
#
个参数,计数从0开始的每一个参数,例如ARGV0为第一个参数,ARGV1为第二个参数,依次类推(注意,对于计数超过ARGC
的ARGV#
,属于未定义的量) - ARGN: 预料之外的参数,定义宏(函数)时参数为2个,实际传了4个,则ARGN代表剩下的两个
例如,定义函数如下
function(test)message(STATUS "ARGC=${ARGC}")message(STATUS "ARGV=${ARGV}")message(STATUS "ARGV2=${ARGV2}")message(STATUS "ARGN=${ARGN}")
endfunction(test)
没有给出一个预期的参数,三组测试结果如下
test(1 2 3)
# -- ARGC=3
# -- ARGV=1;2;3
# -- ARGV2=3
# -- ARGN=1;2;3test()
# -- ARGC=0
# -- ARGV=
# -- ARGV2=
# -- ARGN=test(4 5)
# -- ARGC=2
# -- ARGV=4;5
# -- ARGV2=
# -- ARGN=4;5
参数方式(二)
第二种方式,给function提供默认的形参名,此时传入的参数会依次分配给对应的形参,多的部分还是只能通过ARGN获取。
function(test2 var)message("var=${var}")
endfunction()
测试结果如下
test2("abc") # var=abc
test2("abc" "d") # var=abc 第二个参数丢失了,只能使用ARGN获取set(a 10)
test2(a) # var=a
test2(${a}) # var=10
# 这个例子说明了函数只会把传入的参数视作纯粹的字符串常量,而不是视作变量的重命名
# 如果坚持使用test2(a),那么在test2内部,必须使用${${var}}双层调用,来获取a的值
注意,${var}
不同于"${var}"
(其中的var是一个列表),在函数调用时因为解析的逻辑导致不同的结果
function(PrintVar var)message("${var}")
endfunction()PrintVar(${specialStr}) # aaa
# 这里分开传入,视作多个参数PrintVar("${specialStr}") # aaa;bbb
# 这里作为一个整体传入,视作一个参数
即列表作为参数时
列表var作为参数传入函数时:
- 调用时写成
${var}
,会被展开识别为多个参数 - 调用时写成
"${var}"
,会被打包作为一个列表整体,分配给一个参数 - 调用时写成
var
,在函数内需要两层解析${${var}}
才能得到值列表
function(hello var)message("var=${var}|${${var}}")
endfunction()set(A x y z)hello(A)
hello(${A})
hello("${A}")# var=A|x;y;z
# var=x|
# var=x|y;z,
参数方式(三)
推荐在复杂参数情形下,使用cmake_parse_arguments
命令来解析函数或宏的参数,因为这个命令非常强大。 (不推荐与其他参数方式混用,不要在function命令中指定参数名称,而是放到函数体内部,使用cmake_parse_arguments
命令实现)
语法形式为
cmake_parse_arguments(<prefix> <options> <one_value_keywords><multi_value_keywords> <args>...)
我们通过例子来解释这个命令
function(test)# 选项(布尔参数)的关键词列表set(argop "OA;OB;OC")# 单值参数的关键词列表set(arg "SA;SB;SC")# 列表参数的关键词列表set(arglist "LA;LB;LC")cmake_parse_arguments("ARG" "${argop}" "${arg}" "${arglist}" ${ARGN})message("-----------")message("${ARGV}")message("-----------")message("ARG_OA=${ARG_OA}")message("ARG_OB=${ARG_OB}")message("ARG_OC=${ARG_OC}")message("ARG_SA=${ARG_SA}")message("ARG_SB=${ARG_SB}")message("ARG_SC=${ARG_SC}")message("ARG_LA=${ARG_LA}")message("ARG_LB=${ARG_LB}")message("ARG_LC=${ARG_LC}")message("-----------")
endfunction()
其中的"ARG"是我们指定的前缀,避免名称冲突,此时cmake会自动创建若干名称为ARG_XXX
的变量,来承接传入的参数。
- 调用函数时,如果传入了OA或者其它的argop中的内容,则定义
ARG_OA
变量为真
test(OA ...)# ARG_OA=True
- 调用函数时,如果传入了SA并且附带一个值value,则定义
ARG_SA
变量,它的值即为value
test(... SA "abc")# ARG_SA="abc"
- 调用函数时,如果传入了LA并且附带一个列表values,则定义
ARG_LA
变量,它的值即为values
test(... LA "a;b;c")# ARG_SA=a;b;c
- 习惯在命令的最后使用
${ARGN}
,承接一些额外的参数
测试结果如下
# 第一个,不使用任何参数
# test()
----------------------
ARG_OA=FALSE
ARG_OB=FALSE
ARG_OC=FALSE
ARG_SA=
ARG_SB=
ARG_SC=
ARG_LA=
ARG_LB=
ARG_LC=
-----------# 第二个,使用选项OA,传入LA对应的值列表
# test(OA LA a;b;c)
-----------
OA;LA;a;b;c
-----------
ARG_OA=TRUE
ARG_OB=FALSE
ARG_OC=FALSE
ARG_SA=
ARG_SB=
ARG_SC=
ARG_LA=a;b;c
ARG_LB=
ARG_LC=
-----------# 第三个,使用选项OB,传入LA的列表,再传入SA的值
# test(OB LA "a;b;c" SA "e")
-----------
OB;LA;a;b;c;SA;e
-----------
ARG_OA=FALSE
ARG_OB=TRUE
ARG_OC=FALSE
ARG_SA=e
ARG_SB=
ARG_SC=
ARG_LA=a;b;c
ARG_LB=
ARG_LC=
-----------
以上是正确使用三类参数的情形,可以发现我们并不需要按照选项、单值参数和列表参数的顺序,只需要在正确的关键字后面跟上值或者列表即可。
换言之,cmake_parse_arguments
命令使得我们可以用无序的参数键值对的形式,向调用的函数传入指定名称指定值的参数。
cmake_parse_arguments(<prefix> <options> <one_value_keywords><multi_value_keywords> <args>...)
cmake_parse_arguments
会解析传入的所有参数,按照指定的关键词和逻辑去分析捕获参数,并产生带有指定前缀的一组新变量:
<prefix>
是我们希望给捕获产生的所有新变量添加的前缀- 如果遇到
<options>
中的关键词,则对应产生的新变量为true;否则默认为false - 如果遇到
<one_value_keywords>
中的关键词,则把随后的一个值赋给产生的新变量;否则默认为空(未定义) - 如果遇到
<multi_value_keywords>
中的关键词,则把随后的一个列表赋给产生的新变量,直到遇见下一个关键词;否则默认为空(未定义) - 最后的
<args>...
部分,习惯上都使用${ARGN}
- 如果给的参数无法被cmake按照上述逻辑捕获,仍然可以通过传统的
ARGN
方式获得:由于我们没有在function命令指定任何参数名,其实所有的参数都被扔进了ARGN
或ARGV
。
变量作用域
在函数内部直接使用set命令不会影响到函数体之外,在退出函数体之后变量即失效
function(foo)set(A 100)
endfunction()
# 在内部对A的定义无效,对于外层来说,无事发生
可以使用PARENT_SCOPE,表明退出这一层作用域后,变量仍然存在
function(foo)set(A 100 PARENT_SCOPE)
endfunction()
# 在内部对A的定义仍然有效
例如
message("(1)A=${A}")
foo1()
message("(2)A=${A}")function(foo2)set(A 200 PARENT_SCOPE)
endfunction()message("(3)A=${A}")
foo2()
message("(4)A=${A}")# (1)A=10
# (2)A=10
# (3)A=10
# (4)A=200
这里体现的不仅仅是函数变量,而是所有的普通变量都具有的变量作用域机制:(与之相对的,缓存变量是全局的)
- 通过add_subdirectory()进入新的CMakeLists.txt具有独立的变量作用域。
- 通过include导入的.cmake文件没有独立的变量作用域。
- 自定义函数具有独立的变量作用域。
- 自定义的宏没有独立的变量作用域。
普通变量对子作用域可见,但是子作用域中的变量不会影响到父作用域,相当于定义了同名变量并覆盖,除非使用PARENT_SCOPE选项。
函数返回值
cmake的函数可以在函数体的任何地方直接返回,使用return()
即可,但是cmake的函数语法有一个致命的问题,居然不支持返回值!在CMake中为了得到函数的返回值,我们必须把一个变量传给函数,然后通过提升作用域的方式实现,例如
function(test_return rst arg)math(EXPR argnew "${arg}+1")set(${rst} ${argnew} PARENT_SCOPE)
endfunction()set(b 10)
test_return(b 1)
message("b=${b}") # b=2
还可以这么干,直接使用一个变量负责传入传出,需要两层解析来获取传入的值,再使用PARENT_SCOPE
把变量传回去
function(test_return2 rst)set(tmp "${${rst}}")math(EXPR tmp "${tmp}+1")set(${rst} ${tmp} PARENT_SCOPE)
endfunction()set(b 10)
test_return2(b)
message("b=${b}") # b=11
函数执行状态
在函数体内,可以使用如下的特殊变量,获取当前的函数执行状态
CMAKE_CURRENT_FUNCTION
当前执行的函数名CMAKE_CURRENT_FUNCTION_LIST_DIR
当前执行的函数所在CMakeLists.txt文件的目录CMAKE_CURRENT_FUNCTION_LIST_FILE
当前执行的函数所在CMakeLists.txt文件名(完整带路径)CMAKE_CURRENT_FUNCTION_LIST_LINE
当前执行的函数所在的行数(function语句的行号)
宏
宏的标准语法如下,定义名为<name>
的宏,接收名为<arg1>...
的参数,<commands>
表示宏定义的功能,在调用宏之前不会被执行。
macro(<name> [<arg1> ...])<commands>
endmacro()
宏和函数很相似,宏的名称是大小写不敏感的,建议完全小写。宏也支持使用ARGV
之类的默认参数名称,或者使用指定参数名称的方式,再或者cmake_parse_arguments
的方式。
我们重点关注宏和函数的不同点:
- 宏并没有像函数一样单独开辟一个作用域,而是简单地执行了两步操作
- 完成对参数的字符串替换
- 把命令部分拷贝过来执行
- 在宏的内部禁止使用
return()
命令,因为这个命令的效果不是退出宏,而是退出上一层 - 在宏的内部没有单独作用域,而且
ARGV0
不是通常的变量,只是类似于单纯的字符串替换,因此不能使用如下的语法
if(ARGV0) # 在函数中可以,但是在宏内部错误,if不会解析ARGV0
if(${ARGV0}) # 正确,注意在进入宏之后,这里就完成了对ARGV0的字符串展开
模块
模块就是以xxx.cmake结尾的文件,通常将一些通用的函数或宏封装到到一个指定的文件中,然后通过include(xxx)
方式引用,可以达到代码复用的目的(但是并不会创建独立的变量作用域)。 模块既可以被CMakeLists.txt引用,也可以被其它模块引用。
模块的查找可以是从当前位置出发的相对路径,此时需要带文件后缀,例如include(cmake/xxx.cmake)
;也可以从CMAKE_MODULE_PATH
查找,此时只需要include(xxx)
,在查找前可以修改这个变量,让CMake顺利找到模块。
string字符串处理
原文链接:https://blog.csdn.net/jidushanbojueA/article/details/137595128
一、字符串处理指令string
在CMake中,string 指令提供了一系列的操作来处理字符串。这些操作包括但不限于比较、替换、连接、长度计算等等。
基本语法
string 指令的基本语法如下:
string(<command> ...)
参数含义:
<command> : 是您希望执行的操作类型,比如 LENGTH,APPEND,REPLACE 等。LENGTH:计算字符串的长度。APPEND:将一个字符串附加到另一个字符串后面。REPLACE:替换字符串中的一部分内容。SUBSTRING:提取字符串的子字符串。COMPARE:比较两个字符串大小,是否相等。TOUPPER/TOLOWER:将字符串转换为大写/小写。REGEX REPLACE:字符串查找和替换,使用正则表达式匹配和替换字符串的某些部分。CONCAT:字符串连接。FIND:用于在字符串中查找一个子字符串位置。REGEX MATCH:用于在字符串中查找正则表达式匹配的内容。…: 后面的形参根据不同的指令各有差异,下面我们会根据command类型一一示例进行介绍。
二、字符串运算符
下面我们根据command类型一一示例进行介绍:
字符串比较
字符串比较有两种方法。
一种方法是通过条件语句使用 STREQUAL,STRLESS 和 STRGREATER 来比较字符串。
STREQUAL:字符串相等比较。如果相等,则表达式结果为 TRUE,否则为 FALSE。
STRLESS:用于判断一个字符串是否在字典排序上小于另一个字符串。如果是,则表达式结果为 TRUE,否则为 FALSE。
STRGREATER:用于判断一个字符串是否在字典排序上大于另一个字符串。如果是,则表达式结果为 TRUE,否则为 FALSE。
set(var1 "Hello")
set(var2 "World")if("${var1}" STRLESS "${var2}")message(STATUS "var1 is less than var2.")
else()message(STATUS "var1 is not less than var2.")
endif()if("${var1}" STRGREATER "${var2}")message(STATUS "var1 is greater than var2.")
else()message(STATUS "var1 is not greater than var2")
endif()if("${var1}" STREQUAL "${var2}")message(STATUS "var1 is equal to var2.")
else()message(STATUS "var1 is not equal to var2.")
endif()
另一种比较字符串的方法是:string(COMPARE <op> <string1> <string2> <output variable>)。
这里的<op>是比较操作符,
<op>是:EQUAL、LESS、GREATER,
比较结果的返回值同上面:STREQUAL、STRLESS、STRGREATER。
<string1>和<string2>是:要比较的字符串,
<output variable>是:用来存储比较结果的变量。
比如:
set(var1 "Hello")
set(var2 "World")string(COMPARE EQUAL "${var1}" "${var2}" result)
if(result)message(STATUS "The strings are equal")
else()message(STATUS "The strings are not equal")
endif()string(COMPARE LESS "${var1}" "${var2}" result)
if(result)message(STATUS "var1 is less than var2")
else()message(STATUS "var1 is not less than var2")
endif()string(COMPARE GREATER "${var1}" "${var2}" result)
if(result)message(STATUS "var1 is greater than var2")
else()message(STATUS "var1 is not greater than var2")
字符串连接
字符串连接也有两种方法。
一种方法是通过 set 命令直接将多个字符串变量或值连接起来。
set(STR1 "Hello, ")
set(STR2 "world!")
set(FULL_STR "${STR1}${STR2}")
message(STATUS "完整字符串:${FULL_STR}")
另一个连接字符串的方法是使用string(CONCAT <output_variable> <input> …)命令。
将所有 <input> 字符串拼接起来,结果存储在 <output_variable> 中
set(STR1 "Hello, ")
set(STR2 "World!")
string(CONCAT CONNECTED_STR ${STR1} ${STR2})
message(STATUS "Connected string: ${CONNECTED_STR}")
字符串替换
使用string(REPLACE <match_string> <replace_string> <output_variable> <input>) 替换字符串中的某部分。
在<input>字符串中搜索 <match_string> 并将其替换为<replace_string>,结果存储在 <output_variable> 中。
set(MY_STRING "Hello CMake world")
string(REPLACE "CMake" "awesome" MY_STRING_MODIFIED ${MY_STRING})
message(STATUS "Modified String: ${MY_STRING_MODIFIED}")
字符串长度
获取字符串长度使用 string(LENGTH <string> <output_variable>)。
用来计算字符串<string> 的长度,并将结果存储在<output_variable> 中。
set(MY_STR "Hello, world!")
string(LENGTH "${MY_STR}" MY_STR_LENGTH)
message(STATUS "字符串长度: ${MY_STR_LENGTH}")
字符串截取
提取子字符串使用 string(SUBSTRING <string> <begin> <length> <output_variable>)。
提取 <string> 的子字符串,从 <begin> 开始,长度为 <length>,结果存储在 <output_variable> 中。
set(MY_STR "Hello, world!")
string(SUBSTRING "${MY_STR}" 0 5 SUB_STR)
message(STATUS "子字符串: ${SUB_STR}") # 输出应为 "Hello"
字符串转换大小写
把字符串转换为全部大写string(TOUPPER <string> <output_variable>),将 <string> 转换为大写,结果存储在<output_variable>中。
把字符串转换为全部小写string(TOLOWER <string> <output_variable>),将 <string> 转换为小写,结果存储在<output_variable>中。
set(MY_STR "Hello, World!")
string(TOUPPER "${MY_STR}" MY_STR_UPPER)
string(TOLOWER "${MY_STR}" MY_STR_LOWER)
message(STATUS "大写: ${MY_STR_UPPER}") # 输出全部大写
message(STATUS "小写: ${MY_STR_LOWER}") # 输出全部小写
字符串正则表达式匹配和替换
使用正则表达式匹配和替换字符串的某些部分string(REGEX REPLACE <regex> <replace_expression> <output_variable> <input>)。
对 <input> 使用正则表达式 <regex> 进行查找,并用<replace_expression> 进行替换,结果存储在 <output_variable> 中。
set(MY_STR "The quick brown fox")
string(REGEX REPLACE "quick brown" "slow red" MY_NEW_STR ${MY_STR})
message(STATUS "替换后的字符串: ${MY_NEW_STR}") # 输出 "The slow red fox"
字符串查找子字符串
在字符串中查找一个子字符串string(FIND <string> <substring> <output_variable> [REVERSE])。
在 <string> 中查找 <substring> 的位置,位置基础是从0开始,结果是<substring> 开始的索引,存储在 <output_variable> 中,如果没找到,输出 -1。可选的[REVERSE]参数表示从字符串的末尾开始向前搜索。使用REVERSE选项对于查找最后一次出现的位置特别有用,尤其是在处理包含多个相同子字符串的长字符串时。这提供了一种灵活的方式来从不同的方向分析和处理字符串数据。
set(MY_STRING "Hello World")
string(FIND "${MY_STRING}" "World" MY_INDEX)
message(STATUS "Index: ${MY_INDEX}") # 输出为Index: 6set(MY_STRING "Hello World, Hello Universe")
string(FIND "${MY_STRING}" "Hello" MY_INDEX REVERSE)
message(STATUS "Last index: ${MY_INDEX}") # 输出应该是13
字符串中查找正则表达式匹配的内容
在字符串中查找正则表达式匹配的内容string(REGEX MATCH <regular_expression> <output variable> <input_string> )。
使用正则表达式 <regular_expression> 匹配 <input_string> 字符串,结果存储在<output_variable> 中。如果有匹配,该变量<output_variable>将包含匹配的文本;否则,它将是空的。
或者是用于匹配所有匹配项的变种:string(REGEX MATCHALL <regular_expression> <output_variable> <input_string>)
set(INPUT_STRING "The quick brown fox jumps over the lazy dog")
string(REGEX MATCH "quick.*fox" MATCHED_STRING ${INPUT_STRING})
message(STATUS "Matched string: '${MATCHED_STRING}'")
这段代码会把 MATCHED_STRING 设置为 quick brown fox,因为这是 <input_string> 中第一个匹配 <regular_expression> 的部分。REGEX MATCHALL 的行为类似,但它会找到所有匹配的实例而不仅仅是第一个。这很有用,特别是当你需要从输入字符串中提取一系列的匹配数据时。
# 设置包含数字的文本字符串
set(input_text "The 2 quick brown foxes jump over 13 lazy dogs 8 times.")# 使用正则表达式匹配所有数字
string(REGEX MATCHALL "[0-9]+" matched_numbers ${input_text})# 打印匹配到的所有数字
message("Matched numbers are: ${matched_numbers}")
字符串附加
字符串附加另一个字符串string(APPEND <variable> <string> [<string>...])。
将一个或多个 <string> 附加到变量 <variable> 的值上
# 初始为空的编译标志
set(COMPILER_FLAGS "")# 根据需要附加标志
string(APPEND COMPILER_FLAGS "-Wall ")
string(APPEND COMPILER_FLAGS "-Werror ")
string(APPEND COMPILER_FLAGS "-O3")# 打印最终的编译标志
message(STATUS "Compiler flags: ${COMPILER_FLAGS}")
注意事项:
1、在指定多个值进行追加时,string(APPEND …)不会在这些值之间自动添加任何分隔符。如果需要分隔符(如空格或其他字符),你必须自己包括它们在值中。
2、和大部分CMake命令一样,string(APPEND …)修改变量的作用是在原地执行的,意味着它直接改变了传入的变量而不是返回一个新的修改后的副本。
字符串连接CONCAT和字符串附加APPEND的区别
CMake中的string(APPEND ...)和string(CONCAT ...)命令都是用于处理字符串的,但它们在功能上有一些区别:
1、string(APPEND …)
string(APPEND …)命令用于向一个已经存在的变量追加内容。使用这个命令,我们可以向一个字符串变量的末尾添加更多的文本。这个操作是就地完成的,即对原有变量的内容直接进行扩展。
语法:
string(APPEND <variable> <value> [<value>...])
例子:
set(MY_STRING "Hello")
string(APPEND MY_STRING ", World!")
# 在这里,MY_STRING的值变成了"Hello, World!"
2、string(CONCAT …)
相比之下,string(CONCAT …)命令用于将多个字符串值合并为一个新的字符串。它不修改任何原有的字符串变量,而是创建一个新的字符串结果。
语法:
string(CONCAT <variable> <value> [<value>...])
例子:
set(STR1 "Hello")
set(STR2 ", ")
set(STR3 "World!")
string(CONCAT MY_NEW_STRING ${STR1} ${STR2} ${STR3})
# 在这里,创建了一个新的变量MY_NEW_STRING,其值为"Hello, World!"
总结:
1、string(APPEND …)是用来修改已存在变量的内容,通过追加方式扩展该变量的字符串值。
2、string(CONCAT …)是用于合并多个字符串值创建一个新的字符串变量,原有变量的值不会改变。
3、在选择使用哪一个的时候,可以根据你的具体需求来决定:如果你想要修改已有变量的值,可以使用APPEND;如果你想保留原有变量不变,而创建一个新的字符串,那么CONCAT会是更好的选择。
以上为一些基础的示例,实际应用中可能需要根据具体需求组合使用这些命令。CMake 的字符串操作十分灵活,掌握好这些基础操作,可以帮助你更好地管理和处理项目中的字符串数据。
原文链接:https://blog.csdn.net/jidushanbojueA/article/details/137595128
math数学运算
有时候我们需要对cmake变量之间进行数学运算,这时候cmake提供了math()这个命令,命令格式如下:
math(EXPR outVar mathExpr [OUTPUT_FORMAT format])
这个命令也是很简单:直接通过cmake变量结合数学运算符组成mathExpr,然后将结果保存到outVar中。
【参数解释】
EXPR:标识,
outVar:存储计算结果变量,
mathExpr:要进行运算的表达式
OUTPUT_FORMAT 是可选参数,表示 输出结果的格式。
可以是:
HEXADECIMAL:输出16进制结果.
DECIMAL:输出10进制结果
【示例】
message("-------------------------------")
set(x 3)
set(y 7)# 将表达式计算结果存入zDec
math(EXPR zDec "(${x} + ${y}) * 2")
# 输出结果zDec日志
message("zDec = ${zDec}")# 将表达式计算结果,以“十进制”方式存入zDec
math(EXPR zDec "(${x} + ${y}) * 2" OUTPUT_FORMAT DECIMAL)
message("DECIMAL zDec = ${zDec}")# 将表达式计算结果,以“十六进制”方式存入zDec
math(EXPR zDec "(${x} + ${y}) * 2" OUTPUT_FORMAT HEXADECIMAL)
message("HEXADECIMAL zDec = ${zDec}")
编辑