指定搜寻包含makefile文件的路径‘dir’。参阅包含其它makefile文件。如果同时使用几个‘-I’选项用于指定路径,则按照指定的次序搜寻这些路径。
`-j [jobs]'
`--jobs[=jobs]'
指定同时执行的命令数目。如果没有参数make将同时执行尽可能多的任务;如果有多个‘-j’选项,则仅最后一个选项有效。详细内容参阅并行执行。注意在MS-DOS下,该选项被忽略。
`-k'
`--keep-going'
在出现错误后,尽可能的继续执行。当一个目标创建失败,则所有依靠它的目标文件将不能重建,而这些目标的其它依赖则可继续处理。参阅测试编译程序。
`-l [load]'
`--load-average[=load]'
`--max-load[=load]'
指定如果有其它任务正在运行,并且平均负载已接近或超过‘load’(一个浮点数),则此时不启动新任务。如果没有参数则取消以前关于负载的限制。参阅并行执行。
`-n'
`--just-print'
`--dry-run'
`--recon'
打印要执行的命令,但却不执行它们。参阅代替执行命令。
`-o file'
`--old-file=file'
`--assume-old=file'
即使文件file比它的依赖‘旧’,也不重建该文件。不要因为文件file的改变而重建任何其它文件。该选项本质上是假装将该文件的时间戳改为旧的时间戳,以至于依靠它的规则被忽略。参阅避免重新编译文件。
`-p'
`--print-data-base'
打印数据库(规则和变量的值),这些数据来自读入makefile文件的结果;然后象通常那样执行或按照别的指定选项执行。如果同时给出‘-v’开关,则打印版本信息(参阅下面内容)。使用‘make –qp’则打印数据库后不试图重建任何文件。使用‘make –p –f/dev/null’则打印预定义的规则和变量的数据库。数据库输出中包含文件名,以及命令和变量定义的行号信息。它是在复杂环境中很好的调试工具。
`-q'
`--question'
‘问题模式’。不打印输出也不执行命令,如果所有目标都已经更新到最新,make的退出状态是0;如果一部分需要更新,退出状态是1;如果make遇到错误,退出状态是2,参阅代替执行命令。
`-r'
`--no-builtin-rules'
排除使用内建的隐含规则(参阅使用隐含规则)。您仍然可以定义您自己的格式规则(参阅定义和重新定义格式规则)。选项‘-r’同时也清除了缺省的后缀列表和后缀规则(参阅过时的后缀规则)。但是您可以使用.SUFFIXES规则定义您自己的后缀。注意,使用选项‘-r’仅仅影响规则;缺省变量仍然有效(参阅隐含规则使用的变量);参阅下述的选项‘-R’。
`-R'
`--no-builtin-variables'
排除使用内建的规则变量(参阅隐含规则使用的变量)。当然,您仍然可以定义自己的变量。选项‘-R’自动使选项‘-r’生效;因为它去掉了隐含规则所使用的变量的定义,所以隐含规则也就失去了存在的意义。
`-s'
`--silent'
`--quiet'
沉默选项。不回显那些执行的命令。参阅命令回显。
`-S'
`--no-keep-going'
`--stop'
使选项‘-k’失效。除非在递归调用make时,通过变量MAKEFLAGS从上层make继承选项‘-k’,或您在环境中设置了选项‘-k’,否则没有必要使用该选项。
`-t'
`--touch'
标志文件已经更新到最新,但实际没有更新它们。这是假装那些命令已经执行,用于愚弄将来的make调用。参阅代替执行命令。
`-v'
`--version'
打印make程序的版本信息,作者列表和没有担保的注意信息,然后退出。
`-w'
`--print-directory'
打印执行makefile文件时涉及的所有工作目录。这对于跟踪make递归调用时复杂嵌套产生的错误非常有用。参阅递归调用make。实际上,您很少需要指定该选项,因为make已经替您完成了指定。参阅‘--print-directory’选项。
`--no-print-directory'
在指定选项‘-w’的情况下,禁止打印工作路径。这个选项在选项‘-w’自动打开而且您不想看多余信息时比较有用。参阅‘--print-directory’选项。
`-W file'
`--what-if=file'
`--new-file=file'
`--assume-new=file'
假装目标文件已经更新。在使用标志‘n’时,它将向您表明更改该文件会发生什么。如果没有标志‘n’它和在运行make之前对给定的文件使用touch命令的结果几乎一样,但使用该选项make只是在的想象中更改该文件的时间戳。参阅代替执行命令。
`--warn-undefined-variables'
当make看到引用没有定义的变量时,发布一条警告信息。如果您按照复杂方式使用变量,当您调试您的makefile文件时,该选项非常有用。
10 使用隐含规则
重新创建目标文件的一些标准方法是经常使用的。例如,一个传统的创建OBJ文件的方法是使用C编译器,如cc,编译C语言源程序。
隐含规则能够告诉make怎样使用传统的技术完成任务,这样,当您使用它们时,您就不必详细指定它们。例如,有一条编译C语言源程序的隐含规则,文件名决定运行哪些隐含规则;另如,编译C语言程序一般是使用‘.c’文件,产生‘.o’文件。因此,
make据此和文件名的后缀就可以决定使用编译C语言源程序的隐含规则。一系列的隐含规则可按顺序应用;例如,make可以从一个‘.y’文件,借助‘.c’文件,重建一个‘.o’文件,参阅隐含规则链。内建隐含规则的命令需要使用变量,通过改变这些变量的值,您就可以改变隐含规则的工作方式。例如,变量CFLAGS控制隐含规则用于编译C程序传递给C编译器的标志,参阅隐含规则使用的变量。通过编写格式规则,您可以创建您自己的隐含规则。参阅定义和重新定义格式规则。
后缀规则是对定义隐含规则最有限制性。格式规则一般比较通用和清楚,但是后缀规则却要保持兼容性。参阅过时的后缀规则。
10.1 使用隐含规则
允许make对一个目标文件寻找传统的更新方法,您所有做的是避免指定任何命令。可以编写没有命令行的规则或根本不编写任何规则。这样,make将根据存在的源文件的类型或要生成的文件类型决定使用何种隐含规则。
例如,假设makefile文件是下面的格式:
foo : foo.o bar.o
cc -o
foo foo.o bar.o $(CFLAGS) $(LDFLAGS)
因为您提及了文件‘foo.o’,但是您没有给出它的规则,make将自动寻找一条隐含规则,该规则能够告诉make怎样更新该文件。无论文件‘foo.o’存在与否,make都会这样执行。
如果能够找到一条隐含规则,则它就能够对命令和一个或多个依赖(源文件)提供支持。如果您要指定附加的依赖,例如头文件,但隐含规则不能支持,您需要为目标‘foo.o’写一条不带命令行的规则。
每一条隐含规则都有目标格式和依赖格式;也许多条隐含规则有相同的目标格式。例如,有数不清的规则产生‘.o’文件:使用C编译器编译‘.C’文件;使用Pascal编译器编译‘.p’文件;等等。实际应用的规则是那些依赖存在或可以创建的规则。所以,如果您有一个‘.C’文件,make将运行C编译器;如果您有一个‘.p’文件,make将运行Pascal编译器;等等。
当然,您编写一个makefile文件时,您知道您要make使用哪一条隐含规则,以及您知道make将选择哪一条规则,因为您知道那个依赖文件是假设存在的。预定义的隐含规则列表的详细内容参阅隐含规则目录。
首先,我们说一条隐含规则可以应用,该规则的依赖必须‘存在或可以创建’。一个文件‘可以创建’是说该文件在makefile中作为目标或依赖被提及,或者该文件可以经过一条隐含规则的递归调用后能够创建。如果一条隐含规则的依赖是另一条隐含规则的结果,我们说产生了‘链’。参阅隐含规则链。
总体上说,make为每一个目标搜寻隐含规则,为没有命令行的双冒号规则搜寻隐含规则。仅作为依赖被提及的文件,将被认为是一个目标,如果该目标的规则没有指定任何内容, make将为它搜寻隐含规则。对于详细的搜寻过程参阅隐含规则的搜寻算法。
注意,任何具体的依赖都不影响对隐含规则的搜寻。例如,认为这是一条具体的规则:
foo.o: foo.p
文件foo.p不是首要条件,这意味着make按照隐含规则可以从一个Pascal源程序(‘.p’文件)创建OBJ文件,也就是说一个‘.o’文件可根据‘.p’文件进行更新。但文件foo.p并不是绝对必要的;例如,如果文件foo.c也存在,按照隐含规则则是从文件foo.c重建foo.o,这是因为C编译规则在预定义的隐含规则列表中比Pascal规则靠前,参阅隐含规则目录。
如果您不希望使用隐含规则创建一个没有命令行的目标,您可以通过添加分号为该目标指定空命令。参阅使用空命令。
10.2隐含规则目录
这里列举了预定义的隐含规则的目录,这些隐含规则是经常应用的,当然如果您在makefile文件中重载或删除后,这些隐含规则将会失去作用,详细内容参阅删除隐含规则。选项‘-r’或‘--no-builtin-rules’将删除所有预定义的隐含规则。
并不是所有的隐含规则都是预定义的,在make中很多预定义的隐含规则是后缀规则的扩展,因此,那些预定义的隐含规则和后缀规则的列表相关(特殊目标.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。所有下面描述的隐含规则,如果它们的依赖中有一个出现在这个后缀列表中,则是后缀规则。如果您更改这个后缀列表,则只有那些由一个或两个出现在您指定的列表中的后缀命名的预定义后缀规则起作用;那些后缀没有出现在列表中的规则被禁止。对于详细的关于后缀规则的描述参阅过时的后缀规则。
Compiling C programs(编译C程序)
‘n.o' 自动由‘n.c' 使用命令 ‘$(CC)
-c $(CPPFLAGS) $(CFLAGS)'生成 。
Compiling C++ programs (编译C++程序)
‘n.o'自动由‘n.cc' 或‘n.C'使用命令‘$(CXX)
-c $(CPPFLAGS) $(CXXFLAGS)’生成。 我们鼓励您对C++源文件使用后缀‘.cc' 代替后缀‘.C’。
Compiling Pascal programs (编译Pascal程序)
‘n.o'自动由‘n.p'使用命令‘$(PC) -c $(PFLAGS)'生成。
Compiling Fortran and Ratfor programs (编译Fortran 和 Ratfor程序)
‘n.o'自动由‘n.r', ‘n.F'或‘n.f' 运行Fortran编译器生成。使用的精确命令如下:
`.f'
`$(FC) -c $(FFLAGS)'.
`.F'
`$(FC) -c $(FFLAGS) $(CPPFLAGS)'.
`.r'
`$(FC) -c $(FFLAGS) $(RFLAGS)'.
Preprocessing Fortran and Ratfor programs (预处理Fortran 和 Ratfor程序)
‘n.f' 自动从 ‘n.r'或‘n.F'得到。该规则仅仅是与处理器把一个Ratfor 程序或能够预处理的 Fortran 程序转变为标准的 Fortran 程序。使用的精确命令如下:
`.F'
`$(FC) -F $(CPPFLAGS) $(FFLAGS)'.
`.r'
`$(FC) -F $(FFLAGS) $(RFLAGS)'.
Compiling Modula-2 programs(编译Modula-2程序)
‘n.sym'自动由‘n.def'使用命令‘$(M2C) $(M2FLAGS) $(DEFFLAGS)'生成。
‘n.o' 从‘n.mod'生成;命令为:‘$(M2C) $(M2FLAGS) $(MODFLAGS)'。
Assembling and preprocessing assembler programs (汇编以及预处理汇编程序)
‘n.o'自‘n.S'运行C编译器,cpp,生成。命令为:‘$(CPP) $(CPPFLAGS)'。
Linking a single object file (连接一个简单的OBJ文件)
‘n' 自动由‘n.o' 运行C编译器中的连接程序 linker (通常称为
ld)生成。命令为: ‘$(CC) $(LDFLAGS) n.o
$(LOADLIBES) $(LDLIBS)'。该规则对仅有一个源程序的简单程序或对同时含有多个OBJ文件(可能来自于不同的源文件)的程序都能正常工作。如果同时含有多个OBJ文件,则其中必有一个OBJ文件的名字和可执行文件名匹配。例如:
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
对于更复杂的情况,例如没有一个OBJ文件的名字和可执行文件名匹配,您必须为连接写一条具体的命令。每一种能自动生成‘.o’的文件,可以在没有‘-c’选项的情况下使用编译器(‘$(CC)',‘$(FC)'或‘$(PC)'; C编译器‘$(CC)'也适用于汇编程序)自动连接。当然也可以使用OBJ文件作为中间文件,但编译、连接一步完成速度将快很多。
Yacc for C programs (由Yacc生成C程序)
‘n.c'自动由‘n.y'使用命令‘$(YACC) $(YFLAGS)'运行 Yacc生成。
Lex for C programs (由Lex生成C程序)
‘n.c'自动由‘n.l' 运行 Lex生成。命令为:‘$(LEX) $(LFLAGS)'。
Lex for Ratfor programs (由Lex生成Rator程序)
‘n.r'自动由‘n.l' 运行 Lex生成。命令为:‘$(LEX) $(LFLAGS)'。 对于所有的Lex文件,无论它们产生C代码或Ratfor 代码,都使用相同的后缀‘.l’进行转换,在特定场合下,使用make自动确定您使用哪种语言是不可能的。如果make使用‘.l’文件重建一个OBJ文件,它必须猜想使用哪种编译器。它很可能猜想使用的是 C 编译器, 因为C 编译器更加普遍。如果您使用 Ratfor语言, 请确保在makefile文件中提及了‘n.r',使make知道您的选择。否则,如果您专用Ratfor语言,不使用任何C 文件, 请在隐含规则后缀列表中将‘.c’剔除:
.SUFFIXES:
.SUFFIXES: .o .r .f .l ...
Making Lint Libraries from C, Yacc, or Lex programs(由C, Yacc, 或 Lex程序创建Lint库)
‘n.ln' 可以从‘n.c' 运行lint产生。命令为:‘
$(LINT) $(LINTFLAGS) $(CPPFLAGS) –i’。用于C程序的命令和用于‘n.y'或‘n.l'程序相同。
TeX and Web(TeX 和 Web)
‘n.dvi'可以从‘n.tex' 使用命令‘$(TEX)'得到。‘n.tex'可以从‘n.web'使用命令‘$(WEAVE)'得到;或者从‘n.w' (和‘n.ch',如果‘n.ch'存在或可以建造) 使用命令‘$(CWEAVE)'。‘n.p' 可以从‘n.web'使用命令‘$(TANGLE)'产生。‘n.c' 可以从‘n.w' (和‘n.ch',如果‘n.ch'存在或可以建造) 使用命令‘$(CTANGLE)'得到。
Texinfo and Info(Texinfo和Info)
‘n.dvi'可以从‘n.texinfo',‘n.texi', 或‘n.txinfo', 使用命令‘$(TEXI2DVI)
$(TEXI2DVI_FLAGS)'得到。‘n.info'可以从‘n.texinfo',‘n.texi', 或‘n.txinfo', 使用命令‘$(MAKEINFO)
$(MAKEINFO_FLAGS)'创建。
RCS
文件‘n'必要时可以从名为‘n,v'或‘RCS/n,v'的RCS文件中提取。具体命令是:‘$(CO)
$(COFLAGS)'。文件‘n'如果已经存在,即使RCS文件比它新,它不能从RCS文件中提取。用于RCS的规则是最终的规则,参阅万用规则,所以RCS不能够从任何源文件产生,它们必须存在。
SCCS
文件‘n'必要时可以从名为‘s.n'或‘SCCS/s.n'的SCCS文件中提取。具体命令是:‘$(GET)
$(GFLAGS)'。用于SCCS的规则是最终的规则,参阅万用规则,所以SCCS不能够从任何源文件产生,它们必须存在。SCCS的优点是,文件 ‘n' 可以从文件 ‘n.sh'拷贝并生成可执行文件(任何人都可以)。这用于shell的脚本,该脚本在SCCS内部检查。因为RCS 允许保持文件的可执行性,所以您没有必要将该特点用于RCS文件。我们推荐您避免使用SCCS,RCS不但使用广泛,而且是免费的软件。选择自由软件代替相当的(或低劣的)收费软件,是您对自由软件的支持。
通常情况下,您要仅仅改变上表中的变量,需要参阅下面的文档。
隐含规则的命令实际使用诸如COMPILE.c, LINK.p, 和 PREPROCESS.S等等变量,它们的值包含以上列出的命令。Make按照惯例进行处理,如,编译‘.x’源文件的规则使用变量‘COMPILE.x’;从‘.x’源文件生成可执行文件使用变量‘LINK.x’;预处理‘.x’源文件使用变量‘PREPROCESS.x’。
任何产生OBJ文件的规则都使用变量‘OUTPUT_OPTION’;make依据编译时间选项定义该变量的值是‘-o $@’或空值。当源文件分布在不同的目录中,您应该使用‘-O’选项保证输出到正确的文件中;使用变量VPATH时同样(参阅为依赖搜寻目录)。一些系统的编译器不接受针对OBJ文件的‘-o’开关;如果您在这样的系统上运行,并使用了变量VPATH,一些文件的编译输出可能会放到错误的地方。解决办法是将变量OUTPUT_OPTION值设为:‘; mv $*.o $@’。
10.3隐含规则使用的变量
内建隐含规则的命令对预定义变量的使用是开放的;您可以在makefile文件中改变变量的值,也可以使用make的运行参数或在环境中改变,如此,在不对这些规则本身重新定义的情况下,就可以改变这些规则的工作方式。您还可以使用选项‘-R’或‘--no-builtin-variables’删除所有隐含规则使用的变量。
例如,编译C程序的命令实际是‘$(CC) -c $(CFLAGS) $(CPPFLAGS)’,变量缺省的值是‘cc’或空值,该命令实际是‘cc –c’。如重新定义变量‘CC’的值为‘ncc’,则所有隐含规则将使用‘ncc’作为编译C语言源程序的编译器。通过重新定义变量‘CFLAGS’的值为‘-g’,则您可将‘-g’选项传递给每个编译器。所有的隐含规则编译C程序时都使用‘$CC’获得编译器的名称,并且都在传递给编译器的参数中都包含‘$(CFLAGS)’。
隐含规则使用的变量可分为两类:一类是程序名变量(象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 源文件转换为信息文件的程序;缺省为:‘makeinfo'.
TEX
从TeX源产生TeX DVI文件的程序;缺省为:‘tex'.
TEXI2DVI
从Texinfo源产生TeX DVI 文件的程序;缺省为:‘texi2dvi'.
WEAVE
将Web翻译成TeX的程序;缺省为:‘weave'.
CWEAVE
将CWeb翻译成TeX的程序;缺省为:‘cweave'.
TANGLE
将Web翻译成 Pascal的程序;缺省为:‘tangle'.
CTANGLE
将Web翻译成C的程序;缺省为:‘ctangle'.
RM
删除文件的命令;缺省为:‘rm -f'.
这里是值为上述程序附加参数的变量列表。在没有注明的情况下,所有变量的值为空值。
ARFLAGS
用于档案管理程序的标志,缺省为:‘rv'.
ASFLAGS
用于汇编编译器的额外标志 (当具体调用‘.s'或‘.S'文件时)。
CFLAGS
用于C编译器的额外标志。
CXXFLAGS
用于C++编译器的额外标志。
COFLAGS
用于RCS co程序的额外标志。
CPPFLAGS
用于C预处理以及使用它的程序的额外标志 (C和 Fortran 编译器)。
FFLAGS
用于Fortran编译器的额外标志。
GFLAGS
用于SCCS get程序的额外标志。
LDFLAGS
用于调用linker(‘ld’)的编译器的额外标志。
LFLAGS
用于Lex的额外标志。
PFLAGS
用于Pascal编译器的额外标志。
RFLAGS
用于处理Ratfor程序的Fortran编译器的额外标志。
YFLAGS
用于Yacc的额外标志。Yacc。
10.4 隐含规则链
有时生成一个文件需要使用多个隐含规则组成的序列。例如,从文件‘n.y’生成文件‘n.o’,首先运行隐含规则Yacc,其次运行规则cc。这样的隐含规则序列称为隐含规则链。
如果文件‘n.c’存在或在makefile文件中提及,则不需要任何特定搜寻:make首先发现通过C编译器编译‘n.c’可生成该OBJ文件,随后,考虑生成‘n.c’时,则使用运行Yacc的规则。这样可最终更新‘n.c’和‘n.o’。
即使在文件‘n.c’不存在或在makefile文件中没有提及的情况下,make也能想象出在文件‘n.y’和‘n.o’缺少连接!这种情况下,‘n.c’称为中间文件。一旦make决定使用中间文件,它将把中间文件输入数据库,好像中间文件在makefile文件中提及一样;按照隐含规则的描述创建中间文件。
中间文件和其它文件一样使用自己的规则重建,但是中间文件和其它文件相比有两种不同的处理方式。
第一个不同的处理方式是如果中间文件不存在make的行为不同:平常的文件b如果不存在,make认为一个目标依靠文件b,它总是创建文件b,然后根据文件b更新目标;但是文件b若是中间文件,make很可能不管它而进行别的工作,即不创建文件b,也不更新最终目标。只有在文件b的依赖比最终目标‘新’时或有其它原因时,才更新最终目标。
第二个不同点是make在更新目标创建文件b后,如果文件b不再需要,make将把它删除。所以一个中间文件在make运行之前和make运行之后都不存在。Make向您报告删除时打印一条‘rm –f’命令,表明有文件被删除。
通常情况下,任何在makefile文件中提及的目标和依赖都不是中间文件。但是,您可以特别指定一些文件为中间文件,其方法为:将要指定为中间文件的文件作为特殊目标 .INTERMEDIATE的依赖。这种方法即使对采用别的方法具体提及的文件也能生效。
您通过将文件标志为secondary文件可以阻止自动删除中间文件。这时,您将您需要保留的中间文件指定为特殊目标 .SECONDARY的依赖即可。对于secondary文件,make不会因为它不存在而去创建它,也不会自动删除它。secondary文件必须也是中间文件。
您可以列举一个隐含规则的目标格式(例如%.o)作为特殊目标 .PRECIOUS的依赖,这样您就可以保留那些由隐含规则创建的文件名匹配该格式的中间文件。参阅中断和关闭make。
一个隐含规则链至少包含两个隐含规则。例如,从‘RCS/foo.y,v’创建文件‘foo’需要运行RCS、Yacc和cc,文件foo.y和foo.c是中间文件,在运行结束后将被删掉。
没有一条隐含规则可以在隐含规则链中出现两次以上(含两次)。这意味着,make不会简单的认为从文件‘foo.o.o’创建文件foo不是运行linker两次。这还可以强制make在搜寻一个隐含规则链时阻止无限循环。
一些特殊的隐含规则可优化隐含规则链控制的特定情况。例如,从文件foo.c创建文件foo可以被拥有编译和连接的规则链控制,它使用foo.o作为中间文件。但是对于这种情况存在一条特别的规则,使用简单的命令cc可以同时编译和连接。因为优化规则在规则表中的前面,所以优化规则和一步一步的规则链相比,优先使用优化规则。
10.5定义与重新定义格式规则
您可以通过编写格式规则定义隐含规则。该规则看起来和普通规则类似,不同之处在于格式规则的目标中包含字符‘%’(只有一个)。目标是匹配文件名的格式;字符‘%’可以匹配任何非空的字符串,而其它字符仅仅和它们自己相匹配。依赖用‘%’表示它们的名字和目标名关联。
格式‘%.o : %.c’是说将任何‘stem.c’文件编译为‘stem.o’文件。
在格式规则中使用的‘%’扩展是在所有变量和函数扩展以后进行的,它们是在makefile文件读入时完成的。参阅使用变量和转换文本函数。
10.5.1格式规则简介
格式规则是在目标中包含字符‘%’(只有一个)的规则,其它方面看起来和普通规则相同。目标是可以匹配文件名的格式,字符‘%’可以匹配任何非空的字符串,而其它字符仅仅和它们自己相匹配。
例如‘%.c’匹配任何以‘.c’结尾的文件名;‘s.%.c’匹配以‘s.’开始并且以‘.c’结尾的文件名,该文件名至少包含5个字符(因为‘%’至少匹配一个字符)。匹配‘%’的子字符串称为stem(径)。依赖中使用‘%’表示它们的名字中含有和目标名相同的stem。要使用格式规则,文件名必须匹配目标的格式,而且符合依赖格式的文件必须存在或可以创建。下面规则:
%.o : %.c ; command...
表明要创建文件‘n.o’,使用‘n.c’作为它的依赖,而且文件‘n.c’ 必须存在或可以创建。
在格式规则中,依赖有时不含有‘%’。这表明采用该格式规则创建的所有文件都是采用相同的依赖。这种固定依赖的格式规则在有些场合十分有用。
格式规则的依赖不必都包含字符‘%’,这样的规则是一个有力的常规通配符,它为任何匹配该目标格式规则的文件提供创建方法。参阅定义最新类型的缺省规则。
格式规则可以有多个目标,不象正常的规则,这种规则不能扮演具有相同依赖和命令的多条不同规则。如果一格式规则具有多个目标,make知道规则的命令对于所有目标来说都是可靠的,这些命令只有在创建所目标时才执行。当为匹配一目标搜寻格式规则时,规则的目标格式和规则要匹配的目标不同是十分罕见的,所以make仅仅担心目前对文件给出命令和依赖是否有问题。注意该文件的命令一旦执行,所有目标的时间戳都会更新。
格式规则在makefile文件中的次序很重要,因为这也是考虑它们的次序。对于多个都能使用的规则,使用最先出现的规则。您亲自编写的规则比内建的规则优先。注意依赖存在或被提及的规则优先于依赖需要经过隐含规则链生成的规则。
10.5.2格式规则的例子
这里有一些实际在make中预定义的格式规则例子,第一个,编译‘.c’文件生成‘.o’文件的规则:
%.o : %.c
$(CC) -c
$(CFLAGS) $(CPPFLAGS) $< -o $@
定义了一条编译‘x.c’文件生成‘x.o’文件的规则,命令使用自动变量‘$@’和‘$<’ 替换任何情况使用该规则的目标文件和源文件。参阅自动变量。
第二个内建的例子:
% :: RCS/%,v
$(CO)
$(COFLAGS) $<
定义了在子目录‘RCS’中根据相应文件‘x.v’生成文件‘x’的规则。因为目标是‘%’,只要相对应的依赖文件存在,该规则可以应用于任何文件。双冒号表示该规则是最终规则,它意味着不能是中间文件。参阅万用规则。
下面的格式规则有两个目标:
%.tab.c %.tab.h: %.y
bison -d
$<
这告诉make执行命令‘bison -d x.y’将创建‘x.tab.c’和‘x.tab.h’。如果文件foo依靠文件‘parse.tab.o’和‘scan.o’,而文件‘scan.o’又依靠文件‘parse.tab.h’,当‘parse.y’发生变化,命令‘bison -d parse.y’执行一次。‘parse.tab.o’和‘scan.o’的依赖也随之更新。(假设文件‘parse.tab.o’由文件‘parse.tab.c’编译生成,文件‘scan.o’由文件‘scan.c’生成,当连接‘parse.tab.o’、‘scan.o’和其它依赖生成文件foo时,上述规则能够很好执行。)
10.5.3自动变量
假设您编写一个编译‘.c’文件生成‘.o’文件的规则:您怎样编写命令‘CC’,使它能够操作正确的文件名?您当然不能将文件名直接写进命令中,因为每次使用隐含规则操作的文件名都不一样。
您应该使用make的另一个特点,自动变量。这些变量在规则每次执行时都基于目标和依赖产生新值。例如您可以使用变量‘$@’代替目标文件名,变量‘$<’代替依赖文件名。
下面是自动变量列表:
$@
规则的目标文件名。如果目标是一个档案成员,则变量‘$@’ 档案文件的文件名。对于有多个目标的格式规则(参阅格式规则简介),变量‘$@’是那个导致规则命令运行的目标文件名。
$%
当目标是档案成员时,该变量是目标成员名,参阅使用make更新档案文件。例如,如果目标是‘foo.a(bar.o)',则‘$%'的值是‘bar.o',‘$@'的值是‘foo.a'。如果目标不是档案成员,则‘$%'是空值。
$<
第一个依赖的文件名。如果目标更新命令来源于隐含规则,该变量的值是隐含规则添加的第一个依赖。参阅使用隐含规则。
$?
所有比目标‘新’的依赖名,名字之间用空格隔开。对于为档案成员的依赖,只能使用已命名的成员。参阅使用make更新档案文件。
$^
所有依赖的名字,名字之间用空格隔开。对于为档案成员的依赖,只能使用已命名的成员。参阅使用make更新档案文件。对同一个目标来说,一个文件只能作为一个依赖,不管该文件的文件名在依赖列表中出现多少次。所以,如果在依赖列表中,同一个文件名出现多次,变量‘$^’的值仍然仅包含该文件名一次。
$+
该变量象‘$^',但是,超过一次列出的依赖将按照它们在makefile文件中出现的次序复制。这主要的用途是对于在按照特定顺序重复库文件名很有意义的地方使用连接命令。
$*
和隐含规则匹配的stem(径),参阅格式匹配。如果一个目标为‘dir/a.foo.b',目标格式规则为:‘a.%.b' ,则stem为‘dir/foo'。在构建相关文件名时stem 十分有用。在静态格式规则中,stem是匹配目标格式中字符‘%’的文件名中那一部分。在一个没有stem具体规则中;变量‘$*' 不能以该方法设置。如果目标名以一种推荐的后缀结尾(参阅过时的后缀规则),变量‘$*'设置为目标去掉该后缀后的部分。例如,如果目标名是‘foo.c',则变量‘$*' 设置为‘foo', 因为‘.c' 是一个后缀。GNU make 处理这样奇怪的事情是为了和其它版本的make兼容。在隐含规则和静态格式规则以外,您应该尽量避免使用变量‘$*'。在具体规则中如果目标名不以推荐的后缀结尾,则变量‘$*’在该规则中设置为空值。
当您希望仅仅操作那些改变的依赖,变量‘$?' 即使在具体的规则中也很有用。例如,假设名为‘lib’的档案文件包含几个OBJ文件的拷贝,则下面的规则仅将发生变化的OBJ文件拷贝到档案文件:
lib: foo.o bar.o lose.o win.o
ar r lib $?
在上面列举的变量中,有四个变量的值是单个文件名。三个变量的值是文件名列表。这七个变量都有仅仅存放文件的路径名或仅仅存放目录下文件名的变体。变量的变体名是由变量名追加字母‘D’或‘F’构成。这些变体在GNU
make中处于半废状态,原因是使用函数T dir和notdir 能够得到相同的结果。参阅文件名函数。注意,‘F'变体省略所有在dir函数中总是输出的结尾斜杠这里是这些变体的列表:
`$(@D)'
目标文件名中的路径部分,结尾斜杠已经移走。如果变量`$@'的值是`dir/foo.o',变体
`$(@D)'的值是`dir'。 如果变量`$@'的值不包含斜杠,则变体的值是`.'。
`$(@F)'
目标文件名中的真正文件名部分。如果变量`$@'的值是`dir/foo.o',变体 `$(@F)'的值是` foo.o '。`$(@F)' 等同于 `$(notdir $@)'。
`$(*D)'
`$(*F)'
stem(径)中的路径名和文件名;在这个例子中它们的值分别为:`dir' 和 `foo' 。
`$(%D)'
`$(%F)'
档案成员名中的路径名和文件名;这仅对采用‘archive(member)’形式的档案成员目标有意义,并且当成员包含路径名时才有用。参阅档案成员目标。
`$(<D)'
`$(<F)'
第一个依赖名中的路径名和文件名。
`$(^D)'
`$(^F)'
所有依赖名中的路径名和文件名列表。
`$(?D)'
`$(?F)'
所有比目标‘新’的依赖名中的路径名和文件名列表。
注意,在我们讨论自动变量时,我们使用了特殊格式的惯例;我们写"the value of‘$<'", 而不是"the
variable <" ;和我们写普通变量,例如变量 objects 和 CFLAGS一样。我们认为这种惯例在这种情况下看起来更加自然。这并没有其它意义,变量‘$<'的变量名为 < 和变量‘$(CFLAGS)' 实际变量名为CFLAGS一样。您也可以使用‘$(<)'代替‘$<'。
10.5.4格式匹配
目标格式是由前缀、后缀和它们之间的通配符%组成,它们中的任一个或两个都可以是空值。格式匹配一个文件名只有该文件名是以前缀开始,后缀结束,而且两者不重叠的条件下,才算匹配。前缀、后缀之间的文本成为径(stem)。当格式‘%.o’匹配文件名‘test.o’时,径(stem)是‘test’。格式规则中的依赖将径(stem)替换字符%,从而得出文件名。对于上例中,如果一个依赖为‘%.c’,则可扩展为‘test.c’。
当目标格式中不包含斜杠(实际并不是这样),则文件名中的路径名首先被去除,然后,将其和格式中的前缀和后缀相比较。在比较之后,以斜杠结尾的路径名,将会加在根据格式规则的依赖规则产生的依赖前面。只有在寻找隐含规则时路径名才被忽略,在应用时路径名绝不能忽略。例如,‘e%t’和文件名‘src/eat’匹配,stem(径)是‘src/a’。当依赖转化为文件名时,stem中的路径名将加在前面,stem(径)的其余部分替换‘%’。使用stem(径)
‘src/a’和依赖格式规则‘c%r’匹配得到文件名‘src/car’。
10.5.5万用规则
一个格式规则的目标仅仅包含‘%’,它可以匹配任何文件名,我们称这些规则为万用规则。它们非常有用,但是make使用它们的耗时也很多,因为make必须为作为目标和作为依赖列出的每一个文件都考虑这样的规则。
假设makefile文件提及了文件foo.c。为了创建该目标,make将考虑是通过连接一个OBJ文件‘foo.c.o’创建,或是通过使用一步的C编译连接程序从文件foo.c.c创建,或是编译连接Pascal程序foo.c.p创建,以及其它的可能性等。
我们知道make考虑的这些可能性是很可笑的,因为foo.c就是一个C语言源程序,不是一个可执行程序。如果make考虑这些可能性,它将因为这些文件诸如foo.c.o和foo.c.p等都不存在最终拒绝它们。但是这些可能性太多,所以导致make的运行速度极慢。
为了加快速度,我们为make考虑匹配万用规则的方式设置了限制。有两种不同类型的可以应用的限制,在您每次定义一个万用规则时,您必须为您定义的规则在这两种类型中选择一种。
一种选择是标志该万用规则是最终规则,即在定义时使用双冒号定义。一个规则为最终规则时,只有在它的依赖存在时才能应用,即使依赖可以由隐含规则创建也不行。换句话说,在最终规则中没有进一步的链。
例如,从RCS和SCCS文件中抽取原文件的内建的隐含规则是最终规则,则如果文件‘foo.c,v' 不存在,make绝不会试图从一个中间文件‘foo.c,v.o’或‘RCS/SCCS/s.foo.c,v’在创建它。 RCS 和 SCCS 文件一般都是最终源文件,它不能从其它任何文件重新创建,所以,make可以记录时间戳,但不寻找重建它们的方式。
如果您不将万用规则标志为最终规则,那么它就是非最终规则。一个非最终万用规则不能用于指定特殊类型数据的文件。如果存在其它规则(非万用规则)的目标匹配一文件名,则该文件名就是指定特殊类型数据的文件名。
例如,文件名‘foo.c' 和格式规则 `%.c : %.y' (该规则运行Yacc)。无论该规则是否实际使用(如果碰巧存在文件‘foo.y’,该规则将运行),和目标匹配的事实就能足够阻止任何非最终万用规则在文件foo.c上使用。这样,make 考虑就不试图从文件‘foo.c.o',‘foo.c.c', ‘foo.c.p'等创建可执行的‘foo.c'。
内建的特殊伪格式规则是用来认定一些特定的文件名,处理这些文件名的文件时不能使用非最终万用规则。这些伪格式规则没有依赖和命令,它们用于其它目的时被忽略。例如,内建的隐含规则:
%.p :
存在可以保证Pascal源程序如‘foo.p' 匹配特定的目标格式,从而阻止浪费时间寻找‘foo.p.o' 或‘foo.p.c'。
在后缀规则中,为后缀列表中的每一个有效后缀都创建了伪格式规则,如‘%.p' 。参阅过时的后缀规则。
10.5.6删除隐含规则
通过定义新的具有相同目标和依赖但不同命令的规则,您可以重载内建的隐含规则(或重载您自己定义的规则)。一旦定义新的规则,内建的规则就被代替。
新规则在隐含规则次序表中的位置由您编写规则的地方决定。
通过定义新的具有相同目标和依赖但不含命令的规则,您可以删除内建的隐含规则。例如,下面的定义规则将删除运行汇编编译器的隐含规则:
%.o : %.s
10.6 定义最新类型的缺省规则
您通过编写不含依赖的最终万用格式规则,您可以定义最新类型的缺省规则。参阅万用规则。这和其它规则基本一样,特别之处在于它可以匹配任何目标。因此,这样的规则的命令可用于所有没有自己的命令的目标和依赖,以及用于那些没有其它隐含规则可以应用的目标和依赖。
例如,在测试makefile时,您可能不关心源文件是否含有真实数据,仅仅关心它们是否存在。那么,您可以这样做:
%::
touch $@
这导致所有必需的源文件(作为依赖)都自动创建。
您可以为没有规则的目标以及那些没有具体指定命令的目标定义命令。要完成上述任务,您需要为特殊目标.DEFAULT 编写规则。这样的规则可以在所有具体规则中用于没有作为目标出现以及不能使用隐含规则的依赖。自然,如果您不编写定义则没有特殊目标.DEFAULT 的规则。
如果您使用特殊目标.DEFAULT 而不带任何规则和命令:
.DEFAULT:
则以前为目标.DEFAULT定义的命令被清除。如此make的行为和您从来没有定义目标.DEFAULT一样。
如果您不需要一个目标从万用规则和目标.DEFAULT 中得到命令,也不想为该目标执行任何命令,您可以在定义时使用空命令。参阅使用空命令。
您可以使用最新类型规则重载另外一个makefile文件的一部分内容。参阅重载其它makefile文件。