参考教程:https://www.bilibili.com/video/BV14s4y1g7Zj/?spm_id_from=333.1387.favlist.content.click&vd_source=8f8a7bd7765d52551c498d7eaed8acd5
文章内容主要参考:https://subingwen.cn/cmake/CMake-primer/#2-4-%E5%8C%85%E5%90%AB%E5%A4%B4%E6%96%87%E4%BB%B6
二、CMake进阶
1、通过CMake制作库文件
(1)有时候编写源代码并不是为了编译生成可执行程序,而是生成一些静态库或动态库,提供给第三方使用。
(2)制作静态库:
①CMake提供了制作静态库的命令:
add_library(<库名称> STATIC <源文件1> [<源文件2> ...])
# STATIC表示生成的是静态库文件
# 实际生成的静态库文件,名为lib<库名称>,命令中“lib”是省略的
# 源文件可有多个,但至少需要有1个
②举例:
项目的目录结构如下(Linux环境)

那么相应地,CMakeLists.txt文件的内容如下,最终生成静态库文件libcalc.a
cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
add_library(calc STATIC ${SRC_LIST})
(3)制作动态库:
①CMake提供了制作动态库的命令:
add_library(<库名称> SHARED <源文件1> [<源文件2> ...])
# SHARED表示生成的是动态库文件
# 实际生成的动态库文件,名为lib<库名称>,命令中“lib”是省略的
# 源文件可有多个,但至少需要有1个
②举例:
项目的目录结构如下(Linux环境)

那么相应地,CMakeLists.txt文件的内容如下,最终生成动态库文件libcalc.a
cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
add_library(calc SHARED ${SRC_LIST})
(4)指定库文件的输出路径:
①在Linux环境下生成的动态库,默认有执行权限,所以可以按照可执行程序的方式去指定它的生成目录(通过set命令设置宏EXECUTABLE_OUTPUT_PATH),如果路径不存在则自动创建。
cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
# 设置动态库生成路径
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
add_library(calc SHARED ${SRC_LIST})
②不管是动态库还是静态库,只要是生成库文件,那么都可以通过set命令设置宏LIBRARY_OUTPUT_PATH,以设置库文件的生成路径,如果路径不存在则自动创建。
cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
# 设置动态库/静态库生成路径
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
# 生成动态库
#add_library(calc SHARED ${SRC_LIST})
# 生成静态库
add_library(calc STATIC ${SRC_LIST})
2、在程序中链接静态库
(1)在CMake中,链接静态库的命令如下:
link_libraries(<static lib> [<static lib>...])
# <static lib>:要链接的静态库名,可以是全名,也可以掐头去尾(去掉“lib”和后缀)
# 一条命令可链接若干个静态库,但至少链接一个
(2)如果该静态库不是系统提供的(自己制作或者使用第三方提供的静态库),可能出现静态库找不到的情况,此时可以使用命令link_directories,将静态库的路径也指定出来。
link_directories(<lib path>)
(3)举例:
项目的目录结构如下(Linux环境)

那么相应地,CMakeLists.txt文件的内容如下
cmake_minimum_required(VERSION 3.0)
project(CALC)
# 搜索指定目录下源文件
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
# 包含头文件路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 包含静态库路径
link_directories(${PROJECT_SOURCE_DIR}/lib)
# 链接静态库
link_libraries(calc)
add_executable(app ${SRC_LIST})
3、在程序中链接动态库
(1)在程序编写过程中,除了在项目中引入静态库,大多时候也会使用一些标准的或者第三方提供的一些动态库。
(2)在CMake中,链接动态库的命令如下:
target_link_libraries(
<target>
<PRIVATE|PUBLIC|INTERFACE> <item>...
[<PRIVATE|PUBLIC|INTERFACE> <item>...]...)
# 指定目标<target>(可以是可执行文件或库文件)在编译时需要链接的库,必须是用add_executable命令或add_library命令创建的目标名
# <item>为要链接的库名
# 在CMake中指定链接动态库的时候,应将命令写在生成可执行文件之后
(3)动态库的访问权限:
①动态库的连接具有传递性,如果动态库A链接了动态库B、C,动态库D链接了动态库A,此时动态库D相当于也链接了动态库B、C,并可以使用动态库B、C中定义的方法。
target_link_libraries(A B C)
target_link_libraries(D A)
②三个访问权限:
[1]PUBLIC:在PUBLIC后面的库会被Link到前面的target中,并且里面的符号也会被导出,提供给第三方使用。(适用情景:库A用了库B,其它链接库A的目标也需要库B)

[2]PRIVATE:在PRIVATE后面的库仅被Link到前面的target中,并且终结掉,第三方无法感知target调了什么库。(适用情景:可执行文件用了一个内部库,头文件不对外暴露)

[3]INTERFACE:在INTERFACE后面引入的库不会连接到前面的target中,只会导出符号。(适用情景:库只是转发了其它库的头文件,自身不链接)

③如果各个动态库之间没有依赖关系,无需做任何设置,三者没有区别,一般无需指定,使用默认的PUBLIC即可。
(4)链接系统动态库(如libpthread.a)比较简单,如下所示:
cmake_minimum_required(VERSION 3.0)
project(TEST)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
# 添加并指定最终生成的可执行程序名
add_executable(app ${SRC_LIST})
# 指定可执行程序要链接的动态库名字
target_link_libraries(app pthread)
(5)如果是链接第三方库,需要通过命令link_directories指定出要链接的动态库的位置(与指定静态库位置使用的命令一致),如下所示:

cmake_minimum_required(VERSION 3.0)
project(TEST)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
# 指定源文件或者动态库对应的头文件路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 指定要链接的动态库的路径
link_directories(${PROJECT_SOURCE_DIR}/lib)
# 添加并生成一个可执行程序
add_executable(app ${SRC_LIST})
# 指定要链接的动态库
target_link_libraries(app pthread calc)
需要注意的是,命令link_directories只能解决编译链接阶段找不到库文件的问题,当程序运行时,如果库路径不在操作系统的检索范围内,操作系统还是会找不到库文件,对此有几种解决办法:
①将库安装到系统标准路径
②运行前设置“export LD_LIBRARY_PATH=项目绝对路径/lib”,扩充运行时的搜索路径(这种属于临时方案)
③在编译时用RPATH把库路径硬编码到可执行文件中
add_executable(app main.cpp)
# 给app单独设置RPATH
set_target_properties(app PROPERTIES
BUILD_RPATH "${PROJECT_SOURCE_DIR}/lib"
INSTALL_RPATH "${PROJECT_SOURCE_DIR}/lib"
)
(6)实际上,target_link_libraries和 link_libraries都可以用于链接动态库和静态库,但实际中前者更常用,后者只能设置全局链接库,这些库会链接到定义的所有目标上,影响所有的目标,缺乏针对具体目标的控制,容易导致意外的依赖关系,不适合复杂的项目结构。
4、CMake日志
(1)在CMake中可以用命令显示一条消息,该命令的名字为message,其参数分为消息类型和消息本体(双引号包含,内部可用$引用变量)两部分。
message([STATUS|WARNING|AUTHOR_WARNING|FATAL_ERROR|SEND_ERROR] "message to display" ...)
消息类型如果省略,则为重要消息
STATUS:非重要消息
WARNING:CMake警告,会继续执行
AUTHOR_WARNING:CMake警告(dev),会继续执行
SEND_ERROR:CMake错误,继续执行,但是会跳过生成的步骤
FATAL_ERROR:CMake错误,终止所有处理过程
(2)CMake的命令行工具会在stdout上显示STATUS消息,在stderr上显示其它所有消息;CMake的GUI会在它的log区域显示所有消息。
(3)CMake警告和错误消息的文本显示使用的是一种简单的标记语言,文本没有缩进,超过长度的行会回卷,段落之间以新行做为分隔符。
(4)举例:
# 输出一般日志信息
message(STATUS "source path: ${PROJECT_SOURCE_DIR}")
# 输出警告信息
message(WARNING "source path: ${PROJECT_SOURCE_DIR}")
# 输出错误信息
message(FATAL_ERROR "source path: ${PROJECT_SOURCE_DIR}")
5、变量(字符串)操作
(1)字符串追加:
使用set命令拼接:
set(<变量名1> ${<变量名1>} ${<变量名2>} ...)
# 将从第二个参数开始往后所有的字符串按顺序进行拼接,最后将结果存储到第一个参数中,如果第一个参数中原来有数据,则原数据被覆盖
使用list命令拼接:
list(APPEND <list> [<element> ...])
# list的功能比较丰富,在第一个参数的位置指定需要进行的操作,APPEND表示数据追加
# <list>是拼接字符串的存储变量,<element>为被拼接的字符串
在CMake中,可以使用set命令创建一个list(list本身也是变量),list的底层会用分号分割子串,这是为了list做分割字符串而设计的,用户并不可见,如果打印list的变量值,分号不会被打印出来
(2)字符串移除:
使用list命令移除:
list(REMOVE_ITEM <list> <value> [<value> ...])
# 在第一个参数的位置指定需要进行的操作,REMOVE_ITEM表示数据移除
# <list>是当前操作的列表,<value>为需要移除的字符串
# 移除带路径的字符串时,需要注意,如果希望移除某个文件,但字符串中该文件是带路径的,那么<value>必须要将该文件的路径也指定出来,也就是说,需要移除的字符串,必须一字不落地体现在<value>中
(3)list命令的其它功能:
获取list的长度:
list(LENGTH <list> <output variable>)
# <list>:当前操作的列表
# <output variable>:新创建的变量,用于存储列表的长度
读取列表中指定索引的的元素(可以指定多个索引):
list(GET <list> <element index> [<element index> ...] <output variable>)
# <list>:当前操作的列表
# <element index>:列表元素的索引
#[[ 从0开始编号,索引0的元素为列表中的第一个元素;索引也可以是负数,-1表示列表的最后一个元素,-2表示列表倒数第二个元素,以此类推
当索引(不管是正还是负)超过列表的长度,运行会报错]]
# <output variable>:新创建的变量,存储指定索引元素的返回结果,也是一个列表
将列表中的元素用连接符(字符串)连接起来,组成一个字符串:
list (JOIN <list> <glue> <output variable>)
# <list>:当前操作的列表
# <glue>:指定的连接符(字符串)
# <output variable>:新创建的变量,存储返回的字符串
查找列表是否存在指定的元素:
list(FIND <list> <value> <output variable>)
# <list>:当前操作的列表
# <value>:需要在列表中搜索的元素
# <output variable>:新创建的变量
# 如果列表<list>中存在<value>,那么返回<value>在列表中的索引,如果不存在则返回-1
在list中指定的位置插入若干元素:
list(INSERT <list> <element_index> <element> [<element> ...])
# <list>:当前操作的列表
# <element_index>:需要插入元素位置的索引
# <element>:插入的元素值
将元素插入到列表的0索引位置:
list (PREPEND <list> [<element> ...])
# <list>:当前操作的列表
# <element>:插入的元素值
将列表中最后元素移除:
list (POP_BACK <list> [<out-var>...])
# <list>:当前操作的列表
将列表中第一个元素移除:
list (POP_FRONT <list> [<out-var>...])
# <list>:当前操作的列表
将指定索引的元素从列表中移除:
list (REMOVE_AT <list> <index> [<index> ...])
# <list>:当前操作的列表
# <index>:需要移除元素位置的索引
移除列表中的重复元素:
list (REMOVE_DUPLICATES <list>)
# <list>:当前操作的列表
列表翻转:
list (REVERSE <list>)
# <list>:当前操作的列表
列表排序:
list (SORT <list> [COMPARE <compare>] [CASE <case>] [ORDER <order>])
# <list>:当前操作的列表
# [[ COMPARE:指定排序方法,有如下几种值可选
STRING:按照字母顺序进行排序,为默认的排序方法
FILE_BASENAME:如果是一系列路径名,会使用basename进行排序
NATURAL:使用自然数顺序排序
CASE:指明是否大小写敏感,有如下几种值可选
SENSITIVE:按照大小写敏感的方式进行排序,为默认值
INSENSITIVE:按照大小写不敏感方式进行排序
ORDER:指明排序的顺序,有如下几种值可选
ASCENDING:按照升序排列,为默认值
DESCENDING:按照降序排列
]]
6、宏定义
(1)在进行程序测试的时候,通常会在代码中添加一些宏定义,通过这些宏来控制这些代码是否生效,如下所示。
#include <stdio.h>
#define NUMBER 3
int main(){
int a = 10;
#ifdef DEBUG
printf("我是一个程序猿, 我不会爬树...\n");
#endif
for (int i = 0; i < NUMBER; ++i){
printf("hello, GCC!!!\n");
}
return 0;
}
(2)上面的代码中,对DEBUG宏进行了判断,这个宏是在代码中定义的,为了让测试更灵活,这个宏可以不在代码中定义,而是在测试的时候把它定义出来,其中一种方式就是在gcc/g++命令中指定,比如:
$ gcc test.c -DDEBUG -o app
# 在gcc/g++命令中通过参数-D指定出要定义的宏的名字,这样就相当于在代码中定义了一个宏,名字为DEBUG
(3)既然可以通过gcc/g++命令定义宏,那么在CMake中也同样可以实现,对应的命令是add_definitions。
add_definitions(-D<宏名称>)
(4)gcc/g++命令定义宏举例:
cmake_minimum_required(VERSION 3.0)
project(TEST)
# 自定义DEBUG宏
add_definitions(-DDEBUG)
add_executable(app ./test.c)
(5)CMake中一些常用的宏:
|
宏 |
功能 |
|
PROJECT_SOURCE_DIR |
使用cmake命令后紧跟的目录,一般是工程的根目录 |
|
PROJECT_BINARY_DIR |
执行cmake命令的目录 |
|
CMAKE_CURRENT_SOURCE_DIR |
当前处理的CMakeLists.txt所在的路径 |
|
CMAKE_CURRENT_BINARY_DIR |
target 编译目录 |
|
EXECUTABLE_OUTPUT_PATH |
重新定义目标二进制可执行文件的存放位置 |
|
LIBRARY_OUTPUT_PATH |
重新定义目标链接库文件的存放位置 |
|
PROJECT_NAME |
返回通过PROJECT指令定义的项目名称 |
|
CMAKE_BINARY_DIR |
项目实际构建路径,假设在build目录进行的构建,那么得到的就是这个目录的路径 |
7、嵌套CMake
(1)如果项目很大,或者项目中有很多的源码目录,在通过CMake管理项目的时候如果只使用一个CMakeLists.txt,那么这个文件相对会比较复杂,有一种化繁为简的方式就是给每个源码目录都添加一个CMakeLists.txt文件(头文件目录不需要),由根节点的CMakeLists.txt统一调用,这样每个文件都不会太复杂,而且更灵活,更容易维护。(和makefile嵌套调用的思想是一致的)
(2)节点关系:
①Linux的目录是树状结构,所以嵌套的CMake也是一个树状结构,最顶层的CMakeLists.txt是根节点,其次都是子节点。
②根节点CMakeLists.txt中的变量全局有效,父节点CMakeLists.txt中的变量可以在子节点中使用,子节点CMakeLists.txt中的变量只能在当前节点中使用。

③CMake中父子节点之间的关系使用add_subdirectory命令建立(命令由父节点执行)。
add_subdirectory(<source_dir> [<binary_dir>] [EXCLUDE_FROM_ALL])
# <source_dir>:指定子节点CMakeLists.txt的位置,其实就是指定子目录
# <binary_dir>:指定输出文件的路径,一般不需要指定,忽略即可
# EXCLUDE_FROM_ALL:在子路径下的目标默认不会被包含到父路径的ALL目标里,并且也会被排除在IDE工程文件之外;用户必须显式构建在子路径下的目标
(3)举例:
①本例工程的目录结构如下:

②对于calc和sort目录中的源文件来说,可以将它们先编译成库文件(可以是静态库,也可以是动态库),然后在提供给测试文件使用即可。
③根目录中的CMakeLists.txt文件内容如下:
cmake_minimum_required(VERSION 3.0)
project(test)
# 定义全局变量
# 静态库生成的路径
set(LIB_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib)
# 测试程序生成的路径
set(EXEC_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin)
# 头文件目录
set(HEAD_PATH ${CMAKE_CURRENT_SOURCE_DIR}/include)
# 静态库的名字
set(CALC_LIB calc)
set(SORT_LIB sort)
# 可执行程序的名字
set(APP_NAME_1 test1)
set(APP_NAME_2 test2)
# 添加子目录(建立父子节点关系)
add_subdirectory(calc)
add_subdirectory(sort)
add_subdirectory(test1)
add_subdirectory(test2)
④calc目录和sort目录中的CMakeLists.txt文件:
[1]calc目录中的CMakeLists.txt文件内容如下:
cmake_minimum_required(VERSION 3.0)
project(CALCLIB)
aux_source_directory(./ SRC) # 搜索当前目录(calc目录)下的所有源文件,存于SRC
include_directories(${HEAD_PATH}) # 包含头文件路径
set(LIBRARY_OUTPUT_PATH ${LIB_PATH}) # 设置库的生成的路径
add_library(${CALC_LIB} STATIC ${SRC}) # 生成静态库
[2]sort目录中的CMakeLists.txt文件内容如下:
cmake_minimum_required(VERSION 3.0)
project(SORTLIB)
aux_source_directory(./ SRC) # 搜索当前目录(sort目录)下的所有源文件,存于SRC
include_directories(${HEAD_PATH}) # 包含头文件路径
set(LIBRARY_OUTPUT_PATH ${LIB_PATH}) # 设置库的生成的路径
add_library(${SORT_LIB} SHARED ${SRC}) # 生成动态库
⑤test1目录和test2目录中的CMakeLists.txt文件:
[1]test1目录中的CMakeLists.txt文件内容如下:
cmake_minimum_required(VERSION 3.0)
project(CALCTEST)
aux_source_directory(./ SRC)
include_directories(${HEAD_PATH}) # 包含头文件路径
link_directories(${LIB_PATH})
link_libraries(${CALC_LIB}) # 指定可执行程序要链接的静态库
set(EXECUTABLE_OUTPUT_PATH ${EXEC_PATH}) # 指定可执行程序生成的路径
add_executable(${APP_NAME_1} ${SRC}) # 生成可执行程序
[2]test2目录中的CMakeLists.txt文件内容如下:
cmake_minimum_required(VERSION 3.0)
project(SORTTEST)
aux_source_directory(./ SRC)
include_directories(${HEAD_PATH}) # 包含头文件路径
set(EXECUTABLE_OUTPUT_PATH ${EXEC_PATH}) # 指定可执行程序生成的路径
link_directories(${LIB_PATH}) # 指定可执行程序要链接的动态库的路径
add_executable(${APP_NAME_2} ${SRC}) # 生成可执行程序
target_link_libraries(${APP_NAME_2} ${SORT_LIB}) # 指定可执行程序要链接的动态库的名字
⑥以上内容完成后,进入根结点目录的build目录中,执行“cmake ..”命令,然后在build目录下执行“make”命令,即可得到如下内容。

8、CMake中的条件判断
(1)条件判断的语法格式:
if(<condition1>)
<commands1> # 满足condition1则执行commands1
elseif(<condition>) # 可选块,可以有多个elseif
<commands2> # 不满足condition1、满足condition2则执行commands2
else() # 可选块
<commands3> # 不满足condition1、不满足condition2则执行commands3
endif()
(2)基本表达式:
if(<expression>)
# <expression>有三种情况——常量、变量、字符串
# 如果是1、ON、YES、TRUE、Y、非零值、非空字符串时,条件判断返回True
# 如果是0、OFF、NO、FALSE、N、IGNORE、NOTFOUND、空字符串时,条件判断返回False
(3)逻辑判断运算:
①非运算NOT:
if(NOT <condition>)
# 如果条件<condition>为True将返回False,如果条件<condition>为False将返回True
②且运算AND:
if(<cond1> AND <cond2>)
# 如果cond1和cond2同时为True,则返回True,否则返回False
③或运算OR:
if(<cond1> OR <cond2>)
# 如果cond1和cond2至少有一个为True,则返回True,否则返回False
(4)比较运算:
①基于数值的比较:
# 如果左侧数值小于右侧,返回True,否则返回False
if(<variable|string> LESS <variable|string>)
# 如果左侧数值大于右侧,返回True,否则返回False
if(<variable|string> GREATER <variable|string>)
# 如果左侧数值等于右侧,返回True,否则返回False
if(<variable|string> EQUAL <variable|string>)
# 如果左侧数值小于等于右侧,返回True,否则返回False
if(<variable|string> LESS_EQUAL <variable|string>)
# 如果左侧数值大于等于右侧,返回True,否则返回False
if(<variable|string> GREATER_EQUAL <variable|string>)
②基于字符串的比较(从首字母开始比较ASCII码大小):
# 如果左侧字符串小于右侧,返回True,否则返回False
if(<variable|string> STRLESS <variable|string>)
# 如果左侧字符串大于右侧,返回True,否则返回False
if(<variable|string> STRGREATER <variable|string>)
# 如果左侧字符串等于右侧,返回True,否则返回False
if(<variable|string> STREQUAL <variable|string>)
# 如果左侧字符串小于等于右侧,返回True,否则返回False
if(<variable|string> STRLESS_EQUAL <variable|string>)
# 如果左侧字符串大于等于右侧,返回True,否则返回False
if(<variable|string> STRGREATER_EQUAL <variable|string>)
(5)文件相关判断:
①判断文件或目录是否存在:
if(EXISTS <path-to-file-or-directory>)
# 如果文件或者目录存在则返回True,否则返回False
②判断是否是目录:
if(IS_DIRECTORY <path>)
# 此处的<path>必须是绝对路径
# 如果目录存在则返回True,目录不存在返回False
③判断是否是软链接(相当于Windows中的快捷方式):
if(IS_SYMLINK <file-name>)
# 此处的<file-name>必须是绝对路径
# 如果是软链接则返回True,否则返回False
④判断是否是绝对路径:
if(IS_ABSOLUTE <path>)
# 如果是Linux,该路径需要从根目录开始描述;如果是Windows,该路径需要从盘符开始描述
# 如果是绝对路径则返回True,否则返回False
⑤比较两个路径是否相等:
if(<variable|string> PATH_EQUAL <variable|string>)
# CMake 版本要求大于等于3.24
# 如果两个路径相等,则返回True,否则返回False
(6)判断某个元素是否在列表中:
if(<variable|string> IN_LIST <variable>)
# CMake 版本要求大于等于3.3
# 如果元素在列表中,则返回True,否则返回False
9、CMake中的循环
(1)使用foreach进行循环:
①语法格式:
foreach(<loop_var> <items>)
<commands>
endforeach()
# 对items中的数据进行遍历,然后通过loop_var将遍历到的当前的值取出,每取出一个数据,执行一次<commands>
②取值用法1:
命令格式:foreach(<loop_var> RANGE <stop>)
RANGE:关键字,表示要遍历范围,步长为1
stop:这是一个正整数,表示范围的结束值,在遍历的时候从0开始,最大值为stop
loop_var:存储每次循环取出的值
举例:
cmake_minimum_required(VERSION 3.2)
project(test)
# 循环
foreach(item RANGE 10)
message(STATUS "当前遍历的值为: ${item}" )
endforeach()
③取值用法2:
命令格式:foreach(<loop_var> RANGE <start> <stop> [<step>])
RANGE:关键字,表示要遍历范围
start:这是一个正整数,表示范围的起始值,也就是说最小值为start
stop:这是一个正整数,表示范围的结束值,也就是说最大值为stop
step:控制每次遍历的时候以怎样的步长增长,默认为1,可以不设置
loop_var:存储每次循环取出的值
举例:
cmake_minimum_required(VERSION 3.2)
project(test)
foreach(item RANGE 10 30 2)
message(STATUS "当前遍历的值为: ${item}" )
endforeach()
④取值用法3:
命令格式:foreach(<loop_var> IN [LISTS [<list>...]] [ITEMS [<item>...]])
IN:关键字,表示在xxx里边
LISTS:关键字,后面接的是列表变量名,可以有多个
ITEMS:关键字,后面接的是变量或常量,可以有多个,变量需要通过$取值
loop_var:存储每次循环取出的值(以单个项为单位,遍历LISTS/ITEMS列出的列表及单个项)
举例:
cmake_minimum_required(VERSION 3.2)
project(test)
# 创建list
set(WORD a b c "d e f") # 如果某个字符串中有空格,可以通过双引号将其包裹起来
set(NAME ace sabo luffy)
# 遍历list
foreach(item IN LISTS WORD NAME)
message(STATUS "当前遍历的值为: ${item}" )
endforeach()
# 再遍历list
foreach(item IN ITEMS ${WORD} ${NAME})
message(STATUS "当前遍历的值为: ${item}" )
endforeach()
⑤取值用法4(要求CMake的版本大于等于3.17):
命令格式:foreach(<loop_var>... IN ZIP_LISTS <list>... )
IN:关键字,表示在xxx里边
ZIP_LISTS:关键字,对应的是列表list
loop_var:存储每次循环取出的值,可以根据要遍历的列表的数量指定多个变量,用于存储对应的列表当前取出的那个值
如果指定了多个变量名,它们的数量应该和列表的数量相等;如果只指定一个变量名,CMake自动生成一系列的loop_var_N变量来存储对应列表中的当前项,loop_var_0对应第一个列表,loop_var_1对应第二个列表,以此类推
如果遍历的多个列表中有一个列表较短,当它遍历完成之后,将不会再参与后续的遍历(因为其它列表还没有遍历完)

举例:
cmake_minimum_required(VERSION 3.17)
project(test)
# 通过list给列表添加数据
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()
message("=============================")
# 遍历列表
foreach(item IN ZIP_LISTS WORD NAME)
message(STATUS "当前遍历的值为: item1 = ${item_0}, item2=${item_1}" )
endforeach()
(2)使用while进行循环:
①语法格式:
while(<condition>)
<commands>
endwhile()
②举例:
cmake_minimum_required(VERSION 3.5)
project(test)
# 创建一个列表 NAME
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()

2761

被折叠的 条评论
为什么被折叠?



