什么是 make/makefile
在之前的开发过程中,我们通常对单个文件进行编译链接(如使用 gcc)。但如果一个项目中源代码文件非常多,逐个编译效率极低。因此,学习 make/makefile 实现自动化构建显得尤为重要。
make 是一个命令工具,用于解释执行 makefile 中的指令;makefile 则是一种工程编译方法。二者搭配使用可完成项目的自动化构建,显著提升软件开发效率。
在一个工程中,源代码文件按类型、功能、模块分布在若干目录下。makefile 定义一系列规则来指定哪些文件需要先编译、哪些需要后编译、哪些需要重新编译,甚至执行更复杂的操作。
makefile 基本使用
1. 依赖关系和依赖方法
makefile 的核心在于理解依赖关系和依赖方法。
- 依赖关系:表明目标文件和依赖文件之间的关系。
- 依赖方法:表明如何由依赖文件列表生成目标文件。
例如,code 作为目标文件,code.c 作为依赖文件,通过依赖关系 gcc code.c -o code 来生成目标文件。
code: code.c
gcc code.c -o code
.PHONY: clean
clean:
rm -f code
2. 伪目标 .PHONY
如果连续多次执行 make,第二次不会执行,因为目标文件已存在且时间未变。但 make clean 可以连续执行多次而不报错。
这是因为 .PHONY 修饰了伪目标。伪目标总是被执行的,不依赖于任何实际文件。像 clean 这种清理目标,通常设置为伪目标。
伪目标
clean不依赖于任何文件,执行make clean会执行依赖方法rm -f code,虽然它没有生成clean文件,但完成了清理工作。
3. ACM 时间判断机制
make 根据文件的修改时间来判断是否需要重新编译。主要涉及三个时间戳:
- Access (A):文件最近访问时间。
- Modify (M):文件内容最近修改时间。
- Change (C):文件属性最近修改时间。
make 比较的是 Modify (M) 时间。只有当依赖文件的 Modify 时间晚于目标文件的 Modify 时间时,才会触发重新编译。
.PHONY 之所以能一直被执行,是因为它忽视了对比时间。
4. 推导过程
如果依赖文件不存在于当前目录,而是依赖于其他中间文件(如 .o, .s, .i),make 支持自动推导过程。
code: code.o
gcc code.o -o code
code.o: code.s
gcc -c code.s -o code.o
code.s: code.i
gcc -S code.i -o code.s
gcc -E code.c -o code.i
rm -f *.i *.s *.o code


