目录
- 简介
- 隐含规则的变量
- 模式规则
- 老式风格的后缀规则
简介
在我们使用Makefile时,有一些我们会经常使用,而且使用频率非常高的东西,就是在 Makefile 中的“隐含的”,早先约定了的,不需要我们再写出来的规则。
例如,把.c
文件编译成.o
文件这一规则,你根本就不用写出来,make 会自动推导出这种规则,并生成我们需要的.o
文件。
“隐含规则”会使用一些我们系统变量,我们可以改变这些系统变量的值来定制隐含规则的运行时的参数。如系统变量“CFLAGS”可以控制编译时的编译器参数。
如何使用
如果要使用隐含规则生成你需要的目标,你所需要做的就是不要写出这个目标的规则。那么make会试图去自动推导产生这个目标的规则和命令,如果 make 可以自动推导生成这个目标的规则和命令,那么这个行为就是隐含规则的自动推导。例如:
1
2foo : foo.o bar.o
cc –o foo foo.o bar.o $(CFLAGS) $(LDFLAGS)我们可以注意到,这个 Makefile 中并没有写下如何生成
foo.o
和bar.o
这两目标的规则和命令。因为 make 的“隐含规则”功能会自动为我们自动去推导这两个目标的依赖目标和生成命令。 make 会在自己的“隐含规则”库中寻找可以用的规则,如果找到,那么就会使用。如果找不到就会报错。在上面的那个例子中,make 调用的隐含规则是,把.o
的目标的依赖文件置成.c
,并使用 C 的编译命令cc –c $(CFLAGS) [.c]
来生成.o
的目标。也就是说,我们完全没有必要写下下面的两条规则:
1
2
3
4foo.o : foo.c
cc –c foo.c $(CFLAGS)
bar.o : bar.c
cc –c bar.c $(CFLAGS)因为,这已经是“约定”好了的事了,这就是隐含规则。 当然,如果我们为
.o
文件书写了自己的规则,那么 make 就不会自动推导并调用隐含规则,它会按照我们写好的规则忠实地执行。隐含规则有优先级
在 make 的“隐含规则库”中,每一条隐含规则都在库中有其顺序,越靠前的则是越被经常使用的,这会导致我们有些时候即使我们显示地指定了目标依赖,make也不会管。如下面这条规则(没有命令):
1
foo.o : foo.p
依赖文件“foo.p”(Pascal 程序的源文件)有可能变得没有意义。如果目录下存在了
foo.c
文件,那么我们的隐含规则一样会生效,并会通过foo.c
调用C的编译器生成foo.o
文件。因为,在隐含规则中,Pascal 的规则出现在 C 的规则之后,所以,make 找到可以生成foo.o
的 C的规则就不再寻找下一条规则了。如果你确实不希望任何隐含规则推导,那么,你就不要只写出“依赖规则”,而不写命令。(或者使用make的-r参数禁用所有隐含规则),即使是我们指定了“-r”参数,某些隐含规则还是会生效,因为有许多的隐含规则都是使用了“后缀规则”来定义的,所以,只要隐含规则中有“后缀列表”(也就一系统定义在目标.SUFFIXES的依赖目标 ),那么隐含规则就会生效。 默认的后缀列表是:.out,.a, .ln, .o, .c, .cc, .C, .p, .f, .F, .r, .y, .l, .s, .S, .mod, .sym,.def, .h, .info, .dvi, .tex, .texinfo, .texi, .txinfo, .w, .ch .web, .sh, .elc, .el。
常见隐含规则
规则名称 格式 内容 C程序的隐含规则 .o “ .o”的目标的依赖目标会自动推导为“ .c”,并且其生成命令是“$(CC) –c $(CPPFLAGS) $(CFLAGS)” 其余的语言的用不到,此处不表
隐含规则的变量
在隐含规则中的命令中,基本上都是使用了一些预先设置的变量。你可以在你的 makefile 中改变这些变量的值,或是在 make 的命令行中传入这些值,或是在你的环境变量中设置这些值,无论怎么样,只要设置了这些特定的变量,那么其就会对隐含规则起作用。当然,你也可以利用 make 的-R
或--no–builtin-variables
参数来取消你所定义的变量对隐含规则的作用。
下面列出一些常用变量和其对应的参数,即这些变量在makefile中都是预先设定好的
变量 | 含义 | 默认值 | 对应参数 | 默认值 |
---|---|---|---|---|
AR | 函数库打包程序(.a静态库) | ar | ARFLAGS | rv |
AS | 汇编语言编译程序 | as | ASFLAGS | 空 |
CC | C 语言编译程序 | cc | CFLAGS | 空 |
CXX | C++语言编译程序 | g++ | CXXFLAGS | 空 |
CPP | C 程序的预处理器(输出是标准输出设备) | $(CC) –E | C 预处理器参数 | 空 |
YACC | Yacc 文法分析器(针对于 C 程序) | yacc | YFLAGS | 空 |
RM | 删除文件命令 | rm –f | 无 | 无 |
LDFLAGS(链接器参数) | 空 |
模式规则
介绍
你可以使用模式规则来定义一个隐含规则。一个模式规则就好像一个一般的规则,只是在规则中,目标的定义需要有%
字符,它的意思是表示一个或多个任意字符。在依赖目标中同样可以使用%
,只是依赖目标中的%
的取值,取决于其目标。模式规则中,至少在规则的目标定义中要包含
%
,否则,就是一般的规则。目标中的%
定义表示对文件名的匹配,表示长度任意的非空字符串。例如:%.c
表示以.c
结尾的文件名(文件名的长度至少为 3),而s.%.c
则表示以s.开头,.c结尾
的文件名(文件名的长度至少为 5)。例如有一个模式规则如下:
1
%.o : %.c ; <command ......>
其含义是,指出了怎么从所有的
.c
文件生成相应的.o
文件的规则。如果要生成的目标是a.o b.o
,那么%c
就是a.c b.c
。一旦依赖目标中的%
模式被确定,那么,make 会被要求去匹配当前目录下所有的文件名,一旦找到,make 就会执行规则下的命令,所以,在模式规则中,目标可能会是多个的,如果有模式匹配出多个目标,make 就会产生所有的模式目标,此时,make 关心的是依赖的文件名和生成目标的命令这两件事。自动化变量
变量 说明 $@ 表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"$@"就是匹配于目标中模式定义的集合 $% 仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是"foo.a (bar.o)",那么,"$%"就是"bar.o","$@"就是"foo.a"。如果目标不是函数库文件,那么其值为空 $< 依赖目标中的第一个目标名字。如果依赖目标是以模式(即"%")定义的,那么"$<"将是符合模式的一系列的文件集。注意,其是一个一个取出来的 $? 所有比目标新的依赖目标的集合。以空格分隔 $^ 所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标,只保留一份 $+ 这个变量很像"$^",也是所有依赖目标的集合。只是它不去除重复的依赖目标 $* 这个变量表示目标模式中"%"及其之前的部分。如果目标是"dir/a.foo.b",并且目标的模式是"a.%.b",那么,"$*"的值就是"dir/a.foo"。这个变量对于构造有关联的文件名是比较有较。如果目标中没有模式的定义,那么"$*"也就不能被推导出,但是,如果目标文件的后缀是 make 所识别的,那么"$*"就是除了后缀的那一部分。例如:如果目标是"foo.c",因 为".c"是 make 所能识别的后缀名,所以,"$*"的值就是"foo"。这个特性是 GNU make 的,很有可能不兼容于其它版本的 make,所以,你应该尽量避免使用"$*",除非是在隐含规则或是静态模式中。如果目标中的后缀是 make 所不能识别的,那么"$*"就是空值。
老式风格的后缀规则
后缀规则是一个比较老式的定义隐含规则的方法,后缀规则会被模式规则逐步地取代,因为模式规则更强更清晰。
双后缀规则
定义了一对后缀:目标文件的后缀和依赖目标(源文件)的后缀,例如:
1
".c.o"
相当于
1
"%o : %c"。
单后缀
单后缀规则只定义一个后缀,也就是源文件的后缀,例如:
.c
相当于1
`% :%.c`。
注意:后缀规则不允许任何的依赖文件,如果有依赖文件的话,那就不是后缀规则,那些后缀统统被认为是文件名,如:
1
2.c.o: foo.h
$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<这个例子是说,文件
.c.o
依赖于文件foo.h
,而不是我们想要的这样:1
2%.o: %.c foo.h
$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<