CMake 学习引入:从 GCC 到 Makefile 再到 CMake

通过三个阶段的演进,理解为什么需要 CMake:GCC 命令行 → Makefile → CMake


示例代码

add.h:

#ifndef ADD_H
#define ADD_H
int add(int a, int b);
#endif

add.c:

#include "add.h"
int add(int a, int b) {
    return a + b;
}

main.c:

#include <stdio.h>
#include "add.h"

int main() {
    int result = add(10, 20);
    printf("sum:%d\n", result);
    return 0;
}

第一阶段:GCC 命令行

项目结构:

cmakeS/
├── main.c
├── add.c
└── add.h

编译运行

gcc -o main main.c add.c

运行结果

$ ./main
sum:30

存在的问题

⚠️ 警告

  • 每次修改代码都要重新编译所有文件(即使只改了一个文件)
  • 文件多了命令会很长,容易出错
  • 无法自动管理依赖关系
  • 团队协作时每个人的编译命令可能不一致

如果只修改了 add.c,理论上只需要重新编译 add.c,但 GCC 命令会重新编译所有文件,浪费时间。


第二阶段:Makefile

项目结构:

cmakeS/
├── main.c
├── add.c
├── add.h
└── Makefile        # 新增

Makefile 内容

Makefile:

# 目标:依赖
# <tab>命令

# 最终目标:生成可执行文件 main
main: main.o add.o
    gcc main.o add.o -o main

# 编译 main.c 生成 main.o
main.o: main.c
    gcc -c main.c -o main.o

# 编译 add.c 生成 add.o
add.o: add.c
    gcc -c add.c -o add.o

# 清理编译产物
clean:
    rm -rf *.o main

# 测试运行
test:
    ./main
⚠️ 重要:Makefile 语法规则

目标: 依赖文件
<TAB>命令
  • 目标:要生成的文件(如 main.o
  • 依赖:生成目标需要的文件(如 main.c
  • 命令:如何生成目标(必须用 TAB 缩进,不能用空格)

编译过程

$ make
gcc -c main.c -o main.o
gcc -c add.c -o add.o
gcc main.o add.o -o main

$ make test
./main
sum:30

增量编译演示

# 第一次编译:编译所有文件
$ make
gcc -c main.c -o main.o
gcc -c add.c -o add.o
gcc main.o add.o -o main

# 修改 add.c 后再次编译:只重新编译 add.c
$ make
gcc -c add.c -o add.o        # 只编译 add.c
gcc main.o add.o -o main     # 重新链接

# 如果没有修改任何文件
$ make
make: 'main' is up to date.  # 不做任何事

Makefile 的优势

改进

  • 增量编译:只重新编译修改过的文件
  • 依赖管理:自动检测文件变化
  • 命令统一:团队成员使用相同的编译命令
  • 自动化:一个 make 命令完成所有操作

仍然存在的问题

⚠️ 警告

  • 手动维护依赖:每个 .c 文件都要手写规则
  • 不跨平台:Linux 用 Makefile,Windows 用 Visual Studio 项目文件
  • 难以扩展:文件多了 Makefile 会变得很复杂
  • 重复劳动:每个项目都要写类似的 Makefile

如果项目有 100 个 .c 文件,你需要写 100 条类似的规则,非常繁琐。


第三阶段:CMake

项目结构:

cmakeS/
├── main.c
├── add.c
├── add.h
├── CMakeLists.txt  # 新增
└── build/          # 构建目录

CMakeLists.txt 内容

CMakeLists.txt:

# 指定 CMake 最低版本
cmake_minimum_required(VERSION 3.10)

# 项目名称和语言
project(study_cmake C)

# 创建可执行文件
add_executable(main main.c add.c)
⚠️ 重要:CMake 规定语言标识符必须大写:

  • C 语言 → C
  • C++ 语言 → CXX
  • Fortran 语言 → Fortran

编译流程

# 1. 创建构建目录
mkdir build && cd build

# 2. 生成 Makefile
cmake ..

# 3. 编译(两种方式都可以)
make                # 方式1:直接用 make
cmake --build .     # 方式2:用 cmake 命令(推荐,跨平台)

# 4. 运行
./main

输出示例

$ cmake ..
-- Configuring done (1.2s)
-- Generating done (0.0s)
-- Build files have been written to: /root/cmakeS/build

$ make
[ 33%] Building C object CMakeFiles/main.dir/main.c.o
[ 66%] Building C object CMakeFiles/main.dir/add.c.o
[100%] Linking C executable main
[100%] Built target main

$ ./main
sum:30

CMake 的优势

改进

  • 自动生成构建文件:CMake 自动生成 Makefile(或其他构建系统)
  • 跨平台:同一个 CMakeLists.txt 在 Linux/Windows/macOS 都能用
  • 简洁:3 行代码替代几十行 Makefile
  • 自动依赖:CMake 自动处理头文件依赖
  • 易于扩展:添加新文件只需修改一行
  • 现代化:支持库管理、第三方依赖、测试等高级功能

三个阶段对比

特性 GCC 命令行 Makefile CMake
编译命令 手动输入完整命令 make cmake .. && make
增量编译 不支持 支持 支持
依赖管理 手动 半自动 全自动
跨平台 不支持 不支持 支持
代码量 一行命令 10+ 行 3 行
维护成本
适用场景 单文件测试 小型项目 任何规模项目

为什么需要 CMake

跨平台编译

项目需要在 Linux、Windows、macOS 上编译。

传统方式: Linux 写 Makefile,Windows 创建 VS 项目,macOS 写 Xcode 项目

CMake 方式: 只写一个 CMakeLists.txt,自动生成对应平台的构建文件

复杂项目管理

项目有 100 个源文件。

Makefile 方式: 需要手写 100 条规则

CMake 方式:

add_executable(main file1.c file2.c ... file100.c)

第三方库集成

项目需要使用 OpenCV 等第三方库。

Makefile 方式:

CFLAGS = -I/usr/include/opencv4
LDFLAGS = -L/usr/lib -lopencv_core ...

CMake 方式:

find_package(OpenCV REQUIRED)
target_link_libraries(main ${OpenCV_LIBS})

CMake 工作流程

CMake 工作流程
CMake 工作流程

CMakeLists.txt → cmake .. → Makefile → cmake --build . → 可执行文件
  1. 编写 CMakeLists.txt:描述项目结构、指定源文件
  2. 运行 cmake:检测编译器、查找依赖库、生成 Makefile
  3. 运行 cmake --build:编译代码、生成可执行文件(或直接用 make

Out-of-Source Build

使用独立的构建目录,保持源代码目录干净。

推荐结构:

cmakeS/
├── CMakeLists.txt
├── main.c
├── add.c
├── add.h
└── build/            # 所有编译产物都在这里
    ├── Makefile
    └── main

操作:

mkdir build && cd build
cmake ..
make

# 清理:删除 build 目录即可
cd .. && rm -rf build

常用命令

GCC

gcc -o main main.c add.c

Makefile

make           # 编译
make clean     # 清理

CMake

# 标准流程
mkdir build && cd build
cmake ..
cmake --build .     # 推荐,跨平台
# 或
make                # Linux/macOS 可用

# 一行命令
cmake -B build && cmake --build build

# 清理
rm -rf build

总结

演进路径:

GCC 命令行 → Makefile → CMake
手动编译   → 半自动   → 全自动

CMake 核心价值:

  1. 跨平台:一次编写,到处编译
  2. 自动化:自动管理依赖和编译
  3. 简洁:3 行代码替代几十行 Makefile

基本流程:

mkdir build && cd build
cmake ..
cmake --build .     # 或 make
./main

最后更新:2026-05-26