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

一文入门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啦,感觉不过瘾的小伙伴可以去官方文档进行更深一步的学习。英语不好的我快看吐了。。。

相关文章:

一文入门CMake

我们前几篇文章已经入门了gcc和Makefile,现在可以来玩玩CMake了。 CMake和Makefile是差不多的,基本上是可以相互替换使用的。CMAke可以生成Makefile,所以本质上我们还是用的Makefile,只不过用了CMake就不用再写Makefile了&#x…...

【LeetCode面试经典150题】117. 填充每个节点的下一个右侧节点指针 II

一、题目 117. 填充每个节点的下一个右侧节点指针 II - 力扣(LeetCode) 给定一个二叉树: struct Node {int val;Node *left;Node *right;Node *next; } 填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个…...

RTDETR更换优化器——Lion

RTDETR更换Lion优化器 论文:https://arxiv.org/abs/2302.06675 代码:https://github.com/google/automl/blob/master/lion/lion_pytorch.py 简介: Lion优化器是一种基于梯度的优化算法,旨在提高梯度下降法在深度学习中的优化效果…...

Spring Boot中最佳实践:数据源配置详解

Spring Boot中最佳实践:数据源配置详解 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!今天我们将深入探讨在Spring Boot中如何进行最佳实践的数据源…...

第1章 物联网模式简介---独特要求和体系结构原则

物联网用例的独特要求 物联网用例往往在功耗、带宽、分析等方面具有非常独特的要求。此外,物联网实施的固有复杂性(一端的现场设备在计算上受到挑战,另一端的云容量几乎无限)迫使架构师做出艰难的架构决策和实施选择。可用实现技…...

数据挖掘概览

数据挖掘(Data Mining)就是从大量的,不完全的,有噪声的,模糊的,随机的实际应用数据中,提取隐含在其中的,人们事先不知道的,但又是潜在有用的信息和知识的过程. 预测性数据挖掘 分类 定义:分类就是把一些新的数据项映射到给定类别中的某一个类别 分类流程&#x…...

【学习】软件测试中常见的文档类型及其作用

在软件开发的生命周期中,软件测试是确保产品质量的关键步骤。为了系统地进行测试活动,并保证测试结果的有效性和可追溯性,产生了一系列标准化的测试文档。这些文档不仅为测试人员提供了执行指南,而且为项目管理者和利益相关者提供…...

electron的托盘Tray

1.在主进程文件background.js中引入需要的文件 import { Tray, Menu } from "electron"; const path require("path");2.获取托盘图标 const baseSRC process.cwd(); //这里不能使用__dirname,使用dirname会直接获取dist_electron中的文件,…...

Harmony OS UI框架探索笔记

本文探讨了如何将现有的常用架构理论与Arkts和ArkUI结合起来,使代码更有条理,并利用Previewer快速调整布局,同时在不改变代码的情况下运行显示真实数据。 开发环境 Windows 11DevEco Studio 4.0 ReleaseBuild Version: 4.0.0.600, built on…...

transformers evaluate

☆ Evaluate https://huggingface.co/docs/evaluate/main/en/installation ★ 解决方案 常用代码 # 查看支持的评估函数 evaluate.list_evaluation_modules(include_communityTrue)# 加载评估函数 accuracy evaluate.load("accuracy")# load function descripti…...

【ONLYOFFICE深度探索】:ONLYOFFICE桌面编辑器8.1震撼发布,打造高效办公新境界

文章目录 一、功能完善的PDF编辑器:解锁文档处理新维度二、幻灯片版式设计:释放创意,打造专业演示三、改进从右至左显示:尊重多元文化,优化阅读体验四、新增本地化选项:连接全球用户,跨越语言障…...

C++系统相关操作4 - 获取CPU(指令集)架构类型

1. 关键词2. sysutil.h3. sysutil.cpp4. 测试代码5. 运行结果6. 源码地址 1. 关键词 关键词: C 系统调用 CPU架构 指令集 跨平台 实现原理: Unix-like 系统: 可以通过 uname -m 命令获取 CPU 架构类型。Windows 系统: 可以通过环境变量 PROCESSOR_A…...

whisper 实现语音转文字

准备需要转码的音频 https://support.huaweicloud.com/sdkreference-sis/sis_05_0039.html 编码转吗的代码 import whisperif __name__ "__main__":file_path "16k16bit.wav"model whisper.load_model("small")result model.transcribe(f…...

使用VLLM部署llama3量化版

1.首先去魔塔社区下载量化后的llama3模型 git clone https://www.modelscope.cn/huangjintao/Meta-Llama-3-8B-Instruct-AWQ.git 2.跑起来模型 1)python -m vllm.entrypoints.openai.api_server --model /home/cxh/Meta-Llama-3-8B-Instruct-AWQ --dtype auto --…...

计算机缺失OpenCL.dll怎么办,OpenCL.dll丢失的多种解决方法

在使用电脑的过程中,我们经常会遇到一些开机弹窗问题。其中,开机弹窗找不到OpenCL.dll是一种常见的情况。本文将详细介绍开机弹窗找不到OpenCL.dll的原因分析、解决方法以及预防措辞,帮助大家更好地解决这一问题。 一,了解OpenCL.…...

git 本地代码管理

简介 git 能实现本地代码多个更改版本的管理和导出。 首先复制好项目(参考 git clone 别人项目后正确的修改和同步操作 中的前三步) 实操 克隆原始项目 首先,从远程仓库克隆项目到本地: git clone https://github.com/libo-huan…...

Docker(九)-Docker运行redis6.0.8容器实例

1.宿主机新建目录存放redis.conf文件 目的:运行redis容器实例时使用自己的配置文件2.运行redis容器实例 docker run -d -p 6379:6379 --privilegedtrue -v 【宿主机配置文件目录】:/etc/redis/redis.conf -v 【宿主机数据目录】:/data --nameredis6.0.8 redis:6.0…...

似然 与 概率

概率似然概率函数与似然函数的关系似然与机器学习的关系最大似然估计 似然与概率分别是针对不同内容的估计和近似 概率 概率:概率表达给定参数 θ \theta θ下样本随机向量 X x \textbf{X} {x} Xx的可能性。 概率密度函数的定义形式是 f ( x ∣ θ ) f(x|\t…...

Tableau数据可视化与仪表盘搭建

Tableau的主要目的 数据赋能和数据探索。 数据赋能: 1.分析师可以将数据看板发布到线上给其他部门使用 2.自动更新看板 3.自由下载数据 4.线上修改图表 5.邮件发送数据 6.设置数据预警 数据探索: 1.支持亿级数据的连接和处理 2.自由地对字段进行各种…...

web前端——HTML

目录 一、HTML概述 1.HTML是什么? 2.HTML具体化解释 二、HTML基本语法 1.声明 2. Head头标签 3.body身体标签 4.一个html的基本结构 5.标签 6.标签属性 ①属性的格式 ②属性的位置 ③添加多个属性 三、基本常用标签 1.超链接 2.图像标签 ①图像标…...

计算机网络 之 【自定义协议、序列化与反序列化】(C++使用JSON示例)

目录 1.自定义协议与序列化/反序列化 2.Json简介 Json是什么 第三方库提供,使用时包含头文件 JSON 的数据类型 JSON结构示例 C使用JSON示例 1.自定义协议与序列化/反序列化 协议的必要性 协议是通信双方的约定,它定义了数据的格式和含义&#xff…...

Flutter 实现点击任意位置收起键盘的最佳实践

痛点 在 Flutter 开发中,TextField 聚焦后会弹出键盘,关闭键盘通常需要: 点击系统返回键点击输入框外的空白区域(但很多情况下点击空白区域也没反应)点击其他输入框(键盘会切换到另一个输入框,不…...

SeqGPT-560M中文理解深度测评:对古汉语、方言、行业黑话的泛化能力分析

SeqGPT-560M中文理解深度测评:对古汉语、方言、行业黑话的泛化能力分析 1. 模型背景与核心能力 SeqGPT-560M是阿里达摩院推出的零样本文本理解模型,专门针对中文场景优化,无需训练即可完成文本分类和信息抽取任务。这个560M参数的轻量级模型…...

从零部署RK3588 MPP:硬编解码环境搭建与核心工具解析

1. RK3588 MPP硬编解码环境搭建全流程 第一次在ArmSoM-W3开发板上折腾RK3588的MPP硬编解码环境时,我踩了不少坑。这里把完整搭建过程拆解成可复现的步骤,用最直白的语言分享给各位开发者朋友。 MPP(Media Process Platform)是瑞芯…...

十大经典排序算法解析与实现

## 1. 十大经典排序算法技术解析### 1.1 算法分类体系 排序算法可分为两大技术类别:**比较类排序**: - 通过元素间比较确定相对次序 - 时间复杂度下限为O(nlogn) - 典型代表:快速排序、堆排序、归并排序**非比较类排序**: - 不依赖…...

scanf_s使用避坑指南:如何正确应对C6064警告(含C6054连带问题处理)

scanf_s安全使用全指南:彻底解决C6064与C6054警告 在Windows平台进行C/C开发时,使用scanf_s函数处理用户输入是常见场景。但许多开发者都会遇到两个令人困惑的警告——C6064和C6054。这些警告看似简单,实则暗藏玄机。本文将带你深入理解这两个…...

十 438. 找到字符串中所有字母异位词

438. 找到字符串中所有字母异位词https://leetcode.cn/problems/find-all-anagrams-in-a-string/ 给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。 示例 1: 输入: s "cbaebabacd"…...

解码 DINO 核心:三大创新如何重塑端到端目标检测

1. 从DETR到DINO:目标检测的范式革命 记得我第一次用Faster R-CNN做目标检测时,光是调整锚框尺寸就花了整整三天。这种传统检测方法就像用老式打字机写代码——每个环节都需要手工微调。直到2020年DETR横空出世,才让我意识到目标检测还能这么…...

避坑指南:HuggingFace本地数据集加载常见的5个报错及解决方法

HuggingFace本地数据集加载实战:5类典型报错深度解析与解决方案 当你第一次尝试将本地数据集加载到HuggingFace生态系统中时,可能会遇到各种令人困惑的错误信息。这些报错往往隐藏着数据格式、特征定义或路径处理等关键问题。本文将剖析开发者最常遇到的…...

Sqoop性能调优之 --fetch-size:小参数,大作用

Sqoop性能调优之 --fetch-size:小参数,大作用1. 引言:被忽视的"隐形冠军"2. 什么是 --fetch-size?2.1 基本定义2.2 核心作用3. 底层原理:从逐行到批量3.1 没有 --fetch-size 的情况(逐行读取&…...