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

news2024/11/28 7:37:02

project

        project命令用于设置工程名称:

# 设置工程名称为 HELLO
project(HELLO)

        执行这个之后会引入两个变量:HELLO_SOURCE_DIR 和 HELLO_BINARY_DIR,注意这两个变量名的前缀就是工程名称,HELLO_SOURCE_DIR 变量指的是 HELLO 工程源码目录、HELLO_BINARY_DIR 变量指的是 HELLO 工程源码的输出文件目录;我们可以使用 message 命令打印变量,譬如 CMakeLists.txt 内容如下所示:

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

message(${HELLO_SOURCE_DIR})
message(${HELLO_BINARY_DIR})

        进入 build 目录下,执行 cmake:

         但如果不加入 project(HELLO)命令,这两个变量是不存在的;工程源码目录指的是顶层源码所在目录,cmake定义了两个等价的变量PROJECT_SOURCE_DIR 和 PROJECT_BINARY_DIR   通常在 CMakeLists.txt 源码中都会使用这两个等价的变量。

        通常只需要在顶层 CMakeLists.txt 源码中调用 project 即可!

set

        set 命令用于设置变量,命令定义如下所示:

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

        设置变量的值,可选参数 PARENT_SCOPE 影响变量的作用域。

        譬如 CMakeLists.txt 源码内容如下所示:

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

# set 命令
set(VAR1 Hello) #设置变量 VAR1=Hello
set(VAR2 World) #设置变量 VAR2=World

# 打印变量
message(${VAR1} " " ${VAR2})

        对应的打印信息:

        字符串列表

        通过 set 命令实现字符串列表,如下所示:

# 字符串列表
set(SRC_LIST 1.c 2.c 3.c 4.c 5.c)

        此时 SRC_LIST 就是一个列表,它包含了 5 个元素(1.c、2.c、3.c、4.c、5.c),列表的各个元素使用分号“;”分隔,如下:

SRC_LIST = 1.c;2.c;3.c;4.c;5.c #列表

        我们来测试一下,譬如 CMakeLists.txt 源码内容如下所示:

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

# set 命令
set(SRC_LIST 1.c 2.c 3.c 4.c 5.c)

# 打印变量
message(${SRC_LIST})

         执行 cmake 命令打印信息如下:

         乍一看这个打印信息你是不是觉得 SRC_LIST 就是一个普通的变量(SRC_LIST= 1.c2.c3.c4.c5.c),并不是列表呢?事实并非如此,我们可以修改 message 命令,将${SRC_LIST}放置在双引号中,如下:

# 打印变量
message("${SRC_LIST}")

        再次执行 cmake,打印信息如下:

         可以看到此时打印出来的确实是一个列表,既然是列表,那自然可以使用 list 命令对列表进行相关的操作:

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

# 列表
set(SRC_LIST main.c world.c hello.c)
message("SRC_LIST: ${SRC_LIST}")

#列表操作
list(LENGTH SRC_LIST L_LEN)
message("列表长度: ${L_LEN}")
list(GET SRC_LIST 1 VAR1)
message("获取列表中 index=1 的元素: ${VAR1}")

list(APPEND SRC_LIST hello_world.c) #追加元素
message("SRC_LIST: ${SRC_LIST}")

list(SORT SRC_LIST) #排序
message("SRC_LIST: ${SRC_LIST}")

        cmake 打印信息如下:

         除此之外,在 cmake 中可以使用循环语句依次读取列表中的各个元素,后续再向大家介绍。

target_include_directories 和 target_link_libraries

        target_include_directories 命令为指定目标设置头文件搜索路径,而 target_link_libraries 命令为指定目标设置链接库文件,这听起来跟 include_directories 和 link_libraries 命令有着相同的作用,确实如此,它们的功能的确相同,但是在一些细节方面却有不同,关于它们之间的区别稍后再给大家进行解释!

        target_include_directories 和 target_link_libraries 命令定义如下所示:

target_include_directories(<target> [SYSTEM] [BEFORE]
                         <INTERFACE|PUBLIC|PRIVATE> [items1...]
                         [<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
                
target_link_libraries(<target>
                     <PRIVATE|PUBLIC|INTERFACE> <item>...
                     [<PRIVATE|PUBLIC|INTERFACE> <item>...]...)

        这两个命令都有一个相同的参数目标,这个目标指的就是譬如 add_executable、add_library 命令所创建的目标。 首先对于target_include_directories 命令来说 ,SYSTEM、BEFORE 这两个 选项与 include_directories 命令中 SYSTEM、BEFORE 选项的意义相同,这里不再多说!

        我们重点关注的是 INTERFACE|PUBLIC|PRIVATE 这三个选项有何不同?通过一个示例向大家说明, 譬如工程目录结构如下所示:

├── build //build 目录
├── CMakeLists.txt
├── hello_world //生成 libhello_world.so,调用 libhello.so 和 libworld.so
│ ├── CMakeLists.txt
│ ├── hello //生成 libhello.so
│ │ ├── CMakeLists.txt
│ │ ├── hello.c
│ │ └── hello.h //libhello.so 对外头文件
│ ├── hello_world.c
│ ├── hello_world.h //libhello_world.so 对外头文件
│ └── world //生成 libworld.so
│   ├── CMakeLists.txt
│   ├── world.c
│   └── world.h //libworld.so 对外头文件
└── main.c

        调用关系

                                 ├────libhello.so
可执行文件────libhello_world.so
                                 ├────libworld.so

        根据以上工程,我们对 INTERFACE、PUBLIC、PRIVATE 三个关键字进行说明:         PRIVATE:私有的。main.c 程序调用了 libhello_world.so,生成 libhello_world.so 时,只在 hello_world.c 中包含了 hello.h,libhello_world.so 对外的头文件——hello_world.h 中不包含 hello.h。而且 main.c 不会调用 hello.c 中的函数,或者说 main.c 不知道 hello.c 的存在,它只知道 libhello_world.so 的存在;那么在 hello_world/CMakeLists.txt 中应该写入:

target_link_libraries(hello_world PRIVATE hello)
target_include_directories(hello_world PRIVATE hello)

        INTERFACE:接口。生成 libhello_world.so 时,只在 libhello_world.so 对外的头文件——hello_world.h 中包含了 hello.h,hello_world.c 中不包含 hello.h,即 libhello_world.so 不使用 libhello.so 提供的功能,但是 main.c 需要使用 libhello.so 中的功能。那么在 hello_world/CMakeLists.txt 中应该写入:

target_link_libraries(hello-world INTERFACE hello)
target_include_directories(hello-world INTERFACE hello

        PUBLIC:公开的。PUBLIC = PRIVATE + INTERFACE。生成 libhello_world.so 时,在 hello_world.c 和 hello_world.h 中都包含了hello.h 。 并且main.c 中也需要使用libhello.so 提供的功能。那么在hello_world/CMakeLists.txt 中应该写入:

target_link_libraries(hello-world PUBLIC hello)
target_include_directories(hello-world PUBLIC hello)

        不知道大家看懂了没有,其实理解起来很简单,对于 target_include_directories 来说,这些关键字用于指示何时需要传递给目标的包含目录列表,指定了包含目录列表的使用范围:

⚫ 当使用 PRIVATE 关键字修饰时,意味着包含目录列表仅用于当前目标;

⚫ 当使用 INTERFACE 关键字修饰时,意味着包含目录列表不用于当前目标、只能用于依赖该目标的其它目标,也就是说 cmake 会将包含目录列表传递给当前目标的依赖目标;

⚫ 当使用 PUBLIC 关键字修饰时,这就是以上两个的集合,包含目录列表既用于当前目标、也会传递给当前目标的依赖目标。

        对于 target_link_libraries 亦是如此,只不过包含目录列表换成了链接库列表。譬如:

         target_link_libraries(hello_world INTERFACE hello):表示目标 hello_world 不需要链接 hello 库,但是对于hello_world目标的依赖目标(依赖于hello_world的目标)需要链接 hello 库。

        以上便是笔者对 INTERFACE、PUBLIC、PRIVATE 这三个关键字的概括性理解,所以整出这几个关键 字主要还是为了控制包含目录列表链接库列表的使用范围,这就是 target_include_directories、 target_link_libraries 命令与 include_directories、link_libraries 命令的不同之处。target_include_directories()、 target_link_libraries()的功能完全可以使用 include_directories()、link_libraries()来实现。但是笔者建议大家使用 target_include_directories()和 target_link_libraries()。

        include_directories()、link_libraries()是针对当前源码中的所有目标,并且还会向下传递(譬如通过 add_subdirectory 加载子源码时,也会将其传递给子源码)。在一个大的工程当中,这通常不规范、有时还会编译出现错误、混乱,所以我们应尽量使用 target_include_directories()和 target_link_libraries(),保持整个工程的目录清晰。

        总结

        关于cmake常用命令、变量的学习到此结束了,给大家介绍了一些基本、常用的命令,并进行了详细的解释说明,除此之外,还有很多的命令并未提及,将会在后面介绍。

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

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

相关文章

华为推出首款全栈自主数据库 GaussDB,你怎么看?

鸿蒙套壳&#xff0c;鸿蒙套壳完了ERP套&#xff0c;ERP套壳&#xff0c;ERP套壳完了数据库套壳&#xff0c;数据库套壳完了…… 犹记得GaussDB之前一直宣传是基于PostgreSQL研发而来&#xff0c;不知道今天为啥摇身一变为首款全栈自主分布式数据库了。 基于开源研发改不恶心。…

智能设备管理系统

传统设备实施管理难点&#xff1a; 1、日常工作繁琐&#xff0c;手动纸质记录和 Excel 管理设备数据麻烦&#xff0c;后期难以汇总管理&#xff0c;且数据易丢失。 2、需核对设备巡检、保养、故障维修记录&#xff0c;手动更新设备状态和最近维修时间等。 3、无法实时获取设备最…

大学生网络工程想走网络安全方向该怎么规划?

明确需求,确定方向 网络安全 网络安全 是一个很广的概念&#xff0c;涉及的岗位也是非常多的&#xff0c;有安全服务、安全运维、渗透测试、web安全、安全开发、安全售前等等。可以看看下面每个岗位的要求与自身兴趣能力匹配度再决定最适合自己的方向。 渗透测试/Web安全工程师…

开启人机协作新时代:协作机器人的应用与展望

原创 | 文 BFT机器人 01 蓄势待发&#xff0c;产业变革新引擎 近年来&#xff0c;在政策扶持、资本助推和技术创新的共同作用下&#xff0c;产业迎来发展黄金期。日前&#xff0c;各行各业正经历产业智能化转型&#xff0c;机器人市场规模不断扩大&#xff0c;发展前景广阔&…

万宾建筑结构健康监测系统方案

建筑结构健康监测是现代建筑工程领域的重要措施之一。通过实时监测和评估建筑物的结构状态&#xff0c;可以及早发现潜在的问题&#xff0c;保障建筑物的安全性和稳定性。 随着城市化进程的加速和建筑规模的扩大&#xff0c;建筑结构的安全性和稳定性越来越受到关注。然而&…

chatgpt赋能python:Python快速打开:如何提高Python执行速度

Python 快速打开&#xff1a;如何提高 Python 执行速度 介绍 Python 是一种解释型语言&#xff0c;由于其简洁易读&#xff0c;广泛用于数据科学、机器学习、Web 开发等领域。然而&#xff0c;它的执行速度相对较慢&#xff0c;这通常是由于其解释器中面临的硬件资源限制以及…

如何查看docker下的mysql版本

进入运行的mysql的容器 docker exec -it mysqlserver bash 标红的位置可以是我们运行的别名&#xff0c;也可以为id 我们想连接mysql服务时报错了 我们看下配置文件 cat /etc/my.cnf 可以看到配置文件的sock文件位置并不在/var/lib/mysql文件夹中 这里又两种办法 1.直接修改…

JVM--方法区元空间

前言 本篇对java的JVM线程共享内存中的方法区进行系统性的讲解。 1、方法区&元空间概念 方法区是《Java虚拟机规范》中规定的一个内存区域&#xff0c;它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等。 元空间是方法区的实现。方法…

python基础----09-----类、对象、魔法方法、封装、继承、类型注解、多态、数据分析案例

一 初识对象 说白了就是类的实例化&#xff0c;类是一个抽象层的定义。 例如下面class Student就是定义的一个类&#xff0c;它是抽象层&#xff0c;然后stu_1 Student()&#xff0c;我们根据类创建了一个对象&#xff0c;就是对类的实例化&#xff0c;这个实例化对象我们是可…

paas云底座-数字化转型,你真的了解么

云底座是什么&#xff1f; 如图 底座其实就是一个基础打个比方&#xff1a; 把底座看成一块土地&#xff0c;我们在这块土地上可以盖楼房&#xff0c;可以挖一个游泳池&#xff0c;可以开一家饭店 也就是说我们这块土地可以开发很多东西言归正传 云底座其实就是数字化转型的“底…

【快速幂】-迭代法:详解

何为快速幂&#xff1f; 我们经常会计算&#xff1a;。STL中有自带的pow函数&#xff0c;如果当n很大的时候&#xff0c;那么一定会TLE。 因此&#xff0c;我们需要另一种求值的方法&#xff1a;快速幂&#xff01; 快速幂有两种做法&#xff1a;1&#xff1a;递归 2…

谷歌云 | 宣布跨云互连:无缝连接到您的所有云

【本文由Cloud Ace整理发布&#xff0c;Cloud Ace 是谷歌云全球战略合作伙伴&#xff0c;拥有 300 多名工程师&#xff0c;也是谷歌最高级别合作伙伴&#xff0c;多次获得 Google Cloud 合作伙伴奖。作为谷歌托管服务商&#xff0c;我们提供谷歌云、谷歌地图、谷歌办公套件、谷…

1 软件测试基本概念

文章目录 课程目标1. 入门前的7个基础问题2. 软件测试基本概念2.1 需求的概念2.1.1需求的基本概念2.1.2 从软件测试人员角度看需求2.1.3 为什么需求对软件测试人员如此重要&#xff1f;2.1.4 如何才可以深入理解被测试软件的需求 2.2 bug的概念(了解)2.3测试用例的概念2.3.1 概…

AI读心术

近期&#xff0c;德克萨斯大学奥斯汀分校的神经科学家们展开了一场「AI」实验&#xff0c;利用人工智能聊天机器人ChatGPT&#xff0c;将大脑活动转化为文字信息。 参与实验的志愿者&#xff0c;在进行长达20小时的「训练」后&#xff0c;成功被「AI」识别出正在进行的活动。按…

portraiture宿主插件最新v4中文版本下载及使用教程

自拍怎么可以不修图呢&#xff1f;如果要修图的话&#xff0c;磨皮就是其中非常重要的一环。皮肤看起来细腻光滑了&#xff0c;整个人的颜值都会瞬间拉高。下面就让我们介绍一下磨皮用什么软件好用&#xff0c;什么软件可以手动磨皮的相关内容。portraiture是ps人像修图中常用的…

喜报!恭喜知了堂学员成功通过CISP-PTE证书考试

有人说&#xff0c;大学可以不谈恋爱&#xff0c;但一定要考证。 考证&#xff0c;是大学校园生活的关键一环。充分利用在校时间为后期就业提前储备优势&#xff0c;毕业后如果从事IT领域&#xff0c;证书就是一个高含金量的专业认证&#xff0c;尤其是网络安全行业&#xff0…

干货|SpringBoot-Maven与Gradle多模块搭建

比较熟悉的模式是SpringbootMaven多模块的组织方式&#xff0c;由于近期Gradle势力很猛&#xff0c;据Gradle官网给出的5种压测场景的数据来看&#xff0c;Gradle的处理性能确实比Maven快&#xff0c;所以就来体验一下Gradle怎么做多模块搭建和各种依赖的引入和查看。 对Gradl…

【unity】燧光MR设备接入极简教程

官网说明文档&#xff1a; https://doc.ximmerse.com/sdkconf/unityxrsdk/index.html 一、环境准备 1、Unity环境准备 配置adb环境 官网下载&#xff1a;https://developer.android.google.cn/studio/releases/platform-tools 找到SDK Platform-Tools下载 参照 此教程 配置ad…

vulnhub dc-9

1.信息搜集 端口 80 20 filter 存活ip 172.16.1.65 2.访问网站进行信息搜集 cms staff 寻找漏洞 登录处尝试弱口令失败&#xff0c;尝试sql失败 search处sql注入 3.sqlmap跑用户名和密码 注意这题两个库的用户名和密码都需要 UserDetails&#xff0c;Users search处是post传参&…

trace clock structure的若干方法

这里分享几个trace clock structure的方法,各有特点。 1)report_clock_qor -to FF/CK -clock $clk_name -type structure 报告的结果与innovus的clock structure报告类似,如下示例。 (H) clock_root_name # sdc中的root name (0) cts_buf: A-> Y [REF: XX/BUF_XX] [Lo…