【CMake】CMake工程构建全流程 以及 相关命令行工具的介绍

文章目录

  • 前言
  • 一、CMake的优势
  • 二、使用CMake前的准备
    • [1. CMake 安装](#1. CMake 安装)
    • [2. VS Code CMake 插件安装](#2. VS Code CMake 插件安装)
  • [三、CMake 工程构建流程 及 相关命令行工具的介绍](#三、CMake 工程构建流程 及 相关命令行工具的介绍)
    • [1. CMake 工程构建流程图](#1. CMake 工程构建流程图)
    • [2. 编写工程代码](#2. 编写工程代码)
    • [3. 生成构建系统](#3. 生成构建系统)
    • [4. 编译链接](#4. 编译链接)
    • [5. 测试](#5. 测试)
    • [6. 本机安装](#6. 本机安装)
    • [7. 打包](#7. 打包)
    • [8. 补充部分命令行工具](#8. 补充部分命令行工具)
      • [8.1 脚本模式](#8.1 脚本模式)
      • [8.2 调用外部命令](#8.2 调用外部命令)
      • [8.3 查看帮助](#8.3 查看帮助)

前言

该博客中,cmake命令执行环境为:
编辑环境:VS Code
编译环境:VS Code Remote SSH模式 + Ubuntu 24.04
CMake 官方源代码下载地址:https://cmake.org/download/
CMake 官方英文档地址:https://cmake.org/cmake/help/latest/index.html


一、CMake的优势

(1)传统方式:

(2)CMake方式:

传统方式的缺陷 CMake方式 优势
人工编辑Makefile等配置文件 CMake自动帮我们生成构建配置文件 解决跨平台构建难题,一处配置,到处构建
Makefile 等语法复杂 语法简单,表达能力强大 大幅减少学习成本,提升研发效率
手动查找包 自动查找包 包管理规规范化,解决包管理难题
每个IDE都有自己的构建方式 各个IDE都支持使用cmake来构建程序 IDE对CMake支持度高,⼀处配置,多IDE支持

下图展示了:主流C++商业级开发IDE 对CMake的支持情况

结论:

CMake语法简单易上手,功能强大,使用广泛,IDE支持度高,已经是C/C++事实上的构建标准,也是⼀个十分重要的C/C++工程管理工具。

二、使用CMake前的准备

1. CMake 安装

Step 1:使用ubuntu自带apt 安装:

bash 复制代码
sudo apt install cmake

Step 2:验证安装:

安装完成后,可通过以下命令验证 CMake 是否安装成功以及查看其版本。

bash 复制代码
cmake --version


.

2. VS Code CMake 插件安装

VS Code CMake 插件有以下2点好处:

  • 语法高亮和代码补全:对 CMakeLists.txt 文件提供语法高亮显示,使代码结构更加清晰易读。同时,支持代码补全功能,当你输⼊ CMake 命令或变量时,插件会自动提示可能的选项,减少手动输入的错误和时间。
  • 智能分析和错误检查:能够对 CMakeLists.txt ⽂件进行智能分析,检查其中的语法错误和潜在问题,并在编辑器中实时显示错误提示和警告信息,帮助你及时发现和解决问题。

安装步骤如下:
Step 0:打开 VS Code,点击左侧活动栏中的 扩展图标(或按 Ctrl+Shift+X )。

Step 1:在搜索框中输入 CMake ,我们选择安装以下4个插件:

• CMake Tools

• CMake Language Support

• CMake IntelliSence

• CMake

三、CMake 工程构建流程 及 相关命令行工具的介绍

1. CMake 工程构建流程图


构建流程全过程,使用的命令行工具如下:

bash 复制代码
# 0. 编写工程代码并创建CMakeLists.txt文件
touch CMakeLists.txt

# 1. 创建构建⽬录并进⼊
mkdir build && cd build

# 2. 配置项⽬
cmake ..

# 3. 构建项⽬
make
或者
cmake --build .

# 4. 执⾏测试(如果有)
make test
或者
ctest .

# 5. 安装项⽬
make install
或者
cmake --install .

# 6. ⽣成安装包
make package
或者
cpack

# 7 解压 ⽣成的tar 包
tar xvf TestCMakeTools-0.1.1-Linux.tar.gz

2. 编写工程代码

  • Step 0:目录结构
bash 复制代码
cmake_tools
├── build(目录)
├── CMakeLists.txt
├── main.cpp
└── test.cp
  • Step 1.1:编辑 main.cpp
cpp 复制代码
#include <iostream>

int main()
{
	std::cout << "hello world!" << std::endl;
	return 0;
}

Step 1.2:编辑 test.cpp

cpp 复制代码
#include <iostream>
#include <cassert>

int main() 
{
	assert( 1 + 2 == 3); 
	std::cout << "test passed!" << std::endl; 
	return 0; 
}
  • Step 2:编辑文件 CMakeLists.txt
bash 复制代码
# 1 设置能运行此cmake 工程的最低cmake版本要求
cmake_minimum_required(VERSION 3.18)

# 2 设置项目名称
project(helloWorld)

# 3 添加构建目标
# g++ main.cpp -o main
add_executable(main main.cpp)
# 生成测试二进制可执行程序
add_executable(testAdd test.cpp)

# 4 开启测试功能 & 集成测试逻辑
include(CTest)
add_test(
    NAME Case_Add
    COMMAND testAdd
)

# 5 安装二进制可执行程序到本地
include(GNUInstallDirs)
install(TARGETS main)

# 6 开启打包功能 & 打包二进制可执行程序
include(CPack)
# cpack 默认收集install 对应的目标,然后会把收集到的目标 打包在压缩包里

注: 为什么需要设置最低的cmake版本?

CMake 是一个不断迭代的工具(目前最新4.x,历史有3.x),不同版本可能会引入新的语法、命令、模块或行为变更。如果项目中使用了高版本 CMake 才支持的特性(例如特定的函数、生成器表达式、目标属性等),而用户本地安装的cmake版本低于项目要求的版本,就会出现无法解释或者产生不可预知的行为。为了防止以上情况出现:

CMake 给我们提供了 cmake_minimum_required ,这个命令会在配置阶段( cmake 命令执行时)检查当前 CMake 版本:

(1)若当前版本低于最低要求,CMake 会直接终止并报错,明确提示 "需要至少 X.X 版本",避免后续因版本不兼容导致的模糊错误。

(2)若当前安装的版本满足要求,则继续执行后续配置流程。

3. 生成构建系统

通过 cmake 可以看到cmake命令支持的详细参数,常用的参数如下:

bash 复制代码
cmake [options] <path-to-source>
cmake [options] <path-to-existing-build>
cmake [options] -S <path-to-source> -B <path-to-build>
参数 含义
-S 指定源文件根目录,必须包含一个CMakeLists.txt文件
-B 指定构建目录,构建生成的中间文件和目标文件的生成路径,通常会保存⼀个CMakeCache.txt来存储⼀些变量
  • 源文件目录 用 -S 选项指定
    在 CMake 中,源文件树(Source Tree) 指的是项目源代码的目录结构,CMake 通过 CMakeLists.txt 文件来组织和管理这个结构。通常使用顶层 CMakeLists.txt 来标识。

// 源文件目录的特征:包含源文件 和 CMakeLists.txt文件

// 例如:cmake_tools 就是源文件目录,它包含源文件main.cpp、test.cpp 以及 CMakeLists.txt文件

bash 复制代码
cmake_tools
├── build(目录)
├── CMakeLists.txt
├── main.cpp
└── test.cpp
  • 构建目录 用 -B 选项指定
    在 CMake 中,构建树(Build Tree) 是指项目构建过程中生成的临时文件目录,它与源文件树(Source Tree) 相对应。构建树包含编译过程中生成的中间文件(如目标文件、依赖信息)和最终产物(如可执行文件、库文件)。通常使用CMakeCache.txt来标识。

// 用于存放 项目构建过程中生成的文件 (含编译过程中生成的中间文件 和 最终产物)的目录,被称之为构建目录


  • 构建时,根据构建中间文件是独立保存还是放在当前源代码目录下, 构建过程分为以下2种:

(1)源内构建:

在包含顶级CMakeLists.txt的源代码目录下进行直接构建

下图中,当前所在目录:源文件目录cmake_tools

bash 复制代码
cmake 源文件目录
// cmake 查找到指定目录下有CMakeLists.txt文件,通过CMakeLists.txt文件进行构建,将构建过程中生成的临时文件存放到当前目录下

将源文件目录 作为 构建目录,存放 构建过程中生成的临时文件。这一方式是极不推荐的,将源文件目录 和 构建目录混在一起,非常不利于管理!

(2)源外构建

为了维护一个纯净的源文件树,可通过使用一个单独目录来进行源外构建

使用 -S 参数指定源文件目录也就是包含CMakeLists.txt的目录,再使用 -B 参数指定一个构建目录(此处选择空目录 build):

c 复制代码
cmake -S . -B ./build
// 当前处于 源文件目录cmake_tools中

  • 构建生成的CMakeCache.txt文件中,会存储⼀些变量,记录各种信息(如构建目录 和 源文件目录的路径)

cd进入构建目录build,然后执行命令:

c 复制代码
cmake . 

发现竟然 构建成功了,可是cmake不是要指定 包含CMakeLists.txt文件的源文件目录,才能执行构建嘛?

当前目录是构建目录,其下没有CMakeLists.txt文件,为什么构建能成功执行呢?

答:当前目录下确实没有CMakeLists.txt文件,但是当前目录下有CMakeCache.txt文件,该文件记录了源文件目录的路径,通过该路径,能找到源文件目录下的CMakeLists.txt文件,所以构建能成功执行。


4. 编译链接

  • 方式1:使用make命令

进入构建目录build,该目录下有 构建过程中生成的Makefile文件:


可以直接使用make命令,它会搜索当前目录下的Makefile文件 进行编译链接:


  • 方式2:cmake --build 指定构建目录

该命令的效果:相当于 在指定的构建目录下执行make指令


原理解析:

CMakeCache.txt文件中存储了 make命令的软连接路径

当执行 "cmake --build 指定构建目录" 命令时,系统会根据构建目录下的 CMakeCache.txt文件 中记录的软连接路径,在指定的构建目录下执行make命令!


5. 测试

在构建前,就已经在源文件目录的 CMakeLists.txt文件中设置好了测试case的数量(通过 add_test添加,我只设置了一个测试case),并为每一个测试case设置好 测试名 以及 要测试的目标 可执行文件

构建时,会在构建目录下创建一个叫 CTestTestfile.cmake的文件,里面会设置 测试相关的配置信息(根据 CMakeLists.txt文件中的add_test相关信息 进行配置):

在编译链接阶段,生成了可执行文件testAdd(由源文件test.cpp 编译链接生成):

// 源文件test.cpp中代码 没有任何问题,所以生成的可执行文件testAdd在测试时也不会出问题


(1) 先对没有问题的可执行文件testAdd进行测试:

有两种测试方法:

  • 在构建文件下,使用 ctest命令 (该命令会查询当前目录下的CTestTestfile.cmake文件,根据文件中的配置信息 进行测试操作)
  • 在构建文件下,使用 make test命令

查看一下Makefile下的 test目标,发现调用了 /usr/bin/ctest
所以make test命令 的本质就是 调用ctest命令


修改源文件test.cpp中代码,添加一行执行时会报错的代码:

执行make命令,重新编译链接生成 有问题的可执行文件testAdd:


(2)有问题的可执行文件testAdd进行测试:


6. 本机安装

在构建前,就已经在源文件目录的 CMakeLists.txt文件中设置好了 要安装的目标文件 以及 要安装在哪一个路径下:

Linux下,默认安装路径为 /usr/local

构建时,会在构建目录下创建一个叫 cmake_install.cmake 的文件,里面会设置 安装相关的配置信息(根据 CMakeLists.txt文件中的相关信息 进行配置):


安装目标是编译链接阶段生成的 可执行文件main,要将该目标安装到指定路径下。

要达成上述目标,一共有两种方式:

  • 方式一:cmake --install 指定构建目录

该命令会搜索构建目录下的 cmake_install.cmake文件,根据该文件的配置信息,就能知道该操作是要将 构建目录下的可执行文件main 安装(拷贝)到 /usr/local/bin 路径下

/usr/local/bin 目录的读写权限都是root,所以需要sudo提高权限到root,才能将 可执行文件main 拷贝到 /usr/local/bin 路径下

安装完成后,会在构建目录下一个叫 install_manifest.txt的文件,该文件被称为资源清单,会记录安装好的文件:

cmake_install.cmake文件中的最后一步:将安装好的文件路径 写入 构建目录下的 install_manifest.txt文件中
(第一次执行安装操作时,会在构建目录下创建install_manifest.txt文件,再进行写入;后续再执行安装操作,直接将安装好的文件路径写入已创建的install_manifest.txt文件)


  • 方式二:make install

Makefile文件下,执行install目标 实际也是 根据cmake_install.cmake文件来进行安装操作!

7. 打包

打包操作,需要在构建目录下执行以下命令:

bash 复制代码
cpack
或者
make package

使用cpack命令时,一般都要加sudo提权,否则可能执行失败


cpack指令具体做了哪些工作:

  1. 先在构建目录下创建 _CPack_Packages/Linux目录,再查找CPackConfig.cmake文件,设置好临时安装目录:

./_CPack_Packages/Linux/STGZ/helloWorld-0.1.1-Linux/
./_CPack_Packages/Linux/TGZ/helloWorld-0.1.1-Linux/
./_CPack_Packages/Linux/TZ/helloWorld-0.1.1-Linux/

  1. 执行构建目录下的 cmake_install.cmake文件,将 install对应的目标main 拷贝到 临时安装目录下:

./_CPack_Packages/Linux/STGZ/helloWorld-0.1.1-Linux/bin/main
./_CPack_Packages/Linux/TGZ/helloWorld-0.1.1-Linux/bin/main
./_CPack_Packages/Linux/TZ/helloWorld-0.1.1-Linux/bin/main

树状结构如下:

c 复制代码
./_CPack_Packages
└── Linux
    ├── STGZ
    │   └── helloWorld-0.1.1-Linux
    │       └── bin
    │           └── main
    │  
    ├── TGZ
    │   └── helloWorld-0.1.1-Linux
    │       └── bin
    │           └── main
    │  
    └── TZ
        └── helloWorld-0.1.1-Linux
            └── bin
                └── main

cpack命令前加sudo的原因:
cpack命令的执行过程中会调用cmake_install.cmake文件,而cmake_install.cmake文件最后会将安装文件的信息 写入install_manifest.txt文件中,但是install_manifest.txt文件的权限是root,所以必须使用root权限!


install_manifest.txt文件的权限为什么会是root?
install_manifest.txt文件是在第一次使用 安装命令时创建的,我们第一次 使用安装指令时用了sudo提权,所以创建的 install_manifest.txt文件也是root权限

  1. 执行打包
c 复制代码
./_CPack_Packages
└── Linux
    ├── STGZ
    │   ├── helloWorld-0.1.1-Linux
    │   │   └── bin
    │   │       └── main
    │   └── helloWorld-0.1.1-Linux.sh
    ├── TGZ
    │   ├── helloWorld-0.1.1-Linux
    │   │   └── bin
    │   │       └── main
    │   └── helloWorld-0.1.1-Linux.tar.gz
    └── TZ
        ├── helloWorld-0.1.1-Linux
        │   └── bin
        │       └── main
        └── helloWorld-0.1.1-Linux.tar.Z
  1. 将压缩包拷贝到构建目录

8. 补充部分命令行工具

8.1 脚本模式

bash 复制代码
cmake -P <file> = Process script mode.

CMake脚本模式,不会生成构建产物,也不会生成中间过程。 适合处理各种与构建系统无关的自动化任务,通过编写简洁的脚本文件,你可以实现环境检查、文件处理、部署打包等功能。

以下是hello_world 工程里cmake生成用于安装目标的主makefile代码片段,在主makefile里使用 cmake的脚本模式调用了cmake_install.cmake,在这里执行文件的拷贝等操作。

8.2 调用外部命令

bash 复制代码
cmake -E = CMake command mode.

⼀下是hello_world 工程里主makefile里的 RM命令,其实就是使用命令模式调用了rm -f 外部命令:

bash 复制代码
# The command to remove a file.
RM = /usr/bin/cmake -E rm -f

8.3 查看帮助

bash 复制代码
cmake --help

相关推荐
与遨游于天地2 小时前
从 BPF 到 eBPF:一场 Linux 内核的“可编程”革命
linux·运维·arm开发
ShineWinsu2 小时前
对于Linux:基础指令的介绍—中
linux·运维·服务器·c++·面试·笔试·系统
是小小张呀2 小时前
ubuntu更换国内阿里镜像源
linux
maqiang_7202 小时前
为什么centos+vmware虚机 用NAT 模式总连不上外网
linux·运维·centos
Z...........2 小时前
进 程
linux·运维·服务器
顺顺 尼2 小时前
基础开发工具
linux
AuroBreeze2 小时前
RISC-V: Minimal U-mode implementation
linux·c语言·c++·risc-v
阿常呓语2 小时前
Linux命令 echo详解
linux·服务器·linux命令·echo
梦想的初衷~2 小时前
Python驱动的WRF模式自动化:业务化预报系统搭建实战
linux·python·自动化·大气科学·气候环境·风能太阳能