当前位置:Linux教程 - Linux - GNU 编码标准 Part 2

GNU 编码标准 Part 2

Makefile惯例
本章叙述为GNU程序书写Makefile的惯例。
Makefile的通用惯例
每个Makefile都应该包含这一行:
SHELL = /bin/sh
以避免那些由从环境中继承SHELL变量的系统带来的麻烦。(GNU make永远不会出现这个
问题。)
不要假定``.''出现在用于寻找可执行的命令的路径中。当你需要在make期间运行作为你的
包的一部分的程序时,如果程序是作为make的一部分而创建的,请确保它使用了``./'',
或者如果文件是不会被改变的源代码的一部分,请确保它使用了``$(srcdir)/''。
如果运行``configure''时使用了选项``--srcdir'',那么``./''与 ``$(srcdir)/''之间的区别
就十分重要。一下形式的规则:
foo.1 : foo.man sedscript
sed -e sedscript foo.man > foo.1
将在当前目录不是源代码目录的情况下导致错误,这是因为``foo.man''和``sedscript'' 不
在当前目录中。
在使用GNU make的时候,由于不论源文件在那里,``make''的自动变量``$<'' 都将表示它,
所以在只存在一个依赖文件的情况下,依靠``VPATH''来寻找源文件仍然是可行的。(许多
版本的make只在隐含规则中设置``$<''。)如下的makefile目标:
foo.o : bar.c
$(CC) -I. -I$(srcdir) $(CFLAGS) -c bar.c -o foo.o
将被如下目标所替代:
foo.o : bar.c
$(CC) $(CFLAGS) $< -o $@
以便使``VPATH''能够正确地工作。当目标含有多的依赖文件时,显式地使用``$(srcdir)''
让规则正常工作的最简单办法。例如,上述为``foo.1''而提供的目标最好被写作:
foo.1 : foo.man sedscript
sed -s $(srcdir)/sedscript $(srcdir)/foo.man > foo.1
Makefile中的工具
书写能够在sh,而不是在csh,中运行的Makefile命令(以及任何shell脚本,例如 con
figure)。不要使用任何ksh或者bash特殊的功能。
为创建和安装而提供的configure脚本和Makefile规则不要直接使用任何工具,除了以下
的几个之外:
cat cmp cp echo egrep expr grep
ln mkdir mv pwd rm rmdir sed test touch
坚持使用这些程序通常支持的选项。例如,因为许多系统不支持``mkdir -p'',尽管它可
能有些方便,但不要使用它。
为创建和安装而提供的Makefile规则还可以使用编译器和相关的程序,但应该通过make
变量以便用户对它们进行替换。下面是一些我们所说的相关的程序:
ar bison cc flex install ld lex
make makeinfo ranlib texi2dvi yacc
在你使用ranlib的时候,你应该测试它是否存在,并且仅仅在它存在的情况下运行它,
以使得发布版本在那些没有ranlib的系统中也能够工作。
如果你使用了符号连接,你应该为没有符号连接的系统实现一个替代手段。
你可以在只打算用于特定系统的Makefile的部分(或者脚本)中使用你能够确认在那些
系统上存在的工具。
为用户提供的标准目标
所有的GNU程序应该在它们的Makefile中含有下列目标:
``all''
编译整个程序。它应该是缺省目标。这个目标不需要重新创建任何文档文件;Info文件
被包含在发布版本中,同时,只有在用户明确地要求创建DVI文件的时候才创建DVI文件

``install''
编译程序并且把可执行文件、库文件等文件复制到它们在实际应用中应该存在的位置。
如果存在一个可以检测程序是否被正确地安装了的简单测试,本目标将首先运行这个测
试。
如果文件的安装目录不存在,该命令将创建这样的目录。它们包括由变量prefix和 exe
c_prefix的值指明的目录,以及需要的所有目录。完成该任务的一种方式是按照后面所
说明的方式通过目标installdirs来完成。
在任何用户安装man手册的命令之前使用``-'',以使得make忽略所有的错误。错误将在那
些没有安装Unix man手册文档系统的系统中出现。
安装Info文件的方式是用$(INSTALL_DATA)把它们复制到``$(infodir)''中。(参见为指明
命令而提供的变量),并且如果有程序install-info存在,那么就运行它。install-in
fo是一个脚本,它编辑Info ``dir''文件以把给定的Info文件添加或者更新目录项的脚本
;它将是Texinfo包的一个部分。下面是用于安装一个Info文件一个简单规则:
$(infodir)/foo.info: foo.info
# There may be a newer info file in . than in srcdir.
-if test -f foo.info; then d=.;
else d=$(srcdir); fi;
$(INSTALL_DATA) $$d/foo.info $@;
# Run install-info only if it exists.
# Use ``if'' instead of just prepending ``-'' to the
# line so we notice real errors from install-info.
# We use ``$(SHELL) -c'' because some shells do not
# fail gracefully when there is an unknown command.
if $(SHELL) -c ''install-info --version''
>/dev/null 2>&1; then
install-info --infodir=$(infodir) $$d/foo.info;
else true; fi
``uninstall''
删除所有由``install''目标创建的所有安装的文件(但不包括那些由诸如``make all'' 之
类的目标创建的,没有被安装的文件)。
``clean''
从当前目录中删除所有在创建程序过程中创建的文件。不要删除那些纪录配置情况的文
件。有些文件可能是在创建过程中创建的,但因为它们是和发布版本一起发布的,通常
不是在创建过程中创建的,这样的文件也需要保留下来。
如果``.dvi''文件不是发布版本的一部分,就删除它们。
``distclean''
从当前目录中删除所有在程序的配置和创建过程中创建的文件。如果你解包源代码并且
在没有添加任何其它文件的情况下创建程序,``make distclean''将仅仅保留那些出现在
发布版本中的文件。
``mostlyclean''
类似于``clean'',可能不会删除少数人们通常不希望重新编译的文件。例如,GCC的 ``mo
stlyclean''目标不会删除``libgcc.a'',这是因为很少需要重新编译并且重新编译将花费
大量的时间。
``realclean''
从当前目录中删除所有可以由Makefile重新创建的文件。这通常包括所有由distclean删
除的文件,以及:由Bison生成的C源文件、标记表(tags tables)、Info文件等等。
然而有一个例外:即使``configure''可以通过使用Makefile中的规则重新创建,``make r
ealclean'' 也不会删除``configure''。更一般地说,``make realclean''将不会删除为了运
行``configure'' 而存在的任何东西,并且随后开始创建程序。
``TAGS''
为本程序更新标记表(tags table)。
``info''
生成所有需要的Info文件。书写该规则的最佳方式是:
info: foo.info
foo.info: foo.texi chap1.texi chap2.texi
$(MAKEINFO) $(srcdir)/foo.texi
你必须在Makefile中定义变量MAKEINFO。它应该运行程序makeinfo,该程序是Texinfo发
布版本的一部分。
``dvi''
Generate DVI files for all TeXinfo documentation. For example:
dvi: foo.dvi
foo.dvi: foo.texi chap1.texi chap2.texi
$(TEXI2DVI) $(srcdir)/foo.texi
你必须在Makefile中定义变量TEXI2DVI。它应该运行程序texi2dvi,该程序也是Texinf
o 发布版本的一部分。作为另一个选择,只要写依赖文件并且允许GNU Make提供这个命
令就行了。
``dist''
为本程序创建一个发布版本tar文件。该tar文件将被设置以使得在tar文件中的文件名以
子目录名开头,这个子目录名是包用于发布的名字。这个名字可以包含版本号。
例如,GCC版本1.40的发布tar文件将被解包到名为``gcc-1.40''的子目录中。
完成该任务的最简单方式是以适当的名称创建一个子目录,使用ln或者cp把正确的文件
安装到该目录中,而后tar这个子目录。
目标dist应该显式地依赖于发布版本中所有的非源文件,以确保它们在发布版本中都不
是过时的。参见 制作发布包
``check''
(如果有的话)执行自检测。用户必须在运行测试之前,但不必在安装程序之前创建程
序;你应该写下自检测以便它们在程序创建之后而没有被安装之前进行工作。
对于那些适用于以下的目标的程序,建议你按照常用的名字提供它们。
installcheck
(如果有的话)执行安装监测。用户必须在运行该检测之前创建并且安装程序。你不应
该假定``$(bindir)'' 出现在搜索路径中。
installdirs
添加一个名为``installdirs''的目标,以便创建安装文件的目录和它们的父目录。有一个
称为``mkinstalldirs''的脚本可以为此提供便利;在Texinfo包中可以找到它。你可以使
用象下面那样的规则:
# Make sure all installation directories (e.g. $(bindir))
# actually exist by making them if necessary.
installdirs: mkinstalldirs
$(srcdir)/mkinstalldirs $(bindir) $(datadir)
$(libdir) $(infodir)
$(mandir)
为指明命令而提供的变量
Makefile应该提供变量以覆盖某些命令、选项等等。
特别地,你应该通过变量来运行大部分工具程序。因此,如果你使用了Bison,就定义一
个缺省值是通过``BISON = bison'' 来设定的变量BISON,并且在你需要使用Bison的所有
地方通过$(BISON)引用它。
在这种方式下,文件管理工具:ln、rm、mv等等并不需要通过变量引用,这是因为用户
不需要用其它程序来替代它们。
每个程序名变量都应该有一个对应的变量以便为程序提供选项。把``FLAGS''附加到程序名
变量名的后面就是选项变量名--例如,BISONFLAGS。(名字CFLAGS是这项规则的一个例
外,但因为它是标准的而保留了它。)在任何运行预处理器的编译命令中使用CPPFLAGS
,在任何进行连接的编译命令和任何对ld的直接使用中使用LDFLAGS。
如果存在一些为了正确地编译某些文件而必须使用的C编译器选项,不要把它们包括在C
FLAGS中。用户希望能够自由地指明CFLAGS的值。替代的方式是:通过在编译命令行中显
式地给出这些必要的选项或者通过定义一条隐含规则,从而以独立于CFLAGS的方式把选
项传递给C编译器。
CFLAGS = -g
ALL_CFLAGS = -I. $(CFLAGS)
.c.o:
$(CC) -c $(CPPFLAGS) $(ALL_CFLAGS) $<
把选项``-g''包括在CFLAGS中,因为它对于正确的编译来说并不是必要的。你可以认为它
仅仅是关于缺省值的一个建议。如果包被设置成在缺省的状态下由GCC编译,那么你可能
还需要把``-O'' 包括在CFLAGS的缺省值之中。
把CFLAGS放在编译命令行的最后,就是在其它包含了编译选项的变量之后,以便于用户
使用CFLAGS 来覆盖其它的选项。
每个Makefile都应该定义变量INSTALL,它是把一个文件安装到系统中的基本命令。
每个Makefile还应该定义变量INSTALL_PROGRAM和INSTALL_DATA。(两者的缺省值都应该
是 $(INSTALL)。)而后,Makefile应该使用这些变量作为实际安装的命令,分别用于安
装可执行文件和不可执行的文件。按照下面的方式使用这些变量:
$(INSTALL_PROGRAM) foo $(bindir)/foo
$(INSTALL_DATA) libfoo.a $(libdir)/libfoo.a
总是把文件名,而不是目录名,作为安装命令的第二个参数。为每个需要安装的文件使
用独立的命令。
为安装目录而提供的变量
安装目录总是应该通过变量来命名,以易于把包安装在其它非标准的位置。这些变量的
标准名字是:
``prefix''
用于构造下列变量的缺省值的前缀。prefix的缺省值应该是``/usr/local''(至少现在是
它)。
``exec_prefix''
用于构造下列某些变量的缺省值的前缀。exec_prefix的缺省值应该是$(prefix)。
一般来说,$(exec_prefix)指的是用于储存与机器有关的文件(比如说可执行文件和子
程序库)的目录,而$(prefix)则被直接用于其它目录。
``bindir''
用于储存用户可以运行的可执行程序的目录。一般来说应该是``/usr/local/bin'',但应
该被写作 ``$(exec_prefix)/bin''。
``libdir''
用于安装由程序运行,而不是由用户运行的可执行文件的目录。Object文件和object代
码库也应该被储存在这个目录。提供该目录的意图是为了储存适用于特殊机器结构,但
又不必出现在命令路径中的文件。libdir的值通常是 ``/usr/local/lib'',但应该被写作
``$(exec_prefix)/lib''。
``datadir''
用于安装程序在运行时需要访问的只读数据文件的目录。该目录用于储存与使用的机器
独立的文件。它通常是 ``/usr/local/lib'',但应该被写作``$(prefix)/lib''。
``statedir''
用于安装程序在运行时需要修改的数据文件的目录。这些文件应该与使用的机器类型独
立,并且应该可以在网络安装的情况下载不同的机器之间共享。它通常应该是``/usr/lo
cal/lib'',但应该被写作``$(prefix)/lib''。
``includedir''
用于储存将被用户程序以C预处理指令``#include''引入的头文件的目录。它通常应该是
``/usr/local/include'',但应该被写作``$(prefix)/include''。
除了GCC以外,大部分编译器并不在``/usr/local/include''中寻找头文件。所以以这种方
式安装头文件仅仅适用于GCC。但有些库被设计成与其它编译器共同工作。它们应该在两
个地方安装它们的头文件,一个由includedir 给出,另一个由oldincludedir给出。
``oldincludedir''
为除了GCC之外的其它编译器安装头文件的目录。这通常应该是``/usr/include''。
Makefile命令应该检测oldincludedir的值是否为空。如果为空,Makefile命令就不应该
试图使用 oldincludedir;Makefile命令应该放弃对头文件的第二个安装。
除非头文件来自于同一个包,包不应该替换已经存在的头文件。因此,如果你的Foo包提
供了一个头文件``foo.h'',并且如果它没有出现在oldincludedir目录中或者oldinclude
dir目录中的``foo.h''也是来自与Foo包,那么Foo包就应该把头文件安装到oldincludedi
r中。
为了判定``foo.h''是否来自于Foo包,可把一个特殊的字符串作为注释的一部分放在文件
中,而后用grep搜索这个字符串。
``mandir''
(如果存在)本包安装man手册的目录。它应该包含对应于正确的手册部分的后缀-- 对
于一个工具来说通常是``1''。它一般是``/usr/local/man/man1'',但你应该把它写成:``$
(prefix)/man/man1''。
``man1dir''
安装man手册第一部分的目录。
``man2dir''
安装man手册第二部分的目录。
``...''
如果包需要把man手册安装到手册系统的多个部分,就用这些名字来代替``mandir''。
不要把man手册作为GNU软件的主要文档。用Texinfo书写文档来代替它。Man手册只是因
为人们在Unix,它只是一个次要的应用程序,上运行GNU软件才存在的。
``manext''
作为需要安装的man手册的文件的扩展名。它应该是一个点加上一个适当的数字;通常它
应该是:``.1''。
``man1ext''
将被安装到man手册第一部分的文件的扩展名。
``man2ext''
将被安装到man手册第二部分的文件的扩展名。
``...''
如果包需要把man手册安装到手册系统的多个部分,就用这些名字代替``manext''。
``infodir''
为本包安装Info文件的目录。在缺省状态下,它应该是``/usr/local/info'',当它应该被
写成 ``$(prefix)/info''。
``srcdir''
用于编译源代码的目录。该变量的值通常是由configureshell脚本插入的。
例如:
# Common prefix for installation directories.
# NOTE: This directory must exist when you start the install.
prefix = /usr/local
exec_prefix = $(prefix)
# Where to put the executable for the command ``gcc''.
bindir = $(exec_prefix)/bin
# Where to put the directories used by the compiler.
libdir = $(exec_prefix)/lib
# Where to put the Info files.
infodir = $(prefix)/info
如果你的程序在一个标准的用户给定的目录中安装了大量的文件,可能把为程序特别提
供的文件存放到子目录中会有用一些。如果你这样做了,你应该改写install规则以创建
这些子目录。
不要指望用户会把子目录名包括在上面列出的变量的值中。为安装目录提供统一的变量
名集合的意图是使得用户可以为一些不同的GNU包指明完全相同的值。为了使这些规定变
得有用,所有的包都必须这样设计以便在用户这样做的时候它们将能够有效地工作。
配置是如何进行的
每个GNU发布版本都应该还有一个名为configure的shell脚本。你需要把你希望在那种机
器和系统上编译程序作为参数告诉这个脚本。
脚本configure必须记录配置信息以便它们可以影响编译工作。
这样做的一种方式是把一个诸如``config.h''的标准名字和为选定的系统匹配的正确配置
文件连接起来。如果你使用了这种技术,发布版本中就不应该包含名为 ``config.h''的文
件。这样做是为了保证用户在配置程序之前不能够创建它。
configure可以做的另一件事情是编辑Makefile。如果你这样做了,发布版本中就不能包
含名为``Makefile''的文件。用``Makefile.in''来代替它,并且``Makefile.in''为configur
e的编辑提供了输入。同样,这样做是为了保证用户在配置程序之前不能创建它。
如果configure生成了``Makefile'',那么``Makefile'' 就应该包含一个名为``Makefile''的
目标,这个目标将重新运行configure 以与获取上一次配置相同的配置信息。由config
ure读取的文件,应该作为依赖性文件而在``Makefile''中被列出。
所有由configure脚本生成的文件在它们的第一行都应该包含一条注释以说明它们是由c
onfigure自动生成的。这样做是为了确保用户不会试图手工修改它们。
脚本configure应该写入一个名为``config.status''的文件,该文件说明了在程序的最后
一次配置中给出了那些配置选项。该文件应该是一个shell脚本,如果运行它,将重新生
成相同的配置。
脚本configure应该接受形式为``--srcdir=dirname'' 的选项以指明在那个目录中可以找
到源代码(如果源代码不在当前目录中)。这使得可以在实际代码目录没有被修改的情
况下,在分离的中创建程序成为可能。
如果用户没有给出``--srcdir'',那么configure将在``.'' 和``..''中寻找源文件。如果它在
上述地方之一发现源文件,它就应该在那里使用它们。否则,它应该报告它没有找到源
文件,并且以非零状态退出。
通常,支持``--srcdir''的简单方式是通过编辑被放到Makefile中的一个 VPATH的定义。
可能有一些规则需要被显式地引用以指明源代码目录。为了达到这个目的,configure可
以把一个名为srcdir的变量添加到Makefile中,该变量的值就是给定的目录。
脚本configure还应该提供一个可以指明程序是究竟为那种系统而创建的选项。这个选项
看起来应该象:
cpu-company-system
例如,一个Sun 3可能是``m68k-sun-sunos4.1''。
脚本configure需要能够解释所有对机器的似是而非的描述方式。因此, ``sun3-sunos4
.1''应该是有效的别名。``sun3-bsd4.2''也是如此,因为SunOS是基于BSD的并且没有其它
的BSD系统被用于Sun。对于许多程序来说,因为Ultrix和BSD之间的区别很少被注意到,
所以``vax-dec-ultrix''将是 ``vax-dec-bsd''的一个别名。但少数程序可能需要区分它们

这里有一个被称为``config.sub''的shell脚本,你可以把它作为一个子程序使用以检查系
统类型并且对别名进行规范化。
允许出现其它选项以指明关于机器的软件或者硬件的更多细节:
``--with-package''
包package将被安装,所以把本包配置成与package一同工作。
package可能的取值包括``x''、``gnu-as''(或者 ``gas'')、``gnu-ld''、``gnu-libc''和``gdb
''。
``--nfp''
目标机器没有浮点数处理器。
``--gas''
目标机器的汇编器是GAS,GNU的汇编器。该选项已经过时了;用``--with-gnu-as''来代替

``--x''
目标机器已经安装了X Window系统。该选项已经过时了;用``--with-x'' instead来代替

所有的configure脚本都应该接受所有这些“细节”选项,而不论它们是否会对手头的特
定包产生影响。特别地,它们应该接受任何以``--with-''开头的选项。这样做是因为这使
得用户可以用同一组选项配置整个GNU源代码树。
作为编译的一部分的包可能支持交叉编译(cross-compliation)。在这种情况下,程序
的主机和目标机器可能是不同的。configure通常把指明的系统类型当作主机和目标机器
,因此将创建与运行它的机器类型相同的机器上运行的程序。
创建交叉编译器(cross-compiler)、交叉汇编器(cross-assembler)、或者你自己的
程序,通过在运行 configure时给出选项``--host=hosttype''来完成。它在不影响目标机
器的情况下指明了主机名。hosttype的语法与前面所说的一样。
因为为互操作(cross-operation)配置整个操作系统是一件没有意义的事,对互操作来
说没有意义的程序就不必接受选项``--host''。
有些程序自动地配置它们自己。如果你的程序被设置成这样,你的configure脚本只需要
简单地忽略它的大部分参数就行了。