GNU编码标准
- 引用私有程序
- 接受他人的奉献
- 修改日志(Change Logs)
- 与其它实现的兼容性
- Makefile惯例
- 配置是如何进行的
- 使用C以外的语言
- 格式化你的源代码
- 为你的工作写注释
- 清晰地使用C语言成分
- 命名变量和函数
- 使用非标准的特征
- 适用于所有程序的程序行为
- 格式化错误信息
- 库的行为
- 适用于GNU的移植性
- 命令行界面标准
- 为程序制作文档
- 制作发行包
GNU编码标准
GNU编码标准@author{Richard Stallman} @author{last updated 03 Feb 1993}
Copyright (C) 1992, 1993 Free Software Foundation
Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies.
Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one.
Permission is granted to copy and distribute translations of this manual into another language, under the above conditions for modified versions, except that this permission notice may be stated in a translation approved by Free Software Foundation.
本文由王立翻译. 1999.11.9
引用私有程序
不要在任何情况下,为你在的GNU中的工作或者在工作中引用Unix的源代码(或者任何其它私有程序)。
如果你对一个Unix程序内容有一些模糊的记忆,这并不因为着你绝对写程序来模仿它, 但请试图在内部使用不同的代码行来组织它,因为这将使你工作的结果在细节上与Unix版本有所不同。
例如,Unix工具通常进行了优化以使用最少的内存;如果你更希望提高速度,你的程序将会有很大的不同。 你可以在内核中保存整个输入文件并且在内存中扫描而不是使用stdio。使用比Unix程序更新的、 更明智的算法。不使用暂时文件。在一遍扫描而不是两遍扫描中完成任务(我在assembler(汇编器)中这样做了)。
或者相反,强调简单性而不是速度。对于一些应用程序来说,今天的计算机只要使用简单的算法就够了。
或者注重一般性。例如,Unix程序通常使用静态的表格和固定大小的字符串,这导致了不可改变的限制; 用动态分配来代替。确认你的程序处理了输入文件为空和其它滑稽的情况。为增加扩展性而增加一种 程序语言并且用那种语言完成程序的一个部分。
或者把程序的一部分修改成独立的库。或者用一个简单的废物收集器而不是在释放内存的时候精确地进行跟踪, 或者使用诸如obstacks这样的新的GNU工具。
接受他人的奉献
如果其他人发给你一段添加到你正在编写程序中的代码,我们需要准许使用它的法律文书 --我们将需要从你那里取得同样的法律文书。程序的每个重要的贡献者都必须签署 某种法律文书以使得我们可以给程序一个清晰的标题。仅有主要作者是不够的。
所以,在把来自于他人的任何共享添加到程序中之前,告诉我们以便我们可以做出安排 以获取文书。在你实际地使用贡献之前,请等待直到我们告诉你我们已经收到了签署的文书。
这即适用于你发行程序之前也适用于发行之后。如果你收到了一个修正bug的补丁,并且 它们做了主要的修改,我们就需要为他提供法律文书。
你不需要为这里或者那里的少数几行修改提供文书,因为对于达到版权目的没有意义。 还有,如果你从建议中获得的仅仅是一些想法,而不是你实际上使用的代码,你也不需要文书。 例如,如果你写了一个程序的不同解决方案,你并不需要获得许可文书。
我知道这是十分麻烦的;它对我们来说也十分麻烦。但如果你不等待,你就可能误入歧途, 如果这个贡献者的雇主不肯签署弃权声明怎么办?你可能不得不再次把代码剔除出来!
最糟糕的情况是如果你忘记告诉我们其它的贡献者,我们可能会因此而窘迫地出现在法庭上。
修改日志(Change Logs)
为每个目录维护一个修改日志,以记述对这个目录下源文件的修改。这样做的目的是使得 在将来寻找bug的人可以指导大致是那些修改导致了错误。通常,一个新的bug可以在最近进行 的修改中被找到。更重要的事,修改日志有助于消除程序的不同部分之间在概念上的不一致性; 它们可以告诉我们概念冲突产生的历史。
使用Emacs命令M-x add-change在修改日之中创建一个新的条目。一个条目应该 包含一个星号、被修改的文件的名称以及被扩在括号内的、被修改了的函数、变量或者任何东西。 括号之后是冒号和对你对函数或变量的修改的说明。
用空行把无关的条目分隔开。如果两个条目反映了同一个修改,因而它们一同工作,那就不要 在它们之间使用空行。如果后续的条目针对的是相同的文件,那么你可以忽略文件名的星号。
下面是一些例子:
* register.el (insert-register): Return nil. (jump-to-register): Likewise. * sort.el (sort-subr): Return nil. * tex-mode.el (tex-bibtex-file, tex-file, tex-region): Restart the tex shell if process is gone or stopped. (tex-shell-running): New function. * expr.c (store_one_arg): Round size up for move_block_to_reg. (expand_call): Round up when emitting USE insns. * stmt.c (assign_parms): Round size up for move_block_from_reg.
在这里没有必要叙述修改的完整目录和它们是如何协同工作的。把这些说明作为注释放到 代码中更好一些。这就是说为什么只要给出“New function”就够了;在源代码中,与函数 放在一起的注释说明了它是做什么的。
然而,有时为一大堆修改写上一行文字以描述它们的整体目的是有用的。
在概念上,你可以把修改日志看作解释原始版本与当前版本的不同的“undo列表”。 人们可以阅读当前的版本;他们不需要修改日志告诉他们其中有什么。他们从修改日之中 得到的是关于早期版本的不同的清晰解释。
在你以简单的方式修改函数的调用顺序,并且你修改了所有对函数的调用时,不必为所有 的调用创建单独的条目。只要在被调用的函数的条目中写“All callers changed.”即可。
在你仅仅修改了注释或者文档字符串的时候,为该文件写一个条目,而不必提到函数, 就足够了。只要写"Doc fix."。不必为文档文件维护修改日志。这是因为文档不那么容易 受到难以修正的错误的影响。文档不是由那些必须以精确地工程方式相互作用的部分组成的; 要修改一个错误,你不需要知道这个错误传播的历史。
与其它实现的兼容性
作为一个特例,对于GNU中的工具程序和库,它们应该和Berkeley Unix相应的部分向上兼容, 如果标准C定义了它们的行为,那它们应该和标准C向上兼容,如果POSIX规范定义了它们的行为, 那它们也应该与POSIX规范向上兼容。
当这些标准发生冲突的时候,为每个标准提供兼容模式是有用的。
标准C和POSIX禁止进行任何形式的扩展。自由地进行你的扩展,并且把选项 `--ansi'或`--compatible'包括进来以关闭你的扩展。 但是如果扩展很可能导致任何实际程序或者脚本的崩溃,那么它可能实际上不是向上兼容的。 尝试一下重新定义它的界面。
当一个特征仅仅被用户(而不会被程序或者命令文件)所使用的时候,并且在Unix中它完成得 并不好,请自由地用完全不同并且更好的方式代替它。(例如,用Emacs代替vi。)但同时提供兼容 模式仍然是很好的。(现在有自由的vi实现,所以我们提供了它。)
欢迎提供Berkeley Unix没有提供的有用功能。Unix中没有的附加功能可能是有用的,但我们 优先复制那些Unix已经有的功能。
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) ___FCKpd___4lt; -o $@
以便使`VPATH'能够正确地工作。当目标含有多的依赖文件时,显式地使用`$(srcdir)' 让规则正常工作的最简单办法。例如,上述为`foo.1'而提供的目标最好被写作:
foo.1 : foo.man sedscript sed -s $(srcdir)/sedscript $(srcdir)/foo.man > foo.1
Makefile中的工具
书写能够在sh,而不是在csh,中运行的Makefile命令(以及任何shell脚本,例如 configure)。不要使用任何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和exec_prefix的值指明的目录,以及需要的所有目录。完成该任务的一种方式是按照后面所说明的 方式通过目标installdirs来完成。在任何用户安装man手册的命令之前使用`-',以使得
make忽略所有的错误。 错误将在那些没有安装Unix man手册文档系统的系统中出现。安装Info文件的方式是用
$(INSTALL_DATA)把它们复制到`$(infodir)'中。 (参见为指明命令而提供的变量),并且如果有程序install-info存在, 那么就运行它。install-info是一个脚本,它编辑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的 `mostlyclean'目标不会删除`libgcc.a',这是因为很少需要重新编译并且重新编译将花费大量的时间。
- `realclean'
- 从当前目录中删除所有可以由Makefile重新创建的文件。这通常包括所有由
distclean删除的文件, 以及:由Bison生成的C源文件、标记表(tags tables)、Info文件等等。然而有一个例外:即使`configure'可以通过使用Makefile中的规则重新创建,`make realclean' 也不会删除`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,该程序也是Texinfo 发布版本的一部分。作为另一个选择,只要写依赖文件并且允许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编译器选项,不要把它们包括在CFLAGS中。用户希望 能够自由地指明CFLAGS的值。替代的方式是:通过在编译命令行中显式地给出这些必要的选项或者通过定义一条隐含 规则,从而以独立于CFLAGS的方式把选项传递给C编译器。
CFLAGS = -g ALL_CFLAGS = -I. $(CFLAGS) .c.o: $(CC) -c $(CPPFLAGS) $(ALL_CFLAGS) ___FCKpd___12lt;
把选项`-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
总是把文件名,而不是目录名,作为安装命令的第二个参数。为每个需要安装的文件使用独立的命令。





