如何编写Makefile(8)-隐含规则(一)

目录

一、隐含规则使用

二、隐含规则概览

三、隐含规则使用的变量

3.1 关于命令的变量

3.2 关于命令参数的变量

四、隐含规则链


        我们继续Makefile函数使用的学习,上一节可查看:↓传送门↓

如何编写Makefile(7)-运行make-优快云博客icon-default.png?t=O83Ahttps://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),于是优化过的规则就不会生成中间文件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猿核试Bug愁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值