Makefile 中的自动变量

Makefile 中的变量

在 Makefile 中定义的变量,就像是C/C++语言中的宏一样,他代表了一个文本字串,在
Makefile 中执行的时候其会自动原模原样地展开在所使用的地方。与C/C++所不同的是,你
可以在 Makefile 中改变其值。在 Makefile 中,变量可以在“目标”,“依赖目标”,
“命令”或是 Makefile 的其它部分中使用。

可以把每个 recipe 当作字符串理解,通过各种方法组成一个字符串,然后交给 shell 执行。

注意:文中的“命令”一词表示以上语法中的 recipe。

本文主要说自动变量,如有疏漏,还请大方指教。(可是现在没有评论,怎么指教呢?)

规则语法:

1
2
3
targets : prerequisites
recipe

或者是:

1
2
3
targets : prerequisites ; recipe
recipe

静态模式规则 (Static Pattern Rules):

1
2
3
targets …: target-pattern: prereq-patterns …
recipe

可以使用模式规则来定义一个隐含规则。一个模式规则就好像一个一般的规则,只是在规则
中,目标的定义需要有”%”字符。”%”的意思是表示一个或多个任意字符。在依赖目标中同样
可以使用”%”,只是依赖目标中的”%”的取值,取决于其目标。

有一点需要注意的是,”%”的展开发生在变量和函数的展开之后,变量和函数的展开发生在
make载入Makefile时,而模式规则中的”%”则发生在运行时。

我也不知道为什么叫静态模式,也许还有一个动态的吧。

模式规则中,至少要在规则的目标定义中包含”%”,否则,就是一般的规则。目标中的”%”定
义表示对文件名的匹配,”%”表示长度任意的非空字符串。

$@

先看官方文档里的说明:

The file name of the target of the rule. If the target is an archive member,
then ‘$@’ is the name of the archive file. In a pattern rule that has multiple
targets, ‘$@’ is the name of whichever target caused the rule’s recipe to be
run.

所以,$@表示一条规则中目标的名字。

看一个例子:

1
2
3
4
5
output:
@echo "[CC] $@"

bigoutput littleoutput :
@echo "[CC] $@"

执行结果为:

1
2
3
4
5
6
$ make output
[CC] output
$ make bigoutput
[CC] bigoutput
$ make littleoutput
[CC] littleoutput

可以看出$@表示的就是目标名字,而且目标是单独列出还是和其他一起列出也不影响。

另一个例子:

1
2
3
4
5
6
objects = foo.o bar.o

all: $(objects)

$(objects):
@echo "[CC] $@"

执行结果为:

1
2
3
$ make all
[CC] foo.o
[CC] bar.o

可以看到在目标是一个变量表示的集合时,$@的值仍为规则展开后的对应的目标名字。

但文档里说的并不是规则名字,而是 the file name of the target of the rule。但第一
个例子中output 也并非是一个文件名,所以此处有些疑问。

$<

The name of the first prerequisite. If the target got its recipe from an
implicit rule, this will be the first prerequisite added by the implicit rule.

变量 $< 表示依赖文件(就是 target 冒号后面的文件集合)中的第一个。先看一个简单的例子:

1
2
3
4
5
noinput:
@echo "[CC] $<"

output: empty.c main.c
@echo "[CC] $<"

输出为:

1
2
3
4
5
$ touch empty.c main.c
$ make noinput
[CC]
$ make output
[CC] empty.c

如果把 empty.c t main.c 调换位置的话:

1
2
output: main.c empty.c
@echo "[CC] $<"

这时的输出结果为:

1
2
$ make output
[CC] main.c

再复杂一点的话:

1
2
3
4
5
6
objects = foo.o bar.o

all: $(objects)

$(objects): %.o: %.h %.c
@echo "[CC] $<"

输出为:

1
2
3
$ make all
[CC] foo.h
[CC] bar.h

从例子中确实可以看出,$<表示的是第一个依赖的名字。

$^ 和 $+

$^ 表示所有的依赖的集合。以空格分隔。如果在依赖中有多个重复的,那个这个变量会
去除重复的依赖,只保留一份。

$+$^ 十分类似,只是它不去除重复的依赖。

还是看一个简单的例子:

1
2
3
4
5
noduplicate: main.c empty.c main.c
@echo "[CC] $^"

duplicate: main.c empty.c main.c
@echo "[CC] $+"

执行一下:

1
2
3
4
$ make noduplicate
[CC] main.c empty.c
$ make duplicate
[CC] main.c empty.c main.c

注意其中重复的文件 main.c 的出现次数。

$%

变量$%是专门用来处理库文件的自动变量,文档中的解释是:

The target member name, when the target is an archive member. See Archives.
For example, if the target is foo.a(bar.o) then ‘$%’ is bar.o and ‘$@’ is
foo.a. ‘$%’ is empty when the target is not an archive member.

因为库文件的打包方式几乎只有一种,似乎 Makefile 为它单独做了处理,有特别的语法。例子:

1
2
3
4
5
libfoo.a(foo.o): foo.o
@echo "[AR] $% : $@"

lib.a: libfoo.a(foo.o)
@echo "[AR] $% : $@"
1
2
3
$ make lib.a
[AR] foo.o : libfoo.a
[AR] : lib.a

可以看出$%$@的不同,其中 lib.a: libfoo.a(foo.o) 不是 archive(member)
的形式,即文档中说的“not an archive member”,所以$%的值为空。

$*

变量$*表示%匹配到的字符串。例如:

1
2
%:
gcc -o $* $*.c

如果在 shell 里执行 $ make test,make 命令就会执行 gcc -o test test.c

再例如:

1
2
bigoutput littleoutput : %output : test.c
@echo [echo] $*

这里的$*会被展开为 ‘big’ 或者 ‘little’

1
2
3
4
5
$ touch test.c
$ make bigoutput
[echo] big
$ make littleoutput
[echo] little

tip: echo 命令前面的@表示不回显命令, 如果不理解可以尝试删除它。

以下是 linux kernel 中的一个例子:

1
2
3
4
5
6
7
8
.c.s:
$(CC) $(CFLAGS) \
-S -o $*.s $<
.s.o:
$(AS) -c -o $*.o $<
.c.o:
$(CC) $(CFLAGS) \
-c -o $*.o $<

其中的.c.s:等可以理解为%.s: %.c

References

GNU make manual
跟我一起写 Makefile(五)
跟我一起写 Makefile(十三)
Advanced Makefile Tricks

0%