目录
我们继续Makefile函数使用的学习,上一节可查看:↓传送门↓。
如何编写Makefile(7)-运行make-优快云博客https://blog.youkuaiyun.com/L_peanut/article/details/144403705?spm=1001.2014.3001.5501 本节我们将介绍make的隐含规则。我们使用makefile时,有一些内容是我们经常会用到的,例如编译C/C++的源文件为中间目标文件,我们可以通过隐含规则减少makefile文件编写时的重复性内容。
“隐含规则”是一种惯例,make会按照这种惯例来执行,即使我们没有在makefile中明确写出这样的规则。使用“模式规则”会显得更加智能和清晰,但是“后缀规则”可以用来保证我们Makefile的兼容性。
一、隐含规则使用
如果要使用隐含规则生成目标,那就不需要写出这个目标的规则。make会试图去自动推导产生这个目标的规则和命令,如果make可以自动推导生成这个目标的规则和命令,那么这个行为就是隐含规则的自动推导。如下:
foo:foo.c bar.o
cc -o foo foo.o bar.o $(CFLAGS) $(LDFLAGS)
上面的例子中并没有写明如何生成foo.o和bar.o这两个目标的规则和命令,make的隐含规则会自动去推导这两个目标的依赖和生成命令。
make会在自己的“隐含规则”库里面寻找可以用的规则,如果找到,就可以使用,如果没有找到,就会报错。在这个例子中,make调用的隐含规则是把.o目标的依赖文件置成.c,然后使用C的编译命令cc -c $(CFLAGS) foo.c来生成foo.o的目标。也就是说我们可以不用写下面的两条规则:
foo.o:foo.c
cc -c foo.c $(CFLAGS)
bar.o:bar.c
cc -c bar.o $(CFLAGS)
因为这已经是“约定”好的事情,mkae和我们约定好了用C编译器cc生成.o文件的规则,这就是隐含规则。此外,在make的“隐含规则库”中每条隐含规则都在库中有其顺序,越靠前的越被经常使用,所以这导致我们有些时候即使显式指定了目标依赖,make也不理会,比如:
foo.o:foo.p
依赖文件foo.p(Pascal程序的源文件)有可能变得没有意义,如果目录下存在了foo.c文件,那么我们的隐含规则同样会生效,并会通过foo.c调用C编译器生成foo.o文件。因为Pascal的规则在C的规则之后,所以,make找到可以生成foo.c的C的规则就不再寻找下一条规则了。如果不希望出现任何隐含规则推导,就需要同时写出依赖规则和执行命令。
二、隐含规则概览
下面介绍所有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, .h, .info, .dvi, .tex, .texinfo, .texi, .txinfo, .w, .ch, .web, .sh, .elc, .el。
下面列举一些常用的隐含规则:
①编译C程序的隐含规则
.o目标的依赖目标会自动推导为.c,并且其生成命令是$(CC) -c $(CPPFLAGS) $(CFLAGS)
②编译C++程序的隐含规则
.o目标的依赖目标会自动推导为.cc或是.C,并且其生成命令是$(CXX) -c $(CPPFLAGS) $(CXXFLAGS)。
③编译Pascal程序的隐含规则
.o目标的依赖会自动推导为.p,并且其生成命令是$(PC) -c $(PFLAGS)。
④编译Fortran/Ratfor程序的隐含规则
.o目标的依赖目标会自动推导为.r或.F或.f,并且其生成命令是:
.f $(FC) -c $(FFLAGS)
.F $(FC) -c $(FFLAGS) $(CPPFLAGS)
.r $(FC) -c $(FFLAGS) $(RFLAGS)
⑤预处理Fortran/Ratfor程序的隐含规则
.f目标的依赖目标会自动推导为.r或.F,这个规则只是转换Ratfor或有预处理的Fortran程序到一个标准的Fortran程序。其使用的命令是:
.F $(FC) -F $(CPPFLAGS) $(FFLAGS)
.r $(FC) -F $(FFLAGS) $(RFLAGS)
⑥编译Modula-2程序的隐含规则
.sym目标的依赖目标会自动推导为.def,并且其生成命令是$(M2C) $(M2FLAGS) $(DEFFLAGS)。.o目标的依赖目标会自动推导为.mod,并且其生成命令是$(M2C) $(M2FLAGS) $(MODFLAGS)。
⑦汇编和汇编预处理的隐含规则
.o目标的依赖目标会自动推导为.s,默认使用编译器as,并且其生成命令是$(AS) $(ASFLAGS)。.s目标的依赖目标会自动推导为.mod,默认使用C预编译器cpp,并且其生成命令是$(AS) $(ASFLAGS)。
⑧链接Object文件的隐含规则
目标依赖于.o,通过运行C编译器来运行链接程序生成(一般是ld),其生成命令是$(CC) $(LDFLAGS) .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
如果没有一个源文件和你的目标名字相关联,最好写出自己的生成规则,不然隐含规则会报错。
⑨Yacc C程序时的隐含规则
.c的依赖文件被自动推导为.y,其生成命令是$(YACC) $(YFLAGS)。
⑩从C程序、Yacc文件或Lex文件创建Linux库的隐含规则
.ln(lint生成的文件)的依赖文件被自动推导为.c,其生成命令是$(LINT) $(LINTFLAGS) $(CPPFLAGS) -i。对于.y和.l也是同样的规则。
三、隐含规则使用的变量
在隐含规则中的命令中,基本上都是使用了预先设置的变量。你也可以在自己的makefile中改变这些变量的值,或者在make的命令行中传入这些值,或者是在环境变量中设置这些值。但无论哪种方式,只要设置了这些特定的变量,就会对隐含规则起作用。你也可以利用make的-R或--no-builtin-variables参数来取消所定义的变量对隐含规则的作用。
比如第一条隐含规则——编译C程序的隐含规则的命令是$(CC) -c $(CFLAGS) $(CPPFLAGS)。Make默认的编译命令是cc,如果你将变量$(CC)重定义成gcc,把变量$(CFLAGS)编译重定义成-g,那隐含规则中的命令全部会以gcc -c -g $(CPPFLAGS)的样子来执行。
我们可以把隐含规则中使用的变量分为两种:一种是命令相关的,如CC;一种是参数相关的,如CFLAGS。
3.1 关于命令的变量
AR:函数库打包程序,默认命令ar;
AS:汇编语言编译程序,默认命令as;
CC:C语言编译程序,默认命令cc;
CXX:C++语言编译程序,默认命令cc;
CO:从RCS文件中扩展文件程序,默认命令是co;
CPP:C程序的预处理器(输出是标准输出设备),默认命令$(CC) -E;
FC: Fortran和Ratfor的编译器和预处理程序,默认命令是f77。
GET:从SCCS 文件中扩展文件的程序,默认命令是f77。
LEX:Lex方法分析器程序(针对C或Ratfor),默认命令是lex。
PC :Pascal语言编译程序,默认命令是pc。
YACC:Yacc文法分析器(针对于C程序),默认命令是yacc。
YACCR:Yacc文法分析器(针对于Ratfor程序),默认命令是yacc -r。
MAKEINFO:转换Texinfo源文件到Info文件程序。默认命令是makeinfo。
TEX:从TeX源文件创建TeX DVI文件的程序。默认命令是tex。
TEXI2DVI:从Texinfo源文件创建TeX DVI文件的程序。默认命令是tex2dvi。
WEAVE:转换Web到Tex的程序,默认命令是weave。
CWEAVE:转换C Web到TeX的程序,默认命令是cweave。
TANGLE:转换Web到Pascal语言的程序,默认命令是tangle。
CTANGLE:转换C Web到C,默认命令是ctangle。
RM:删除文件命令。默认命令是rm -f。
3.2 关于命令参数的变量
下面这些变量都是相关上面的命令参数,如果没有明确指明其默认值,那么默认值为空。
ARFLAGS:函数库打包程序AR命令的参数,默认值是rv。
ASFLAGS:汇编语言编译器参数。
CFLAGS:C语言编译器参数。
CXXFLAGS:C++语言编译器参数。
COFLAGS:RCS命令参数。
CPPFLAGS:C预处理参数。
FFLAGS:Fortran语言编译器参数。
GFLAGS:SCCS "get"程序参数。
LDFLAGS:链接器参数。
LFLAGS:Lex文法分析器参数。
PFLAGS:Pascal语言编译器参数。
RFLAGS:Ratfor程序的Fortran编译器参数。
YFLAGS:Yacc文法分析器参数。
四、隐含规则链
有时候一个目标可能被一系列的隐含规则所作用,例如一个.o文件生成,可能会是先被Yacc的.y文件先生成.c,然后再被C的编译器生成。这一系列的隐含规则称为“隐含规则链”。
如果.c文件存在,那么就直接调用C的编译器的隐含规则,如果没有.c文件,但有一个.y文件,那么Yacc的隐含规则会被调用,生成.c文件,然后再调用C编译的隐含规则最终由.c生成.o文件,达成目标。我们将这种.c的文件叫做中间目标。无论如何,make会尽可能推导生成目标的一切方法,不管中间目标有多少。
在默认情况下,对于中间目标,它和一般目标有两个不同地方:①除非中间的目标不存在,才会引发中间规则;②只要目标成功产生,生成最终目标过程中所产生的中间目标文件会被rm -f删除。
通常一个被makefile指定成目标或是依赖目标的文件不能被当作中介。但是,可以明确地说明一个文件或是目标是中介目标,可以使用伪目标.INTERMEDIATE来强制声明(如.INTERMEDIATE:mid)。你也可以阻止make自动删除中间目标,可以通过使用伪目标.SECONDARY来强制声明(如:.SECONDAR:sec)。你还可以把你的目标以模式的方式来指定(如:%.o)成伪目标.PRECIOUS的依赖目标,以保存被隐含规则生成的中间文件。
在“隐含规则链”中,禁止同一个目标出现两次或两次以上,这样就可以防止make自动推导时出现无限递归的情况。Make会优化一些特殊的隐含规则而不生成中间文件,如文件foo.c生成目标程序foo,按道理,make会编译生成中间文件foo.o,然后链接成foo,但在实际情况下,这一动作可以被一条cc命令完成(cc -o foo foo.c),于是优化过的规则就不会生成中间文件。