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、
1、
在‘t’中分离出路径部分,称为‘d’,剩下部分称为‘n’。例如如果‘t’是‘src/foo.o’,那么‘d’是‘src/’;‘n’是‘foo.o’。
2、
2、
建立所有目标名匹配‘t’和‘n’的格式规则列表。如果目标格式中含有斜杠,则匹配‘t’,否则,匹配‘n’。
3、
3、
如果列表中有一个规则不是万用规则,则从列表中删除所有非最终万用规则。
4、
4、
将没有命令的规则也从列表中移走。
5、
5、
对每个列表中的格式规则:
1、
1、 寻找stem‘s’,也就是和目标格式中%匹配的‘t’或‘n’部分。
2、
2、 使用stem‘s’计算依赖名。如果目标格式不包含斜杠,则将‘d’添加在每个依赖的前面。
3、
3、 测试所有的依赖是否存在或能够创建。(如果任何文件在makefile中作为目标或依赖被提及,则我们说它应该存在。)如果所有依赖存在或能够创建,或没有依赖,则可使用该规则。
6、
6、
如果到现在还没有发现能使用的规则,进一步试。对每一个列表中的规则:
1、
1、 如果规则是最终规则,则忽略它,继续下一条规则。
2、
2、 象上述一样计算依赖名。
3、
3、 测试所有的依赖是否存在或能够创建。
4、
4、 对于不存在的依赖,按照该算法递归调用查找是否能够采用隐含规则创建。
5、
5、 如果所有依赖存在或能使用隐含规则创建,则应用该规则。
7、
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 $< -o $*.o
$(AR) r $@ $*.o
$(RM) $*.o
这和下面的格式规则工作完全一样:
(%.o): %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -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 $< -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)
使用ranlib或ldconfig,您应该确定如果系统中不存在要使用的程序不会引起任何副作用。安排忽略这些命令产生的错误,并且打印信息告诉用户该命令运行失败并不意味着存在问题。(Autoconf‘AC_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) $<
变量CFLAGS中包括选项‘-g’,因为它对于一些编译并不是必需的,您可以认为它是缺省推荐的选项。如果数据包创建使用GCC作为编译器,则变量CFLAGS中包括选项‘-o’,而且以它为缺省值。
将变量CFLAGS放到编译命令的最后,在包含编译选项其它变量的后边,因此用户可以使用变量CFLAGS对其它变量进行重载。
每次调用C编译器都用到变量CFLAGS ,无论进行编译或连接都一样。
任何Makefile文件都定义变量INSTALL,变量INSTALL是将文件安装到系统中的基本命令。
任何Makefile文件都定义变量INSTALL_PROGRAM 和INSTALL_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是一个编辑Info‘dir'文件的程序,它可以为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