CMake学习:快速一步一步入门到进阶静态库+动态库(篇一)

💡 提示:本笔记特点 CMake学习篇一:快速一步一步入门到进阶 静态库+动态库

  • 每一步都有完整的文件路径
  • 每个文件的内容都完整展示
  • 按照操作顺序编号
  • 清晰标注哪个文件在哪个文件夹

第一部分:最简单的项目(只有一个 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