【操作系统】Makefile
make 是一个根据指定的 Shell 命令进行构建的工具——你规定要构建哪个文件、它依赖哪些源文件,当那些文件有变动时,如何重新构建它。
Makefile 格式
<target> : <prerequisites>
[tab] <commands>
目标
- 目标可以是一个文件名,也可以是多个文件名,之间用空格分隔;除了文件名,目标还可以是某个操作的名字。
- 如果 make 命令运行时没有指定目标,默认会执行 Makefile 文件的第一个目标。
- 伪目标
.PHONY:声明某个“伪目标”后,make 就不会去检查是否存在一个叫做“伪目标”的文件,也不会检查其依赖的修改时间,每次运行都执行对应的命令。
依赖
- 前置文件(依赖)通常是一组文件名,之间用空格分隔。
- 指定了"目标"是否重新构建的判断标准:只要有一个前置文件不存在,或者有过更新(前置文件的 last modification 时间戳比目标的时间戳新),"目标"就需要重新构建。
- 依赖可以省略。
all: test.txt
echo hello
test.txt:
echo world > test.txt
若当前目录中没有 test.txt,在 Shell 中执行 make 将会先执行 echo world > test.txt,随后执行 echo hello。
规则
-
规则是构建"目标"的具体指令。
-
每行命令在一个单独的 Shell 中执行,这些 Shell 之间没有继承关系;若想让命令间有继承关系:
-
将两行命令写在一行,中间用分号分隔;
-
或在换行符前加反斜杠转义;
all: mkdir a; cd a; \ touch b; -
或使用
.ONESHELL:命令,Mac 自带 Make 3.81 不支持.ONESHELL:,需要:brew instal make。open ~/zshrc。- 在
export PATH中添加/usr/local/bin和/usr/local/opt/make/libexec/gnubin,使用:分隔,如:export PATH=/usr/local/bin:/usr/local/opt/make/libexec/gnubin:$PATH。
-
-
每行命令之前必须有一个
[tab]键。如果想用其他键,可以用内置变量.RECIPEPREFIX声明。
.RECIPEPREFIX=>
.ONESHELL:
all:
>mkdir a
>cd a
>touch b
Makefile 语法
回声
- 正常情况下,make 会打印每条命令,然后再执行,这就叫做回声(echoing)。
- 在命令的前面加上
@,就可以关闭回声。
通配符
- 用来指定一组符合条件的文件名。
- Makefile 的通配符与 Bash 一致,主要有星号
*、问号?和[...]。- 通配符
?:匹配一个任意字符。 - 通配符
*: 匹配0个或任意多个字符,也就是可以匹配任何内容。- 例:对于文件 foo, foo1, foo2, foo10, bar:
rm foo?删除 foo1 和 foo2。rm foo*删除除bar外的所有文件。
- 通配符
[ ]:匹配中括号中任意一个字符。[-]代表范围,[^]代表逻辑非。- 例:
[abc]匹配a或b或c;[a-z]匹配所有小写字母;[^0-9]匹配一个不是数字的字符。
- 例:
- 通配符
模式匹配
- make 命令允许对文件名,进行类似正则运算的匹配,主要用到的匹配符是
%。 - 使用匹配符
%,可以将大量同类型的文件,只用一条规则就完成构建。 - 咕咕咕~
变量和赋值
赋值:
-
VAR=value:在执行时(引用时)扩展,允许递归扩展。varA = hi varB = $(varA) varA = hello all: @echo $(varB) # 输出 hello -
VAR:=value:在定义时(赋值时)扩展。varA := hi varB := $(varA) varA := hello all: @echo $(varB) # 输出 hi -
VAR?=value:只有在该变量为空时才设置值。 -
VAR+=value:将值追加到变量的尾端。
调用:
- 变量需要放在
$( )之中。 - 调用 Shell 变量,需要在美元符号前,再加一个美元符号,如
$$HOME。
内置变量
$(CC)指向当前使用的编译器。$(MAKE)指向当前使用的 make 工具。
自动变量
$@:指代当前“目标”。$<:指代第一个“依赖”。$?:指代比“目标”新的所有“依赖”。$^:指代所有“依赖”。$*:指代匹配符%匹配的部分。$(@D):指向$@的目录名。$(@F):指向$@的文件名。$(<D):指向$<的目录名。$(<F):指向$<的文件名。
判断和循环
判断:
- 使用
ifeq、else和endif,没有缩进。
all:
ifeq ($(CC),gcc)
@echo hi
else
@echo hello
endif
# 输出 hello
- 使用 Bash 语法,有缩进。
.ONESHELL:
all:
@if [ 1 == 2 ]
@then
@echo hi
@else
@echo hello
@fi
# 输出 hello
循环:
.ONESHELL:
LIST = one two three
all:
@for i in $(LIST)
@do
@echo $$i;
@done
函数
-
调用方法:
$(function arguments)或${function arguments}。 -
内置函数:咕咕咕~

浙公网安备 33010602011771号