---
title: C++工程化：CMake构建与多文件项目管理
categories:
  - C与C++
date: 2026-09-02 19:59:30
desc: 本篇讲解了 C++ 工程化核心的CMake 构建系统：最小项目与构建流程、多文件项目的目录组织与静态库与动态库的构建与选型（含对比表）、条件编译/option/查找与自动拉取依赖（find_packa…
abbrlink: c-gong-cheng-hua-cmake-gou-jian-yu-duo-wen-jian-xiang-mu
---
> 本文是 C++ 系列教程的第 26 篇。上一篇完成了并发编程三篇，本篇进入工程化阶段：CMake 构建系统从零到实战、多文件项目组织、可执行文件与静态/动态库构建、跨平台配置与安装部署，覆盖 10 个完整示例代码。

## 一、为什么需要 CMake

### 1.1 从手动编译到构建系统

小型程序可以用一行 g++ 编译，但真实项目有几十上百个源文件、依赖第三方库、需要跨平台。构建系统自动处理：依赖关系、增量编译、链接、测试与安装：

```bash
# 手动编译（文件一多就难以维护）
g++ -std=c++17 -O2 main.cpp math.cpp utils.cpp -o app

# 多平台差异（Linux 用 -pthread，Windows 用不同标志）
g++ -std=c++17 main.cpp -pthread -o app   # Linux
```

CMake 用一份 `CMakeLists.txt` 描述项目，自动生成各平台的构建文件（Makefile、Ninja、Visual Studio 工程等），是当前 C++ 项目的事实标准。

### 1.2 最小 CMake 项目

```cmake
cmake_minimum_required(VERSION 3.16)   # 最低版本要求
project(HelloWorld VERSION 1.0.0 LANGUAGES CXX)  # 项目名与语言

set(CMAKE_CXX_STANDARD 17)             # C++17 标准
set
(CMAKE_CXX_STANDARD_REQUIRED ON)

add_executable(hello main.cpp)         # 生成可执行文件
```

```cpp
// main.cpp
#include <iostream>
using namespace std;

int main() {
    cout << "Hello CMake!" << endl;
    return 0;
}
```

构建命令：`cmake -B build`（配置，生成 build 目录）、`cmake --build build`（编译）、`./build/hello`（运行）。推荐**外部构建**（build 目录与源码分离），保持源码目录干净。

## 二、多文件项目组织

### 2.1 经典目录结构

```
project/
├── CMakeLists.txt          # 顶层构建文件
├── include/                # 头文件
│   └── math/
│       └── calc.h
├── src/                    # 源文件
│   ├── main.cpp
│   └── calc.cpp
└── tests/                  # 测试
    └── test_calc.cpp
```

```cpp
// include/math/calc.h
#pragma once
namespace math {
    int add(int a, int b);
    int multiply(int a, int b);
}
```

```cpp
// src/calc.cpp
#include "math/calc.h"
namespace math {
    int add(int a, int b) { return a + b; }
    int multiply(int a, int b) { return a * b; }
}
```

### 2.2 顶层 CMakeLists.txt 组织多目标

```cmake
cmake_min
imum_required(VERSION 3.16)
project(MathLib VERSION 1.0.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 头文件目录（供所有目标使用）
include_directories(${PROJECT_SOURCE_DIR}/include)

# 可执行文件
add_executable(app src/main.cpp src/calc.cpp)

# 输出目录放到 build/bin
set_target_properties(app PROPERTIES
    RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
```

```cpp
// src/main.cpp
#include <iostream>
#include "math/calc.h"
using namespace std;

int main() {
    cout << "add(3,5) = " << math::add(3, 5) << endl;
    cout << "multiply(3,5) = " << math::multiply(3, 5) << endl;
    return 0;
}
```

`include_directories` 把 `include/` 加入搜索路径，头文件用 `"math/calc.h"` 带子目录引用，避免同名冲突。

## 三、静态库与动态库

### 3.1 构建静态库（.a / .lib）

把 `calc.cpp` 编译为静态库，供多个程序复用：

```cmake
cmake_minimum_required(VERSION 3.16)
project(MathLib VERSION 1.0.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)

include_directories(${PROJECT_SOURCE_DIR}/include)

# 静态库：编译时把代码打进可执行文件
add_library(math_static STATIC src/calc.cpp)

# 可执行文件链接静态库
add_executable(app src/main.cpp)
target_link_libraries(app PRIVATE math_static)
```

静态库在链接时被整体拷贝进可执行文件，产物独立可分发，但体积大、更新需重编。

### 3.2 构建动态库（.so / .dll / .dylib）

```cmake
cmake_minimum_required(VERSION 3.16)
project(MathLib VERSION 1.0.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)

include_directories(${PROJECT_SOURCE_DIR}/include)

# 动态库：运行时加载，多个程序共享一份
add_library(math_shared SHARED src/calc.cpp)
set_target_properties(math_shared PROPERTIES
    VERSION ${PROJECT_VERSION}
    SOVERSION 1)

add_executable(app src/main.cpp)
target_link_libraries(app PRIVATE math_shared)
```

动态库在运行时加载，可被多个程序共享、可独立升级；但分发时需连同 `.so`/`.dll` 一起，且存在「依赖地狱」风险。

### 3.3 库类型对比

| 特性 | 静态库 | 动态库 |
| --- | --- | --- |
| 链接时机 | 编译期（拷贝进可执行文件） | 运行期（加载） |
| 产物体积 | 可执行文件大 | 可执行文件小 + 库文件 |
| 更新部署 | 需重新编译可执行文件 | 替换库文件即可 |
|  |  |  |
| 依赖管理 | 简单，自包含 | 需管理库依赖关系 |

## 四、常用 CMake 语法与配置

### 4.1 条件编译与编译选项

```cmake
cmake_minimum_required(VERSION 3.16)
project(ConfigDemo LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 添加编译选项
add_compile_options(-Wall -Wextra)

# Debug/Release 配置
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
    message(STATUS "构建 Debug 版本（带调试信息）")
    add_compile_options(-g)
else()
    message(STATUS "构建 Release 版本（优化）")
    add_compile_options(-O2)
endif()

# 自定义选项：是否启用测试
option(BUILD_TESTS "构建单元测试" ON)
if(BUILD_TESTS)
    enable_testing()
    add_executable(test_calc tests/test_calc.cpp src/calc.cpp)
    add_test(NAME test_calc COMMAND test_calc)
endif()

add_executable(app src/main.cpp)
```

配置时指定类型：`cmake -B build -DCMAKE_BUILD_TYPE=Release`。`option` 用于开关功能，配合 `add_test`/`enable_testing` 集成 CTest。

### 4.2 查找第三方库

`find_package` 查找系统已安装的库，找不到时可配合 `FetchContent` 自动下载：

```cmake
cmake_minimum_required(VERSION 3.16)
pr
oject(UseLib LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)

# 方式一：查找系统安装的库
find_package(Threads REQUIRED)
find_package(OpenSSL QUIET)

# 方式二：自动下载依赖（如 nlohmann/json）
include(FetchContent)
FetchContent_Declare(json
    URL https://github.com/nlohmann/json/releases/download/v3.11.2/json.tar.xz)
FetchContent_MakeAvailable(json)

add_executable(app main.cpp)

# 链接线程库与 json 头文件库
target_link_libraries(app PRIVATE Threads::Threads nlohmann_json::nlohmann_json)
```

```cpp
// main.cpp
#include <iostream>
#include <thread>
#include <nlohmann/json.hpp>
using namespace std;
using json = nlohmann::json;

int main() {
    thread t([] { cout << "线程运行" << endl; });
    t.join();

    json data = {{"name", "张三"}, {"age", 25}};
    cout << data.dump() << endl;   // {"age":25,"name":"张三"}
    return 0;
}
```

`FetchContent` 让「依赖即源码」成为可能：构建时自动拉取并编译依赖，无需手动安装，配合 `find_package` 是现代 CMake 的最佳实践。

## 五、实战：完整的多目标项目

综合运用可执行文件、静态库、测试与安装规则：

```cmake
cmake_minimum_required(
VERSION 3.16)
project(FullProject VERSION 1.0.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 源文件自动收集（src 下所有 .cpp）
file(GLOB_RECURSE SRC_FILES ${PROJECT_SOURCE_DIR}/src/*.cpp)

# 核心库（静态）
add_library(core STATIC ${SRC_FILES})
target_include_directories(core PUBLIC ${PROJECT_SOURCE_DIR}/include)

# 主程序
add_executable(app src/main.cpp)
target_link_libraries(app PRIVATE core)

# 测试
enable_testing()
add_executable(unit_tests tests/unit_tests.cpp)
target_link_libraries(unit_tests PRIVATE core)
add_test(NAME unit_tests COMMAND unit_tests)

# 安装：make install 时拷贝产物与头文件
install(TARGETS app RUNTIME DESTINATION bin)
install(TARGETS core ARCHIVE DESTINATION lib)
install(DIRECTORY include/ DESTINATION include)
```

```cpp
// tests/unit_tests.cpp —— 简单断言测试
#include <iostream>
#include "math/calc.h"
using namespace std;

int main() {
    int failures = 0;
    if (math::add(2, 3) != 5) { cout << "FAIL: add" << endl; failures++; }
    if (math::multiply(2, 3) != 6) { cout << "FAIL: multiply" << endl; failures++; }

    if (failures == 0) {
        cout << "全部测试通过!"
ld && cmake --build build && ctest` 三连命令。

下一篇将讲解 **C++ 工程化：编译链接、调试与性能优化**（预处理/编译/汇编/链接、GDB/内存检测与性能剖析），敬请期待！
 << endl;
        return 0;
    }
    return 1;
}
```

```cpp
// src/main.cpp
#include <iostream>
#include "math/calc.h"
using namespace std;

int main() {
    cout << "2+3=" << math::add(2, 3)
         << " 2*3=" << math::multiply(2, 3) << endl;
    return 0;
}
```

构建全流程：`cmake -B build && cmake --build build && ctest --test-dir build`（编译 + 运行测试），`cmake --install build --prefix /usr/local`（安装）。

## 总结

本篇讲解了 C++ 工程化核心的 **CMake 构建系统**：最小项目与构建流程、多文件项目的目录组织与 `include_directories`、**静态库与动态库**的构建与选型（含对比表）、**条件编译/option/查找与自动拉取依赖**（find_package + FetchContent），最后用完整的「库 + 可执行文件 + 测试 + 安装」多目标项目实战串联全部知识。建议实际动手跑一遍 `cmake -B bui


---

**作者：**Wang Shijie

**本文链接：**[https://wsj-blog.pages.dev/posts/c-gong-cheng-hua-cmake-gou-jian-yu-duo-wen-jian-xiang-mu/](https://wsj-blog.pages.dev/posts/c-gong-cheng-hua-cmake-gou-jian-yu-duo-wen-jian-xiang-mu/)

本文采用[知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议](https://creativecommons.org/licenses/by-nc-sa/4.0/)进行许可。