Makefile
makefile: 用于管理项目。
文件命名:只能是 makefile 或者 Makefile
配置完成后直接使用 make 命令执行
1 个规则:
目标:依赖条件
(一个tab缩进)命令
要求: 目标的时间必须晚于依赖条件的时间,否则,更新目标 依赖条件如果不存在,找寻新的规则去产生依赖条件。
ALL:指定 makefile 的终极目标。(因为makefile默认第一个目标文件为终极目标, 防止将中间文件放在第一个造成执行中断)
2 个函数:
src = $(wildcard ./*.c): 匹配当前工作目录下的所有.c 文件。将文件名组成列表,赋值给变量 src (例如src = add.c sub.c div1.c)
obj = $(patsubst %.c, %.o, $(src)): 将参数3($(src))中,包含参数1(%.c)的部分,替换为参数2(%.o)。 obj = add.o sub.o div1.o
在脚本语言中对一个对象使用$表示取其值
clean: (没有依赖)
-rm -rf $(obj) a.out
上述脚本可以实现调用make clean即可清除.o文件
“-”:作用是,删除不存在文件时,不报错。顺序执行结束。
3 个自动变量:
$@: 在规则的命令中,表示规则中的目标。
$^: 在规则的命令中,表示所有依赖条件。
$<: 在规则的命令中,表示第一个依赖条件。如果将该变量应用在模式规则中,它可将依赖条件列表中的依赖依次取出,套用模式规则。
也就是上面三者用在命令中, 代指命令上一行的目标和依赖
模式规则:
%.o:%.c
gcc -c $< -o %@
含义是对于所有目标为.o文件的对象, 根据.c文件来执行gcc -c (%.c) -o (%.o)
静态模式规则:
$(obj):%.o:%.c
gcc -c $< -o %@
只对obj所指的对象执行这条命令
伪目标:
.PHONY: clean ALL
防止文件中出现同名clean和ALL的文件阻碍make
参数:
-n:模拟执行make、make clean 命令。
-f:指定文件执行 make 命令。
makefile实现增量执行, 其检测原理: 修改文件后,文件的修改时间发生变化,会出现目标文件的时间早于作为依赖材料的时间,出现这种情况的文件会重新编译。 修改sub.c后,sub.o的时间就早于sub.c ,a.out的时间也早于sub.o的时间了,于是重新编译这俩文件了。
还有一点, 编译时的参数,-g,-Wall这些,也可以放在makefile里面. 一份最终的makefile示例如下:
1 | src = $(wildcard *.c) # 搜索当前目录下所有 .c 文件 |
export
export 是 GNU Make 中一个非常重要的指令,它的核心作用是控制变量如何从一个 Makefile 传递到它的子 Make 进程中。
为什么需要 export?
在大型项目中,我们通常会将代码和 Makefile 分散在不同的子目录中。顶层的 Makefile 负责调用子目录中的 Makefile 来完成局部的编译任务。这种通过一个 make 进程调用另一个 make 进程的方式,被称为递归 Make (Recursive Make) 或子 Make (Sub-make)。
这时就出现了一个问题:默认情况下,父 Makefile 中定义的变量,在子 Make 进程中是不可见的。export 指令就是为了解决这个问题而存在的。
export 的作用与 Shell 脚本中的 export 命令非常相似。它将一个 Make 变量“导出”为一个环境变量,这个环境变量对 make 启动的所有子进程(包括 Shell 命令和子 Make 进程)都是可见的。
当顶层 make 调用子目录的 make 时, make 会为这个子 make 命令创建一个新的进程。在创建这个子进程之前,make 会将所有通过 export 指令导出的变量设置到该子进程的环境中。
子 make 进程启动后,会从它的环境中读取这些变量,并将它们当作自己在 Makefile 中定义的变量来使用。
简而言之:export 是父 Makefile 向子 Makefile 传递变量的桥梁。
export 的使用示例
假设我们有一个简单的项目结构如下:
1 | 项目结构: |
不使用 export 的顶层 Makefile 可能如下所示:
1 | # 顶层 Makefile |
子 Makefile 可能如下所示:
1 | # 子 Makefile |
在这个例子中,顶层 Makefile 定义了编译器 (GCC)和编译选项 (CFLAGS),但是子 Makefile 并不知道这些变量,因为它们没有被传递过去。
如果我们想让子 Makefile 使用顶层 Makefile 中定义的变量,我们可以使用 export 指令:
1 | # 顶层 Makefile |
现在,当顶层 Makefile 调用子 Makefile 时,子 Makefile 可以访问 CC 和 CFLAGS 变量,并使用它们来编译代码。
不同语法形式
- 先定义,后导出: 这种方式最清晰,易于阅读和维护。
1
2VAR = value
export VAR - 定义时直接导出:
1
export VAR = value
- 全部导出: 如果 export 后面不跟任何变量名,它会导出当前 Makefile 中定义的所有变量。这通常被认为是不良实践,因为它可能会意外地将一些不希望传递的内部变量传递给子 Make,导致难以调试的错误。明确地导出需要的变量是更好的选择。
1
export
与 export 相对的是 unexport,它用于取消一个变量的导出。
如果一个变量是通过环境传入 make 的(例如,你在 shell 中 export CFLAGS=“-O3”),make 默认会把它再次 export 给子 Make。如果你不希望某个子 Make 继承这个环境变量,就可以使用 unexport。