1. 课程导入
各位同学,欢迎来到《Makefile 系统实战》的第一章。
我先问大家一个问题:你平时是怎么编译代码的?
我猜,八成是直接在终端里敲 gcc main.c tool.c -o app。项目小的时候,这没什么问题。但一旦文件多起来——几十个、上百个源文件——每次改一行代码都要重新敲一遍命令,或者按一下方向键上翻历史,是不是有点烦?
更头疼的是,你只改了 tool.c,却要把所有文件重新编译一遍。一个项目编译一次要 5 分钟,一天改 20 次,那就是 100 分钟白白浪费掉了。
嗯,这就是我们为什么要学 Makefile 的原因。
1.1 什么是 Makefile?
说白了,Makefile 就是一个文本文件。它告诉计算机:你要构建什么、用什么工具构建、以及哪些文件需要重新构建。
你可以把它理解成一张“施工图纸”。图纸上画清楚了:
- 最终要造出什么(目标)
- 需要哪些原材料(依赖)
- 怎么造(命令)
Make 工具就是那个“包工头”。它拿着图纸,按步骤干活。而且它很聪明——只有原材料变了,它才重新造那一部分。
1.2 为什么需要 Make?
你可能会想:“我手动编译也挺快的啊,有必要学这个吗?”
我跟你讲一个我踩过的坑。几年前我做嵌入式开发,一个项目有 200 多个 .c 文件。我那时候刚入行,不懂 Makefile,每次改完代码就执行一个 shell 脚本,把全部文件重新编译一遍。编译一次要 8 分钟。
有一天我改了一个头文件里的宏定义,然后去泡了杯咖啡。回来发现编译还在跑。那天我改了 6 次,浪费了将近 1 个小时在等编译。
后来我花了一个下午写了个 Makefile,从那以后,增量编译只需要十几秒。你想想看,这效率差距有多大。
Make 的核心价值就三点:
| 价值 | 说明 |
|---|---|
| 自动化 | 一条 make 命令搞定所有编译、链接、清理工作 |
| 增量编译 | 只重新编译修改过的文件,节省大量时间 |
| 可重复 | 换一台机器,只要 Makefile 在,构建结果一致 |
1.3 Makefile 的核心思想:目标、依赖、命令
Makefile 的规则其实就一句话:
目标: 依赖1 依赖2 ...
命令1
命令2
...
这三个要素,缺一不可。我画了一张图,帮你理解它们的关系:
我来解释一下这张图:
- 目标:就是你要生成的东西。通常是一个可执行文件、一个 .o 文件,或者一个伪目标(比如
clean)。 - 依赖:构建目标所需要的文件。如果依赖文件比目标文件新,Make 就知道“哦,需要重新编译了”。
- 命令:如何从依赖生成目标。注意,命令前面必须是一个 Tab 键,不是空格。这是新手最容易犯的错误。
1.4 第一个 Makefile 示例
光说不练假把式。我们直接上手写一个。
假设你有两个源文件:
// main.c
#include <stdio.h>
void hello();
int main() {
hello();
return 0;
}
// hello.c
#include <stdio.h>
void hello() {
printf("Hello, Makefile!\n");
}
传统编译方式:
gcc -c main.c -o main.o
gcc -c hello.c -o hello.o
gcc main.o hello.o -o app
现在,我们写一个 Makefile:
app: main.o hello.o
gcc main.o hello.o -o app
main.o: main.c
gcc -c main.c -o main.o
hello.o: hello.c
gcc -c hello.c -o hello.o
clean:
rm -f app main.o hello.o
把这个文件保存为 Makefile(注意大小写,首字母大写 M),然后执行:
$ make
gcc -c main.c -o main.o
gcc -c hello.c -o hello.o
gcc main.o hello.o -o app
看到没?一条 make 命令,自动完成了三步操作。
如果你再执行一次 make:
$ make
make: 'app' is up to date.
因为所有文件都没变,Make 直接告诉你“已经是最新的了”。
如果你修改了 hello.c,再执行 make:
$ make
gcc -c hello.c -o hello.o
gcc main.o hello.o -o app
它只重新编译了 hello.c,然后重新链接。main.o 没变,所以跳过了。这就是增量编译的魅力。
- Makefile 由一条条规则组成
- 每条规则包含:目标、依赖、命令
- 命令前必须是 Tab,不是空格
- Make 通过比较文件时间戳决定是否重新编译
1.5 本章小结
这一章我们搞清楚了:
- Makefile 是“施工图纸”,Make 是“包工头”
- 核心三要素:目标、依赖、命令
- 增量编译是 Make 最大的价值
- 第一个 Makefile 示例,你已经能用了
说实话,这个例子虽然简单,但它包含了 Makefile 的全部核心思想。后面 29 章,我们都是在给这个骨架添加血肉——变量、函数、条件判断、自动推导、多目录管理……
但万变不离其宗。只要你把“目标、依赖、命令”这三个概念刻在脑子里,后面的内容学起来会非常顺畅。