一、为什么学习Cmake
在软件开发中,一个可执行文件从源代码 (.c / .cpp) 到最终运行的程序 (.exe / .out) 需要经历 预处理 → 编译 → 汇编 → 链接 四个核心步骤。
对于小项目,手写命令还可以完成;但在 大型项目、多文件工程、跨平台编译 中,这些步骤非常复杂,需要构建工具来帮助管理。
CMake 正是其中最主流的跨平台构建工具。
为了理解为什么要学习 CMake,必须先理解下面这些基础的构建流程
二、一般的可执行文件是如何生成的
1. 预处理(Preprocessing)
处理以 # 开头的指令,包括:
-
展开头文件
示例:
#include <iostream>→ 把对应头文件内容插入代码 -
宏替换
#define PI 3.14→ 所有 PI 替换为 3.14 -
条件编译
#ifdef DEBUG、#if 0等 -
删除注释
📌 预处理器输出一个 .i(或 .ii)文件,纯 C/C++ 代码,无宏、无注释、无头文件引用。
2. 编译(Compilation)
将预处理后的 .i 文件翻译成汇编代码(.s 或 .asm):
-
语法分析(解析 C/C++ 语句)
-
语义分析
-
生成中间代码
-
优化代码
-
生成汇编代码
📌 编译器输出汇编语言文件,接近底层 CPU 指令。
3. 汇编(Assembling)
将汇编代码 (.s) 转换为 机器码 ,生成 .o 或 .obj 文件。
-
把汇编指令翻译成 CPU 机器指令
-
构建符号表(记录函数、变量的地址信息)
-
每一个 .cpp 会生成一个独立的
.o / .obj
📌 汇编器输出目标文件(Object File),但此时还不能运行。
4. 链接(Linking)
将多个 .o 文件 + 库文件合成为最终可执行程序。
主要作用:
-
符号解析
比如
main.cpp调用了add(),链接器负责找到add在 math.o 中的位置。 -
地址重定位
把所有函数、变量的"地址引用"转换为最终的实际运行地址。
-
连接静态库/动态库
如
libopencv_world.a,libstdc++.so -
生成最终可执行文件
Windows:
.exeLinux:无后缀(ELF)
📌 链接器输出最终可执行文件。
✅ 三、CMake 中常用的命令
下面列出 最常用、最基础、最必须掌握 的 CMake 命令(已整理表达):
| 命令 | 作用说明 |
|---|---|
cmake_minimum_required() |
指定 CMake 的最低版本要求 |
project() |
设置项目名称、版本、语言等 |
add_executable() |
生成可执行文件 |
add_library() |
生成静态库或动态库 |
target_link_libraries() |
链接库 |
target_include_directories() |
添加头文件目录 |
set() |
设置变量 |
message() |
输出信息(调试用) |
add_subdirectory() |
添加子目录(管理大型项目必备) |
find_package() |
查找外部库,如 OpenCV、PCL、Ceres |
file(GLOB ...) |
扫描文件(自动收集源码) |
install() |
安装可执行文件或库 |
这些命令构成了 CMakeLists.txt 的基础写法。
四、CMakeLists.txt的编写
4.1. CMake 的使用步骤(概述)
编写 CMakeLists.txt 的典型步骤如下:
指定 CMake 最低版本
声明项目
设置编译选项(可选)
指定源文件
生成可执行文件或库
链接外部库(如 OpenCV)
设置头文件路径
执行 cmake 命令生成工程
使用构建工具编译
4.2、CMakeLists.txt 的具体写法
1. cmake_minimum_required()
作用:
指定当前项目可使用的 CMake 最低版本。如果版本过低,CMake 会报错,避免使用老版本带来的兼容性问题。
语法:
cmake_minimum_required(VERSION 3.14)
意思:
👉 要求 CMake 版本 至少是 3.14 才能构建该项目。
2. project()
作用:
定义项目名称、版本和使用的语言。
语法:
project(MyProject LANGUAGES CXX)
意思:
项目叫 MyProject ,主要使用 C++(CXX)。
也可以写得更详细:
project(MyProject VERSION 1.0 LANGUAGES C CXX)
3. add_executable()
作用:
告诉 CMake: "我要生成一个可执行程序,这个程序由哪些源文件组成"。
语法:
add_executable(目标名称 源文件列表)
比如:
add_executable(MyApp main.cpp) 单个.CPP
详细解释:
-
MyApp------ 最终生成的可执行文件的名称✔ Windows 下生成:
MyApp.exe✔ Linux / macOS 下生成:
MyApp -
main.cpp------ 编译这个可执行文件所需的源代码文件
也可以是多个文件:
add_executable(MyApp main.cpp utils.cpp math.cpp)
多个文件中间可以用:空格 或者;隔开
4. add_library()
作用:
生成一个库(静态库或动态库)。
库文件可以被其他程序调用。
add_library(MyLib math.cpp utils.cpp)
| 平台 | 动态库文件名 | 说明 |
|---|---|---|
| Linux | libMyLib.so |
so = shared object |
| macOS | libMyLib.dylib |
Apple 独有格式 |
| Windows | MyLib.dll(以及对应的 MyLib.lib 导入库) |
Windows 动态库为 .dll |
-
生成静态库:
libMyLib.a或MyLib.lib -
静态库(Static Library)
| 平台 | 静态库文件名 | 说明 |
|---|---|---|
| Linux | libMyLib.a |
自动加前缀 lib,扩展名为 .a |
| macOS | libMyLib.a |
与 Linux 相同 |
| Windows | MyLib.lib |
无 lib 前缀,扩展名 .lib |
总结:
-
Unix 系(Linux/macOS) →
lib + 名称 + .a -
Windows →
名称 + .lib -
生成动态库:
-
libMyLib.so、libMyLib.dylib或MyLib.dll
5. target_link_libraries()
作用:
告诉 CMake:
"把某个库链接到可执行文件或另一个库"。
示例:
target_link_libraries(MyApp PRIVATE MyLib)
意思:
MyApp 这个可执行文件要链接 MyLib 这个库。
6. target_include_directories()
作用:
为某个目标(可执行文件/库)指定头文件路径。
target_include_directories(MyApp PRIVATE ${CMAKE_SOURCE_DIR}/include)
意思:
让编译器去 include 目录里找头文件。
7. set()
作用:
设置变量(字符串、数字、路径、列表都可以)。
set(SRC main.cpp utils.cpp)
8. message()
作用:
打印信息,用于调试。
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
9. add_subdirectory()
作用:
用于多文件夹(大型工程)时,将子目录添加进来。
add_subdirectory(src)
10. find_package()
作用:
查找系统中的外部库,例如 OpenCV、PCL、Eigen。
find_package(OpenCV REQUIRED) target_link_libraries(MyApp PRIVATE ${OpenCV_LIBS})
4.3、CMakeLists.txt 的编写(带解释)
下面给一份最小可运行 + 每句话都有解释 的 CMakeLists.txt。
📌 示例项目结构
MyProject/
│── CMakeLists.txt
│── main.cpp
📌 CMakeLists.txt
# 1. 指定 CMake 最低版本(3.14 是常用稳定版本)
cmake_minimum_required(VERSION 3.14)
# 2. 定义项目名称为 MyProject,语言使用 C++
project(MyProject LANGUAGES CXX)
# 3. 指定 C++ 标准为 C++17
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 4. 定义一个可执行文件
# MyApp 是可执行程序的名字(最终生成 MyApp.exe 或 MyApp)
# main.cpp 是构建这个程序需要的源文件
add_executable(MyApp main.cpp)
📌 main.cpp
#include <iostream>
int main() {
std::cout << "Hello CMake!" << std::endl;
return 0;
}
🎯 五、构建步骤(适合教学)
在终端执行:
mkdir build
cd build
cmake ..
cmake --build .
运行:
./MyApp
输出:
Hello CMake!