Makefile简介
1.规则
每条规则由三部分组成分别是目标(target),依赖(depend),命令(command)
1 | # 语法格式: |
确定目标
当你运行 make 命令时,make 需要明确知道它的目标是什么。目标可以是:
默认目标:如果你没有指定目标,
make默认会尝试构建 Makefile 中第一个目标(也就是第一条规则定义的目标)。显式指定的目标:你可以通过命令行参数指定目标。例如:
1
make clean
这里,
clean就是显式指定的目标。
示例
假设你有一个简单的 C 程序,包含两个文件:main.c 和 utils.c。Makefile 可能如下:
1 | # 定义目标文件 |
递归解析
- 确定最终目标
你运行了 make all,make 首先确定最终目标是 all。
- 解析 all 的规则
make 查找 all 的规则:
1 | all: main |
这表示 all 依赖于 main。因此,make 需要先生成 main。
- 解析 main 的规则
make 接下来查找 main 的规则:
1 | main: main.o utils.o |
这表示 main 依赖于 main.o 和 utils.o。因此,make 需要先生成 main.o 和 utils.o。
- 递归解析 main.o 的规则
make 查找 main.o 的规则:
1 | main.o: main.c |
这表示 main.o 依赖于 main.c。make 会检查 main.c 是否存在:
如果
main.c存在,make会比较main.c和main.o的时间戳。如果
main.c比main.o新,或者main.o不存在,make会执行命令:1
gcc -c main.c
生成
main.o。
- 递归解析 utils.o 的规则
make 查找 utils.o 的规则:
1 | utils.o: utils.c |
这表示 utils.o 依赖于 utils.c。make 会检查 utils.c 是否存在:
如果
utils.c存在,make会比较utils.c和utils.o的时间戳。如果
utils.c比utils.o新,或者utils.o不存在,make会执行命令:1
gcc -c utils.c
生成
utils.o。
- 生成 main
一旦 main.o 和 utils.o 都被成功生成(或者确认是最新的),make 会回到 main 的规则:
1 | main: main.o utils.o |
make 会检查 main.o 和 utils.o 是否存在,并比较它们与 main 的时间戳:
如果
main.o或utils.o比main新,或者main不存在,make会执行命令:1
gcc -o main main.o utils.o
生成
main。
- 完成 all
最后,main 被成功生成后,make 认为 all 的依赖已经满足,整个构建过程完成。
2.变量
Makefile 支持变量的使用,可以简化规则的编写。例如:
1 | CC = gcc |
CC是编译器变量。CFLAGS是编译选项变量。
3.模式规则
模式规则是 Makefile 中非常强大的功能,它可以让你为一组文件定义通用的构建规则,而无需为每个文件单独编写规则。模式规则使用 % 作为通配符,% 可以匹配任意长度的字符串。
基本语法
1 | %.target: %.dependency |
- %.target:目标文件的模式,
%表示任意文件名。 - %.dependency:依赖文件的模式,
%表示与目标文件匹配的部分。 - command:执行的命令。
示例
假设你有多个 .c 文件需要编译成 .o 文件,可以使用模式规则:
1 | %.o: %.c |
- %.o:表示所有以
.o结尾的目标文件。 - %.c:表示所有以
.c结尾的依赖文件。 - $<:自动变量,表示依赖列表中的第一个依赖文件(这里是
.c文件)。 - $@:自动变量,表示目标文件(这里是
.o文件)。
工作原理
当你运行 make 时,make 会根据目标文件的名称自动推导出依赖文件的名称。例如:
如果目标是
main.o,make会查找main.c,并执行命令:1
gcc -c main.c -o main.o
如果目标是
utils.o,make会查找utils.c,并执行命令:1
gcc -c utils.c -o utils.o
4.伪目标
伪目标是 Makefile 中的特殊目标,它们不对应实际的文件。伪目标通常用于定义一些特殊的任务,如 clean 或 all。伪目标的作用是避免与同名文件冲突。
声明伪目标
1 |
- .PHONY:告诉
make,clean和all是伪目标,即使存在同名文件,make也会执行这些目标。
示例
1 |
|
- all:伪目标,用于构建最终的可执行文件
main。 - clean:伪目标,用于清理生成的文件。
工作原理
- 如果你运行
make all,make会构建main。 - 如果你运行
make clean,make会删除所有生成的文件。 - 即使存在名为
clean或all的文件,make也会正确执行伪目标的规则。
不定义 .PHONY 的潜在问题
假设你没有定义 .PHONY,make 会将所有目标(包括 all 和 clean)都视为可能的文件名。
make clean:
- 如果当前目录下没有
clean文件,make会执行clean目标,删除文件。 - 如果当前目录下存在一个名为
clean的文件,make会认为clean已经是最新的,不会执行任何命令。
5.条件判断
Makefile 支持条件判断,这可以在不同情况下动态调整变量或规则。条件判断的语法如下:
ifeq 和 ifneq
1 | ifeq (arg1, arg2) |
- ifeq:如果
arg1等于arg2,则执行commands。 - ifneq:如果
arg1不等于arg2,则执行commands。
条件判断可以嵌套,也可以使用 else 分支:
1 | ifeq ($(CC), gcc) |
- 如果
CC的值是gcc,则会添加编译选项-std=c99。
6.使用 Makefile
在命令行中运行 make 命令时,make 会读取当前目录下的 Makefile 文件,并根据规则执行任务。例如:
make:默认执行第一个目标。make clean:执行clean目标。make main:执行main目标。
