makefile的隐含规则

目录

  • 简介
  • 隐含规则的变量
  • 模式规则
  • 老式风格的后缀规则

简介

在我们使用Makefile时,有一些我们会经常使用,而且使用频率非常高的东西,就是在 Makefile 中的“隐含的”,早先约定了的,不需要我们再写出来的规则。
例如,把.c文件编译成.o文件这一规则,你根本就不用写出来,make 会自动推导出这种规则,并生成我们需要的.o文件。

“隐含规则”会使用一些我们系统变量,我们可以改变这些系统变量的值来定制隐含规则的运行时的参数。如系统变量“CFLAGS”可以控制编译时的编译器参数。

  1. 如何使用

    如果要使用隐含规则生成你需要的目标,你所需要做的就是不要写出这个目标的规则。那么make会试图去自动推导产生这个目标的规则和命令,如果 make 可以自动推导生成这个目标的规则和命令,那么这个行为就是隐含规则的自动推导。例如:

    1
    2
    foo : foo.o bar.o 
    cc –o foo foo.o bar.o $(CFLAGS) $(LDFLAGS)

    我们可以注意到,这个 Makefile 中并没有写下如何生成 foo.obar.o 这两目标的规则和命令。因为 make 的“隐含规则”功能会自动为我们自动去推导这两个目标的依赖目标和生成命令。 make 会在自己的“隐含规则”库中寻找可以用的规则,如果找到,那么就会使用。如果找不到就会报错。在上面的那个例子中,make 调用的隐含规则是,把.o的目标的依赖文件置成.c,并使用 C 的编译命令cc –c $(CFLAGS) [.c] 来生成.o的目标。

    也就是说,我们完全没有必要写下下面的两条规则:

    1
    2
    3
    4
    foo.o : foo.c 
    cc –c foo.c $(CFLAGS)
    bar.o : bar.c
    cc –c bar.c $(CFLAGS)

    因为,这已经是“约定”好了的事了,这就是隐含规则。 当然,如果我们为.o文件书写了自己的规则,那么 make 就不会自动推导并调用隐含规则,它会按照我们写好的规则忠实地执行。

  2. 隐含规则有优先级

    在 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。

  3. 常见隐含规则

    规则名称 格式 内容
    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(链接器参数)

模式规则

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

    模式规则中,至少在规则的目标定义中要包含 %,否则,就是一般的规则。目标中的 % 定义表示对文件名的匹配,表示长度任意的非空字符串。例如:%.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 关心的是依赖的文件名和生成目标的命令这两件事。

  2. 自动化变量

    变量 说明
    $@ 表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"$@"就是匹配于目标中模式定义的集合
    $% 仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是"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. 双后缀规则

    定义了一对后缀:目标文件的后缀和依赖目标(源文件)的后缀,例如:

    1
    ".c.o"

    相当于

    1
    "%o : %c"
  2. 单后缀
    单后缀规则只定义一个后缀,也就是源文件的后缀,例如:
    .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 $@ $<