CMake学习:快速一步一步入门到进阶静态库+动态库(篇一)
- 笔记
- 1小时前
- 14热度
- 0评论
- 每一步都有完整的文件路径
- 每个文件的内容都完整展示
- 按照操作顺序编号
- 清晰标注哪个文件在哪个文件夹
第一部分:最简单的项目(只有一个 main.c)
项目结构
hello/ ← 项目根目录
├── CMakeLists.txt ← CMake 配置文件
└── main.c ← 你的 C 代码
1.1 步骤 1:创建项目文件夹
mkdir hello
cd hello
1.2 步骤 2:创建 main.c
文件位置: hello/main.c
#include <stdio.h>
int main() {
printf("Hello, CMake!\n");
return 0;
}
1.3 步骤 3:创建 CMakeLists.txt
文件位置: hello/CMakeLists.txt
# 1. 指定 CMake 最低版本
cmake_minimum_required(VERSION 3.10)
# 2. 项目名称和语言
project(HelloProject LANGUAGES C)
# 3. 设置 C 标准
set(CMAKE_C_STANDARD 11)
# 4. 创建可执行文件
# 格式:add_executable(程序名 源文件)
add_executable(hello main.c)
1.4 步骤 4:编译和运行
# 在 hello 目录下执行
# 4.1 创建构建目录
mkdir build
cd build
# 4.2 生成构建文件
cmake ..
# 4.3 编译
cmake --build .
# 4.4 运行程序
./hello # Linux/macOS
# 或
hello.exe # Windows
输出:
Hello, CMake!

第二部分:创建和使用静态库
项目结构
math_project/ ← 项目根目录
├── CMakeLists.txt ← 根配置文件
├── mymath/ ← 库文件夹
│ ├── CMakeLists.txt ← 库的配置文件
│ ├── mymath.h ← 库的头文件
│ └── mymath.c ← 库的实现文件
└── main.c ← 主程序
2.1 步骤 1:创建项目结构
mkdir math_project
cd math_project
mkdir mymath
2.2 步骤 2:创建库的头文件
文件位置: math_project/mymath/mymath.h
#ifndef MYMATH_H
#define MYMATH_H
// 加法
int add(int a, int b);
// 减法
int subtract(int a, int b);
#endif
2.3 步骤 3:创建库的实现文件
文件位置: math_project/mymath/mymath.c
#include "mymath.h"
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a - b;
}
2.4 步骤 4:创建库的 CMakeLists.txt
文件位置: math_project/mymath/CMakeLists.txt
# 创建静态库
# 格式:add_library(库名 STATIC 源文件列表)
add_library(mymath STATIC
mymath.c
)
# 告诉 CMake 头文件在哪里
# PUBLIC 表示:使用这个库的人也能找到这个头文件
target_include_directories(mymath
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
)
```cmake
add_library(库名 STATIC 源文件)
```
- 库名:你给库起的名字,比如
mymath - STATIC:创建静态库(.a 或 .lib 文件)
- 源文件:库的 .c 文件,可以有多个
2.5 步骤 5:创建主程序
文件位置: math_project/main.c
#include <stdio.h>
#include "mymath.h" // 使用库的头文件
int main() {
int a = 10, b = 5;
printf("%d + %d = %d\n", a, b, add(a, b));
printf("%d - %d = %d\n", a, b, subtract(a, b));
return 0;
}
2.6 步骤 6:创建根 CMakeLists.txt
文件位置: math_project/CMakeLists.txt
# 1. 基本设置
cmake_minimum_required(VERSION 3.10)
project(MathProject LANGUAGES C)
set(CMAKE_C_STANDARD 11)
# 2. 添加库的子目录
# 这会执行 mymath/CMakeLists.txt
add_subdirectory(mymath)
# 3. 创建主程序
add_executable(main main.c)
# 4. 链接库到主程序
# 格式:target_link_libraries(程序名 PRIVATE 库名)
target_link_libraries(main PRIVATE mymath)
```cmake
target_link_libraries(程序名 PRIVATE 库名)
```
- 程序名:你的可执行文件名,比如
main - PRIVATE:这个库只给这个程序用
- 库名:要链接的库,比如
mymath
2.7 步骤 7:编译和运行
# 在 math_project 目录下执行
mkdir build
cd build
cmake ..
cmake --build .
./main
输出:
10 + 5 = 15
10 - 5 = 5

第三部分:创建和使用动态库
项目结构(和静态库一样)
dynamic_project/
├── CMakeLists.txt
├── mymath/
│ ├── CMakeLists.txt
│ ├── mymath.h
│ └── mymath.c
└── main.c
3.1 唯一的区别:库的 CMakeLists.txt
文件位置: dynamic_project/mymath/CMakeLists.txt
# 创建动态库
# 格式:add_library(库名 SHARED 源文件列表)
add_library(mymath SHARED
mymath.c
)
target_include_directories(mymath
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
)
```cmake
add_library(库名 SHARED 源文件)
```
- 库名:你给库起的名字
- SHARED:创建动态库(.so 或 .dll 文件)
- 源文件:库的 .c 文件
3.2 其他文件完全相同
mymath.h- 和静态库一样mymath.c- 和静态库一样main.c- 和静态库一样- 根
CMakeLists.txt- 和静态库一样
3.3 编译和运行
mkdir build
cd build
cmake ..
cmake --build .
# Linux/macOS
./main
# Windows(需要把 dll 放在 exe 旁边)
main.exe

第四部分:main 使用库的完整步骤
4.1 总结:main 使用库需要 3 步
步骤 1:在 main.c 中包含头文件
#include "mymath.h" // 包含库的头文件
步骤 2:在 CMakeLists.txt 中链接库
# 根文件路径
target_link_libraries(main PRIVATE mymath)
步骤 3:确保库的头文件路径已设置
# 在库的 CMakeLists.txt 中
target_include_directories(mymath PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
4.2 流程图
main.c
↓ (包含头文件)
mymath.h
↓ (CMake 链接)
mymath 库 (mymath.c 编译成的)
↓ (最终生成)
可执行文件 main
第五部分:静态库 vs 动态库对比
5.1 一句话区别
| 类型 | CMake 指令 | 生成文件 | 特点 |
|---|---|---|---|
| 静态库 | add_library(mymath STATIC ...) |
libmymath.a (Linux)mymath.lib (Windows) |
编译时链接,程序包含库代码 |
| 动态库 | add_library(mymath SHARED ...) |
libmymath.so (Linux)mymath.dll (Windows) |
运行时加载,程序不包含库代码 |
5.2 什么时候用哪个?
先用静态库,简单、不容易出错。
静态库适合:
- 小项目
- 不需要更新库
- 想要独立的可执行文件
动态库适合:
- 大型库(节省空间)
- 需要更新库(不用重新编译程序)
- 多个程序共享同一个库
第六部分:完整命令速查
6.1 创建可执行文件
add_executable(程序名 源文件1.c 源文件2.c ...)
例子:
add_executable(main main.c utils.c)
6.2 创建静态库
add_library(库名 STATIC 源文件1.c 源文件2.c ...)
例子:
add_library(mymath STATIC add.c subtract.c)
6.3 创建动态库
add_library(库名 SHARED 源文件1.c 源文件2.c ...)
例子:
add_library(mymath SHARED add.c subtract.c)
6.4 设置头文件路径
target_include_directories(库名 PUBLIC 路径)
例子:
target_include_directories(mymath PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
推荐:在库的 CMakeLists.txt 中设置
```cmake
# mymath/CMakeLists.txt
add_library(mymath STATIC mymath.c)
target_include_directories(mymath PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
```
优点:
- 库是自包含的,可以独立使用
- 符合模块化原则
- 其他项目使用时不需要额外配置
可行但不推荐:在主项目中设置
```cmake
# 主项目的 CMakeLists.txt
add_subdirectory(mymath)
target_include_directories(mymath PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/mymath)
```
问题:
- 库的配置泄露到主项目
- 如果库被多个项目使用,每个项目都要重复配置
- 违反了"库应该知道自己需要什么"的原则
最佳实践:让库自己管理头文件路径,主项目只负责链接库即可。
6.5 链接库
target_link_libraries(程序名 PRIVATE 库名)
例子:
target_link_libraries(main PRIVATE mymath)
答:给 main 程序添加 mymath 库
理解方式:
target_link_libraries(main ...)→ 给main设置... PRIVATE mymath→ 添加mymath库
完整含义:告诉 CMake,编译 main 程序时,需要链接 mymath 库的代码。
类比:就像告诉编译器:"main 程序需要用到 mymath 库,请把它们连接在一起"
6.6 添加子目录
add_subdirectory(文件夹名)
例子:
add_subdirectory(mymath)
第七部分:常见错误和解决
7.1 错误 1:找不到头文件
错误信息:
fatal error: mymath.h: No such file or directory
原因: 没有设置头文件路径
解决: 在库的 CMakeLists.txt 中添加
target_include_directories(mymath PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
7.2 错误 2:链接错误
错误信息:
undefined reference to `add'
原因: 没有链接库
解决: 在根 CMakeLists.txt 中添加
target_link_libraries(main PRIVATE mymath)
7.3 错误 3:找不到库文件
错误信息:
cannot find -lmymath
原因: 没有添加库的子目录
解决: 在根 CMakeLists.txt 中添加
add_subdirectory(mymath)
第八部分:完整项目模板(直接复制用)
8.1 推荐的项目结构
my_project/
├── CMakeLists.txt ← 根配置
├── src/ ← 主程序源文件
│ └── main.c
├── lib/ ← 库文件夹
│ ├── mylib/
│ │ ├── CMakeLists.txt
│ │ ├── mylib.h
│ │ └── mylib.c
│ └── another_lib/
│ ├── CMakeLists.txt
│ ├── another.h
│ └── another.c
└── build/ ← 构建目录(不提交到 git)
8.2 根 CMakeLists.txt 模板
cmake_minimum_required(VERSION 3.10)
project(MyProject VERSION 1.0 LANGUAGES C)
# C 标准
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
# 添加库
add_subdirectory(lib/mylib)
add_subdirectory(lib/another_lib)
# 创建主程序
add_executable(main src/main.c)
# 链接库
target_link_libraries(main
PRIVATE mylib
PRIVATE another_lib
)
8.3 库的 CMakeLists.txt 模板
# 创建库(STATIC 或 SHARED)
add_library(mylib STATIC
mylib.c
)
# 设置头文件路径
target_include_directories(mylib
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
)
场景:mylib 依赖 another_lib
推荐:在 mylib 的 CMakeLists.txt 中处理依赖
```cmake
# lib/mylib/CMakeLists.txt
# 1. 添加依赖的库(如果在同级目录)
add_subdirectory(../another_lib another_lib_build)
# 2. 创建自己的库
add_library(mylib STATIC mylib.c)
# 3. 链接依赖的库
target_link_libraries(mylib PUBLIC another_lib)
# 4. 设置头文件路径
target_include_directories(mylib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
```
这样做的好处:
- 库的依赖关系清晰(mylib 自己管理自己的依赖)
- 主项目不需要知道 mylib 依赖了什么
- 符合模块化原则
主项目的 CMakeLists.txt 变简单了:
```cmake
# 只需要添加 mylib,不用管 another_lib
add_subdirectory(lib/mylib)
add_executable(main src/main.c)
target_link_libraries(main PRIVATE mylib) # mylib 会自动带上 another_lib
```
注意:add_subdirectory(../another_lib another_lib_build) 中的第二个参数是构建目录名,避免路径冲突。
为什么需要第二个参数?
| 情况 | 示例 | 需要第二个参数? | 原因 |
| :--- | :--- | :--- | :--- |
| 向下(子目录) | add_subdirectory(lib/mylib) | 不需要 | CMake 自动在 build/lib/mylib 创建构建目录 |
| 向上/平行(非子目录) | add_subdirectory(../another_lib) | 必须 | CMake 不知道在哪里创建构建目录,会报错 |
完整格式:
```cmake
add_subdirectory(源目录路径 构建目录名)
```
例子:
```cmake
# ✅ 正确:向上引用,提供构建目录名
add_subdirectory(../another_lib another_lib_build)
# ❌ 错误:向上引用,没有构建目录名
add_subdirectory(../another_lib) # CMake 会报错
# ✅ 正确:向下引用,不需要构建目录名
add_subdirectory(lib/mylib)
```
总结
1. 创建库:
```cmake
add_library(库名 STATIC/SHARED 源文件.c)
target_include_directories(库名 PUBLIC 路径)
```
2. 使用库:
```cmake
add_subdirectory(库文件夹)
add_executable(程序名 main.c)
target_link_libraries(程序名 PRIVATE 库名)
```
3. 编译运行:
```bash
mkdir build && cd build
cmake ..
cmake --build .
./程序名
```
相关笔记
- CMake.md - 完整学习指南
- CMake 库完全指南.md - 库的深入讲解
- CMake C语言开发指南.md - C 语言专用
最后更新:2026-05-26