Makefile 入门:从基础到实用技巧
一、为什么需要Makefile?
简单说,Makefile是「编译规则说明书」。一个工程可能有几十上百个源文件,按功能放在不同目录里。Makefile定义了:
- 哪些文件先编译,哪些后编译
- 哪些文件修改后需要重新编译
- 甚至可以执行打包、备份等额外操作
有了Makefile,只需敲一个make
命令,整个工程就会自动编译,极大提高开发效率。
二、先搞懂:编译和链接
在讲Makefile之前,先明确两个基本概念:
- 编译(compile):把源代码(.c/.cpp)变成中间目标文件(Unix下是.o,Windows下是.obj)。编译器只检查语法和函数/变量是否声明。
- 链接(link):把一堆中间目标文件拼成可执行文件。链接器找函数的实现,找不到就会报错(比如VC里的Link 2001)。
举个例子:main.c
编译成main.o
,tool.c
编译成tool.o
,最后链接成app
可执行文件。
三、Makefile 基本规则
Makefile的核心是「依赖关系」和「执行命令」,格式如下:
1 | 目标(target)... : 依赖(prerequisites)... |
- 目标(target):可以是可执行文件、中间目标文件(.o),甚至是一个动作(如clean)。
- 依赖(prerequisites):生成目标需要的文件或其他目标。
- 命令(command):生成目标的具体操作(必须以Tab键开头)。
规则逻辑:如果「依赖文件」比「目标文件」新(或目标不存在),就执行命令生成目标。
四、一个简单示例
假设工程有8个.c文件和3个头文件,要编译成可执行文件edit
。一个基础的Makefile如下:
1 | # 最终目标:edit(依赖所有.o文件) |
用法:
- 敲
make
:自动编译所有需要更新的文件,生成edit
。 - 敲
make clean
:删除edit
和所有.o文件,方便重新编译。
五、简化Makefile的技巧
上面的示例有很多重复代码(比如一堆.o文件名),可以用「变量」和「自动推导」简化。
5.1 用变量减少重复
把重复出现的内容定义成变量,比如所有.o文件:
1 | # 定义变量objects,包含所有.o文件 |
以后新增.o文件,只需改objects
变量即可。
5.2 自动推导(隐含规则)
GNU make很智能:看到.o
文件,会自动找对应的.c
文件作为依赖,并且自动生成编译命令(cc -c 源文件
)。
简化后的Makefile:
1 | objects = main.o kbd.o command.o display.o insert.o search.o files.o utils.o |
是不是简洁多了?
六、实用技巧
6.1 伪目标(.PHONY)
像clean
这种「动作型目标」,不是真实文件,最好用.PHONY
声明,避免和目录中同名文件冲突:
1 | .PHONY : clean # 声明clean是伪目标 |
6.2 自动生成依赖
大型工程中,手动写.o
依赖的头文件很麻烦。可以用编译器的-MM
参数自动生成依赖:
1 | # 自动生成每个.c的依赖文件(.d) |
这样头文件修改后,Makefile会自动识别需要重新编译的文件。
6.3 嵌套执行make
大型工程可以按模块分目录,每个目录放一个Makefile,总控Makefile调用子目录的Makefile:
1 | # 编译子目录subdir |
七、常用函数
Makefile有一些实用函数,帮你处理字符串、文件名等:
字符串替换:
$(subst 旧字符串,新字符串,原字符串)
例:$(subst .o,.c,main.o)
→ 结果是main.c
取目录:
$(dir 文件名)
例:$(dir src/main.c)
→ 结果是src/
过滤文件:
$(filter 模式,文件列表)
例:$(filter %.c,main.c tool.o test.c)
→ 结果是main.c test.c