为安装目录而提供的变量
安装目录总是应该通过变量来命名,以易于把包安装在其它非标准的位置。这些变量的标准名字是:
- `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/local/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目录中或者oldincludedir目录中的`foo.h'也是来自 与Foo包,那么Foo包就应该把头文件安装到oldincludedir中。为了判定`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'为configure的编辑提供了输入。同样, 这样做是为了保证用户在配置程序之前不能创建它。
如果configure生成了`Makefile',那么`Makefile' 就应该包含一个名为`Makefile'的目标,这个目标将重新运行configure 以与获取上一次配置相同的配置信息。由configure读取的文件,应该作为 依赖性文件而在`Makefile'中被列出。
所有由configure脚本生成的文件在它们的第一行都应该包含一条注释 以说明它们是由configure自动生成的。这样做是为了确保用户不会试图手工 修改它们。
脚本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脚本只需要简单地 忽略它的大部分参数就行了。
使用C以外的语言
使用C以外的语言就好像使用非标准特征:它将为用户带来麻烦。即使GCC能够支持其它语言, 用户也可能因为不得不安装其它语言的编译器以创建你的程序而感到不便。所以请使用C语言。
这条规则有三个例外:
- 如果有些程序包括了特殊语言的解释器,那么就可以使用这种语言。
因此,GNU Emacs包含用Emacs Lisp写的代码就没有问题,因为GNU Emacs包含了Lisp解释器。
- 如果一个工具就是为了某种语言而编写的,那么就可以使用那种语言。
这是因为那些需要创建这个工具的人必然是那些已经安装了其它语言的人。
- 如果一个应用程序没有被极端广泛地关注,那么应用程序的安装不太方面就不是特别重要。
格式化你的源代码
把作为C函数的开头的左花括号放到第零列是十分重要的,并且避免把任何其它的左花括号、左括号 或者左方括号放到第零列。有些工具通过寻找在第零列的左花括号来寻找C函数的起点。这些工具将不能 处理那些不按照这种方式排版的代码。
对于函数定义来说,把函数名的起始字符放到第零列也同样重要。这帮助任何寻找函数定义,并且 可能有助于帮助某些工具识别它们。因此,正确的格式应该是:
static char * concat (s1, s2) /* Name starts in column zero here */ char *s1, *s2; { /* Open brace in column zero here */ ... }
或者,如果你希望使用标准C,定义的格式是:
static char * concat (char *s1, char *s2) { ... }
在标准C中,如果参数不能够被美观地放在一行中,按照下面的方式把它们分开:
int lots_of_args (int an_integer, long a_long, short a_short, double a_double, float a_float) ...
对于函数体,我们希望它按照如下方式排版:
if (x < foo (y, z)) haha = bar[4] + 5; else { while (z) { haha += foo (z, z); z--; } return ++x + bar (); }
我们发现如果在左括号之前以及逗号之后添加空格将使程序更加容易阅读。尤其是在 逗号之后添加空格。
当我们把一个表达式分成多行的时候,在操作符之前而不是之后分割。下面是正确的方式:
if (foo_this_is_long && bar > win (x, y, z) && remaining_condition)
尽力避免让两个不同优先级的操作符出现在相同的对齐方式中。例如,不要象下面那样写:
mode = (inmode[j] == VOIDmode || GET_MODE_SIZE (outmode[j]) > GET_MODE_SIZE (inmode[j]) ? outmode[j] : inmode[j]);
应该附加额外的括号以使得文本缩进可以表示出这种嵌套:
mode = ((inmode[j] == VOIDmode || (GET_MODE_SIZE (outmode[j]) > GET_MODE_SIZE (inmode[j]))) ? outmode[j] : inmode[j]);
插入额外的括号以使得Emacs可以正确地对齐它们。例如,如果你手工完成缩进工作, 那么它们看起来不错,但Emacs将把它们混在一起:
v = rup->ru_utime.tv_sec*1000 + rup->ru_utime.tv_usec/1000 + rup->ru_stime.tv_sec*1000 + rup->ru_stime.tv_usec/1000;
但添加一组括号解决了这个问题:
v = (rup->ru_utime.tv_sec*1000 + rup->ru_utime.tv_usec/1000 + rup->ru_stime.tv_sec*1000 + rup->ru_stime.tv_usec/1000);
按照如下方式排版do-while语句:
do { a = foo (a); } while (a > 0);
请按照逻辑关系(而不是在函数中)使用走纸字符(control-L)以把程序划分成页。 页有多长并不重要,因为它们不必被放在一个打印的页中。走纸字符应该单独地出现在一行中。
为你的工作写注释
每个程序都应该以一段简短地、说明其功能的注释开头。 例如:`fmt - filter for simple filling of text'.
请为每个函数书写注释以说明函数做了些什么,需要哪些种类的参数,参数可能值的含义 以及用途。如果按照常见的方式使用C语言类型,就没有必要逐字重写C参数声明的含义。如果 它使用了任何非标准的东西(例如,一个类型为char *的参数实际上给出了 一个字符串的第二个字符,而不是第一个字符,的地址),或者是可能导致函数不能工作的 任何可能的值(例如,不能保证正确处理一个包含了新行的字符串),请确认对它们进行了说明。
如果存在重要的返回值,也需要对其进行解释。
请在你的注释之后添加两个空格,以便Emacs句子命名进行处理。还有,请书写完整的句子 并且使头一个单词以大写字母开头。如果小写字母组成的标识符出现在句子的开头,不要把它 变成大写的!修改拼写就构成了不同的标识符。如果你不希望句子以小写字母开头,可以写下 不同的句子(例如,“The identifier lower-case is ...”)。
如果你使用参数名来说明参数值,关于函数的注释就会更清晰。变量名本身应该是小写的, 但在你说到它的值而不是变量本身的时候就使用大写字母。因此,“the inode number node_num” 比“an inode”要好。
通常在函数之前的注释中没有必要重新提到函数的名字,因为读者可以自己看到它。 一种可能的例外是:注释太长了,以至于函数本身被挤出了屏幕底端之外。
对于每个静态变量,也象下面那样应该提供注释:
/* Nonzero means truncate lines in the display; zero means continue them. */ int truncate_lines;
除非`#endif'是一个没有嵌套而且很短(只有几行)的条件,每个 `#endif'都应该含有一个注释。注释应该说明它所结束的条件,包括它的含义。 `#else'应该含有一个说明条件与随后代码的含义的注释。例如:
#ifdef foo ... #else /* not foo */ ... #endif /* not foo */
但相反,按照如下方式为`#ifndef'写注释:
#ifndef foo ... #else /* foo */ ... #endif /* foo */








