这个章节讲述的是 Makefile 的隐含规则,所谓的隐含规则就是需要我们做出具体的操作,系统自动完成。编写 Makefile 的时候,可以使用隐含规则来简化Makefile 文件编写。

    实例

    1. test : test.o
    2. gcc -o test test.o
    3. test.o : test.c

    我们可以在 Makefile 中这样写来编译 test.c 源文件,相比较之前少写了重建 test.o 的命令。但是执行 make,发现依然重建了 test 和 test.o 文件,运行结果却没有改变。这其实就是隐含规则的作用。在某些时候其实不需要给出重建目标文件的命令,有的甚至可以不需要给出规则。实例:

    1. test : test.o
    2. gcc -o test test.o

    运行的结果是相同的。

    注意:隐含条件只能省略中间目标文件重建的命令和规则,但是最终目标的命令和规则不能省略。

    隐含规则的具体的工作流程:make 执行过程中找到的隐含规则,提供了此目标的基本依赖关系。确定目标的依赖文件和重建目标需要使用的命令行。隐含规则所提供的依赖文件只是一个基本的(在 C 语言中,通常他们之间的对应关系是:test.o 对应的是 test.c 文件)。当需要增加这个文件的依赖文件的时候要在 Makefile 中使用没有命令行的规则给出。实例:

    1. test : test.o
    2. gcc -o test test.o
    3. test : test1.h

    其实在有些时候隐含规则的使用会出现问题。因为有一个 make 的“隐含规则库”。库中的每一条隐含规则都有相应的优先级顺序,优先级也就会越高,使用时也就会被优先使用。

    例如在 Makefile 中添加这行代码:

    1. foo.o : foo.p

    我们都知道 .p 文件是 Pascal 程序的源文件,如果书写规则时不加入命令的话,那么 make 会按照隐含的规则来重建目标文件 foo.o。如果当前目录下恰好存在 foo.c 文件的时候,隐含规则会把 foo.c 当做是 foo.o 的依赖文件进行目标文件的重建。因为编译 .c 文件的隐含规则在编译 .p 文件之前,显然优先级也会越高。当 make 找到生成 foo.o 的文件之后,就不会再去寻找下一条规则。如果我们不想使用隐含规则,在使用的时候不仅要声明规则,也要添加上执行的命令。

    这里讲的是预先设置的隐含规则。如果不明确的写下规则,那么make 就会自己寻找所需要的规则和命令。当然我们也可以使用 make 选项:-r-n-builtin-rules选项来取消所有的预设值的隐含规则。当然即使是指定了“-r”的参数,某些隐含规则还是会生效。因为有很多的隐含规则都是使用了后缀名的规则来定义的,所以只要隐含规则中含有“后缀列表”那么隐含规则就会生效。默认的列表是:

    1. .out .a .in .o .c .cc .C .p .f .F .r .y .l
    2. .s .S .mod .sym .def .h .info .dvi .tex .texinfo .texi
    3. .txinfo .w .sh .el .el

    下面是一些常用的隐含规则:

    • 编译 C 程序
    • 编译 C++ 程序
    • 编译 Pascal 程序
    • 编译 Fortran/Ratfor 程序
    • 预处理 Fortran/Ratfor 程序
    • 编译 Modula-2 程序
    • 汇编和需要预处理的汇编程序
    • 链接单一的 object 文件
    • Yacc C 程序
    • Lex C 程序时的隐含规则

    上面的编译顺序都是一些常用的编程语言执行隐含规则的顺序,我们在 Makefile 中指定规则时,可以参考这样的列表。当需要编译源文件的时候,考虑是不是需要使用隐含规则。如果不需要,就要把相应的规则和命令全部书写上去。

    内嵌隐含规则的命令中,所使用的变量都是预定义的。我们将这些变量称为“隐含变量”。这些变量允许修改:可以通过命令行参数传递或者是设置系统环境变量的方式都可以对它进行重新定义。无论使用哪种方式,只要 make 在运行的,这些变量的定义有效。Makefile 的隐含规则都会使用到这些变量。

    比如我们编译 .c 文件在我们的 Makefile 中就是隐含的规则,默认使用到的编译命令时cc,执行的命令时cc -c我们可以对用上面的任何一种方式将CC定义为ncc。这样我们就编译 .c 文件的时候就可以用ncc进行编译。

    隐含规则中使用的变量可以分成两类:

    1. 代表一个程序的名字。例如:“CC”代表了编译器的这个可执行程序。
    2. 代表执行这个程序使用的参数.例如:变量“CFLAGS”。多个参数之间使用空格隔开。

    下面我们来列举一下代表命令的变量,默认都是小写。

    • AR:函数库打包程序,科创价静态库 .a 文档。
    • AS:应用于汇编程序。
    • CC:C 编译程序。
    • CXX:C++ 编译程序。
    • CO:从 RCS 中提取文件的程序。
    • CPP:C 程序的预处理器。
    • FC:编译器和与处理函数 Fortran 源文件的编译器。
    • GET:从 CSSC 中提取文件程序。
    • LEX:将 Lex 语言转变为 C 或 Ratfo 的程序。
    • PC:Pascal 语言编译器。
    • YACC:Yacc 文法分析器(针对于 C 语言)
    • YACCR:Yacc 文法分析器。