Chinaunix首页 | 论坛 | 博客
  • 博客访问: 104613898
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类: C/C++

2008-04-17 17:17:31

9 运行make
讲述编译程序的makefile文件,可以由多种方式实现。最简单的方式是编译所有过期的文件,对于通常所写的makefile文件,如果不使用任何参数运行make,那么将这样执行。
但是您也许仅仅更新一部分文件;您也许需要使用不同的编译器或不同的编译选项;您也许仅仅希望找出过时的文件而不更新它们。这些只有通过在运行make时给出参数才能实现。退出make状态有三种情况:
0
表示make成功完成退出。
2
退出状态为2表示make运行中遇到错误,它将打印信息描述错误。
1
退出状态为1表示您运行make时使用了‘-q’标志,并且make决定一些文件没有更新。参阅代替执行命令。
9.1 指定makefile文件的参数
指定makefile文件名的方法是使用‘-f’或‘--file’选项(‘--makefile’也能工作)。例如,‘-f altmake’说明名为‘altmake’的文件作为makefile文件。
如果您连续使用‘-f’标志几次,而且每一个‘-f’后面都带有参数,则所有指定的文件将连在一起作为makefile文件。
如果您不使用‘-f’或‘--file’选项,缺省的是按次序寻找‘GNUmakefile', ‘makefile', 和 ‘Makefile',使用这三个中第一个能够找到的存在文件或能够创建的文件,参阅编写makefile文件。
9.2指定最终目标的参数
最终目标(gaol)是make最终努力更新的目标。其它更新的目标是因为它们作为最终目标的依赖,或依赖的依赖,等等以此类推。
缺省情况下,makefile文件中的第一个目标是最终目标(不计算那些以句点开始的目标)。因此,makefile文件的第一个编译目标是对整个程序或程序组描述。如果第一个规则同时拥有几个目标,只有该规则的第一个目标是缺省的最终目标。
您可以使用make的参数指定最终目标。方法是使用目标的名字作为参数。如果您指定几个最终目标,make按您命名时的顺序一个接一个的处理它们。
任何在makefile文件中出现的目标都能作为最终目标(除了以‘-’开始或含有‘=’的目标,它们一种解析为开关,另一种是变量定义)。即使在makefile文件中没有出现的目标,按照隐含规则可以说明怎样生成,也能指定为最终目标。
Make将在命令行中使用特殊变量MAKECMGOALS设置您指定的最终目标。如果没有在命令行指定最终目标,该变量的值为空值。注意该变量值能在特殊场合下使用。
一个合适的例子是在清除规则中避免删除包括‘.d’的文件(参阅自动产生依赖),因这样make不会一创建它们,就立即又删除它们:
sources = foo.c bar.c
 
ifneq ($(MAKECMDGOALS),clean)
include $(sources:.c=.d)
endif
指定最终目标的一个用途是仅仅编译程序的一部分或程序组中的几个程序。如是这样,您可以将您希望变异的文件指定为最终目标。例如,在一个路径下包含几个程序,一个makefile文件以下面的格式开始:
.PHONY: all
all: size nm ld ar as
如果您仅对程序size编译,则您可以使用‘make size’命令,这样就只有您指定的程序才重新编译。
指定最终目标的另一个用途是编译产生哪些没有正常生成的文件。例如,又一个文件需要调试,或一个版本的程序需要编译进行测试,然而该文件不是makefile文件规则中缺省最终目标的依赖,此时,可以使用最终目标参数指定它们。
指定最终目标的另一个用途是运行和一个假想目标(参阅假想目标)或空目标(使用空目标记录事件)相联系的命令。许多makefile文件包含一个假想目标‘clean’删除除了原文件以外的所有文件。正常情况下,只有您具体指明使用‘make clean’命令,make才能执行上述任务。下面列出典型的假想目标和空目标的名称。对GNU make软件包使用的所有标准目标名参阅用户标准目标:
‘all' 

创建makefile文件的所有顶层目标。

`clean' 

删除所有make正常创建的文件。

`mostlyclean' 

象假象目标‘clean’,但避免删除人们正常情况下不重新建造的一少部分文件。例如,用于GCC的目标‘mostlyclean’不删除‘libgcc.a’,因为重建它的情况十分稀少,而且创建它又需要很多时间。
`distclean' 

`realclean' 

`clobber' 

这些目标可能定义为比目标‘clean’ 删除更多的文件。例如,删除配置文件或为编译正常创建的准备文件,甚至makefile文件自身不能创建的文件。
‘install’
向命令搜寻目录下拷贝可执行文件;向可执行文件寻找目录下拷贝可执行文件使用的辅助文件。
‘print’
打印发生变化的文件列表。
‘tar’
创建源文件的压缩‘tar’文件。
‘shar’
为源文件创建一个shell的档案文件。
‘dist’
为源文件创建一个发布文件。这可能是‘tar’文件, ‘shar’文件,或多个上述的压缩版本文件。
‘TAGS’
更新该程序的‘tags’标签。
`check' 

`test' 

对该makefile文件创建的程序执行自我测试。
9.3 代替执行命令
makefile文件告诉make怎样识别一个目标是否需要更新以及怎样更新每一个目标。但是更新目标并不是您一直需要的,一些特定的选项可以用来指定make的其它活动:
`-n' 

`--just-print' 

`--dry-run' 

`--recon' 

‘No-op’。make的这项活动是打印用于创建目标所使用的命令,但并不执行它们。
`-t' 

`--touch' 

‘touch’。这项活动是做更新标志,实际却不更改它们。换句话说,make假装编译了目标,但实际对它们没有一点儿改变。
`-q' 

`--question' 

‘question’。这项活动是暗中察看目标是否已经更新;但是任何情况下也不执行命令。换句话说,即不编译也不输出。
`-W file' 

`--what-if=file' 

`--assume-new=file' 

`--new-file=file' 

‘What if’。每一个‘-W’标志后跟一个文件名。所有文件名的更改时间被make记录为当前时间,但实际上更改时间保持不变。如果您要更新文件,您可以使用‘-W’标志和‘-n’标志连用看看将发生什么。
使用标志‘-n’,make打印那些正常执行的命令,但却不执行它们。

使用标志‘-t’,make忽略规则中的命令,对那些需要更新的目标使用‘touch’命令。如果不使用‘-s’或.SILENT,‘touch’命令同样打印。为了提高执行效率,make并不实际调用程序touch,而是使touch直接运行。

使用标志‘-q’,make不打印输出也不执行命令,如果所有目标都已经更新到最新,make的退出状态是0;如果一部分需要更新,退出状态是1;如果make遇到错误,退出状态是2,因此您可以根据没有更新的目标寻找错误。

在运行make时对以上三个标志如果同时两个或三个将产生错误。标志‘-n’、‘-t’和‘-s’对那些以字符‘+’开始的命令行和包含字符串‘$(MAKE)' 或‘${MAKE}'命令行不起作用。注意仅有这些以字符‘+’开始的命令行和包含字符串‘$(MAKE)' 或‘${MAKE}'命令行运行时不注意这些选项。参阅变量MAKE的工作方式。

‘-W’标志有一下两个特点:

l         如果同时使用标志‘-n’或‘-q’,如果您更改一部分文件,看看make将会做什么。

l         没有使用标志‘-n’或‘-q’,如果make运行时采用标志‘-W’,则make假装所有文件已经更新,但实际上不更改任何文件。

注意选项‘-p’和‘-v’允许您得到更多的make信息或正在使用的makefile文件的信息(参阅选项概要)。

9.4避免重新编译文件
有时您可能改变了一个源文件,但您并不希望编译所有依靠它的文件。例如,假设您在一个许多文件都依靠的头文件种添加了一个宏或一个声明,按照保守的规则,make认为任何对于该头文件的改变,需要编译所有依靠它的文件,但是您知道那是不必要的,并且您没有等待它们完全编译的时间。

如果您提前了解改变头文件以前的问题,您可以使用‘-t’选项。该标志告诉make不运行规则中的命令,但却将所有目标的时间戳改到最新。您可按下述步骤实现上述计划:

1、用make命令重新编译那些需要编译的源文件;

2、更改头文件;

3、使用‘make –t’命令改变所有目标文件的时间戳,这样下次运行make时就不会因为头文件的改变而编译任何一个文件。

如果在重新编译那些需要编译的源文件前已经改变了头文件,则按上述步骤做已显得太晚了;作为补救措施,您可以使用‘-o file’标志,它能将指定的文件的时间戳假装改为以前的时间戳(参阅选项概要)。这意味着该文件没有更改,因此您可按下述步骤进行:

1、使用‘make -o file’命令重新编译那些不是因为改变头文件而需要更新的文件。如果涉及几个头文件,您可以对每个头文件都使用‘-o’标志进行指定。

2、使用‘make –t’命令改变所有目标文件的时间戳。

9.5变量重载
使用‘=’定义的变量:‘v=x’将变量v的值设为x。如果您用该方法定义了一个变量,在makefile文件后面任何对该变量的普通赋值都将被make忽略,要使它们生效应在命令行将它们重载。

最为常见的方法是使用传递附加标志给编译器的灵活性。例如,在一个makefile文件中,变量CFLAGS已经包含了运行C编译器的每一个命令,因此,如果仅仅键入命令make时,文件‘foo.c’将按下面的方式编译:

cc -c $(CFLAGS) foo.c
这样您在makefile文件中对变量CFALAGS设置的任何影响编译器运行的选项都能生效,但是每次运行make时您都可以将该变量重载,例如:如果您说‘make CFLAGS='-g -O'’,任何C编译器都将使用‘cc -c -g -O’编译程序。这还说明了在重载变量时,怎样使用shell命令中的引用包括空格和其它特殊字符在内的变量的值。

变量CFALAGS仅仅是您可以使用这种方式重载的许多标准变量中的一个,这些标准变量的完整列表见隐含规则使用的变量。

您也可以编写makefile察看您自己的附加变量,从而使用户可通过更改这些变量控制make运行时的其它面貌。

当您使用命令参数重载变量时,您可以定义递归调用扩展型变量或简单扩展型变量。上例中定义的是递归调用扩展型变量,如果定义简单扩展型变量,请使用‘:=’代替‘=’。注意除非您在变量值中使用变量引用或函数调用,这两种变量没有任何差异。

利用这种方式也可以改变您在makfile文件中重载的变量。在makfile文件中重载的变量是使用override指令,是和‘override variable = value’相似的命令行。详细内容参阅override指令。

9.6 测试编译程序
正常情况下,在执行shell命令时一旦有错误发生,make立即退出返回非零状态;不会为任何目标继续运行命令。错误表明make不能正确的创建最终目标,并且make一发现错误就立即报告。

当您编译您修改过的程序时,这不是您所要的结果。您希望make能够经可能的试着编译每一个程序,并尽可能的显示每一个错误。

这种情况下,您可以使用‘-k’或‘--keep-going’选项。这种选项告诉make遇到错误返回非零状态之前,继续寻找该目标的依赖,如果有必要则重新创建它们。例如,在编译一个目标文件时发现错误,即使make已经知道连接它们已是不可能的, ‘make –k’也将继续编译其它目标文件。除在shell命令失败后继续运行外,即使发在make不知道如何创建的目标和依赖文件以后,‘make –k’也将尽可能的继续运行。在没有‘-k’选项时,这些错误将是致命的(参阅选项概要)。

通常情况下,make的行为是基于假设您的目标是使最终目标更新;一旦它发现这是不可能的它就立即报告错误。选项‘-k’说真正的目标是尽可能测试改变对程序的影响,发现存在的问题,以便在下次运行之前您可以纠正它们。这是Emacs M-x compile命令缺省传递‘-k’选项的原因。

9.7 选项概要
下面是所有make能理解的选项列表:

`-b' 

`-m' 

和其它版本make兼容时,这些选项被忽略。

`-C dir' 

`--directory=dir' 

在将makefile读入之前,把路径切换到‘dir’下。如果指定多个‘-C’选项,每一个都是相对于前一个的解释:‘-C/-C etc’等同于‘-C/etc’。该选项典型用在递归调用make过程中,参阅递归调用make。

‘-d’

在正常处理后打印调试信息。调试信息说明哪些文件用于更新,哪个文件作为比较时间戳的标准以及比较的结果,哪些文件实际上需要更新,需要考虑、使用哪些隐含规则等等----一切和make决定最终干什么有关的事情。‘-d’选项等同于‘--debug=a’选项(参见下面内容)。

`--debug[=options]' 

在正常处理后打印调试信息。可以选择各种级别和类型的输出。如果没有参数,打印‘基本’级别的调试信息。以下是可能的参数,仅仅考虑第一个字母,各个值之间使用逗号或空格隔开:

a (all) 

显示所有调试信息,该选项等同于‘-d’选项。

b (basic) 

基本调试信息打印每一个已经过时的目标,以及它们重建是否成功。

v (verbose) 

比‘基本’级高一个的等级的调试信息。包括makefile文件的语法分析结果,没有必要更新的依赖等。该选项同时包含基本调试信息。

i (implicit) 

打印隐含规则搜寻目标的信息。该选项同时包含基本调试信息。

j (jobs) 

打印各种子命令调用的详细信息。

m (makefile) 

以上选项不包含重新创建makefile文件的信息。该选项包含了这方面的信息。注意,选项‘all’也不包含这方面信息。该选项同时包含基本调试信息。

`-e' 

`--environment-overrides' 

设置从环境中继承来的变量的优先权高于makefile文件中的变量的优先权。参阅环境变量。

`-f file' 

`--file=file' 

`--makefile=file' 

将名为‘file’的文件设置为makefile文件。参阅编写makefile文件。

`-h' 

`--help' 

向您提醒make 能够理解的选项,然后退出。

`-i' 

`--ignore-errors' 

忽略重建文件执行命令时产生的所有错误。 

`-I dir' 

`--include-dir=dir'

指定搜寻包含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)' 

所有比目标‘新’的依赖名中的路径名和文件名列表。

注意,在我们讨论自动变量时,我们使用了特殊格式的惯例;我们写"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文件。

10.7 过时的后缀规则
后缀规则是定义隐含规则的过时方法。后缀规则因为格式规则更为普遍和简洁而被废弃。它们在GNU make中得到支持是为了和早期的makefile文件兼容。它们分为单后缀和双后缀规则。

双后缀规则被一对后缀定义:目标后缀和源文件后缀。它可以匹配任何文件名以目标后缀结尾的文件。相应的隐含依赖通过在文件名中将目标后缀替换为源文件后缀得到。一个目标和源文件后缀分别为‘.o’和‘.c’双后缀规则相当于格式规则`%.o : %.c'

单后缀规则被单后缀定义,该后缀是源文件的后缀。它匹配任何文件名,其相应的依赖名是将文件名添加源文件后缀得到。源文件后缀为‘.c’的单后缀规则相当于格式规则‘% : %.c’。

通过比较规则目标和定义的已知后缀列表识别后追规则。当make见到一个目标后缀是已知后缀的规则时,该规则被认为是一个单后缀规则。当make见到一个目标后缀包含两个已知后缀的规则时,该规则被认为是一个双后缀规则。

例如,‘.o’和‘.c’都是缺省列表中的已知后缀。所以,如果您定义一个规则,其目标是‘.c.o’,则make认为是一个双后缀规则,源文件后缀是‘.c’,目标后缀是‘.o’。这里有一个采用过时的方法定义编译C语言程序的规则:

.c.o:
        $(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<
后缀规则不能有任何属于它们自己的依赖。如果它们有依赖,它们将不是作为后缀规则使用,而是以令人啼笑皆非的方式处理正常的文件。例如,规则:

.c.o: foo.h
        $(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<
告诉从依赖foo.h生成文件名为‘.c.o’的文件,并不是象格式规则: 

%.o: %.c foo.h
        $(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<
告诉从‘.c'文件生成 ‘.o' 文件‘.c'的方法:创建所有‘.o' 文件使用该格式规则,而且同时使用依赖文件‘foo.h'

没有命令的后缀规则也没有意义。它们并不没有命令的格式规则那样移去以前的规则(参阅删除隐含规则)。他们仅仅简单的在数据库中加入后缀或双后缀作为一个目标。

已知的后缀是特殊目标‘.SUFFIXES’简单的依赖名。通过为特殊目标‘.SUFFIXES’编写规则加入更多的依赖,您可以添加您自己的已知后缀。例如:

.SUFFIXES: .hack .win
把‘.hack' 和‘.win'添加到了后缀列表中。

如果您希望排除缺省的已知后缀而不是仅仅的添加后缀,那么您可以为特殊目标‘.SUFFIXES’编写没有依赖的规则。通过这种方式,可以完全排除特殊目标‘.SUFFIXES’存在的依赖。接着您可以编写另外一个规则添加您要添加的后缀。例如,

.SUFFIXES:            # 删除缺省后缀
.SUFFIXES: .c .o .h   # 定义自己的后缀列表
标志‘-r'或‘--no-builtin-rules'也能把缺省的后缀列表清空。

变量SUFFIXES在make读入任何makefile文件之前定义缺省的后缀列表。您可以使用特殊目标‘.SUFFIXES’改变后缀列表,但这不能改变变量SUFFIXES的值。

10.8隐含规则搜寻算法
这里是make为一个目标‘t’搜寻隐含规则的过程。这个过程用于任何没有命令的双冒号规则,用于任何不含命令的普通规则的目标,以及用于任何不是其它规则目标的依赖。这个过程也能用于来自隐含规则的依赖递归调用该过程搜寻规则链。

在本算法中不提及任何后缀规则,因为后缀规则在makefile文件读入时转化为了格式规则。

对于个是‘archive(member)’的档案成员目标,下述算法重复两次,第一次使用整个目标名‘t’,如果第一次运行没有发现规则,则第二次使用‘(member)’作为目标‘t’。

1、            在‘t’中分离出路径部分,称为‘d’,剩下部分称为‘n’。例如如果‘t’是‘src/foo.o’,那么‘d’是‘src/’;‘n’是‘foo.o’。

2、            建立所有目标名匹配‘t’和‘n’的格式规则列表。如果目标格式中含有斜杠,则匹配‘t’,否则,匹配‘n’。

3、            如果列表中有一个规则不是万用规则,则从列表中删除所有非最终万用规则。

4、            将没有命令的规则也从列表中移走。

5、            对每个列表中的格式规则:

1、  寻找stem‘s’,也就是和目标格式中%匹配的‘t’或‘n’部分。

2、  使用stem‘s’计算依赖名。如果目标格式不包含斜杠,则将‘d’添加在每个依赖的前面。

3、  测试所有的依赖是否存在或能够创建。(如果任何文件在makefile中作为目标或依赖被提及,则我们说它应该存在。)如果所有依赖存在或能够创建,或没有依赖,则可使用该规则。

6、            如果到现在还没有发现能使用的规则,进一步试。对每一个列表中的规则:

1、  如果规则是最终规则,则忽略它,继续下一条规则。

2、  象上述一样计算依赖名。

3、  测试所有的依赖是否存在或能够创建。

4、  对于不存在的依赖,按照该算法递归调用查找是否能够采用隐含规则创建。

5、  如果所有依赖存在或能使用隐含规则创建,则应用该规则。

7、            如果没有隐含规则,则如有用于目标‘.DEFAULT’规则,则应用该规则。在这种情况下,将目标‘.DEFAULT’的命令给与‘t’。

一旦找到可以应用的规则,对每一个匹配的目标格式(无论是‘t’或‘n’)使用stem‘s’替换%,将得到的文件名储存起来直到执行命令更新目标文件‘t’。在这些命令执行以后,把每一个储存的文件名放入数据库,并且标志已经更新,其时间戳和目标文件‘t’一样。

如果格式规则的命令为创建‘t’执行,自动变量将设置为相应的目标和依赖(参阅自动变量)。
阅读(204) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~