【CMake 入门与进阶(3)】 CMakeLists.txt 语法规则基础及部分常用指令(附使用代码)

news2024/9/24 17:20:06

        在上两篇中,笔者通过几个简单地示例向大家演示了 cmake 的使用方法,由此可知,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 命令用于设置变量,第一个参数和第二个参数是必要参数,在参数列表(…表示参 数个数没有限制)的最后可以添加一个可选参数 PARENT_SCOPE(PARENT_SCOPE 选项),既然是可选的,那就不是必须的,根据实际使用情况确定是否需要添加。

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

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

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

  • 变量(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 就是一个自定义变量; 譬如在【CMake 入门与进阶(2)】CMake编译设置——多个源文件编译及生成库文件(附代码)_GPIOB_PIN7的博客-CSDN博客所使用的 LIBRARY_OUTPUT_PATH 和 EXECUTABLE_OUTPUT_PATH 变量则是 cmake 的内置变量,每一个内置变量都有自己的含义,像这样的内置变量还有很多,稍后向大家介绍。

部分常用命令

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

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

command说明
add_executable可执行程序目标
add_library库文件目标
add_subdirectory去指定目录中寻找新的 CMakeLists.txt 文件
aux_source_directory收集目录中的文件名并赋值给变量
cmake_minimum_required设置 cmake 的最低版本号要求
get_target_property获取目标的属性
include_directories设置所有目标头文件的搜索路径,相当于 gcc 的-L 选项
link_directories设置所有目标库文件的搜索路径,相当于 gcc 的-L 选项
link_libraries设置所有目标需要链接的库
list列表相关的操作
message用于打印、输出信息
project设置工程名字
set设置变量
set_target_properties设置目标属性
target_include_directories设置指定目标头文件的搜索路径
target_link_libraries设置指定目标库文件的搜索路径
target_sources设置指定目标所需的源文件

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

  • 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

        add_subdirectory 命令告诉 cmake 去指定的目录中寻找源码并执行它,有点像 Makefile 的 include,其定义如下所示:

add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])

        参数 source_dir 指定一个目录,告诉cmake 去该目录下寻找 CMakeLists.txt文件并执行它;参数binary_dir 指定了一个路径,该路径作为子源码(调用 add_subdirectory 命令的源码称为当前源码或父源码,被执行的源码称为子源码)的输出文件(cmake 命令所产生的中间文件)目录,binary_dir 参数是一个可选参数,如果没有显式指定,则会使用一个默认的输出文件目录;为了后续便于表述,我们将输出文件目录称为 BINARY_DIR。

        譬如工程目录结构如下所示:

├── build
├── CMakeLists.txt
└── src
  ├── CMakeLists.txt
  └── main.c

        顶层 CMakeLists.txt 文件内容如下所示:

# 顶层 CMakeLists.txt
cmake_minimum_required("VERSION" "3.5")
project("HELLO")

# 告诉 cmake 去 src 目录下寻找 CMakeLists.txt
add_subdirectory(src)

        src 目录下的 CMakeLists.txt 文件:

# src 下的 CMakeLists.txt
add_executable(hello main.c)

        进入到 build 目录下,执行 cmake、make 进行构建编译;顶层源码对应的输出文件会存放在 build 目录,也就是执行 cmake 命令所在目录;子源码(src 目录下的 CMakeLists.txt)对应的输出文件会存放在 build/src 目录,包括生成的可执行文件默认会与这些中间文件放置在同一个目录,如下所示:

├── build
│  ├── CMakeCache.txt
│  ├── CMakeFiles
│  ├── cmake_install.cmake
│  ├── Makefile
│  └── src
│    ├── CMakeFiles
│    ├── cmake_install.cmake
│    ├── hello
│    └── Makefile
├── CMakeLists.txt
└── src
  ├── CMakeLists.txt
  └── main.c

        所以由此可知,当前源码调用add_subdirectory命令执行子源码时,若没有为子源码指定BINARY_DIR, 默认情况下,会在当前源码的 BINARY_DIR 中创建与子目录(子源码所在目录)同名的文件夹,将其作为子源码的 BINARY_DIR。 接下来我们修改顶层 CMakeCache.txt 文件:

# 顶层 CMakeLists.txt
cmake_minimum_required("VERSION" "3.5")
project("HELLO")

# 告诉 cmake 去 src 目录下寻找 CMakeLists.txt
add_subdirectory(src output)

        指定子源码的 BINARY_DIR 为 output,这里使用的是相对路径方式,add_subdirectory 命令对于相对路径的解释为:相对于当前源码的 BINARY_DIR;修改完成之后,再次进入到 build 目录下执行 cmake、make 命令进行构建、编译,此时会在 build 目录下生成一个 output 目录,这就是子源码的 BINARY_DIR。

        设置 BINARY_DIR 可以使用相对路径、也可以是绝对路径,相对路径则是相对于当前源码的 BINARY_DIR,并不是当前源码路径,这个要理解。

        通过 add_subdirectory 命令加载、执行一个外部文件夹中的源码,既可以是当前源码路径的子目录、也可以是与当前源码路径平级的目录亦或者是当前源码路径上级目录等等;对于当前源码路径的子目录,不强制调用者显式指定子源码的 BINARY_DIR;如果不是当前源码路径的子目录,则需要调用者显式指定 BINARY_DIR,否则执行源码时会报错。接下来进行测试,譬如工程目录结构如下所示:

├── build
├── CMakeLists.txt
├── lib
│  └── CMakeLists.txt
└── src
  ├── CMakeLists.txt
  └── main.c

        这里一共有 3 个 CMakeLists.txt 文件,lib 目录和 src 目录是平级关系,顶层 CMakeLists.txt 内容如下:

# 顶层 CMakeLists.txt
cmake_minimum_required("VERSION" "3.5")
project("HELLO")

# 加载 src 目录下的源码
add_subdirectory(src)

        src 目录下的 CMakeLists.txt:

# src 目录 CMakeLists.txt
add_executable(hello main.c)

# 加载平级目录 lib 中的源码
add_subdirectory(../lib)

        此时调用 add_subdirectory 加载 lib 目录的源码时并为显式指定 BINARY_DIR,进入到 build 目录下,执行 cmake 命令,发生了报错,而且提示我们 add_subdirectory 命令必须要指定 BINARY_DIR,那我们将 src 目录下的 CMakeLists.txt 进行修改,显式指定 BINARY_DIR,如下所示:

# src 目录 CMakeLists.txt
add_executable(hello main.c)

# 加载平级目录 lib 中的源码
add_subdirectory(../lib output)

        接着再次执行 cmake(每次执行 cmake 前进行清理,将 build 目录下生成的所有文件全部删除)即可。


未完待续...

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/611031.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

操作系统复习2.3.4-进程同步问题

生产者-消费者 系统中有一组生产者进程和一组消费者进程 两者共享一个初始为空&#xff0c;大小为n的缓冲区 缓冲区没满&#xff0c;生产者才能放入 缓冲区没空&#xff0c;消费者才能取出 互斥地访问缓冲区 互斥要在同步之后&#xff0c;不然会导致想要同步&#xff0c;但由…

39从零开始学Java之面向对象的继承到底是怎么回事?

作者&#xff1a;孙玉昌&#xff0c;昵称【一一哥】&#xff0c;另外【壹壹哥】也是我哦 千锋教育高级教研员、CSDN博客专家、万粉博主、阿里云专家博主、掘金优质作者 前言 在上一篇文章中&#xff0c;壹哥给大家讲解了面向对象三大特征之一的封装&#xff0c;现在我们还有另…

JWT strings must contain exactly 2 period characters. Found: 0

登录接口异常报错&#xff1a; 这是登录接口报错&#xff0c;实际上他不走登录接口&#xff0c;直接走的拦截器&#xff0c;拦截器应配置好了登录接口的放行&#xff0c;登录接口写的也没有问题&#xff0c;拦截器解析也没有问题&#xff0c;因为之前都是好用的&#xff0c;本…

人车网租赁软件开发|人车网租赁系统|租赁系统源码功能

经过租赁小程序不只可以使物品得到充沛的运用&#xff0c;还能减少一些资源的浪费&#xff0c;租赁行业这两年因为互联网技术的完善&#xff0c;发展也在不断进步&#xff0c;租赁系统定制开发功能也在不断完善&#xff0c;那么企业想要开发租赁小程序的时分需求留意哪些方面呢…

深入了解Java虚拟机之高效并发

目录 Java内存模型与线程 概述 硬件的效率与一致性 Java内存模型 主内存与工作内存 内存间交互操作 对于volatile型变量的特殊规则 原子性、可见性与有序性 先行发生原则 Java与线程 线程实现 线程调度 状态切换 小结 线程安全与锁优化 概述 线程安全 Java中…

HDR显示技术

什么是HDR? HDR&#xff08;High-Dynamic Range&#xff0c;简称HDR&#xff09;是指高动态范围图像&#xff0c;是一种能够显示更大的亮度范围和对比度的图像技术。HDR可以让暗部的细节变亮&#xff0c;亮部的细节不失真&#xff0c;呈现出更自然、更真实的画面&#xff0c;…

记一次618军演压测TPS上不去排查及优化 | 京东云技术团队

本文内容主要介绍&#xff0c;618医药供应链质量组一次军演压测发现的问题及排查优化过程。旨在给大家借鉴参考。 背景 本次军演压测背景是&#xff0c;2B业务线及多个业务侧共同和B中台联合军演。 现象 当压测商品卡片接口的时候&#xff0c;cpu达到10%&#xff0c;TPS只有…

Tomcat基本原理

1.Tomcat核心&#xff1a; Http服务器Servlet容器 组件分工&#xff1a; 连接器Connector&#xff1a;处理 Socket 连接&#xff0c;负责网络字节流与 Request 和 Response 对象的转化。容器Container&#xff1a;加载和管理 Servlet&#xff0c;以及具体处理 Request 请求。 …

静态杂波滤波算法

静态杂波滤波算法 1.零速通道置零法2.动目标显示&#xff08;MTI&#xff09;3.相量均值相消算法&#xff08;平均相消算法&#xff09;4.总结 1.零速通道置零法 零速通道置零法&#xff0c;是指在2D-FFT&#xff08;速度维FFT&#xff09;后直接将R-V谱矩阵&#xff08;RD图&…

计算机网络学习笔记-传输层

目录​​​​​​​ 概述 与网络层的区别 端口号 概述 分类 重要功能&#xff1a;复用分用 两个重要协议&#xff1a;UDPTCP UDP用户数据报协议 概述 主要特点 首部格式 TCP传输控制协议 主要特点 首部格式 运输连接管理 概述 运输层提供应用进程间的逻辑通信通…

SpringBoot—yml配置多环境(踩坑总结!)

一、实例操作 ①、创建对应的application.yml &#xff08;dev 开发&#xff1b;prod 生产&#xff1b;test 测试&#xff09;文件 ②、在application.yml文件中&#xff0c;放公共的配置部分 &#xff08;这部分最好还是复制&#xff0c;自己敲位置&#xff0c;空格不对都会报…

深入理解一下Python中的面向对象编程

Part1 如何面向“对象” 网上关于Java和**C**的面向对象编程相关介绍的博客文章已经很多了&#xff0c;那我为什么还写呢&#xff1f;因为&#xff0c;人生苦短&#xff0c;刚好我是学Python的... 今天&#xff0c;我们就来走进面向对象编程的理想国——深入理解一下Python中…

2023年6月杭州/广州/深圳NPDP产品经理认证招生简章

产品经理国际资格认证NPDP是新产品开发方面的认证&#xff0c;集理论、方法与实践为一体的全方位的知识体系&#xff0c;为公司组织层级进行规划、决策、执行提供良好的方法体系支撑。 【认证机构】 产品开发与管理协会&#xff08;PDMA&#xff09;成立于1979年&#xff0c;是…

Go语言反射编程指南

反射[1]是一种编程语言的高级特性&#xff0c;它允许程序在运行时检视自身的结构和行为。通过反射&#xff0c;程序可以动态地获取类型(type)与值(value)等信息&#xff0c;并对它们进行操作&#xff0c;诸如修改字段、调用方法等&#xff0c;这使得程序具有更大的灵活性和可扩…

【论文阅读】用于大型城市场景的网格引导神经辐射场

【论文阅读】用于大型城市场景的网格引导神经辐射场 Abstract1. Introduction2. Related Works and Background大规模场景重建和渲染体积场景表示大尺度NeRF 3. Grid-guided Neural Radiance Fields3.1. Multi-resolution Feature Grid Pre-train3.2. Grid-guided Neural Radia…

AI炒股回报率500%?内行揭秘玄机

一篇来自佛罗里达大学的研究报告震惊了金融圈&#xff1a;用ChatGPT对公司新闻进行情绪分析&#xff0c;并按此在股市做多、卖空&#xff0c;最高可获得超过500%的投资回报率。虽然坊间对这份报告中惊人的回报率数据有所怀疑&#xff0c;但金融界正在因AI的介入发生改变。 摩根…

港联证券|龙头齐聚,本周7股将申购!今年第三高价新股也要来了?

本周&#xff08;6月5日—6月9日&#xff09;&#xff0c;共有7只新股将进行申购&#xff0c;其中创业板5只&#xff08;康力源、飞沃科技、恒勃股份、威士顿、海看股份&#xff09;、科创板2只&#xff08;西高院、智翔金泰&#xff09;。 资料显示&#xff0c;康力源是国内健…

Windows下安装与使用Kafka(使用Kafka内置的ZooKeeper图文结合版)

文章目录 Windows安装Kafka1.安装JDK并配置好对应的环境变量 2.安装配置Zookeeper1.下载安装包Apache Zookeeper2.解压并进入Zookeeper目录 防止端口8080启动后被占用&#xff0c;这里考虑先配置下3.安装Kafka3.1 下载安装包3.2、 解压并进入Kafka目录&#xff0c; Windows安装…

Vue.js 中的指令自定义是什么?如何自定义指令?

Vue.js 中的指令自定义是什么&#xff1f;如何自定义指令&#xff1f; Vue.js是一种流行的前端框架&#xff0c;它提供了一种称为“指令”的技术&#xff0c;用于操作DOM元素。Vue.js中内置了一些常用的指令&#xff0c;如v-if、v-show、v-for等。除了内置指令外&#xff0c;V…

基于Tensorflow+VGG+DBN本地化批量图像识别系统(深度学习+Python)含全部工程源码+视频演示+图片数据集

目录 前言总体设计系统整体结构图系统流程图 运行环境1. Python 环境2. Tensorflow 环境3. wxPython 环境4. PIL 环境 模块实现1. 数据预处理2. 模型简化处理3. 用户界面设计4. 翻译模块调用 系统测试1.模型训练效果2. 模型测试效果 代码实现1. 用户界面设计及模型调用2. 模型搭…