GNU Make 使用手册(中译版) 第2部分

原文链接:http://blog.chinaunix.net/resserver.php?blogId=2652&resource=gunmake.htm


 

GNU Make 使用手册(中译版)


翻译:于凤昌

译者注:本人在阅读Linux源代码过程中发现如果要全面了解Linux的结构、理解Linux的编程总体设计及思想必须首先全部读通Linux源代码中各级的Makefile文件。目前,在网上虽然有一些著作,但都不能全面的解释Linux源代码中各级的Makefile文件,因此本人认真阅读了GNU Make 使用手册(3.79)版原文,在此基础上翻译了该手册,以满足对Linux源代码有兴趣或者希望采用GCC编写程序但对缺乏GNU Make全面了解之人士的需要。本人是业余爱好不是专业翻译人士,如果有问题请通过电子信箱与我联系共同商讨,本人的E-mail为:yfc70@public2.lyptt.ha.cn注意在文章中出现的斜体加粗字表示章节。

GNU make Version 3.79

April 2000

Richard M. Stallman and Roland McGrath

 

目录

9         运行make

9.1指定Makefile文件的参数

9.2指定最终目标的参数

9.3代替执行命令

9.4避免重新编译文件

9.5变量重载

9.6测试编译程序

9.7选项概要

10 使用隐含规则

10.1使用隐含规则

10.2隐含规则目录

10.3隐含规则使用的变量

10.4隐含规则链

10.5定义与重新定义格式规则

10.5.1格式规则简介

10.5.2格式规则的例子

10.5.3自动变量

10.5.4格式匹配

10.5.5万用规则

10.5.6删除隐含规则

10.6定义最新类型的缺省规则

10.7过时的后缀规则

10.8隐含规则搜寻算法

11 使用make更新档案文件

11.1档案成员目标

11.2档案成员目标的隐含规则

11.2.1更新档案成员的符号索引表

11.3使用档案的危险

11.4档案文件的后缀规则

12 GNU make的特点

13 不兼容性和失去的特点

14 Makefile文件惯例

14.1makefile文件的通用惯例

14.2makefile文件的工具

14.3指定命令的变量

14.4安装路径变量

14.5用户标准目标

14.6安装命令分类

15快速参考

16make产生的错误

17复杂的Makefile文件例子

   附录  名词翻译对照表

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) ___FCKpd___64lt; -o $@
        
        

定义了一条编译‘x.c’文件生成‘x.o’文件的规则,命令使用自动变量‘$@’和‘$<’ 替换任何情况使用该规则的目标文件和源文件。参阅自动变量

第二个内建的例子:

% :: RCS/%,v
        
        
        $(CO) $(COFLAGS) ___FCKpd___66lt;
        
        

定义了在子目录‘RCS’中根据相应文件‘x.v’生成文件‘x’的规则。因为目标是‘%’,只要相对应的依赖文件存在,该规则可以应用于任何文件。双冒号表示该规则是最终规则,它意味着不能是中间文件。参阅万用规则

下面的格式规则有两个目标:

%.tab.c %.tab.h: %.y
        
        
        bison -d ___FCKpd___68lt;
        
        

这告诉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 dirnotdir 能够得到相同的结果。参阅文件名函数。注意,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 <" ;和我们写普通变量,例如变量 objectsCFLAGS一样。我们认为这种惯例在这种情况下看起来更加自然。这并没有其它意义,变量$<'的变量名为 < 和变量$(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 $@ ___FCKpd___77lt;
        
        

后缀规则不能有任何属于它们自己的依赖。如果它们有依赖,它们将不是作为后缀规则使用,而是以令人啼笑皆非的方式处理正常的文件。例如,规则:

.c.o: foo.h
        
        
        $(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ ___FCKpd___79lt;
        
        

告诉从依赖foo.h生成文件名为‘.c.o’的文件,并不是象格式规则:

%.o: %.c foo.h
        
        
        $(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ ___FCKpd___81lt;
        
        

告诉从.c'文件生成 .o' 文件.c'的方法:创建所有.o' 文件使用该格式规则,而且同时使用依赖文件foo.h'

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

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

.SUFFIXES: .hack .win
        
        

.hack'.win'添加到了后缀列表中。

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

.SUFFIXES:            # 删除缺省后缀
        
        
.SUFFIXES: .c .o .h   # 定义自己的后缀列表
        
        

标志-r'--no-builtin-rules'也能把缺省的后缀列表清空。

变量SUFFIXESmake读入任何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’执行,自动变量将设置为相应的目标和依赖(参阅自动变量)。

11使用make更新档案文件

档案文件是包含子文件的文件,这些子文件有各自的文件名,一般将它们称为成员;档案文件和程序ar一块被提及,它们的主要用途是作为连接的例程库。

11.1档案成员目标

独立的档案文件成员可以在make中用作目标或依赖。按照下面的方式,您可以在档案文件‘archive’中指定名为‘member’的成员:

archive(member)
        
        

这种结构仅仅在目标和依赖中使用,绝不能在命令中应用!绝大多数程序都不在命令中支持这个语法,而且也不能对档案成员直接操作。只有程序ar和那些为操作档案文件设计的程序才能这样做。所以合法的更新档案成员的命令一定使用ar。例如,下述规则表明借助拷贝文件‘hack.o’在档案‘foolib’中创建成员‘hack.o’:

foolib(hack.o) : hack.o
        
        
        ar cr foolib hack.o
        
        

实际上,几乎所有的档案成员目标是采用这种方式更新的,并且有一条隐含规则为您专门更新档案成员目标。注意:如果档案文件没有直接存在,程序ar的‘c’标志是需要的。

在相同的档案中同时指定几个成员,您可以在圆括号中一起写出所有的成员名。例如:

foolib(hack.o kludge.o)
        
        

等同于:

foolib(hack.o) foolib(kludge.o)
        
        

您还可以在档案成员引用中使用shell类型的通配符。参阅在文件名中使用通配符。例如,‘foolib(*.o)' 扩展为在档案‘foolib’中所有存在以‘.o’结尾的成员。也许相当于:foolib(hack.o) foolib(kludge.o)'。

11.2 档案成员目标的隐含规则

对目标‘a(m)’表示名为‘m’的成员在档案文件‘a’中。

Make为这种目标搜寻隐含规则时,是用它另外一个的特殊特点:make认为匹配‘(m)’的隐含规则也同时匹配‘a(m)’。

该特点导致一个特殊的规则,它的目标是‘(%)’。该规则通过将文件‘m’拷贝到档案中更新目标‘a(m)’。例如,它通过将文件‘bar.o’拷贝到档案‘foo.a’中更新档案成员目标‘foo.a(bar.o)’。

如果该规则和其它规则组成链,功能十分强大。‘make "foo.a(bar.o)"'(注意使用双引号是为了保护圆括号可被shell解释)即使没有makefile文件仅存在文件‘bar.c’就可以保证以下命令执行:

cc -c bar.c -o bar.o
        
        
ar r foo.a bar.o
        
        
rm -f bar.o
        
        

这里make假设文件‘bar.o’是中间文件。参阅隐含规则链

诸如这样的隐含规则是使用自动变量‘$%’编写的,参阅自动变量

档案成员名不能包含路径名,但是在makefile文件中路径名是有用的。如果您写一个档案成员规则‘foo.a(dir/file.o)’,make将自动使用下述命令更新:

ar r foo.a dir/file.o
        
        

它的结果是拷贝文件‘dir/file.o’进入名为‘file.a’的档案中。在完成这样的任务时使用自动变量%D和%F。

11.2.1更新档案的符号索引表

用作库的档案文件通常包含一个名为‘__.SYMDEF’ 特殊的成员,成员‘__.SYMDEF’包含由所有其它成员定义的外部符号名的索引表。在您更新其它成员后,您必须更新成员‘__.SYMDEF’,从而使成员‘__.SYMDEF’可以合适的总结其它成员。要完成成员‘__.SYMDEF’的更新需要运行ranlib程序:

ranlib archivefile
        
        

正常情况下,您应该将该命令放到档案文件的规则中,把所有档案文件的成员作为该规则的依赖。例如:

libfoo.a: libfoo.a(x.o) libfoo.a(y.o) ...
        
        
        ranlib libfoo.a
        
        

上述程序的结果是更新档案成员x.o',y.o', 等等, 然后通过运行程序ranlib更新符号索引表表成员__.SYMDEF’。更新成员的规则这里没有列出,多数情况下,您可以省略它们,使用隐含规则把文件拷贝到档案中,具体描述见以前的内容。

使用GNU ar程序时这不是必要的,因为它自动更新成员__.SYMDEF’

11.3 使用档案的危险

同时使用并行执行(-j开关,参阅并行执行)和档案应该十分小心。如果多个命令同时对相同的档案文件操作,它们相互不知道,有可能破坏文件。将来的make版本可能针对该问题提供一个机制,即将所有操作相同档案文件的命令串行化。但是现在,您必须在编写您自己的makefile文件时避免该问题,或者采用其它方式,或者不使用选项-j。

11.4 档案文件的后缀规则

为处理档案文件,您可以编写一个特殊类型的后缀规则。关于所有后缀的扩展请参阅过时的后缀规则。档案后缀规则在GNU make中已被废弃,因为用于档案的格式规则更加通用(参阅档案成员目标的隐含规则),但是为了和其它版本的make兼容,它们仍然被保留。

编写用于档案的后缀规则,您可以简单的编写一个用于目标后缀‘.a’的后缀规则即可。例如,这里有一个用于从C语言源文件更新档案库的过时后缀规则:

.c.a:
        
        
        $(CC) $(CFLAGS) $(CPPFLAGS) -c ___FCKpd___98lt; -o $*.o
        
        
        $(AR) r $@ $*.o
        
        
        $(RM) $*.o
        
        

这和下面的格式规则工作完全一样:

(%.o): %.c
        
        
        $(CC) $(CFLAGS) $(CPPFLAGS) -c ___FCKpd___102lt; -o $*.o
        
        
        $(AR) r $@ $*.o
        
        
        $(RM) $*.o
        
        

实际上,这仅仅是make看到一个以‘.a’作为后缀的后缀规则时,它所做的工作。任何双后缀规则‘.x.a' 被转化为一个格式规则,该格式规则的目标格式是‘(%.o)' ,依赖格式是‘%.x'.。

因为您可能要使用.a' 作为一个文件类型的后缀,make也以正常方式转换档案后缀规则为格式规则,参阅过时的后缀规则这样一个双后缀规则‘.x.a' 产生两个格式规则:‘(%.o): %.x'‘%.a: %.x'.

12 GNU make的特点

这里是GNU make的特点的总结,用于比较其它版本的make。我们以4.2 BSD 中的make的特点为基准。如果您要编写一个可移植的makefile文件,您不要使用这里列出的make的特点,也不要使用不兼容性和失去的特点中列出的内容。

许多特点在System V 中的make也存在。

  • 变量VPATH 以及它特殊的意义。参阅在目录中搜寻依赖。这个特点存在于System V 中的make,但没有事实证明。4.3 BSD make也含有该特点(据说是模仿System V中变量VPATFH的特点)
  • 包含其它makefile文件。参阅包含其它makefile文件允许使用一个指令包含多个文件是GNU的扩展。
  • 通过环境,变量可以读入和通讯,参阅环境变量
  • 通过变量MAKEFLAGS 在递归调用make时可以传递选项。参阅和子make通讯选项
  • 在档案引用中自动变量$% 设置为成员名。参阅自动变量
  • 自动变量$@, $*, $<, $%, 和 $? 有变体形式如$(@F)$(@D)。我们把此概念化,并使用它对自动变量$^ 进行了明显扩展。参阅自动变量
  • 变量引用。参阅变量引用基础
  • 命令行选项-b'-m',接受和忽略。在System V make中,这些选项实际起作用。
  • 即使指定选项-n',-q'或‘-t',也能通过变量MAKE执行地归调用make的命令。参阅递归调用make
  • 在后缀规则中支持后缀.a'。参阅用于档案文件的后缀规则。这个特点在GNU make中几乎不用,因为规则链更加通用的特点(参阅隐含规则链)允许一个格式规则用于在档案中安装成员已经足够(参阅用于档案成员目标的隐含规则)。
  • 在命令中行排列和反斜杠-新行结合依旧保留,当命令打印时,它们出现的格式和它们在makefile文件中基本一样,不同之处是去掉了初始化空白。

下面的特点被各种不同版本的make吸收,但哪些版本吸收了哪些特点并不十分清楚。

  • 在格式规则中使用‘%’。已经有几个不同版本的make使用了该特点。我们不能确认是谁发明了它,但它发展很快。参阅定义与重新定义格式规则
  • 规则链以及隐含中间文件。这个特点首先由Stu Feldman 在它的make版本中实现,并用于AT&T 第八版Unix研究中。后来AT&T贝尔实验室的Andrew Hume 在它的mk程序中应用(这里称为“传递闭合”)。我们并不清楚是从他们那里得到这个特点或是同时我们自己开发出来的。参阅隐含规则链
  • 自动变量包含当前目标的所有依赖的列表。我们一点也不知道是谁做的。参阅自动变量。自动变量$+ 是变量$^的简单扩展。
  • "what if" 标志(GNU make中的-W') 是Andrew Hume 在mk中发明的。参阅代替执行命令
  • 并行执行的概念在许多版本的make中存在,尽管System V 或BSD 并没有实现。参阅执行命令
  • 使用格式替换改变变量引用来自于SunOS 4。参阅变量引用基础。在GNU make中,这个功能在变换语法和SunOS 4兼容之前由函数patsubst提供。不知道谁是权威,因为GNU make 使用函数 patsubst 在 SunOS 4 发布之前。
  • 在命令行前面的‘+’字符有特殊重要的意义(参阅代替执行命令)。这是由IEEE Standard 1003.2-1992 (POSIX.2)定义的。
  • 使用+=语法为变量追加值来自于SunOS 4 make。参阅为变量值追加文本
  • 语法archive(mem1 mem2...)'在单一档案文件中列举多个成员来自于SunOS 4 make.。参阅档案成员目标
  • -include指令包括makefile文件,并且对于不存在的文件也不产生错误。该特点with来自于SunOS 4 make(但是SunOS 4 make 在单个指令中指定多个makefile文件。) 该特点和SGI make 的sinclude 相同,

剩余的特点是由GNU make发明的:

  • 使用-v'或`--version'选项打印版本和拷贝权信息。
  • 使用-h'‘--help' 选项总结make的选项。
  • 简单扩展型变量。参阅变量的两特特色
  • 在递归调用make时,通过变量MAKE自动传递命令行变量。参阅递归调用make
  • 使用命令选项-C'‘--directory'改变路径。参阅选项概要
  • 定义多行变量。参阅定义多行变量
  • 使用特殊目标.PHONY声明假想目标。AT&T 贝尔实验室Andrew Hume 使用不同的语法在它的mk程序中也实现了该功能。这似乎是并行的发现。参阅假想目标
  • 调用函数操作文本。参阅用于转换文本的函数
  • 使用-o'--old-file'选项假装文件是旧文件。参阅避免重新编译文件
  • 条件执行。该特点已在不同版本make中已经实现很长时间了;它似乎是C与处理程序和类似的宏语言的自然扩展,而不是革命性的概念。参阅makefile文件中的条件语句
  • 指定包含的makefile文件的搜寻路径。参阅包含其它makefile文件
  • 使用环境变量指定额外的makefile文件。参阅变量MAKEFILES
  • 从文件名中去除前导斜杠`./' ,因此,‘./file'file' 是指同一个文件。
  • 使用特别搜寻方法搜寻形式如‘-lname’的库依赖。参阅连接库搜寻目录
  • 允许后缀规则中的后缀包含任何字符(参阅过时的后缀规则)。在其它版本的make中后缀必须以‘.’开始,并且不能包含‘/’字符。
  • 包吹跟踪当前make级别适用的变量MAKWFILES的值,参阅递归调用make
  • 将任何在命令行中给出的目标放入变量MAKECMDGOALS。参阅指定最终目标的参数
  • 指定静态格式规则。参阅静态格式规则
  • 提供选择性vpath搜寻。参阅在目录中搜寻依赖
  • 提供可计算的变量引用。参阅量引用基础
  • 更新makefile文件。参阅重建makefile文件。System V make 中有非常非常有限的来自于该功能的形式,它用于为make检查SCCS文件。
  • 各种新建的隐含规则。参阅隐含规则目录
  • 内建变量`MAKE_VERSION' 给出make的版本号。

13 不兼容性和失去的特点

其它版本的make程序也有部分特点在GNU make中没有实现。POSIX.2 标准 (IEEE Standard 1003.2-1992)规定不需要这些特点。

  • file((entry))' 形式的目标代表一个档案文件的成员file。选择该成员不使用文件名,而是通过一个定义连接符号enty的OBJ文件。该特点没有被GNU make 吸收因为该非标准组件将为make加入档案文件符号表的内部知识。参阅更新档案符号索引表
  • 在后缀规则中以字符‘~’结尾的后缀在System V make中有特别的含义;它们指和文件名中没有‘~’的文件通讯的SCCS 文件。例如,后缀规则‘.c~.o'将从名为‘s.n.c'的SCCS文件中抽取文件n.o'。为了完全覆盖,需要这种整系列的后缀规则,参阅过时的后缀规则。在GNU make中,这种整系列的后缀规则由勇于从SCCS文件抽取的两个格式规则掌管,它们可和通用的规则结合成规则链,参阅隐含规则链
  • System V make中, 字符串‘$$@'又奇特的含义,在含有多个规则的依赖中,它代表正在处理的特殊目标。这在GNU make没有定义,因为字符串‘$$'代表一个平常的字符‘$'。使用静态格式规则可以实现该功能的一部分(参阅静态格式规则)。System V make 中的规则:
$(targets): $@.o lib.a
        
        

GNU make 中可以用静态格式规则代替:

$(targets): %: %.o lib.a
        
        
  • System V 和 4.3 BSD make中, 通过VPATH搜寻(参阅为依赖搜寻目录发现的文件,它们的文件名改变后加入到命令字符串中。我们认为使用自动变量更简单明了,所以不引进该特点。
  • 在一些Unix make中,自动变量$*出现在规则的依赖中有令人惊奇的特殊特点:扩展为该规则的目标全名。我们不能明白Unix make 在心中对这是怎样考虑的,它和正常的变量$*定义完全不同。
  • 在一些Unix make中,隐含规则搜寻(参阅使用隐含规则)明显是为所有目标做的,而不仅仅为那些没有命令的目标。这意味着:
foo.o:
        
        
       cc -c foo.c
        
        

Unix make 有直觉知道‘foo.o' 依靠‘foo.c'。我们认为这样的用法易导致混乱。Make中依赖的属性已经定义好(至少对于GNU make是这样),再做这样的事情不合规矩。

  • GNU make不包含任何编译以及与处理EFL程序的隐含规则。如果我们听说谁使用EFL,我们乐意把它们加入。
  • SVR4 make中,一条后缀规则可以不含命令,它的处理方式和它含有空命令的处理方式一样(参阅使用空命令)。例如:
.c.a:
        
        

将重载内建的后缀规则.c.a' 。我们觉得对没有命令的规则简单的为目标添加依赖更为简洁。上述例子和在GNU make中下例的行为相同。

.c.a: ;
        
        
  • 一些版本的make 调用shell使用‘-e'标志,而不是‘-k'标志 (参阅测试程序编译)。标志‘-e'告诉shell 一旦程序运行返回非零状态就立即退出。我们认为根据每一命令行是否需要需要特殊处理直接写入命令中更为清楚。

14 makefile文件惯例

本章描述为GNU make编写makefile文件的惯例。使用Automake将帮助您按照这些惯例编写makefile文件。

14.1 makefile文件的通用惯例

任何makefile文件都应该包含这行:

SHELL = /bin/sh
        
        

避免在系统中变量SHELL可能继承环境中值的麻烦。(在GNU make中这从来不是问题。)

不同的make程序有不同的后缀列表和隐含规则,这有可能造成混乱或错误的行为。因此最好的办法是设置后缀列表,在该列表中,仅仅包含您在特定makefile文件中使用的后缀。例如:

.SUFFIXES:
        
        
.SUFFIXES: .c .o
        
        

第一行清除了后缀列表,第二行定义了在该makefile中可能被隐含规则使用的后缀。

不要假设.' 是命令执行的路径。当您在创建程序过程中,需要运行仅是您程序包中一部分的程序时,请确认如果该程序是要创建程序的一部分使用‘./’,如果该程序是源代码中不变的部分使用‘$(srcdir)’。没有这些前缀,仅仅在当前路径下搜索。

建造目录(build directory )‘./’和源代码目录(source directory) $(srcdir)’的区别是很重要的,因为用户可以在‘configure’中使用‘--srcdir’选项建造一个单独的目录。下面的规则:

foo.1 : foo.man sedscript
        
        
        sed -e sedscript foo.man > foo.1
        
        

如果创建的目录不是源代码目录将失败,因为文件‘foo.man’和‘sedscript’在源代码目录下。

在使用GNU make时,依靠变量‘VPATH’搜寻源文件在单个从属性文件存在情况下可以很好地工作,因为make中自动变量‘$<’中含有源文件的存在路径。(许多版本的make仅在隐含规则中设值变量‘$<’。)例如这样的makefile文件目标:

foo.o : bar.c
        
        
        $(CC) -I. -I$(srcdir) $(CFLAGS) -c bar.c -o foo.o
        
        

将被替换为:

foo.o : bar.c
        
        
        $(CC) -I. -I$(srcdir) $(CFLAGS) -c ___FCKpd___119lt; -o $@
        
        

这是为了保证变量‘VPATH’正确的工作。目标含有多个依赖时,使用名了的‘$(srcdir)’是最容易的保证该规则很好工作的方法。例如,以上例子中的目标‘foo.l’最好写为:

foo.1 : foo.man sedscript
        
        
        sed -e $(srcdir)/sedscript $(srcdir)/foo.man > $@
        
        

GNU的分类中通常包含一些不是源文件的文件——例如,‘Info’文件、从Autoconf, Automake, Bison 或 Flex中输出的文件等。这些文件在源文件目录下,它们也应该在源文件目录下,不应该在建造目录下。因此makefile规则应在源文件目录下更新它们。

然而,如果一个文件没有在分类中出现,makefile文件不应把它们放到源文件目录下,因为按照通常情况创建一个程序,不应该以任何方式更改源文件目录。

试图建造的创建和安装目标,至少(以及它们的子目标)可在并行的make中正确的工作。

14.2 makefile文件的工具

编写在shell sh中运行而不在csh中运行的makefile文件命令(以及shell的脚本,例如‘configure’),不要使用任何ksh或bash的特殊特点。

用于创建和安装的configure’脚本和Makefile 规则除下面所列出工具外不应该直接使用其它的任何工具:

cat cmp cp diff echo egrep expr false grep install-info
        
        
ln ls mkdir mv pwd rm rmdir sed sleep sort tar test touch true
        
        

压缩程序gzip可在dist规则中使用。

坚持使用用于这些程序的通用选项,例如,不要使用mkdir -p',它可能比较方便,但是其它大多数系统却不支持它。

避免在makefile中创造符号连接是非常不错的注意,因为一些系统不支持这种做法。

用于创建和安装的Makefile 规则可以使用编译器以及相关的程序,但应该通过make变量使用它们,这样可以方便用户使用别的进行替换。这里有按照我们的观念编写一些程序:

ar bison cc flex install ld ldconfig lex
        
        
make makeinfo ranlib texi2dvi yacc
        
        

请使用下述make变量运行这些程序:

$(AR) $(BISON) $(CC) $(FLEX) $(INSTALL) $(LD) $(LDCONFIG) $(LEX)
        
        
$(MAKE) $(MAKEINFO) $(RANLIB) $(TEXI2DVI) $(YACC)
        
        

使用ranlibldconfig,您应该确定如果系统中不存在要使用的程序不会引起任何副作用。安排忽略这些命令产生的错误,并且打印信息告诉用户该命令运行失败并不意味着存在问题。(AutoconfAC_PROG_RANLIB'宏可在这方面帮助您。)如果您使用符号连接,对于不支持符号连接的系统您应该有一个低效率运行方案。

附加的工具也可通过make变量使用:

chgrp chmod chown mknod
        
        

它在makefile中(或脚本中),您知道包含这些工具的特定系统中它都可以很好的工作。

14.3 指定命令的变量

Makefile文件应该为重载的特定命令、选项等提供变量。

特别在您运行大部分工具时都应该应用变量,如果您要使用程序Bison, 名为BISON 的变量它的缺省值设置为:‘BISON = bison’,在您需要使用程序Bison时,您可以使用$(BISON)引用。

文件管理器工具如ln, rm, mv等等,不必要使用这种方式引用,因为用户不可能使用别的程序替换它们。

每一个程序变量应该和用于向该程序提供选项的选项变量一起提供。在程序名变量后添加FLAGS'表示向该程序提供选项的选项变量--例如, BISONFLAGS(名为CFLAGS的变量向C编译器提供选项, 名为YFLAGS的变量向yacc提供选项,名为LFLAGS的变量向lex提供选项等是这个规则例外,但因为它们是标准所以我们保留它们。) 在任何进行预处理的编译命令中使用变量CPPFLAGS ,在任何进行连接的编译命令中使用变量LDFLAGS 和直接使用程序ld一样。

对于C编译器在编译特定文件时必须使用的选项,不应包含在变量CFLAGS中,因为用户希望他们能够自由的指定变量CFLAGS。 要独立于变量CFLAGS安排向C编译器传递这些必要的选项, 可以将这些选项写入编译命令行中或隐含规则的定义中,如下例:

CFLAGS = -g
        
        
ALL_CFLAGS = -I. $(CFLAGS)
        
        
.c.o:
        
        
        $(CC) -c $(CPPFLAGS) $(ALL_CFLAGS) ___FCKpd___132lt;
        
        

变量CFLAGS中包括选项‘-g’,因为它对于一些编译并不是必需的,您可以认为它是缺省推荐的选项。如果数据包创建使用GCC作为编译器,则变量CFLAGS中包括选项‘-o’,而且以它为缺省值。

将变量CFLAGS放到编译命令的最后,在包含编译选项其它变量的后边,因此用户可以使用变量CFLAGS对其它变量进行重载。

每次调用C编译器都用到变量CFLAGS ,无论进行编译或连接都一样。

任何Makefile文件都定义变量INSTALL,变量INSTALL是将文件安装到系统中的基本命令。

任何Makefile文件都定义变量INSTALL_PROGRAMINSTALL_DATA(它们的缺省值都是$(INSTALL)) 在实际安装程序时,不论可执行程序或非可执行程序,一般都使用它们作为命令。下面是使用这些变量的例子:

$(INSTALL_PROGRAM) foo $(bindir)/foo
        
        
$(INSTALL_DATA) libfoo.a $(libdir)/libfoo.a
        
        

您可以随意将变量DESTDIR预先设置为目标文件名。这样做允许安装程序创建随后在实际目标文件系统中安装文件的快照。不要再makefile文件中设置变量DESTDIR,也不要包含在安装文件中。用变量DERSTDIR改变上述例子:

$(INSTALL_PROGRAM) foo $(DESTDIR)$(bindir)/foo
        
        
$(INSTALL_DATA) libfoo.a $(DESTDIR)$(libdir)/libfoo.a
        
        

在安装命令中一般使用文件名而不是路径名作为第二个参数。对每一个安装文件都使用单独的命令。

14.4安装路径变量

安装目录经常以变量命名,所以在非标准地方安装也很容易,这些变量的标准名字将在下面介绍。安装目录依据标准文件系统布局,变量的变体已经在SVR4, 4.4BSD, Linux, Ultrix v4, 以及其它现代操作系统中使用。

以下两个变量设置安装文件的根目录,所有的其它安装目录都是它们其中一个的子目录,没有任何文件可以直接安装在这两个根目录下。

`prefix'

前缀是用于构造以下列举变量的缺省值。变量prefix缺省值是/usr/local'。建造完整的GNU系统时,变量prefix的缺省值是空值,/usr' 是符号连接符/'(如果您使用Autoconf,应将它写为@prefix@')使用不同于创建程序时变量prefix的值运行make install',不会重新编译程序。

`exec_prefix'

前缀是用于构造以下列举变量的缺省值。变量exec_prefix缺省值是$(prefix). (如果您使用Autoconf,应将它写为`@exec_prefix@') 一般情况下。变量$(exec_prefix) 用于存放包含机器特定文件的目录,(例如可执行文件和例程库),变量$(prefix) 直接存放其它目录。使用不同于创建程序时变量exec_prefix的值运行make install',不会重新编译程序。

可执行程序安装在以下目录中:

`bindir'

这个目录下用于安装用户可以运行的可执行程序。其正常的值是/usr/local/bin',但是使用时应将它写为$(exec_prefix)/bin' (如果您使用Autoconf, 应将它写为@bindir@')

`sbindir'

这个目录下用于安装从shell中调用执行的可执行程序。它仅仅对系统管理员有作用。它的正常的值是/usr/local/sbin',但是使用时应将它写为$(exec_prefix)/sbin' (如果您使用Autoconf, 应将它写为@sbindir@')

`libexecdir'

这个目录下用于安装其它程序调用的可执行程序。其正常的值是/usr/local/libexec',但是使用时应将它写为$(exec_prefix)/libexec'(如果您使用Autoconf, 应将它写为@libexecdir@'。)

程序执行时使用的数据文件可分为两类:

  • 程序可以正常更改的文件和不能正常更改的文件(虽然用户可以编辑其中的一部分文件)。
  • 体系结构无关文件,指这些文件可被所有机器共享;体系相关文件,指仅仅可以被相同类型机器、操作系统共享的文件;其它是永远不能被两个机器共享的文件。

这可产生六种不同的可能性。我们极力反对使用体系相关的文件,当然OBJ文件和库文件除外。使用其它体系无关的数据文件更加简洁,并且,这样做也不是很难。

所以,这里有 Makefile变量用于指定路径:

`datadir'

这个目录下用于安装只读型体系无关数据文件。其正常的值是/usr/local/share',但是使用时应将它写为$(prefix)/share'(如果您使用Autoconf, 应将它写为@datadir@') 作为例外,参阅下述的变量$(infodir)'$(includedir)'

`sysconfdir'

这个目录下用于安装从属于单个机器的只读数据文件,这些文件是:用于配置主机的文件。邮件服务、网络配置文件,/etc/passwd',等等都属于这里的文件。所有该目录下的文件都是平常的ASCII文本文件。其正常的值是/usr/local/etc', 但是使用时应将它写为$(prefix)/etc'. (如果您使用Autoconf, 应将它写为@sysconfdir@'.) 不要在这里安装可执行文件(它们可能属于$(libexecdir)'$(sbindir)')。也不要在这里安装那些在使用时要更改的文件(这些程序用于改变系统拒绝的配置)。它们可能属于$(localstatedir)'

`sharedstatedir'

这个目录下用于安装程序运行中要发生变化的体系无关数据文件。其正常的值是/usr/local/com',但是使用时应将它写为$(prefix)/com' (如果您使用Autoconf, 应将它写为@sharedstatedir@'。)

`localstatedir'

这个目录下用于安装程序运行中要发生变化的数据文件。但他们属于特定的机器。用户永远不需要在该目录下更改文件配置程序包选项;将这些配置信息放在分离的文件中,这些文件将放入$(datadir)'$(sysconfdir)'中,$(localstatedir)'正常的值是/usr/local/var'但是使用时应将它写为$(prefix)/var' (如果您使用Autoconf, 应将它写为@localstatedir@'。)

`libdir'

这个目录下用于存放OBJ文件和库的OBJ代码。不要在这里安装可执行文件,它们可能应属于$(libexecdir)'。变量libdir正常的值是/usr/local/lib',但是使用时应将它写为$(exec_prefix)/lib'(如果您使用Autoconf, 应将它写为 @libdir@')

`infodir'

这个目录下用于安装软件包的 Info 文件。缺省情况下其值是/usr/local/info',但是使用时应将它写为$(prefix)/info'. (如果您使用Autoconf, 应将它写为@infodir@'.)

`lispdir'

这个目录下用于安装软件包的Emacs Lisp 文件。缺省情况下其值是 /usr/local/share/emacs/site-lisp',但是使用时应将它写为$(prefix)/share/emacs/site-lisp'。如果您使用Autoconf, 应将它写为@lispdir@'。为了保证@lispdir@'工作,您需要将以下几行加入到您的configure.in'文件中:

lispdir='${datadir}/emacs/site-lisp'
        
        
AC_SUBST(lispdir)
        
        

`includedir'

这个目录下用于安装用户程序中C#include'预处理指令包含的头文件。其正常的值是/usr/local/include',但是使用时应将它写为$(prefix)/include' (如果您使用Autoconf, 应将它写为@includedir@'。) 除GCC外的大多数编译器不在目录/usr/local/include'搜寻头文件,因此这种安装方式仅仅适用于GCC。有时,这也不是问题,因为一部分库文件仅仅依靠GCC才能工作。但也有一部分库文件依靠其他编译器,它们将它们的头文件安装到两个地方,一个由变量 includedir 指定,另一个由变量oldincludedir指定。

`oldincludedir'

这个目录下用于安装#include'的头文件,这些头文件用于GCC外的其它C语言编译器。其正常的值是/usr/include'(如果您使用Autoconf, 应将它写为 @oldincludedir@') Makefile命令变量oldincludedir 的值是否为空,如果是空值,它们不在试图使用它,它们还删除第二次安装的头文件。一个软件包在该目录下替换已经存在的头文件,除非头文件来源于同一个软件包。例如,如果您的软件包Foo 提供一个头文件foo.h',则它在变量oldincludedir指定的目录下安装的条件是 (1) 这里没有投文件foo.h' 或 (2) 来源于软件包Foo的头文件foo.h'已经在该目录下存在。要检查头文件foo.h'是否来自于软件包Foo,将一个magic字符串放到文件中--作为命令的一部分--然后使用正则规则(grep)查找该字符串。

Unix风格的帮助文件安装在以下目录中:

`mandir'

安装该软件包的顶层帮助(如果有)目录。其正常的值是/usr/local/man',但是使用时应将它写为$(prefix)/man' (如果您使用Autoconf, 应将它写为@mandir@'。)

`man1dir'

这个目录下用于安装第一层帮助。其正常的值是$(mandir)/man1'

`man2dir'

这个目录下用于安装第一层帮助。其正常的值是$(mandir)/man2'

`...'

不要将任何GNU 软件的主要文档作为帮助页。应该编写使用手册。帮助页仅仅是为了人们在Unix上方便运行GNU软件,它是附属的运行程序。

`manext'

文件名表示对已安装的帮助页的扩展。它包含一定的周期,后跟适当的数字,正常为‘1’。

`man1ext'

文件名表示对已安装的帮助页第一部分的扩展。

`man2ext'

文件名表示对已安装的帮助页第二部分的扩展。

`...'

使用这些文件名代替`manext'。如果该软件包的帮助页需要安装使用手册的多个章节。

最后您应该设置一下变量:

`srcdir'

这个目录下用于安装要编译的原文件。该变量正常的值由shell脚本configure插入。(如果您使用Autoconf, 应将它写为srcdir = @srcdir@'.)

例如:

# 用于安装路径的普通前缀。
        
        
# 注意:该路经在您开始安装时必须存在
        
        
prefix = /usr/local
        
        
exec_prefix = $(prefix)
        
        
# 这里放置`gcc'命令调用的可执行文件。
        
        
bindir = $(exec_prefix)/bin
        
        
# 这里放置编译器使用的目录。
        
        
libexecdir = $(exec_prefix)/libexec
        
        
#这里放置Info文件。
        
        
infodir = $(prefix)/info
        
        

如果您的程序要在标准用户指定的目录中安装大量的文件,将该程序的文件放入到特意指定的子目录中是很有必要的。如果您要这样做,您应该写安装规则创建这些子目录。

不要期望用户在上述列举的变量值中包括这些子目录,对于安装目录使用一套变量名的办法使用户能够对于不同的GNU软件包指定精确的值,为了使这种做法有用,所有的软件包必须设计为当用户使用时它们能够聪明的工作。

14.5用户标准目标

所有的GNU程序中,在makefile中都有下列目标:

`all'

编译整个程序。这应该是缺省的目标。该目标不必重建文档文件,Info文件已正常情况下应该包括在各个发布的文件中,DVI文件只有在明确请求情况下才重建。缺省时,make规则编译和连接使用选项-g',所以程序调试只是象征性的。对于不介意缺少帮助的用户如果他们希望将可执行程序和帮助分开,可以从中剥离出可执行程序。

`install'

编译程序并将可执行程序、库文件等拷贝到为实际使用保留的文件名下。如果是证实程序是否适合安装的简单测试,则该目标应该运行该测试程序。不要在安装时剥离可执行程序,魔鬼很可能关心那些使用install-strip目标来剥离可执行程序的人。如果这是可行的,编写的install目标规则不应该更改程序建造的目录下的任何东西,仅仅提供make all'一切都能完成。这是为了方便用户命名和在其它系统安装建造程序,如果要安装程序的目录不存在,该命令应能创建所有这些目录,这包括变量prefix和exec_prefix特别指定的目录和所有必要的子目录。完成该任务的方法是借助下面描述的目标installdirs。在所有安装帮助页的命令前使用-'使make 能够忽略这些命令产生的错误,这可以确保在没有Unix帮助页的系统上安装该软件包时能够顺利进行。安装Info文件的方法是使用变量$(INSTALL_DATA)将Info文件拷贝到变量$(infodir)'中(参阅指定命令的变量),如果 install-info程序存在则运行它。 install-info是一个编辑Infodir'文件的程序,它可以为Info文件添加或更新菜单;它是Texinfo软件包的一部分。这里有一个安装Info文件的例子:

$(DESTDIR)$(infodir)/foo.info: foo.info
        
        
        $(POST_INSTALL)
        
        
# 可能在‘.’下有新的文件,在srcdir中没有。
        
        
        -if test -f foo.info; then d=.; /
        
        
         else d=$(srcdir); fi; /
        
        
        $(INSTALL_DATA) $d/foo.info $(DESTDIR)$@; /
        
        
#如果 install-info程序存在则运行它。
         
         
# 使用‘if'代替在命令行前的‘-' 
        
        
# 这样,我们可以注意到运行install-info产生的真正错误。
        
        
# 我们使用‘$(SHELL) -c' 是因为在一些shell中
        
        
# 遇到未知的命令不会运行失败。
        
        
        if $(SHELL) -c 'install-info --version' /
        
        
           >/dev/null 2>&1; then /
        
        
          install-info --dir-file=$(DESTDIR)$(infodir)/dir /
        
        
                       $(DESTDIR)$(infodir)/foo.info; /
        
        
        else true; fi
        
        

在编写install目标时,您必须把所有的命令归位三类:正常的命令、 安装前命令和安装后命令。参阅安装命令分类

`uninstall'

删除所有安装的文件--有‘install’目标拷贝的文件。该规则不应更改编译产生的目录,仅仅删除安装文件的目录。反安装命令象安装命令一样分为三类,参阅安装命令分类

`install-strip'

和目标install类似,但在安装时仅仅剥离出可执行文件。 在许多情况下,该目标的定义非常简单:

install-strip:
        
        
        $(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' /
        
        
                install
        
        

正常情况下我们不推荐剥离可执行程序进行安装,只有您确信这些程序不会产生问题时才能这样。剥离安装一个实际执行的可执行文件同时保存那些在这种场合存在BUG的可执行文件是显而易见的。

`clean'

删除所有当前目录下的文件,这些文件正常情况下是那些‘建立程序’创建的文件。不要删除那些记录配置的文件,同时也应该保留那些‘建立程序’能够修改的文件,正常情况下要删除的那些文件不包括这些文件,因为发布文件是和这些文件一起创建的。如果.dvi'文件不是文件发布文件的一部分,则使用目标‘clean’将同时删除.dvi'文件。

`distclean'

删除所有当前目录下的文件,这些文件正常情况下是那些‘建立程序’或‘配置程序’创建的文件。如果您不解包源程序,‘建立程序’不会创建任何其它文件,make distclean'将仅在文件发布文件中留下原有的文件。

`mostlyclean'

和目标clean'类似,但是避免删除人们正常情况下不编译的文件。例如,用于GCC的目标mostlyclean不删除文件libgcc.a',因为在绝大多数情况下它都不需要重新编译。

`maintainer-clean'

几乎在当前目录下删除所有能够使用该makefile文件可以重建的文件。使用该目标删除的文件包括使用目标distclean,删除的文件加上从Bison产生的C语言源文件和标志列表、 Info文件等等。我们说“几乎所有文件”的原因是运行命令make maintainer-clean'不删除脚本configure',即使脚本configure'可以使用Makefile文件创建。更确切地说,运行make maintainer-clean'不删除为了运行脚本configure'以及开始建立程序的涉及的所有文件。这是运行make maintainer-clean'删除所有能够重新创建文件时唯一不能删除的一类文件。目标maintainer-clean'由该软件包的养护程序使用,不能被普通用户使用。您可以使用特殊的工具重建被目标make maintainer-clean'删除的文件。因为这些文件正常情况下包含在发布的文件中,我们并不关心它们是否容易重建。如果您发现您需要对全部发布的文件重新解包,您不能责怪我们。要帮助make 的用户意识到这一点,用于目标maintainer-clean 应以以下两行为开始:

@echo‘该命令仅仅用于养护程序;’
        
        
@echo‘它删除的所有文件都能使用特殊工具重建。’
        
        

`TAGS'

 更新该程序的标志表。

`info'

产生必要的Info文件。最好的方法是编写象下面规则:

info: foo.info
        
        
 
        
        
foo.info: foo.texi chap1.texi chap2.texi
        
        
        $(MAKEINFO) $(srcdir)/foo.texi
        
        

您必须在makefile文件中定以变量MAKEINFO。它将运行makeinfo程序,该程序是发布程序中Texinfo的一部分。正常情况下,一个GNU发布程序和Info文件一起创建,这意味着Info文件存在于源文件的目录下。当用户建造一个软件包,一般情况下,make不更新Info文件,因为它们已经更新到最新了。

`dvi'

创建DVI文件用于更新Texinfo文档。例如:

dvi: foo.dvi
        
        
 
        
        
foo.dvi: foo.texi chap1.texi chap2.texi
        
        
        $(TEXI2DVI) $(srcdir)/foo.texi
        
        

您必须在makefile文件中定义变量TEXI2DVI。它将运行程序texi2dvi,该程序是发布的Texinfo一部分。要么仅仅编写依靠文件,要么允许GNU make提供命令,二者必选其一。

`dist'

为程序创建一个tar文件。创建tar文件可以将其中的文件名以子目录名开始,这些子目录名可以是用于发布的软件包名。另外,这些文件名中也可以包含版本号,例如,发布的GCC 1.40版的tar文件解包的子目录为gcc-1.40'。最方便的方法是创建合适的子目录名,如使用in或cp等作为子目录,在它们的下面安装适当的文件,然后把tar文件解包到这些子目录中。使用gzip压缩这些tar文件,例如,实际的GCC 1.40版的发布文件叫gcc-1.40.tar.gz'。目标dist明显的依靠所有的发布文件中不是源文件的文件,所以你应确保发布中的这些文件已经更新。参阅GNU标准编码创建发布文件

`check'

执行自我检查。用户应该在运行测试之前,应该先建立程序,但不必安装这些程序;您应该编写一个自我测试程序,在程序已建立但没有安装时执行。

以下目标建议使用习惯名,对于各种程序它们很有用:

installcheck

执行自我检查。用户应该在运行测试之前,应该先建立、安装这些程序。您不因该假设$(bindir)'在搜寻路径中。

installdirs

添加名为installdirs'目标对于创建文件要安装的目录以及它们的父目录十分有用。脚本mkinstalldirs'是专为这样处理方便而编写的;您可以在Texinfo软件包中找到它,您可以象这样使用规则:

# 确保所有安装目录(例如 $(bindir))
        
        
# 都实际存在,如果没有则创建它们。
        
        
installdirs: mkinstalldirs
        
        
        $(srcdir)/mkinstalldirs $(bindir) $(datadir) /
        
        
                                $(libdir) $(infodir) /
        
        
                                $(mandir)
        
        

该规则并不更改编译时创建的目录,它仅仅创建安装目录。

14.6 安装命令分类

编写已安装目标,您必须将所有命令分为三类:正常的命令、安装前命令和安装后命令。

正常情况下,命令把文件移动到合适的地方,并设置它们的模式。它们不会改变任何文件,仅仅把它们从软件包中完整地抽取出来。

安装前命令和安装后命令可能更改一些文件,如,它们编辑配置文件后数据库文件。

安装前命令在正常命令之前执行,安装后命令在正常命令执行后执行。

安装后命令最普通的用途是运行install-info程序。 这种工作不能由正常命令完成,因为它更改了一个文件(Info 目录),该文件不能全部、单独从软件包中安装。它是一个安装后命令,因为它需要在正常命令安装软件包中的Info文件后才能执行。

许多程序不需要安装前命令,但是我们提供这个特点,以便在需要时可以使用。

要将安装规则的命令分为这三类,应在命令中间插入category lines(分类行)。 分类行指定了下面叙述的命令的类别。

分类行包含一个Tab、一个特殊的make变量引用,以及行结尾的随机注释。您可以使用三个变量,每一个变量对应一个类别;变量名指定了类别。分类行不能出现在普通的执行文件中,因为这些make变量被由正常的定义(您也不应在makefile文件中定义)。

这里有三种分类行,后面的注释解释了它的含义:

        $(PRE_INSTALL)     # 以下是安装前命令
        
        
        $(POST_INSTALL)    # 以下是安装后命令
        
        
        $(NORMAL_INSTALL)  # 以下是正常命令
        
        

如果在安装规则开始您没有使用分类行,则在第一个分类行出现之前的所有命令都是正常命令。如果您没有使用任何分类行,则所有命令都是正常命令。

这是反安装的分类行

        $(PRE_UNINSTALL)     #以下是反安装前命令
        
        
        $(POST_UNINSTALL)    #以下是反安装后命令
        
        
        $(NORMAL_UNINSTALL)  #以下是正常命令
        
        

反安装前命令的典型用法是从Info目录删除全部内容。

如果目标installuninstall 有依赖作为安装程序的子程序,那么您应该使用分类行先启动每一个依赖的命令,再使用分类行启动主目标的命令。无论哪一个依赖实际执行,这种方式都能保证每一条命令都放置到了正确的分类中。

安装前命令和安装后命令除了对于下述命令外,不能运行其它程序:

basename bash cat chgrp chmod chown cmp cp dd diff echo
        
        
egrep expand expr false fgrep find getopt grep gunzip gzip
        
        
hostname install install-info kill ldconfig ln ls md5sum
        
        
mkdir mkfifo mknod mv printenv pwd rm rmdir sed sort tee
        
        
test touch true uname xargs yes
        
        

按照这种方式区分命令的原因是为了创建二进制软件包。典型的二进制软件包包括所有可执行文件、必须安装的其它文件以及它自己的安装文件——所以二进制软件包不需要运行任何正常命令。但是安装二进制软件包需要执行安装前命令和安装后命令。

建造二进制软件包的程序通过抽取安装前命令和安装后命令工作。这里有一个抽取安装前命令的方法:

make -n install -o all /
        
        
      PRE_INSTALL=pre-install /
        
        
      POST_INSTALL=post-install /
        
        
      NORMAL_INSTALL=normal-install /
        
        
  | gawk -f pre-install.awk
        
        

这里文件pre-install.awk'可能包括:

$0 ~ /^/t[ /t]*(normal_install|post_install)[ /t]*$/ {on = 0}
        
        
on {print $0}
        
        
$0 ~ /^/t[ /t]*pre_install[ /t]*$/ {on = 1}
        
        

安装前命令的结果文件是象安装二进制软件包的一部分shell脚本一样执行。

15 快速参考

这是对指令、文本操作函数以及GNU make能够理解的变量等的汇总。对于其他方面的总结参阅特殊的内建目标名隐含规则目录选项概要

这里是GNU make是别的指令的总结:

define variable

endef

定义多行递归调用扩展型变量。参阅定义固定次序的命令

ifdef variable

ifndef variable

ifeq (a,b)

ifeq "a" "b"

ifeq 'a' 'b'

ifneq (a,b)

ifneq "a" "b"

ifneq 'a' 'b'

else

endif

makefile文件中的条件扩展,参阅makefile文件中的条件语句

include file

-include file

sinclude file

包含其它makefile文件,参阅包含其它makefile文件

override variable = value

override variable := value

override variable += value

override variable ?= value

override define variable

endef

定义变量、对以前的定义重载、以及对在命令行中定义的变量重载。参阅override指令

export

告诉make缺省向子过程输出所有变量,参阅与子make通讯的变量

export variable

export variable = value

export variable := value

export variable += value

export variable ?= value

unexport variable

告诉make是否向子过程输出一个特殊的变量。参业与子make通讯的变量

vpath pattern path

制定搜寻匹配‘%’格式的文件的路径。参阅vpath指令

vpath pattern

去除以前为‘pattern’指定的所有搜寻路径。

vpath

去除以前用vpath指令指定的所有搜寻路径。

这里是操作文本函数的总结,参阅文本转换函数

$(subst from,to,text)

在‘text’中用‘to’代替‘from’,参阅字符串替换与分析函数

$(patsubst pattern,replacement,text)

在‘text’中用‘replacement’代替匹配‘pattern’字,参阅字符串替换与分析函数

$(strip string)

从字符串中移去多余的空格。参阅字符串替换与分析函数

$(findstring find,text)

确定‘find’在‘text’中的位置。参阅字符串替换与分析函数

$(filter pattern...,text)

在‘text’中选择匹配‘pattern’的字。参阅字符串替换与分析函数

$(filter-out pattern...,text)

在‘text’中选择不匹配‘pattern’的字。参阅字符串替换与分析函数

$(sort list)

将‘list’中的字按字母顺序排序,并删除重复的字。参阅字符串替换与分析函数

$(dir names...)

从文件名中抽取路径名。参阅文件名函数

$(notdir names...)

从文件名中抽取路径部分。参阅文件名函数

$(suffix names...)

从文件名中抽取非路径部分。参阅文件名函数

$(basename names...)

从文件名中抽取基本文件名。参阅文件名函数

$(addsuffix suffix,names...)

为‘names’中的每个字添加后缀。参阅文件名函数

$(addprefix prefix,names...)

为‘names’中的每个字添加前缀。参阅文件名函数

$(join list1,list2)

连接两个并行的字列表。参阅文件名函数

$(word n,text)

从‘text’中抽取第n个字。参阅文件名函数

$(words text)

计算‘text’中字的数目。参阅文件名函数

$(wordlist s,e,text)

返回‘text’中s到e之间的字。参阅文件名函数

$(firstword names...)

在‘names…’中的第一个字。参阅文件名函数

$(wildcard pattern...)

寻找匹配shell文件名格式的文件名。参阅wildcard函数

$(error text...)

该函数执行时,make产生信息为‘text’的致命错误。参阅控制make的函数

$(warning text...)

该函数执行时,make产生信息为‘text’的警告。参阅控制make的函数

$(shell command)

执行shell命令并返回它的输出。参阅函数shell

$(origin variable)

返回make变量‘variable’的定义信息。参阅函数origin

$(foreach var,words,text)

将列表列表words中的每一个字对应后接var中的每一个字,将结果放在text中。参阅函数foreach

$(call var,param,...)

使用对$(1), $(2)...对变量计算变量 var ,变量$(1), $(2)...分别代替参数 param 第一个、第二个…的值。参阅函数call

这里是对自动变量的总结,完整的描述参阅自动变量

$@

目标文件名。

$%

当目标是档案成员时,表示目标成员名。

$<

第一个依赖名。

$?

所有比目标‘新’的依赖的名字,名字之间用空格隔开。对于为档案成员的依赖,只能使用命名的成员。参阅使用make更新档案文件

$^

$+

所有依赖的名字,名字之间用空格隔开。对于为档案成员的依赖,只能使用命名的成员。参阅使用make更新档案文件。变量 $^ 省略了重复的依赖,而变量 $+ 则按照原来次序保留重复项,

$*

和隐含规则匹配的stem(径)。参阅格式匹配

$(@D)

$(@F)

变量$@.中的路径部分和文件名部分。

$(*D)

$(*F)

变量$*中的路径部分和文件名部分。

$(%D)

$(%F)

变量$%中的路径部分和文件名部分。

$(<D)

$(<F)

变量$<中的路径部分和文件名部分。

$(^D)

$(^F)

变量$^中的路径部分和文件名部分。

$(+D)

$(+F)

变量$+中的路径部分和文件名部分。

$(?D)

$(?F)

变量$?中的路径部分和文件名部分。

以下是GNU make使用变量:

MAKEFILES

每次调用make要读入的Makefiles文件。参阅变量MAKEFILES

VPATH

对在当前目录下不能找到的文件搜索的路径。参阅VPATH: 所有依赖的搜寻路径

SHELL

系统缺省命令解释程序名,通常是`/bin/sh'。您可以在makefile文件中设值变量SHELL改变运行程序使用的shell。参阅执行命令

MAKESHELL

改变量仅用于MS-DOS,make使用的命令解释程序名,该变量的值比变量SHELL的值优先。参阅执行命令

MAKE

调用的make名。在命令行中使用该变量有特殊的意义。参阅变量MAKE的工作方式。

MAKELEVEL

递归调用的层数(子makes)。参阅与子make通讯的变量

MAKEFLAGS

make提供标志。您可以在环境或makefile文件中使用该变量设置标志。参阅与子make通讯的变量。在命令行中不能直接使用该变量,应为它的内容不能在shell中正确引用,但总是允许递归调用make时通过环境传递给子make。

MAKECMDGOALS

该目标是在命令行中提供给make的。设置该变量对make的行为没有任何影响。参阅特别目标的参数

CURDIR

设置当前工作目录的路径名,参阅递归调用make

SUFFIXES

在读入任何makefile文件之前的后缀列表。

.LIBPATTERNS

定义make搜寻的库文件名,以及搜寻次序。参阅连接库搜寻目录

16 make产生的错误

这里是您可以看到的由make产生绝大多数普通错误列表,以及它们的含义和修正它们信息。

有时make产生的错误不是致命的,如一般在命令脚本行前面存在前缀的情况下,和在命令行使用选向‘-k’的情况下产生的错误几乎都不是致命错误。使用字符串***作前缀将产生致命的错误。

错误信息前面都使用前缀,前缀的内容是产生错误的程序名或makefile文件中存在错误的文件名和包含该错误的行的行号和。

在下述的错误列表中,省略了普通的前缀:

`[foo] Error NN'

`[foo] signal description'

这些错误并不是真的make的错误。它们意味着make调用的程序返回非零状态值,错误码(Error NN),这种情况make解释为失败,或非正常方式退出(一些类型信号),参阅命令错误。如果信息中没有附加***,则是子过程失败,但在makefile文件中的这条规则有特殊前缀,因此make忽略该错误。

`missing separator. Stop.'

`missing separator (did you mean TAB instead of 8 spaces?). Stop.'

这意味着make在读取命令行时遇到不能理解的内容。GNU make 检查各种分隔符(:, =, 字符TAB,等) 从而帮助确定它在命令行中遇到了什么类型的错误。这意味着,make不能发现一个合法的分隔符。出现该信息的最可能的原因是您(或许您的编辑器,绝大部分是ms-windows的编辑器)在命令行缩进使用了空格代替了字符Tab。这种情况下,make将使用上述的第二种形式产生错误信息。一定切记,任何命令行都以字符Tab开始,八个空格也不算数。参阅规则的语法

`commands commence before first target. Stop.'

`missing rule before commands. Stop.'

这意味着在makefile中似乎以命令行开始:以Tab字符开始,但不是一个合法的命令行(例如,一个变量的赋值)。任何命令行必须和一定的目标相联系。产生第二种的错误信息是一行的第一个非空白字符为分号,make对此的解释是您遗漏了规则中的"target: prerequisite" 部分,参阅规则的语法

`No rule to make target `xxx'.'

`No rule to make target `xxx', needed by `yyy'.'

这意味着make决定必须建立一个目标,但却不能在makefile文件中发现任何用于创建该目标的指令,包括具体规则和隐含规则。如果您希望创建该目标,您需要另外为改目标编写规则。其它关于该问题产生原因可能是makefile文件是草稿(如文件名错)或破坏了源文件树(一个文件不能按照计划重建,仅仅由于一个依赖的问题)。

`No targets specified and no makefile found. Stop.'

`No targets. Stop.'

前者意味着您没有为命令行提供要创建的目标,make不能读入任何makefile文件。后者意味着一些makefile文件被找到,但没有包含缺省目标以及命令行等。GNU make在这种情况下无事可做。参阅指定makefile文件的参数

`Makefile `xxx' was not found.'

`Included makefile `xxx' was not found.'

在命令行中指定一个makefile文件(前者)或包含的makefile 文件(后者)没有找到。

`warning: overriding commands for target `xxx''

`warning: ignoring old commands for target `xxx''

GNU make允许命令在一个规则中只能对一个命令使用一次(双冒号规则除外)。如果您为一个目标指定一个命令,而该命令在目标定义是已经定义过,这种警告就会产生;第二个信息表明后来设置的命令将改写以前对该命令的设置。参阅具有多条规则的目标

`Circular xxx <- yyy dependency dropped.'

这意味着make检测到一个相互依靠一个循环:在跟踪目标xxx的依赖yyy 时发现,依赖yyy的依赖中一个又以xxx为依赖。

`Recursive variable `xxx' references itself (eventually). Stop.'

这意味着您定义一个正常(递归调用性)make变量xxx,当它扩展时,它将引用它自身。无论对于简单扩展型变量(:=)或追加定义(+=),这也都是不能允许的,参阅使用变量

`Unterminated variable reference. Stop.'

这意味着您在变量引用或函数调用时忘记写右括号。

`insufficient arguments to function `xxx'. Stop.'

这意味着您在调用函数是您密友提供需要数目的参数。关于函数参数的详细描述参阅文本转换函数

`missing target pattern. Stop.'

`multiple target patterns. Stop.'

`target pattern contains no `%'. Stop.'

这些错误信息是畸形的静态格式规则引起的。第一条意味着在规则的目标部分没有规则,第二条意味着在目标部分有多个规则,第三条意味着没有包含格式符%。参阅静态格式规则语法

`warning: -jN forced in submake: disabling jobserver mode.'

该条警告和下条警告是在make检测到在能与子make通讯的系统中包含并行处理的错误(参阅与子make通讯选项)。该警告信息是如果递归调用一个make过程,而且还使用了‘-jn’选项(这里n大于1)。这种情况很可能发生,例如,如果您设置环境变量MAKE为‘make –j2’。这种情况下,子make不能与其它make过程通讯, 而且还简单假装它由两个任务。

`warning: jobserver unavailable: using -j1. Add `+' to parent make rule.'

为了保证make过程之间通讯,父层make将传递信息给子make。这可能导致问题,因为子过程有可能不是实际的一个make,而父过程仅仅认为子过程是一个make而将所有信息传递给子过程。父过程是采用正常的算法决定这些的(参阅变量MAKE的工作方式)。如果makefile文件构建了这样的父过程,它并不知道子过程是否为make,那么,子过程将拒收那些没有用的信息。这种情况下,子过程就会产生该警告信息,然后按照它内建的次序方式进行处理。

17 复杂的makfile文件例子

这是一个用于GNU tar程序的makefile文件;这是一个中等复杂的makefile文件。

因为‘all’是第一个目标,所以它是缺省目标。该makefile文件一个有趣的地方是testpad.h'是由testpad程序创建的源文件,而且该程序自身由testpad.c'编译得到的。

如果您键入make'`make all',则make创建名为tar'可执行文件, 提供远程访问磁带的进程rmt',和名为tar.info'的Info文件。

如果您键入make install',则make不但创建tar',rmt',和‘tar.info',而且安装它们。

如果您键入make clean', 则make删除所有.o'文件,以及tar',rmt',testpad', testpad.h',和core’文件。

如果您键入make distclean', 则make不仅删除make clean'删除的所有文件,而且包括文件TAGS', Makefile', 和‘config.status' 文件。(虽然不明显,但该 makefile (和config.status')是用户用configure程序产生的,该程序是由发布的tar文件提供,但这里不进行说明。)

如果您键入make realclean', 则make删除make distclean '删除的所有文件,而且包括由tar.texinfo'产生的Info文件。

另外,目标shardist创造了发布文件的核心。

# 自动从makefile.in产生
        
        
# 用于GNU tar 程序的Unix Makefile 
        
        
# Copyright (C) 1991 Free Software Foundation, Inc.
        
        
 
        
        
# 本程序是自由软件;在遵照GNU条款的情况下
        
        
# 您可以重新发布它或更改它
        
        
# 普通公众许可证 ...
        
        
...
        
        
...
        
        
 
        
        
SHELL = /bin/sh
        
        
 
        
        
#### 启动系统配置部分 ####
        
        
 
        
        
srcdir = .
        
        
 
        
        
# 如果您使用gcc, 您应该在运行
        
        
# 和它一起创建的固定包含的脚本程序以及 
        
        
# 使用-traditional选项运行gcc中间选择其一。  
        
        
# 另外的ioctl调用在一些系统上不能正确编译
        
        
CC = gcc -O
        
        
YACC = bison -y
        
        
INSTALL = /usr/local/bin/install -c
        
        
INSTALLDATA = /usr/local/bin/install -c -m 644
        
        
 
        
        
# 您应该在DEFS中添加的内容:
        
        
# -DSTDC_HEADERS        如果您有标准C的头文件和
        
        
#                       库文件。
        
        
# -DPOSIX               如果您有POSIX.1的头文件和
        
        
#                       库文件。
        
        
# -DBSD42               如果您有sys/dir.h (除非
        
        
#                       您使用-DPOSIX), sys/file.h,
        
        
#                       和st_blocks在`struct stat'中。
        
        
# -DUSG                 如果您有System V/ANSI C
        
        
#                       字符串和内存控制函数
        
        
#                       和头文件, sys/sysmacros.h,
        
        
#                       fcntl.h, getcwd, no valloc,
        
        
#                       和 ndir.h (除非
        
        
#                       您使用-DDIRENT)。
        
        
# -DNO_MEMORY_H         如果USG或STDC_HEADERS 但是不
        
        
#                       包括memory.h.
        
        
# -DDIRENT              如果USG而且您又用dirent.h
        
        
#                       代替ndir.h。
        
        
# -DSIGTYPE=int         如果您的信号控制器
        
        
#                       返回int,非void.
        
        
# -DNO_MTIO             如果您缺少sys/mtio.h
        
        
#                       (magtape ioctls).
        
        
# -DNO_REMOTE           如果您没有一个远程shell
        
        
#                       或rexec.
        
        
# -DUSE_REXEC           对远程磁带使用rexec
        
        
#                       操作代替
        
        
#                       分支rsh或remsh.
        
        
# -DVPRINTF_MISSING     如果您缺少函数vprintf
        
        
#                       (但是有_doprnt).
        
        
# -DDOPRNT_MISSING      如果您缺少函数 _doprnt.
        
        
#                       同样需要定义
        
        
#                       -DVPRINTF_MISSING.
        
        
# -DFTIME_MISSING       如果您缺少系统调用ftime
        
        
# -DSTRSTR_MISSING      如果您缺少函数strstr。
        
        
# -DVALLOC_MISSING      如果您缺少函数valloc。
        
        
# -DMKDIR_MISSING       如果您缺少系统调用mkdir和
        
        
#                       rmdir。
        
        
# -DRENAME_MISSING      如果您缺少系统调用rename。
        
        
# -DFTRUNCATE_MISSING   如果您缺少系统调用ftruncate。
        
        
#                       
        
        
# -DV7                  在Unix版本7 (没有
        
        
#                       进行长期测试).
        
        
# -DEMUL_OPEN3          如果您缺少3-参数版本
        
        
#                       的open, 并想通过您有的系统调用
        
        
#                       仿真它。
        
        
# -DNO_OPEN3            如果您缺少3-参数版本的open
        
        
#                       并要禁止tar –k选项
        
        
#                       代替仿真open.
        
        
# -DXENIX               如果您有sys/inode.h
        
        
#                       并需要它包含94
        
        
 
        
        
DEFS =  -DSIGTYPE=int -DDIRENT -DSTRSTR_MISSING /
        
        
        -DVPRINTF_MISSING -DBSD42
        
        
# 设置为rtapelib.o ,除非使它为空时
        
        
# 您定义了NO_REMOTE,
        
        
RTAPELIB = rtapelib.o
        
        
LIBS =
        
        
DEF_AR_FILE = /dev/rmt8
        
        
DEFBLOCKING = 20
        
        
 
        
        
CDEBUG = -g
        
        
CFLAGS = $(CDEBUG) -I. -I$(srcdir) $(DEFS) /
        
        
        -DDEF_AR_FILE=/"$(DEF_AR_FILE)/" /
        
        
        -DDEFBLOCKING=$(DEFBLOCKING)
        
        
LDFLAGS = -g
        
        
 
        
        
prefix = /usr/local
        
        
# 每一个安装程序的前缀。
        
        
#正常为空或`g'。
        
        
binprefix =
        
        
 
        
        
# 安装tar的路径
        
        
bindir = $(prefix)/bin
        
        
 
        
        
# 安装info文件的路径.
        
        
infodir = $(prefix)/info
        
        
 
        
        
#### 系统配置结束部分 ####
        
        
 
        
        
SRC1 =  tar.c create.c extract.c buffer.c /
        
        
        getoldopt.c update.c gnu.c mangle.c
        
        
SRC2 =  version.c list.c names.c diffarch.c /
        
        
        port.c wildmat.c getopt.c
        
        
SRC3 =  getopt1.c regex.c getdate.y
        
        
SRCS =  $(SRC1) $(SRC2) $(SRC3)
        
        
OBJ1 =  tar.o create.o extract.o buffer.o /
        
        
        getoldopt.o update.o gnu.o mangle.o
        
        
OBJ2 =  version.o list.o names.o diffarch.o /
        
        
        port.o wildmat.o getopt.o
        
        
OBJ3 =  getopt1.o regex.o getdate.o $(RTAPELIB)
        
        
OBJS =  $(OBJ1) $(OBJ2) $(OBJ3)
        
        
AUX =   README COPYING ChangeLog Makefile.in  /
        
        
        makefile.pc configure configure.in /
        
        
        tar.texinfo tar.info* texinfo.tex /
        
        
        tar.h port.h open3.h getopt.h regex.h /
        
        
        rmt.h rmt.c rtapelib.c alloca.c /
        
        
        msd_dir.h msd_dir.c tcexparg.c /
        
        
        level-0 level-1 backup-specs testpad.c
        
        
 
        
        
all:    tar rmt tar.info
        
        
 
        
        
tar:    $(OBJS)
        
        
        $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
        
        
 
        
        
rmt:    rmt.c
        
        
        $(CC) $(CFLAGS) $(LDFLAGS) -o $@ rmt.c
        
        
 
        
        
tar.info: tar.texinfo
        
        
        makeinfo tar.texinfo
        
        
 
        
        
install: all
        
        
        $(INSTALL) tar $(bindir)/$(binprefix)tar
        
        
        -test ! -f rmt || $(INSTALL) rmt /etc/rmt
        
        
        $(INSTALLDATA) $(srcdir)/tar.info* $(infodir)
        
        
 
        
        
$(OBJS): tar.h port.h testpad.h
        
        
regex.o buffer.o tar.o: regex.h
        
        
# getdate.y 有8个变换/减少冲突。
        
        
 
        
        
testpad.h: testpad
        
        
        ./testpad
        
        
 
        
        
testpad: testpad.o
        
        
        $(CC) -o $@ testpad.o
        
        
 
        
        
TAGS:   $(SRCS)
        
        
        etags $(SRCS)
        
        
 
        
        
clean:
        
        
        rm -f *.o tar rmt testpad testpad.h core
        
        
 
        
        
distclean: clean
        
        
        rm -f TAGS Makefile config.status
        
        
 
        
        
realclean: distclean
        
        
        rm -f tar.info*
        
        
 
        
        
shar: $(SRCS) $(AUX)
        
        
        shar $(SRCS) $(AUX) | compress /
        
        
          > tar-`sed -e '/version_string/!d' /
        
        
                     -e 's/[^0-9.]*/([0-9.]*/).*//1/' /
        
        
                     -e q
        
        
                     version.c`.shar.Z
        
        
 
        
        
dist: $(SRCS) $(AUX)
        
        
        echo tar-`sed /
        
        
             -e '/version_string/!d' /
        
        
             -e 's/[^0-9.]*/([0-9.]*/).*//1/' /
        
        
             -e q
        
        
             version.c` > .fname
        
        
        -rm -rf `cat .fname`
        
        
        mkdir `cat .fname`
        
        
        ln $(SRCS) $(AUX) `cat .fname`
        
        
        tar chZf `cat .fname`.tar.Z `cat .fname`
        
        
        -rm -rf `cat .fname` .fname
        
        
 
        
        
tar.zoo: $(SRCS) $(AUX)
        
        
        -rm -rf tmp.dir
        
        
        -mkdir tmp.dir
        
        
        -rm tar.zoo
        
        
        for X in $(SRCS) $(AUX) ; do /
        
        
            echo $X ; /
        
        
            sed 's/$/^M/' $X /
        
        
            > tmp.dir/$X ; done
        
        
        cd tmp.dir ; zoo aM ../tar.zoo *
        
        
        -rm -rf tmp.dir
        
        

脚注

(1)

MS-DOS 和 MS-Windows编译的GNU Make和将前缀定义为DJGPP树体系的根的行为相同。

(2)

MS-DOS上, 当前工作目录的值是global, 因此改变它将影响这些系统中随后的命令行。

(3)

texi2dvi 使用TeX 进行真正的格式化工作. TeX没有和 Texinfo一块发布。


本文档使用版本1.54的texi2html 翻译器于2000年7月19日产生。

 

 

 

本文档的版权所有,不允许用于任何商业行为。

名词翻译对照表

archive                                         档案

archive member targets                            档案成员目标

arguments of functions                            函数参数

automatic variables                               自动变量

backslash (/)                                     反斜杠

basename                                       基本文件名

binary packages                                  二进制包

compatibility                                    兼容性

data base                                       数据库

default directries                                 缺省目录

default goal                                     缺省最终目标

defining variables verbatim                        定义多行变量

directive                                       指令

dummy pattern rule                               伪格式规则

echoing of commands                            命令回显

editor                                         编辑器

empty commands                                 空命令

empty targets                                     空目标

environment                                      环境

explicit rule                                      具体规则

file name functions                                文件名函数

file name suffix                                   文件名后缀

flags                                            标志

flavors of variables                                变量的特色

functions                                        函数

goal                                            最终目标

implicit rule                                      隐含规则

incompatibilities                                  不兼容性

intermediate files                                 中间文件

match-anything rule                               万用规则

options                                          选项

parallel execution                                  并行执行

pattern rule                                       格式规则

phony targets                                     假想目标

prefix                                           前缀

prerequisite                                      依赖

recompilation                                    重新编译

rule                                            规则

shell command                                   shell命令

static pattern rule                                  静态格式规则

stem                                           

sub-make                                        make

subdirectories                                     子目录

suffix                                            后缀

suffix rule                                        后缀规则

switches                                         开关

target                                           目标

value                                           

variable                                         变量

wildcard                                        通配符

word                                          

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值