当前位置: 首页 > news >正文

CMake之CMakeLists.txt语法规则

本文主要参考正点原子的应用开发手册,仅作为本人学习笔记使用。

目录

cmake 的使用方法其实还是非常简单的,重点在于编写 CMakeLists.txt,CMakeLists.txt 的语法规则也简单,并没有 Makefile的语法规则那么复杂难以理解!本文我们来学习 CMakeLists.txt 的语法规则。

注释

在 CMakeLists.txt 文件中,使用“#”号进行单行注释,譬如:

#
#这是注释信息
#cmake_minimum_required(VERSION 3.5)
project(HELLO)

大多数脚本语言都是使用“#”号进行注释。

命令(command)

通常在 CMakeLists.txt 文件中,使用最多的是命令,譬如上例中的 cmake_minimum_required、project 都是命令;命令的使用方式有点类似于 C 语言中的函数,因为命令后面需要提供一对括号,并且通常需要我们提供参数,多个参数使用空格分隔而不是逗号“,”,这是与函数不同的地方。命令的语法格式如下所示:

command(参数 1 参数 2 参数 3 ...)

不同的命令所需的参数不同,需要注意的是,参数可以分为必要参数和可选参数(通常称为选项),很多命令都提供了这两类参数,必要参数使用<参数>表示,而可选参数使用[参数]表示,譬如 set 命令:

set(<variable> <value>... [PARENT_SCOPE])

set 命令用于设置变量,第一个参数<variable>和第二个参数<value>是必要参数,在参数列表(…表示参数个数没有限制)的最后可以添加一个可选参数 PARENT_SCOPE(PARENT_SCOPE 选项),既然是可选的,那就不是必须的,根据实际使用情况确定是否需要添加。

在 CMakeLists.txt 中,命令名不区分大小写,可以使用大写字母或小写字母书写命令名,譬如:

project(HELLO) #小写
PROJECT(HELLO) #大写

这俩的效果是相同的,指定的是同一个命令,并没区别;这个主要看个人喜好,个人喜欢用小写字母,主要是为了和变量区分开来,因为 cmake 的内置变量其名称都是使用大写字母组成的。

部分常用命令

cmake 提 供 了 很 多 命 令 , 每 一 个 命 令 都 有 它 自 己 的 功 能 、 作 用 , 通 过 这 个 链 接 地 址 cmake-commands(7) — CMake 3.5.2 Documentation 可以查询到所有的命令及其相应的介绍、使用方法等等,如下所示:

大家可以把这个链接地址保存起来,可以把它当成字典的形式在有需要的时候进行查询,由于命令非常多,笔者不可能将所有命令都给大家介绍一遍,这里给大家介绍一些基本的命令,如下表所示:

接下来详细地给大家介绍每一个命令。

add_executable

add_executable 命令用于添加一个可执行程序目标,并设置目标所需的源文件,该命令定义如下所示:

add_executable(<name> [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] source1 [source2 ...])

该命令提供了一些可选参数,这些可选参数的含义笔者就不多说了,通常不需要加入,具体的含义大家可以自己查看 cmake 官方文档(add_executable — CMake 3.5.2 Documentation);只需传入目标名和对应的源文件即可,譬如:

#生成可执行文件 hello

add_executable(hello 1.c 2.c 3.c)

定义了一个可执行程序目标 hello,生成该目标文件所需的源文件为 1.c、2.c 和 3.c。需要注意的是,源文件路径既可以使用相对路径、也可以使用绝对路径,相对路径被解释为相对于当前源码路径(注意,这里源码指的是 CMakeLists.txt 文件,因为 CMakeLists.txt 被称为 cmake 的源码,若无特别说明,后续将沿用这个概念!)。

add_library

add_library 命令用于添加一个库文件目标,并设置目标所需的源文件,该命令定义如下所示:

add_library(<name> [STATIC | SHARED | MODULE] [EXCLUDE_FROM_ALL] source1 [source2 ...])

第一个参数 name 指定目标的名字,参数 source1…source2 对应源文件列表;add_library 命令默认生成的库文件是静态库文件,通过 SHARED 选项可使其生成动态库文件,具体的使用方法如下:

#生成静态库文件 libmylib.a
add_library(mylib STATIC 1.c 2.c 3.c)#生成动态库文件 libmylib.so
add_library(mylib SHARED 1.c 2.c 3.c)

与 add_executable 命令相同,add_library 命令中源文件既可以使用相对路径指定、也可以使用绝对路径指定,相对路径被解释为相对于当前源码路径。

不管是 add_executable、还是 add_library,它们所定义的目标名在整个工程中必须是唯一的,不可出现两个目标名相同的目标。

add_subdirectory

……

更多直接百度或者查看正点原子的手册吧,不一一摘录了。

变量(variable)

在 CMakeLists.txt 文件中可以使用变量,使用 set 命令可以对变量进行设置,譬如:

设置变量 MY_VAL

set(MY_VAL "Hello World!")

上例中,通过 set 命令对变量 MY_VAL 进行设置,将其内容设置为"Hello World!";那如何引用这个变量呢?这与 Makefile 是相同的,通过${MY_VAL}方式来引用变量,如下所示:

#设置变量 MY_VAL

set(MY_VAL "Hello World!")

#引用变量 MY_VAL

message(${MY_VAL})

变量可以分为 cmake 内置变量以及自定义变量,譬如上例中所定义的 MY_VAL 就是一个自定义变量;譬如在 32.3.5 小节中所使用的 LIBRARY_OUTPUT_PATH 和 EXECUTABLE_OUTPUT_PATH 变量则是cmake 的内置变量,每一个内置变量都有自己的含义,像这样的内置变量还有很多,稍后向大家介绍。

部分常用变量

变量也是 cmake 中的一个重头戏,cmake 提供了很多内置变量,每一个变量都有它自己的含义,通过这个链接地址 cmake-variables(7) — CMake 3.5.2 Documentation 可以查询到所有的内置变量及其相应的介绍,如下所示:

在这一份文档中,对变量进行分类,分为:提供信息的变量、改变行为的变量、描述系统的变量、控制编译的变量等等,笔者也按照这个分类给大家介绍一些基本、常用的变量。

提供信息的变量

顾名思义,这种变量可以提供某种信息,既然如此,那么我们通常只需要读取变量即可,而不需要对变量进行修改:

改变行为的变量

顾名思义,意味着这些变量可以改变某些行为,所以我们可以通过对这些变量进行设置以改变行为。

描述系统的变量

顾名思义,这些变量描述了系统相关的一些信息:

控制编译的变量

这些变量可以控制编译过程,具体如下所示:

更多直接参考正点原子手册或者自行百度。

按需要另外自行学习即可。

双引号的作用

CMake 中,双引号的作用我们可以从两个方面进行介绍,命令参数和引用变量。

命令参数

调用命令时,参数可以使用双引号,譬如:

project("HELLO")

也可以不使用双引号,譬如:

project(HELLO)

那它们有什么区别呢?在本例中是没有区别的,命令中多个参数之间使用空格进行分隔,而 cmake 会将双引号引起来的内容作为一个整体,当它当成一个参数,假如你的参数中有空格(空格是参数的一部分),那么就可以使用双引号,如下所示:

message(Hello World)
message("Hello World")

在这个例子中,第一个 message 命令传入了两个参数,而第二个 message 命令只传入一个参数;在第一个 message 命令中,打印信息时,会将两个独立的字符串 Hello 和 World 都打印出来,而且 World 会紧跟在Hello 之后,如下:

HelloWorld

而第二个 message 命令只有一个参数,所以打印信息如下:

Hello World

这就是双引号在参数中的一个作用。

引用变量

我们先来看个例子,如下所示:

CMakeLists.txt

set(MY_LIST Hello World China)
message(${MY_LIST})

这个例子的打印信息如下:

HelloWorldChina

在这个例子中,MY_LIST 是一个列表,该列表包含了 3 个元素,分别是 Hello、World、China。但这个message 命令打印时却将这三个元素全部打印出来,并且各个元素之间没有任何分隔。此时我们可以在引用变量(${MY_LIST})时加上双引号,如下所示:

CMakeLists.txt

set(MY_LIST Hello World China)
message("${MY_LIST}")

此时 message 打印信息如下:

Hello;World;China

因为此时${MY_LIST}是一个列表,我们用"${MY_LIST}"这种形式的时候,表示要让 CMake 把这个数组的所有元素当成一个整体,而不是分散的个体。于是,为了保持数组的含义,又提供一个整体的表达方式,CMake 就会用分号“;”把这数组的多个元素连接起来。而如果不加双引号时,CMake 不会数组当成一个整体看待,而是会将数组中的各个元素提取出进行打印输出。

条件判断

在 cmake 中可以使用条件判断,条件判断形式如下:

else 和 endif 括号中的<expression>可写可不写,如果写了,就必须和 if 中的<expression>一致。

expression 就是一个进行判断的表达式,表达式对照表如下:

上 表 中 只是 列 出其 中一 部 分 表达 式 ,还 有其 它 一 些表 达 式这 里并 未 列 出, 大 家可 以通 过if — CMake 3.5.2 Documentation 这个链接地址进行查看。

老规矩,更多自行百度或者参考正点原子手册。

循环语句

cmake 中除了 if 条件判断之外,还支持循环语句,包括 foreach()循环、while()循环。

一、foreach 循环

①、foreach 基本用法

foreach 循环的基本用法如下所示:

endforeach 括号中的<loop_var>可写可不写,如果写了,就必须和 foreach 中的<loop_var>一致。

参数 loop_var 是一个循环变量,循环过程中会将参数列表中的变量依次赋值给他,类似于 C 语言 for 循环中经常使用的变量 i。

foreach 循环测试

foreach(loop_var A B C D)message("${loop_var}")
endforeach()

打印信息为:

A
B
C
D

使用 foreach 可以编译一个列表中的所有元素,如下所示:

foreach 循环测试

set(my_list hello world china)foreach(loop_var ${my_list})message("${loop_var}")
endforeach()

打印信息如下:

②、foreach 循环之 RANGE 关键字

用法如下所示:

foreach(loop_var RANGE stop)
foreach(loop_var RANGE start stop [step])

对于第一种方式,循环会从 0 到指定的数字 stop,包含 stop,stop 不能为负数。

而对于第二种,循环从指定的数字 start 开始到 stop 结束,步长为 step,不过 step 参数是一个可选参数,如果不指定,默认 step=1;三个参数都不能为负数,而且 stop 不能比 start 小。

接下来我们进行测试,测试一:

foreach 循环测试

foreach(loop_var RANGE 4)message("${loop_var}")
endforeach()

打印信息如下:

测试二:

foreach 循环测试

foreach(loop_var RANGE 1 4 1)message("${loop_var}")
endforeach()

打印信息如下:

③、foreach 循环之 IN 关键字

用法如下:

foreach(loop_var IN [LISTS [list1 [...]]] [ITEMS [item1 [...]]])

循环列表中的每一个元素,或者直接指定元素。

接下来进行测试,测试一:

foreach 循环测试

set(my_list A B C D)foreach(loop_var IN LISTS my_list)message("${loop_var}")
endforeach()

打印信息如下:

测试二:

foreach 循环测试

foreach(loop_var IN ITEMS A B C D)message("${loop_var}")
endforeach()

打印信息同上。

二、while 循环

while 循环用法如下:

endwhile 括号中的 condition 可写可不写,如果写了,就必须和 while 中的 condition 一致。

cmake 中 while 循环的含义与 C 语言中 while 循环的含义相同,但条件 condition 为真时,执行循环体中的命令,而条件 condition 的语法形式与 if 条件判断中的语法形式相同。

while 循环测试

set(loop_var 4)while(loop_var GREATER 0)message("${loop_var}")math(EXPR loop_var "${loop_var} 1")
endwhile()

输出结果如下:

上例中,while 循环的条件是(loop_var GREATER 0),等价于(loop_var > 0),当 loop_var 变量的有效数值大于 0 时,执行 while 循环体;在 while 循环体中使用到了 cmake 中的数学运算命令 math(),关于数学运算下小节会向大家介绍。

在 while 循环体中,打印 loop_var,之后将 loop_var 减一。

三、break、continue

cmake 中,也可以在循环体中使用类似于 C 语言中的 break 和 continue 语句。

①、break

break()命令用于跳出循环,和在 C 语言中的作用是一样的,测试如下:

打印信息如下:

整个代码笔者就不再解释了,注释已经写得很清楚了!

②、continue

continue()命令用于结束本次循环,执行下一次循环,测试如下:

这段 cmake 代码是求 0 到 10 之间的偶数(左闭右开),并将偶数打印出来,使用到了 continue()命令,代码不再解释,注释已经写得很清楚了。

打印结果如下:

关于 break()和 continue()命令的使用就介绍到这里了。

数学运算 math

在 cmake 中如何使用数学运算呢?其实,cmake 提供了一个命令用于实现数学运算功能,这个命令就是 math(),如下所示:

math(EXPR <output variable> <math expression>)

math 命令中,第一个参数是一个固定的关键字 EXPR,第二个参数是一个返回参数,将数学运算结果存放在这个变量中;而第三个参数则是一个数学运算表达式,支持的运算符包括:+(加)、-(减)、*(乘)、/(除)、%(求余)、|(按位或)、&(按位与)、^(按位异或)、~(按位取反)、<<(左移)、>>(右移)以及这些运算符的组合运算,它们的含义与 C 语言中相同。

譬如:

math(EXPR out_var "1+1") #计算 1+1
math(EXPR out_var "100 * 2") ##计算 100x2
math(EXPR out_var "10 & 20") #计算 10 & 20

我们进行测试:

math()命令测试

math(EXPR out_var "100 + 100")
message("${out_var}")math(EXPR out_var "100 - 50")
message("${out_var}")math(EXPR out_var "100 * 100")
message("${out_var}")math(EXPR out_var "100 / 50")
message("${out_var}")math(EXPR out_var "(100 & 100) * 50 - 2")
message("${out_var}")

测试结果如下:

定义函数

在 cmake 中我们也可以定义函数,cmake 提供了 function()命令用于定义一个函数,使用方法如下所示:

endfunction 括号中的<name>可写可不写,如果写了,就必须和 function 括号中的<name>一致。

①、基本使用方法

第一个参数 name 表示函数的名字,arg1、arg2…表示传递给函数的参数。调用函数的方法其实就跟使用命令一样,一个简单地示例如下所示:

打印信息如下:

②、使用 return()命令

在 function()函数中也可以使用 C 语言中的 return 语句退出函数,如下所示:

执行结果如下:

只打印了 Hello,并没有打印 World,说明 return()命令是生效的,执行 return()命令之后就已经退出当前函数了,所以并不会打印 World。但是需要注意的是,return 并不可以用于返回参数,那函数中如何返回参数给调用者呢?关于这个问题,后续再给大家讲解,因为这里涉及到其它一些问题,本小节暂时先不去理会这个问题。

③、可变参函数

在 cmake 中,调用函数时实际传入的参数个数不需要等于函数定义的参数个数(甚至函数定义时,参数个数为 0),但是实际传入的参数个数必须大于或等于函数定义的参数个数,如下所示:

函数 xyz 定义时只有一个参数,但是实际调用时我们传入了 3 个参数,注意这并不会报错,是符合function()语法规则的,会正常执行,打印信息如下:

从打印信息可知,message()命令打印出了调用者传入的第一个参数,也就是 Hello。

这种设计有什么用途呢?正如我们的标题所言,这种设计可用于实现可变参函数(与 C 语言中的可变参数函数概念相同);但是有个问题,就如上例中所示,用户传入了 3 个参数,但是函数定义时并没有定义这些形参,函数中如何引用到第二个参数 World 以及第三个参数 China 呢?其实 cmake 早就为大家考虑到了,并给出了相应的解决方案,就是接下来向大家介绍的内部变量。

④、函数的内部变量

function()函数中可以使用内部变量,所谓函数的内部变量,指的就是在函数内部使用的内置变量,这些内部变量如下所示:

我们可以进行测试:

源码执行结果如下:

这个大家自己去对照一下就知道了。

更多自行百度吧。

宏定义

cmake 提供了定义宏的方法,cmake 中函数 function 和宏定义 macro 在某种程度上来说是一样的,都是创建一段有名字的代码可以在后面被调用,还可以传参数。通过 macro()命令定义宏,如下所示:

endmacro 括号中的<name>可写可不写,如果写了,就必须和 macro 括号中的<name>一致。参数 name表示宏定义的名字,在宏定义中也可以使用前面给大家介绍的 ARGVX(X 是一个数字)、ARGC、ARGV、ARGN 这些变量,所以这些也是宏定义的内部变量。

更多待补充。

文件操作

cmake 提供了 file()命令可对文件进行一系列操作,譬如读写文件、删除文件、文件重命名、拷贝文件、 创建目录等等,本小节我们一起来学习这个功能强大的 file()命令。

①、写文件:写、追加内容

使用 file()命令写文件,使用方式如下所示:

file(WRITE <filename> <content>...)
file(APPEND <filename> <content>...)

将<content>写入名为<filename>的文件中。如果文件不存在,它将被创建;如果文件已经存在,WRITE模式将覆盖它,APPEND 模式将内容追加到文件末尾。

测试代码如下:

注意文件可以使用绝对路径或相对路径指定,相对路径被解释为相对于当前源码路径。

执行 CMakeLists.txt 代码之后,会在当前源码目录下生成一个名为 wtest.txt 的文件。

更多待补充。

关于 cmake,笔者就给大家介绍这么多,已经基本够用了;其实还有很多的命令以及变量没有介绍到,但完全可以自己去看,然后去实战测试!

http://www.dtcms.com/a/266169.html

相关文章:

  • 网安系列【1】:黑客思维、技术与案例解析
  • DDD实战:CQRS模式在电商报表系统中的高性能实践
  • RNN案例人名分类器(完整步骤)
  • MySQL 8.0 OCP 1Z0-908 题目解析(17)
  • POST请求url放参数场景-笔记
  • Spring SseEmitter 系统详细讲解
  • WPF学习笔记(16)树控件TreeView与数据模板
  • WPF学习笔记(22)项面板模板ltemsPanelTemplate与三种模板总结
  • spring-ai-alibaba 1.0.0.2 学习(八)——接入阿里云信息查询服务
  • 深度学习-逻辑回归
  • RJ45 连接器(水晶头)的引脚定义
  • 从0到1解锁Element-Plus组件二次封装El-Dialog动态调用
  • Gemini CLI初体验
  • 二叉树题解——二叉树的层序遍历【LeetCode】队列实现
  • Java中Stream流的使用
  • Web攻防-文件上传黑白名单MIMEJS前端执行权限编码解析OSS存储分域名应用场景
  • 设计模式(九)
  • 魔术方法__call__
  • Redis缓存架构实战
  • Selenium Base全新升级版:新一代自动化框架实战解析
  • Python 的内置函数 range
  • 高边驱动 低边驱动
  • 黑暗中的爆破(船讯网Ais爬虫暨爬虫实战js逆向学习经验分享)
  • 车载以太网-IP 掩码 vlan 端口
  • SciPy 安装使用教程
  • [特殊字符] 电子机械制动(EMB)产业全景分析:从技术演进到千亿市场爆发
  • 高通QCS8550部署YOLO-NAS模型与性能测试
  • 使用excel中的MATCH函数进行匹配数据
  • Python数据库软件:查询与预测功能集成系统
  • Linux常见指令以及权限理解