【CMake 教程】基础使用教程(一)

一、准备

1. CMake 官方下载网址
2. 文件目录结构
MySimpleProject
├── CMakeLists.txt	# CMake 配置文件
└── main.cpp		# 主程序源码

我们在 MySimpleProject 目录中创建 CMakeLists.txtmain.cpp 两个空白文件。

二、CMake 简单概念

要学会使用 CMake,首先要弄清楚 CMake 项目的基本结构。

大体上,可以分为两个主要层级:

  • 项目(Project)
    通常是一个独立的工程,对应一个整体代码库,通常包含多个目标或子模块。
  • 目标(Target)
    核心概念之一,分为可执行文件(Executable)动态/静态库(Library)

此外,CMake支持创建更复杂的层级,比如 子项目子目录(Subdirectory),这些后面会介绍。

提示:如果你熟悉 Visual Studio,可以简单理解:Visual Studio 的 解决方案(Solution) 类似于 CMake 的根项目,而其中的 项目(Project) 则类似于 CMake 中的单个目标。

三、CMakeLists.txt 结构拆解

以下是最简单的 CMakeLists.txt 结构,逐步拆解说明:

# 声明项目名称和版本
cmake_minimum_required(VERSION 3.21)
project(MySimpleProject VERSION 1.0)# 添加可执行目标
add_executable(MyApp main.cpp)
  1. 指定 CMake 的最低版本要求,确保脚本兼容性:
cmake_minimum_required(VERSION 3.21)
  • 如果项目使用了低于 3.21 版本的 CMake 进行构建,会报错。
  • 最好根据项目需求调整版本,保持兼容的同时尽量使用最新功能。
  1. 定义项目名称和版本,类似一个工程的“名片”:
project(MySimpleProject VERSION 1.0)
  • MySimpleProject 是项目名称,不一定是最终生成的文件名(比如可执行文件名)。
  • 可在 project 后添加描述、语言等选项,便于构建配置:
project(MySimpleProject VERSION 1.0 LANGUAGES CXX)
  1. 定义目标,生成可执行文件,参数依次为目标名源码文件
add_executable(MyApp main.cpp)
  • MyApp目标名,最终生成程序的文件名会以此为基础(具体格式取决于平台,如 Windows 的 MyApp.exe
  • main.cpp源码文件路径,支持多个文件:
add_executable(MyApp main.cpp utils.cpp)

如果我们希望项目名目标名一致怎么做呢:

add_executable(${PROJECT_NAME} main.cpp)

${PROJECT_NAME} 指的就是 MySimpleProject。更多细节,后面会详细介绍。

四、构建、编译项目

我们先把 main.cpp 补充完整:

#include <iostream>using namespace std;int main()
{cout << "Hello World!" << endl;return 0;
}

接下来,我们打开命令行,进入项目的根目录,然后执行构建命令

cmake .

接着再执行编译命令

cmake --build .

Linux 下:

在这里插入图片描述

目录结构:

在这里插入图片描述

Windows 下:

在这里插入图片描述

Windows 这里默认用了 msvc 编译器,后面我们再说怎么自定义

目录结构:

在这里插入图片描述

可以看到 sln 文件也出现了,它是执行 cmake . 时生成的

五、中间文件介绍

不同的操作系统,在不同的编译器下,生成的中间文件也是不同的。但我们能发现总有几个相同的文件(或目录),我们一起看看:

1. CMakeCache.txt

保存了 CMake 配置过程中得出的缓存变量的值,比如编译器路径构建类型、依赖的路径等信息。

  • 用途:一般用于确保多次运行 CMake 时可以重复使用之前的配置结果,避免重复计算。
  • 提示不要手动改它,如果发现里面的变量与预期不一样,应该考虑怎么修改 CMake 脚本,而不是来更改这个文件!

2. CMakeFiles目录(通常没必要了解)

CMake 生成的内部的构建工具文件。通常包含一些辅助的构建文件和依赖关系。

3. cmake_install.cmake

CMake 自动生成的脚本文件,描述了如何将构建产物(如可执行文件、库、头文件等)从构建目录安装到目标安装路径。

当你在项目根目录执行 make install(或类似的安装命令,例如 Ninja 的 ninja install,Visual Studio 的 INSTALL 项目)时,系统会调用该文件中的指令完成安装操作

详细作用后面会说到。

六、拓展:指定 include 目录

我们在根目录中创建一个 include 目录,再在里面创建 unit.hpp 文件。目录结构如下:

MySimpleProject
├── CMakeLists.txt
├── include
│   └── unit.hpp	# 新增
└── main.cpp

unit.hpp 内容如下:

inline int sum(int a, int b)
{return a + b;
}

这时候如果我们要在 main.cpp 使用这个头文件,像这样:

#include "include/unit.hpp"

但我是想这么用的:

#include "unit.hpp"

接下来,我们修改一下 CMakeLists.txt 文件:

# 声明项目名称和版本
cmake_minimum_required(VERSION 3.21)
project(MySimpleProject VERSION 1.0)# 添加可执行目标
add_executable(MyApp main.cpp)# 添加头文件目录
target_include_directories(MyApp PRIVATE include)	# 新增

接下来我们就可以愉快的使用 #include "unit.hpp" 了。


target_include_directories 简单介绍

target_include_directories 是 CMake 中推荐的现代用法,用于为特定目标设置包含目录。其格式为:

target_include_directories(<target> [INTERFACE|PUBLIC|PRIVATE] <DIRECTORY>...)

简单介绍参数:

  1. 目标名,如例子中的 MyApp
  2. [INTERFACE|PUBLIC|PRIVATE]
    • PRIVATE:头文件目录只对当前目标生效。
    • PUBLIC:适用于当前目标及依赖它的目标(同时对自己和下游生效)。
    • INTERFACE:仅对依赖它的目标生效(即仅下游生效,对自己不生效)。

INTERFACEPUBLIC 目前可以不用管,等后面写“库”的时候再聊。

在这里使用 PRIVATE 是因为 unit.hpp 仅供 MyApp 使用,不需要暴露给其他目标。


既然有现代用法,那肯定就有古代用法。

include_directories 是早期全局作用的 API,一旦指定会对所有目标生效,容易引入不必要的依赖,维护成本高。(说人话,就是如果你有两个目标,写成 include_directories(include),会使这两个目标都包含 include 目录)。

七、拓展:添加依赖库

我们先清理掉刚刚那些文件,只留下 CMakeLists.txtmain.cpp

注意,我是在 Linux 下测试的,方法在 Windows 通用。

假设我的项目需要依赖 zlib(msvc 没有自带这个库,别试了,换个库测试吧),main.cpp 代码如下:

#include <iostream>
#include <string>
#include <zlib.h>  // 引入 zlib 头文件void compressString(const std::string &str)
{uLong srcLen  = str.size();uLong destLen = compressBound(srcLen);  // 计算压缩缓冲区最大可能大小char *dest    = new char[destLen];      // 创建缓冲区// 调用 zlib 压缩函数if (compress(reinterpret_cast<Bytef *>(dest), &destLen,reinterpret_cast<const Bytef *>(str.c_str()), srcLen)== Z_OK) {std::cout << "原始数据大小: " << srcLen << " 字节" << std::endl;std::cout << "压缩后数据大小: " << destLen << " 字节" << std::endl;}else {std::cerr << "压缩失败!" << std::endl;}delete[] dest;  // 释放内存
}int main()
{std::string data = "这是一段将会被 zlib 压缩的字符串。";compressString(data);return 0;
}

这时候执行 cmake .,一切正常。

但执行 cmake --build . 的时候,就会发现报错了:

在这里插入图片描述

因为没有添加 zlib 依赖库。我们在 CMakeLists.txt 文件中加上下面这段代码:

target_link_libraries(MyApp PRIVATE z)	# 别问我为什么是 z 而不是 zlib,这个问题……

这里的 PRIVATE 意义和上面的 target_include_directories 是一样的。

其实就相当于执行了 g++ main.cpp -o MyAppC -lz

重新 cmake . cmake --build .,成功。


target_link_libraries 简单介绍

有了上面 target_include_directories 的经验,我们快速过一下。

target_link_libraries(<target> [INTERFACE|PUBLIC|PRIVATE] <library>...)

同样,它也有古代写法:

link_libraries(<library>...)

于是,聪明的我们想到了 CMakeLists.txt

# 声明项目名称和版本
cmake_minimum_required(VERSION 3.21)
project(MySimpleProject VERSION 1.0)# 添加可执行目标
add_executable(MyApp main.cpp)# 添加头文件目录
target_include_directories(MyApp PRIVATE include)# 添加链接库
link_libraries(z)	# 新增

然后构建、编译,然后报错:

在这里插入图片描述

为什么?因为 MyApp 目标不知道你加了 zlib 库!

正确应该这样:

# 声明项目名称和版本
cmake_minimum_required(VERSION 3.21)
project(MySimpleProject VERSION 1.0)link_libraries(z)	# 放到目标前添加# 添加可执行目标
add_executable(MyApp main.cpp)# 添加头文件目录
target_include_directories(MyApp PRIVATE include)

想必也不用解释了~

八、源码、中间文件分离

我们发现,执行 cmake . 时,中间文件会出现在根目录中,污染了我们的代码文件。

我们可以在根目录中创建一个 build 目录,接下来有两种做法:

1. 在根目录执行:

cmake -S . -B build
cmake --build build

其中,-S . 指的是源码文件在当前目录,-B build 指的是构建到 build 目录中去;--build build 指的是构建目录是 build 目录,CMake 就知道要在 build 目录中编译了。

2. 在 build 目录中执行:

cmake -S .. -B .
cmake --build .

看第1点的介绍,这里应该很好理解。


【CMake 教程】基础使用教程(一) 至此完毕,希望大家提提意见,欢迎指正!还请大家点点赞,给我点动力~~

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

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

相关文章

智能网络感知,打造极致流畅的鸿蒙原生版中国移动云盘图文体验

背景 中国移动云盘&#xff08;原“和彩云网盘”&#xff09;是中国移动重磅推出的安全、智能、不限速、移动用户免流的智能云盘&#xff0c;致力于成为5G时代用户个人与家庭的数字资产管理中心&#xff0c;是中国移动继语音、短信、流量后的“第四项基础服务”。 照片、音视…

Windows 11【1001问】Windows 11系统硬件配置要求

Windows 11 的设计目标是让用户更贴近自己喜欢的内容&#xff0c;在其发布之际&#xff0c;计算机在连接、创作以及游戏体验方面扮演了更加核心的角色。在设定 Windows 11 的最低系统要求时&#xff0c;我们依据三个关键原则来指导决策&#xff0c;以确保用户能够获得卓越的使用…

SQL笔记#函数、谓词、CASE表达式

目录 一、各种各样的函数 1、函数的种类 2、算术函数 ABS——绝对值 MOD——取余 ROUND——四舍五入 3、字符串函数 ||——拼接 LENGTH——字符串长度 LOWER——小写转换 REPLACE——字符串的替换 SUBSTR——字符串的截取 UPPER——大写转换 4、日期函数 CURRENT_DATE——…

为人工智能驱动的交通研究增强路面传感器数据采集

论文标题 英文标题&#xff1a;Enhancing Pavement Sensor Data Harvesting for AI-Driven Transportation Studies 中文标题&#xff1a;为人工智能驱动的交通研究增强路面传感器数据采集 作者信息 Manish Kumar Krishne Gowda Purdue University, 465 Northwestern Avenue,…

简讯:Rust 2024 edition and v1.85.0 已发布

详见 https://blog.rust-lang.org/2025/02/20/Rust-1.85.0.html 升级方法&#xff1a;rustup update stable

clickhouse-介绍、安装、数据类型、sql

1、介绍 ClickHouse是俄罗斯的Yandex于2016年开源的列式存储数据库&#xff08;DBMS&#xff09;&#xff0c;使用C语言编写&#xff0c;主要用于在线分析处理查询&#xff08;OLAP&#xff09;&#xff0c;能够使用SQL查询实时生成分析数据报告。 OLAP&#xff08;On-Line A…

pycharm技巧--鼠标滚轮放大或缩小 Pycharm 字体大小

1、鼠标滚轮调整字体 设置 Ctrl 鼠标滚轮调整字体大小 备注&#xff1a; 第一个是活动窗口&#xff0c;即缩放当前窗口 第二个是所有编辑器窗口&#xff0c;即缩放所有窗口的字体 2、插件 汉化包&#xff1a; Chinese Simplified 包

R语言安装教程(附安装包)R语言4.3.2版本安装教程

文章目录 前言一、安装包下载二、R-4.3.2安装步骤三、rtools43安装步骤四、RStudio安装步骤 前言 本教程将详细、全面地为你介绍在 Windows 系统下安装 R 语言 4.3.2 的具体步骤。无论你是初涉数据领域的新手&#xff0c;还是希望更新知识体系的专业人士&#xff0c;只要按照本…

从零开始玩转TensorFlow:小明的机器学习故事 5

图像识别的挑战 1 故事引入&#xff1a;小明的“图像识别”大赛 小明从学校里听说了一个有趣的比赛&#xff1a;“美食图像识别”。参赛者需要训练计算机&#xff0c;看一张食物照片&#xff08;例如披萨、苹果、汉堡等&#xff09;&#xff0c;就能猜出这是什么食物。听起来…

01 冲突域和广播域的划分

目录 1、冲突域和广播域的划分 1.1、冲突域 1.2、广播域 1.3、对比总结 1.4、冲突域与广播域个数计算例题 2、交换机和路由器的结构 2.1、交换机的结构 2.2、路由器的结构 1、冲突域和广播域的划分 1.1、冲突域 冲突域是指网络中可能发生数据帧冲突的物理范围。当多…

[C++]使用纯opencv部署yolov12目标检测onnx模型

yolov12官方框架&#xff1a;sunsmarterjie/yolov12 【算法介绍】 在C中使用纯OpenCV部署YOLOv12进行目标检测是一项具有挑战性的任务&#xff0c;因为YOLOv12通常是用PyTorch等深度学习框架实现的&#xff0c;而OpenCV本身并不直接支持加载和运行PyTorch模型。然而&#xff…

神经网络八股(3)

1.什么是梯度消失和梯度爆炸 梯度消失是指梯度在反向传播的过程中逐渐变小&#xff0c;最终趋近于零&#xff0c;这会导致靠前层的神经网络层权重参数更新缓慢&#xff0c;甚至不更新&#xff0c;学习不到有用的特征。 梯度爆炸是指梯度在方向传播过程中逐渐变大&#xff0c;…

zyNo.26

[GXYCTF2019]Ping Ping Ping&#xff08;Web&#xff09; 传/&#xff1f;ip1有ping回显&#xff0c;说明后端可能通过php参数接受了ip参数&#xff0c;并且拼接到了最终执行的命令里形成了ping -c 3$ip&#xff0c;这样可能存在一个命令注入漏洞 要判断是否符合 ping -c 3$ip …

轻量级SDK,大能量:EasyRTC重塑嵌入式设备音视频体验

在智能硬件与物联网迅猛发展的今天&#xff0c;嵌入式设备的音视频通讯能力正变得愈加关键。然而&#xff0c;受限于硬件资源&#xff0c;尤其是Flash存储空间的不足&#xff0c;传统的音视频通讯方案往往难以在嵌入式设备上实现高效集成。EasyRTC凭借其轻量级SDK和先进的技术架…

算法日常刷题笔记(2)

为保持刷题的习惯 计划一天刷3-5题 然后一周总计汇总一下 这是第二篇笔记 笔记时间为2月17日到2月23日 第一天 找到初始输入字符串 找到初始输入字符串 Ihttps://leetcode.cn/problems/find-the-original-typed-string-i/ Alice 正在她的电脑上输入一个字符串。但是她打字技…

[实现Rpc] 客户端 | Requestor | RpcCaller的设计实现

目录 Requestor类的实现 框架 完善 onResponse处理回复 完整代码 RpcCaller类的实现 1. 同步调用 call 2. 异步调用 call 3. 回调调用 call Requestor类的实现 &#xff08;1&#xff09;主要功能&#xff1a; 客户端发送请求的功能&#xff0c;进行请求描述对服务器…

WPS计算机二级•文档的页面设置与打印

听说这是目录哦 纸张大小页边距和装订线❤️‍&#x1f525;打印界面讲解❤️缩印&#x1f495;打印作文稿纸&#x1f49e;将文档打印成书籍&#x1f493;限制编辑设置&#x1f497;给文字文档加密&#x1f496;文档导出为 PDF格式&#x1f498;协作编辑模式&#x1f49d;能量站…

hackmyvm-buster

题目地址 信息收集 主机发现 ┌──(root㉿kali)-[/home/kali] └─# arp-scan -I eth1 192.168.56.0/24 Interface: eth1, type: EN10MB, MAC: 00:0c:29:34:da:f5, IPv4: 192.168.56.103 WARNING: Cannot open MAC/Vendor file ieee-oui.txt: Permission denied WARNING: C…

【入门音视频】音视频基础知识

&#x1f308;前言&#x1f308; 这个系列在我学习过程中&#xff0c;对音视频知识归纳总结的笔记。因为音视频相关讲解非常稀少&#xff0c;所以我希望通过这个音视频系列&#xff0c;跟大家一起学习音视频&#xff0c;希望减少初学者在学习上的压力。同时希望也欢迎指出文章的…

将Ubuntu操作系统的安装源设置为阿里云

在使用Ubuntu操作系统时,默认的软件源通常是国外的仓库,这可能会导致软件安装和更新速度较慢。为了提高下载速度和稳定性,我们可以将Ubuntu的安装源设置为阿里云镜像源。以下是详细步骤: 一、准备工作 在开始之前,请确保您的Ubuntu系统可以正常上网,并且您拥有管理员权…