4. Makefile初体验
说实话,我第一次接触Makefile的时候,心里是有点发怵的。
那时候我刚从单文件编译的舒适区走出来,项目里文件一多,每次编译都要敲一长串命令。什么 gcc -c file1.c、gcc -c file2.c,最后再链接……烦得不行。直到有一天,一位老同事丢给我一个Makefile,说:“你以后就敲一个 make 就行了。”
我试了一下,真的就一行命令,全搞定了。那一刻我就知道,这玩意儿我必须学会。
4.1 什么是Makefile?
Makefile 说白了就是一个“自动化编译脚本”。它告诉 make 这个工具:你要编译什么、怎么编译、先编译谁、后编译谁。
你想想看,如果你有10个源文件,每次改一个文件都要重新编译所有文件,那得多浪费时间?Makefile 的厉害之处就在于——它只重新编译你改过的文件。
嗯,这里要注意:Makefile 不是编程语言,但它有自己的语法。不过别怕,我们从一个最简单的例子开始。
4.2 三要素:目标、依赖、命令
一个Makefile规则,本质上就三样东西:
- 目标(target):你要生成什么文件?比如
hello这个可执行文件。 - 依赖(prerequisites):生成目标需要哪些文件?比如
hello.c。 - 命令(recipe):怎么生成目标?比如
gcc hello.c -o hello。
它们的格式长这样:
目标: 依赖
命令
注意!命令前面必须是一个 Tab 键,不能是空格。我当年就因为这个Tab键的问题,卡了整整一下午。你想想看,一个缩进不对,整个Makefile就不认了,气不气人?
4.3 第一个Makefile:编译单个文件
假设你有一个 hello.c:
#include <stdio.h>
int main() {
printf("Hello, Makefile!\n");
return 0;
}
那么最简单的Makefile就是:
hello: hello.c
gcc hello.c -o hello
保存为 Makefile(注意大小写,M要大写),然后在终端里敲:
$ make
你就会看到:
gcc hello.c -o hello
搞定。就这么简单。
Makefile(首字母大写),这样在 ls 的时候它会排在最前面,方便找。当然 makefile 小写也行,但团队里统一最好。
4.4 目标、依赖、命令的底层逻辑
为什么会这样设计?其实背后的逻辑很朴素:
- 目标:就是你要产出的东西。可以是可执行文件,也可以是中间文件(比如
.o文件)。 - 依赖:就是“原材料”。如果原材料比目标新,那就需要重新生成目标。
- 命令:就是“加工方法”。告诉make怎么把原材料变成目标。
我举个例子你就明白了:
假设你有一个 hello.o 文件,它依赖于 hello.c。如果你修改了 hello.c,那么 hello.o 就“过时”了,make 就会重新执行命令来生成新的 hello.o。
这个“比较时间戳”的机制,就是Makefile最核心的智慧。我在项目中遇到过好几次,有人手动删了 .o 文件然后问我为什么make不重新编译……其实只要 touch 一下源文件就行了。
4.5 多依赖的情况
当然,实际项目里不会只有一个源文件。我们来看一个稍微复杂点的例子:
main: main.o utils.o
gcc main.o utils.o -o main
main.o: main.c
gcc -c main.c -o main.o
utils.o: utils.c utils.h
gcc -c utils.c -o utils.o
这里 main 依赖两个 .o 文件,而每个 .o 文件又依赖各自的源文件。make 会自动分析这个依赖链,按顺序编译。
4.6 一个常见的坑:命令不回显
默认情况下,make 会打印出它执行的命令。如果你不想看到命令,可以在命令前面加一个 @:
hello: hello.c
@gcc hello.c -o hello
这样执行 make 的时候,就不会显示 gcc hello.c -o hello 这一行了。我个人习惯在清理命令(比如 rm)前面加 @,因为用户不需要看到删除过程。
@echo "编译完成",结果因为 echo 前面多了一个空格(不是Tab),make 直接报错。记住:命令行的缩进必须是Tab,但 @ 符号紧跟在Tab后面,中间不要有空格。
4.7 知识体系图
下面这张图,帮你理清Makefile三要素的关系:
4.8 小结
这一章我们只做了一件事:写一个最简单的Makefile,理解它的三要素。
- 目标:你要什么?
- 依赖:你需要什么?
- 命令:你怎么做?
记住这个“三问”,你就掌握了Makefile的核心。后面的章节,我们会在这个基础上不断加料——变量、模式规则、函数……但万变不离其宗。
嗯,今天就到这里。去试试写你的第一个Makefile吧,哪怕只是编译一个 hello.c,那种“敲一个make就搞定”的感觉,真的很爽。