添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

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