一文入门CMake

我们前几篇文章已经入门了gcc和Makefile,现在可以来玩玩CMake了。

CMake和Makefile是差不多的,基本上是可以相互替换使用的。CMAke可以生成Makefile,所以本质上我们还是用的Makefile,只不过用了CMake就不用再写Makefile了,会更简单一些。

一般来说小项目我们直接写Makefile,大项目我们就用CMake。

CMake是跨平台的,写起来也比Makefile简单,所以我们还是很有必要学学CMake的。

可以参考官方文档

CMake Reference Documentation — CMake 3.16.9 Documentationicon-default.png?t=N7T8https://cmake.org/cmake/help/v3.16/

首先我们先来安装一下CMake。

sudo apt-get install cmake

再输入下面命令看看版本,能看到就是正常安装成功了。

cmake --version

接下来我们就可以开始写CMake了,写Makefile的时候我们直接建一个名字叫“Makefile”或者“makefile”然后开始写就行了,CMake也差不多,只不过我们的脚本文件需要叫“CMakeLists.txt”

首先我们先在CMakeLists.txt里开头写上一句。

cmake_minimum_required(VERSION 3.0)

这一句是什么意思呢?从名字我们也可以看个大概意思,就是说我们通过这一句可以指定我们CMake的最低版本(VERSION是固定的,上面3.0是指定的最低版本号)。

官方文档英文看不懂,机翻的稀里糊涂,但是我还是把截图放这边吧,以官方文档说的为准。

因为CMake是跨平台的,所以我们是可以把CMakeLists.txt连通项目文件一起发给别人,然后别人再调用CMake去执行生成对应系统的Makefile,再通过Makefile去编译生成可执行文件,问题就在这,每个人环境不一样,CMake的版本也不一样,如果我们在CMakeLists.txt里用到了高版本才有的语句,那么别人低版本的CMake是会报错的,因此我们可以指定CMake的最低版本来防止这种情况发生。

当然不加这句也可以,但是我们最好是加上。

除了上面的,我们再加一个。

project(test)

这个意思是给我们的项目起名为test。

除了起名之外还以添加别的设置,但是一般情况下我们设置个项目名就行了。

有了上面两行基础的设置之后(可有可无),接下来最关键的还是设置我们要生成的可执行文件以及对应的依赖。

add_executable(test test.c)

使用上面的命令,指定生成的可执行目标,再在后面加上对应的依赖,最简单的CMakeLists.txt就算是写好了。就像下面这样。

接着我们使用cmake,后面跟上执行的CMakeLists.txt文件的路径。

然后我们就可以看到CMake替我们生成了不少文件,但是最重要的是Makefile文件,所以我们还需要执行一下make。

可以看成make之后给我们正常编译生成出了可执行文件,也能正常执行。

test.c里就是普普通通的打印一句“Hello World”。

最简单的用法就如上面这样。

接下来我们深入一点点。

在Makefile中我们可以使用变量,在CMake中我们也是可以使用变量的,不过使用方式略有不同。

我们在CMake中定义变量,需要使用一个命令set。

我们现在来改一改test.c这个文件。

包含了一个头文件,并且调用了其中一个函数。那么我们要编译获取可执行文件的话就需要多一个依赖文件了。

接下来我们再使用变量来改改CMakeLists.txt

使用set来定义一个变量,变量名在set命令中第一个参数的位置,后续用黄框框出的是设置的变量的值,可以有多个,用分号;或者空格隔开。使用的时候和Makefile不一样,CMake中用的是大括号{ }。

接着我们cmake,make,执行,发现可以正常编译生成可执行文件。

set除了可以定义变量,还可以修改宏定义的值,在CMake中我们可以通过修改一些宏定义的值来做一些配置。后面我们碰到了再说。

在一般情况下,工具文件不会和主文件放在一起,那么我们把工具文件放到单独一个文件夹里。

我们在CMakeLists.txt中也需要做出对应的修改,我们需要指定出头文件的路径。

使用的命令是下面这个。

比如说我放在了my_tools这个文件夹里,那么我就像下面这样子写。

include_directories(${PROJECT_SOURCE_DIR}/my_tools)

其中 PROJECT_SOURCE_DIR 是一个宏定义,它表示的是我们执行cmake命令时提供的CMakeLists.txt所在的路径,我们提取出这个宏定义就可以拼接出我们文件所在的位置了(绝对路径)。

除了上面的命令,还有个命令可以给某个具体的文件指定头文件路径。

参数一指定的是分配头文件目录的文件。

参数二是模式,一共有三种,INTERFACE,PUBLIC,PRIVATE。

简单来说就是如果有别的文件包含了这个文件,那么在设置了PUBLC模式的情况下,包含了这个文件的别的文件也可以搜索这个命令指定的头文件目录,设置PRIVATE模式的话就不行。剩下INTERFACE这个模式,这个我不太理解,个人感觉和PRIVATE差不多,比PRIVATE更“私人一点”,没啥特殊需求咱用PUBLIC就行了。

这样我们就指定完头文件路径,CMake也可以正常识别到头文件了。但是这样还是有个问题,因为我把库文件的 .c 和 .h都挪到my_tools这个文件夹里了,而我的CMakeLists.txt是下面这样的。

头文件是能找到,但是头文件对应的源文件还是找不到,原因就是我们像上面这么写的话,CMake会在当前目录下找,而不是跑到头文件路径下找。

解决方案有三。

第一,头文件放一个文件夹里,源文件放一个文件夹里,这样路径也能对得上。

第二,修改变量中mytool1.c的路径。

第三,我们使用CMake中的命令来代替我们写死的源文件路径。

使用上面的file,用法很多,但是我们只看红框框出来的。file可以帮助我们到指定路径去搜索文件并且存到变量里。

我们一共需要给这个命令传入三个参数,第一个是GLOB或者GLOB_RECURSE,用来指定搜索的方式,GLOB只会在指定的路径下搜索(足够了),GLOB_RECURSE会在指定路径下递归寻找(费时间)。

第二个是存放结果的变量。

第三个是指定的路径以及寻找的文件类型。

光说比较空泛,我们直接修改一下我们的CMakeLists.txt。

参数一 GLOB表示让CMake只在我们指定的路径下寻找。

参数二 给一个参数来存放结果,看得出来和我们普通编程不太一样,这个变量可以不声明不初始化,直接给就行。

参数三 路径,可以通过通配符来指定我们要搜索的文件后缀。

这样改过之后,CMake就能正常运行了,生成的Makefile也能够生成可执行文件。

除了我们上面这样分开编写源文件和头文件,然后一起塞给主文件编译生成可执行文件,我们还可以先打包成库文件,再塞给主文件生成可执行文件。

我们先看看静态库怎么做。

使用上面这个命令,我们需要给三个参数。

第一个是指定我们制作的静态库的名字。

第二个我们直接给STATIC表示制作的是静态库。

第三就是把源文件塞进去。

现在我们一样是先改改CMakeLists.txt,把用不到的先注释了。

我们指定的静态库名称是tool,但是生成的是libtool.a ,也就是说CMake会自动帮我们生成静态库名字的前缀和后缀。

如果我们制作的是动态库,则只需要把第二个参数从STATIC改成SHARED即可,其他不用动。

动态库的后缀在Linux中是 .so,而静态库是 .a 。

我们注意到,库文件默认生成的位置是和CMakeLists.txt在同一级目录下的,本来CMake就会生成一堆中间文件,现在又整这一出,会显得我们的文件夹很乱,因此我们可以通过指定库文件输出路径来使得我们的目录变得清爽一点。

我们可以使用set来修改一个宏定义,进而对库文件的输出路径进行设置。这个宏定义就是LIBRARY_OUTPUT_PATH

set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)

可以看到已经给我们输出到指定目录下了,甚至之前我都没这个文件夹,CMake还帮我建了个。

那我们已经制作出了静态库和动态库了,那么下一步就是要使用它们了。

先说怎么使用静态库。

说是使用,也可以说是链接,因为库文件的本质就是将多个工具文件编译成 .o之后再合到一起。因此我们要使用的话,第一就是包含对应的头文件(这样才知道有哪些函数可以使用),第二就是在链接阶段和主文件放一起生成可执行文件。

我们分三步走,第一步是指定头文件路径,第二步是指定库文件路径,第三步是指定链接的库文件。

指定头文件路径我们上面说过了,我们直接跳到第二步,用的是下面这个命令。

我们就像指定头文件路径那样把库文件路径塞进去就行。

下一步指定链接的库文件。

指定链接的库文件的时候我们指定的库文件名字可以是全称libxxx.a,也可以掐头去尾,就像我们制作库文件名字那样。

最终修改一下CMakeLists.txt。

接下来我们再链接一下动态库,链接动态库用的是另一个命令(指定库文件路径的命令和上面静态库的一样)。

第一个参数是我们要链接动态库的文件,一般也就是我们的可执行文件。

剩下的参数以两个为一组,每组添加一个动态库。两个参数依次是链接模式和链接的动态库名字。

模式有三种,INTERFACE,PUBLIC,PRIVATE。

我们请文心一言介绍一下吧。

在 CMake 中,target_link_libraries() 函数用于指定一个目标(如库或可执行文件)所依赖的其他库。当你链接库到另一个目标时,你经常需要指定这些依赖是如何被传播的。这就是 INTERFACEPUBLIC 和 PRIVATE 关键字的作用。它们用于指定库链接时的可见性和传播规则。

以下是这些关键字的详细解释:

  1. PRIVATE

    • 当一个库 A 使用 PRIVATE 关键字链接到另一个库 B 时,这意味着库 A 会直接链接到库 B,但库 A 的消费者(即链接到库 A 的其他目标)不会看到或链接到库 B
    • 简单来说,PRIVATE 链接是库 A 内部的事情,不会影响它的消费者。
  2. PUBLIC

    • 当一个库 A 使用 PUBLIC 关键字链接到另一个库 B 时,这意味着库 A 会直接链接到库 B,并且任何链接到库 A 的目标(如可执行文件或其他库)也会自动链接到库 B
    • 换句话说,库 A 的消费者会“继承”库 A 的 PUBLIC 依赖。
  3. INTERFACE

    • 当一个库 A 使用 INTERFACE 关键字链接到另一个库 B 时,这意味着库 A 的消费者(即链接到库 A 的其他目标)会链接到库 B,但库 A 本身不会直接链接到库 B
    • 这通常用于创建一个“接口”或“契约”,指定库 A 的消费者需要哪些额外的库,但库 A 本身在编译时并不需要这些库。

示例

假设你有三个库:LibALibB 和 LibC

  • LibA 使用 PUBLIC 链接到 LibB
  • 一个可执行文件 Exe 使用 LibA 和 LibC

在这种情况下:

  • LibA 会直接链接到 LibB
  • Exe 会自动链接到 LibA 和 LibB(因为 LibA 是 PUBLIC 链接到 LibB 的),但它不会自动链接到 LibC,除非你也指定了 Exe 需要链接到 LibC

使用这些关键字可以帮助你更精细地控制你的构建系统和依赖关系,特别是在创建库和框架时。

没什么特殊需求的话我们直接给个PUBLIC就行,甚至默认就是PUBLIC,我们不写都可以。

那么到此为止,我们就算入门CMake啦,感觉不过瘾的小伙伴可以去官方文档进行更深一步的学习。英语不好的我快看吐了。。。

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

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

相关文章

【ajax实战05】文章封面发布

一&#xff1a;实现效果 二&#xff1a;实现步骤 1 准备标签结构和样式 html结构样式 <div class"cover"><label for"img">封面&#xff1a;</label><label for"img" class"place"></label><inpu…

CS-隐藏防朔源-数据转发-中间件反向代理-Apache

目录 1、代理机安装Apache: 2、中间件设置转发&#xff1a; 添加代理 3、重启Apache服务 4、CS监听器配置转发机IP 实战情况下还是要准备两台外网服务器. --还是做个中转 1、代理机安装Apache: apt-get install apache2 a2enmod proxy proxy_ajp proxy_balancer proxy_co…

路由(urls)

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 Django的URL路由流程&#xff1a; l Django查找全局urlpatterns变量&#xff08;urls.py&#xff09;。 l 按照先后顺序&#xff0c;对URL逐一匹…

Python 算法交易实验73 QTV200第二步: 数据清洗并写入ClickHouse

说明 先检查一下昨天启动的worker是否正常工作&#xff0c;然后做一些简单的清洗&#xff0c;存入clickhouse。 内容 1 检查数据 from Basefuncs import * # 将一般字符串转为UCS 名称 def dt_str2ucs_blockname(some_dt_str):some_dt_str1 some_dt_str.replace(-,.).re…

【LeetCode】七、树、堆、图

文章目录 1、树结构2、二叉树3、二叉树的遍历4、堆结构&#xff08;Heap&#xff09;5、堆化6、图 1、树结构 节点、根节点、叶子节点&#xff1a; 高度、深度、层三者的示意图&#xff1a; 2、二叉树 相比其他树&#xff0c;二叉树即每个节点最多两个孩子&#xff08;两个分…

Linux高级编程——进程

1.进程的含义? 进程是一个程序执行的过程&#xff0c;会去分配内存资源&#xff0c;cpu的调度 PID, 进程标识符 当前工作路径 chdir umask 0002 进程打开的文件列表 文件IO中有提到 &#xff08;类似于标准输入 标准输出的编号&#xff0c;系统给0&#xff0c;1&#xf…

【UE5.3】笔记5-蓝图类

什么是蓝图类&#xff1a;其实就是C类&#xff0c;只不过是UE封装好的且可以直接拖出来可视化使用。 如何创建蓝图类&#xff1f;蓝图类有哪些&#xff1f; 蓝图类分为基于关卡的&#xff0c;基于Actor的&#xff0c;基于组件Component的。 基于关卡的蓝图类 一个关卡只能有…

涉案财物管理系统|DW-S405系统实现涉案财物科学化管理

随着社会的不断发展&#xff0c;犯罪形式日益复杂&#xff0c;涉案财物的种类和数量也不断增加。传统的涉案财物管理方式已经无法满足现代执法办案的需求。因此&#xff0c;建立一套科学、高效、规范的警用涉案财物管理系统成为公安机关亟待解决的问题。 涉案财物管理系统DW-S…

最近在读《谁说菜鸟不会数据分析 SPSS篇》pdf分享

谁说菜鸟不会数据分析 SPSS篇 《谁说菜鸟不会数据分析&#xff08;SPSS篇&#xff09;》继续采用职场三人行的方式来构建内容&#xff0c;细致梳理了准专业数据分析的常见问题&#xff0c;并且挑选出企业实践中最容易碰到的案例&#xff0c;以最轻松直白的方式来讲好数据分析的…

51单片机看门狗定时器配置

测试环境 单片机型号&#xff1a;STC8G1K08-38I-TSSOP20&#xff0c;其他型号请自行测试&#xff1b; IDE&#xff1a;KEIL C51&#xff1b; 寄存器配置及主要代码 手册中关于看门狗的寄存器描述如下&#xff1a; 启动看门狗&#xff0c;需将B5位EN_WDT置1即可&#xff0c;…

ScheduledThreadPoolExecutor和时间轮算法比较

最近项目中需要用到超时操作&#xff0c;对于不是特别优秀的timer和DelayQueue没有看。 Timer 是单线程模式。如果某个 TimerTask 执行时间很久&#xff0c;会影响其他任务的调度。Timer 的任务调度是基于系统绝对时间的&#xff0c;如果系统时间不正确&#xff0c;可能会出现…

STL中的迭代器模式:将算法与数据结构分离

目录 1.概述 2.容器类 2.1.序列容器 2.2.关联容器 2.3.容器适配器 2.4.数组 3.迭代器 4.重用标准迭代器 5.总结 1.概述 在之前&#xff0c;我们讲了迭代器设计模式&#xff0c;分析了它的结构、角色以及优缺点&#xff1a; 设计模式之迭代器模式-CSDN博客 在 STL 中&a…

Open AI限制来袭?用上这个工具轻松破局!

【导语】近日&#xff0c;AI领域掀起了一场不小的波澜。Open AI宣布&#xff0c;从7月9日起&#xff0c;将对部分地区的开发者实施API调用限制。这一消息对于许多依赖Open AI技术的国内初创团队来说&#xff0c;无疑是一个沉重的打击。 对于这些团队而言&#xff0c;Open AI的A…

关于摄像头模组中滤光片的介绍

1、问题背景 红外截止滤光片&#xff08;IR CUT Filter&#xff09;是应用在摄像头模组中非常重要的一个器件&#xff0c;因人眼与 coms sensor 对光线各波长的响应不同&#xff0c; 人眼看不到红外光&#xff0c;但 sensor 能感应到&#xff08;如下图是某sensor在各波长下的…

使用 SwiftUI 为 macOS 创建类似于 App Store Connect 的选择器

文章目录 前言创建选择器组件使用选择器组件总结前言 最近,我一直在为我的应用开发一个全新的界面,它可以让你查看 TestFlight 上所有可用的构建,并允许你将它们添加到测试群组中。 作为这项工作的一部分,我需要创建一个组件,允许用户从特定构建中添加和删除测试群组。我…

Flutter学习目录

学习Dart语言 官网&#xff1a;https://dart.cn/ 快速入门&#xff1a;Dart 语言开发文档&#xff08;dart.cn/guides&#xff09; 学习Flutter Flutter生命周期 点击跳转Flutter更换主题 点击跳转StatelessWidget和StatefulWidget的区别 点击跳转学习Flutter中新的Navigato…

Linux操作系统通过实战理解CPU上下文切换

前言&#xff1a;Linux是一个多任务的操作系统&#xff0c;可以支持远大于CPU数量的任务同时运行&#xff0c;但是我们都知道这其实是一个错觉&#xff0c;真正是系统在很短的时间内将CPU轮流分配给各个进程&#xff0c;给用户造成多任务同时运行的错觉。所以这就是有一个问题&…

个人网站搭建-步骤(持续更新)

域名申请 域名备案 域名解析 服务器购买 端口转发 Nginx要在Linux上配置Nginx进行接口转发&#xff0c;您可以按照以下步骤进行操作&#xff1a; 安装Nginx&#xff08;如果尚未安装&#xff09;&#xff1a; 使用包管理工具&#xff08;如apt, yum, dnf, 或zypper&#x…

高考志愿不知道怎么填?教你1招,用这款AI工具,立省4位数

高中的岁月&#xff0c;就像一本厚厚的书&#xff0c;我们一页页翻过&#xff0c;现在&#xff0c;终于翻到了最后一页。但这不是结束&#xff0c;这是新的开始&#xff0c;是人生的新篇章。 高考落幕&#xff0c;学子们在短暂的放松后&#xff0c;又迎来了紧张的志愿填报。 “…

使用bootstrap框架做一个Aotm Blog个人博客

使用bootstrap框架做一个Aotm Blog个人博客&#xff0c;展示一些自己的个人信息&#xff0c;有四个博客分类&#xff1a;心情记录、学习笔记、旅行相册、美食打卡。 主界面图&#xff1a; 心情记录界面 学习笔记界面&#xff1a; 旅行相册界面&#xff1a; 美食打卡界面&#…