11.1使用隐含规则
前面一开始的内容已经使用了部分的隐含规则
例如
foo:foo.o bar.o
cc –o foo foo.o bar.o $(CFLAGS) $(LDFLAGS)
在这个里面Makefile 中并没有写下如何生成 foo.o 和 bar.o 这两目标的规则和命令。因为 make 的“隐含规则”功能会自动去推导这两个目标的依赖目标和生成命令。这里make调用的隐含规则是,把[.o]的目标的依赖文件置成[.c],并使用 C 的编译命令“cc –c $(CFLAGS) [.c]”来生成[.o]的目标。省略了
foo.o:foo.c
cc –c foo.c $(CFLAGS)
bar.o:bar.c
cc –c bar.c $(CFLAGS)
因为,这已经是“约定”好了的事了,make 已经约定好了用 C 编译器“cc”生成[.o]文件的规则,这就是隐含规则。
11.2隐含规则一览
这里将讲述所有预先设置(也就是 make 内建)的隐含规则,如果不明确地写下规则,那么,make就会在这些规则中寻找所需要规则和命令。当然,也可以使用 make 的参数“-r”或“--no-builtin-rules”选项来取消所有的预设置的隐含规则。
即使指定了“-r”参数,某些隐含规则还是会生效,因为有许多的隐含规则都是使用了“后缀规则”来定义的,所以,只要隐含规则中有“后缀列表”(也就一系统定义在目标.SUFFIXES 的依赖目标),那么隐含规则就会生效。默认的后缀列表是:
.out,.a,.ln,.o,.c,.cc,.C,.p,.f,.F,.r,.y,.l,.s,.S,.mod,.sym,.def,.
规则1:编译 C 程序的隐含规则。
“<n>.o”的目标的依赖目标会自动推导为“<n>.c”,并且其生成命令是“$(CC) –c $(CPPFLAGS) $(CFLAGS)”
规则2:编译 C++程序的隐含规则。
“<n>.o”的目标的依赖目标会自动推导为“<n>.cc”或是“<n>.C”,并且其生成命令是“$(CXX) –c $(CPPFLAGS) $(CFLAGS)”。
规则3:编译 Pascal 程序的隐含规则。
“<n>.o”的目标的依赖目标会自动推导为“<n>.p”,并且其生成命令是“$(PC) –c $(PFLAGS)”。
注意:隐含规则是有顺序的,有相同的<n>.c,<n>.cc,<n>.p会优先规则1。(后面的几种没用过都是书上内容记录下)
规则4:编译 Fortran/Ratfor 程序的隐含规则。
“<n>.o”的目标的依赖目标会自动推导为“<n>.r”或“<n>.F”或“<n>.f”,并且其生成命令是:
“.f” “$(FC) –c $(FFLAGS)”
“.F” “$(FC) –c $(FFLAGS) $(CPPFLAGS)”
“.f” “$(FC) –c $(FFLAGS) $(RFLAGS)”
规则5:预处理 Fortran/Ratfor 程序的隐含规则
“<n>.f”的目标的依赖目标会自动推导为“<n>.r”或“<n>.F”。这个规则只是转换 Ratfor 或有预处理的 Fortran 程序到一个标准的 Fortran 程序。其使用的命令是:
“.F” “$(FC) –F $(CPPFLAGS) $(FFLAGS)”
“.r” “$(FC) –F $(FFLAGS) $(RFLAGS)”
规则6:编译 Modula-2 程序的隐含规则。
“<n>.sym”的目标的依赖目标会自动推导为“<n>.def”,并且其生成命令是:“$(M2C) $(M2FLAGS) $(DEFFLAGS)”。“<n.o>”的目标的依赖目标会自动推导为“<n>.mod”,并且其生成命令是:“$(M2C) $(M2FLAGS) $(MODFLAGS)”。
规则7:汇编和汇编预处理的隐含规则
“<n>.o”的目标的依赖目标会自动推导为“<n>.s”,默认使用编译品“as”,并且其生成命令是:“$(AS) $(ASFLAGS)”。“<n>.s”的目标的依赖目标会自动推导为“<n>.S”,,默认使用 C 预编译器“cpp”,并且其生成命令是:“$(AS) $(ASFLAGS)”。
规则8:链接 Object 文件的隐含规则
“<n>”目标依赖于“<n>.o”,通过运行 C 的编译器来运行链接程序生成(一般是“ld”),其生成命令是:
“$(CC) $(LDFLAGS) <n>.o $(LOADLIBES) $(LDLIBS)”。
这个规则对于只有一个源文件的工程有效,同时也对多个 Object 文件(由不同的源文件生成)的也有效。例如如下规则:
x:y.o z.o
并且“x.c”、“y.c”和“z.c”都存在时,隐含规则将执行如下命令:
cc -c x.c -o x.o
cc -c y.c -o y.o
cc -c z.c -o z.o
cc x.o y.o z.o -o x
rm -f x.o
rm -f y.o
rm -f z.o
规则9:YaccC 程序时的隐含规则。
“<n>.c”的依赖文件被自动推导为“n.y”(Yacc 生成的文件),其生成命令是:“$(YACC)$(YFALGS)”。(“Yacc”是一个语法分析器,关于其细节请查看相关资料)
规则10:LexC 程序时的隐含规则。
<n>.c”的依赖文件被自动推导为“n.l”(Lex 生成的文件),其生成命令是:“$(LEX)$(LFALGS)”。(关于“Lex”的细节请查看相关资料)
规则11:LexRatfor 程序时的隐含规则。
“<n>.r”的依赖文件被自动推导为“n.l”(Lex 生成的文件),其生成命令是:“$(LEX) $(LFALGS)”。
规则12:从 C 程序、Yacc 文件或 Lex 文件创建 Lint 库的隐含规则。
“<n>.ln”(lint 生成的文件)的依赖文件被自动推导为“n.c”,其生成命令是:“$(LINT) $(LINTFALGS) $(CPPFLAGS) -i”。 对于“<n>.y”和“<n>.l”也是同样的规则。
8.3. 隐含规则使用的变量
在隐含规则中的命令中,基本上都是使用了一些预先设置的变量。可以在makefile 中改变这些变量的值,或是在 make 的命令行中传入这些值,或是在环境变量中设置这些值,无论怎么样,只要设置了这些特定的变量,那么其就会对隐含规则起作用。当然,也可以利用 make 的“-R”或“--no–builtin-variables”参数来取消你所定义的变量对隐含规则的作用。
例如,第一条隐含规则——编译 C 程序的隐含规则的命令是“$(CC) –c $(CFLAGS) $(CPPFLAGS)”。Make 默认的编译命令是“cc”,如果你把变量“$(CC)”重定义成“gcc”,把变量“$(CFLAGS)”重定义成“-g”,那么,隐含规则中的命令全部会以“gcc –c -g $(CPPFLAGS)”的样子来执行了。
可以把隐含规则中使用的变量分成两种:一种是命令相关的,如“CC”;一种是参数相的关,如“CFLAGS”。下面是所有隐含规则中会用到的变量。
关于命令的变量
AR :函数库打包程序。默认命令是“ar”。
AS :汇编语言编译程序。默认命令是“as”。
CC :C 语言编译程序。默认命令是“cc”。
CXX: C++语言编译程序。默认命令是“g++”。
CO:从 RCS 文件中扩展文件程序。默认命令是“co”。
CPP:C 程序的预处理器(输出是标准输出设备)。默认命令是“$(CC) –E”。
FC:Fortran 和 Ratfor 的编译器和预处理程序。默认命令是“f77”。
GET:从 SCCS 文件中扩展文件的程序。默认命令是“get”。
LEX:Lex 方法分析器程序(针对于 C 或 Ratfor)。默认命令是“lex”。
PC:Pascal 语言编译程序。默认命令是“pc”。
YACC:Yacc 文法分析器(针对于 C 程序)。默认命令是“yacc”。
YACCR:Yacc 文法分析器(针对于 Ratfor 程序)。默认命令是“yacc –r”。
MAKEINFO:转换 Texinfo 源文件(.texi)到 Info 文件程序。默认命令是“makeinfo”。
TEX:从 TeX 源文件创建 TeXDVI 文件的程序。默认命令是“tex”。
TEXI2DVI:从 Texinfo 源文件创建军 TeXDVI 文件的程序。默认命令是“texi2dvi”。
WEAVE:转换 Web 到 TeX 的程序。默认命令是“weave”。
CWEAVE:转换 CWeb 到 TeX 的程序。默认命令是“cweave”。
TANGLE:转换 Web 到 Pascal 语言的程序。默认命令是“tangle”。
CTANGLE:转换 CWeb 到 C。默认命令是“ctangle”。
RM:删除文件命令。默认命令是“rm–f”。
关于命令参数的变量
下面的这些变量都是相关上面的命令的参数。如果没有指明其默认值,那么其默认值都是空
ARFLAGS:函数库打包程序 AR 命令的参数。默认值是“rv”。
ASFLAGS:汇编语言编译器参数。(当明显地调用“.s”或“.S”文件时)
CFLAGS:C 语言编译器参数。
CXXFLAGS:C++语言编译器参数
COFLAGS:RCS 命令参数。
CPPFLAGS:C 预处理器参数。(C 和 Fortran 编译器也会用到)。
FFLAGS:Fortran 语言编译器参数。
GFLAGS:SCCS“get”程序参数。
LDFLAGS:链接器参数。(如:“ld”)
LFLAGS:Lex 文法分析器参数。
PFLAGS:Pascal 语言编译器参数。
RFLAGS:Ratfor 程序的 Fortran 编译器参数。
YFLAGS:Yacc 文法分析器参数。
8.4隐含规则链
例如,一个[.o]的文件生成,可能会是先被 Yacc 的[.y]文件先成[.c],然后再被 C 的编译器生成。把这一系列的隐含规则叫做“隐含规则链“。
当然如果文件[.c]存在,那么就直接调用 C 的编译器的隐含规则,如果没有[.c]文件,但有一个[.y]文件,那么 Yacc 的隐含规则会被调用,生成[.c]文件,然后,再调用 C 编译的隐含规则最终由[.c]生成[.o]文件,达到目标。这种[.c]的文件(或是目标),叫做中间目标。有时候中间目标不止一个。
对于中间目标,它和一般的目标有两个地方所不同:第一个不同是除非中间的目标不存在,才会引发中间规则。第二个不同的是,只要目标成功产生,那么,产生最终目标过程中,所产生的中间目标文件会被以“rm-f”删除。
通常,一个被 makefile 指定成目标或是依赖目标的文件不能被当作中介。然而,可以明显地说明一个文件或是目标是中介目标,可以使用伪目标“.INTERMEDIATE”来强制声明。(如:.INTERMEDIATE:mid)也可以阻止 make 自动删除中间目标,要做到这一点,可以使用伪目标“.SECONDARY”来强制声明(如:.SECONDARY:sec)。还可以把目标,以模式的方式来指定(如:%.o)成伪目标“.PRECIOUS”的依赖目标,以保存被隐含规则所生成的中间文件。在“隐含规则链”中,禁止同一个目标出现两次或两次以上,这样一来,就可防止在 make 自动推导时出现无限递归的情况。
Make 会优化一些特殊的隐含规则,而不生成中间文件。如,从文件“foo.c”生成目标程序“foo”,按道理,make会编译生成中间文件“foo.o”,然后链接成“foo”,但在实际情况下,这一动作可以被一条“cc”的命令完成(cc –o foo foo.c),于是优化过的规则就不会生成中间文件。