本章介绍为 GNU 程序编写 Makefile 时的约定。使用 Automake 有助于编写遵循这些约定的 Makefile。想进一步了解可移植的 Makefile,请参阅 POSIX 规范以及 Autoconf 手册中的 Portable Make Programming 一节。
DESTDIR:对分阶段安装的支持每个 Makefile 都应当包含下面这一行:
SHELL = /bin/sh
这是为了在 SHELL 变量可能从环境继承下来的系统上避免麻烦。(对于 GNU make,这个问题根本不会出现。)
某些 make 程序的后缀列表和隐含规则彼此不兼容,这有时会造成混乱或误动作。因此,最好在该 Makefile 中只使用所需的后缀,显式地设置后缀列表。可以这样写:
.SUFFIXES: .SUFFIXES: .c .o
第一行先清空后缀列表,第二行引入本 Makefile 中可能成为隐含规则对象的所有后缀。
不要假定执行命令时的路径中包含 .(当前目录)。在 make 过程中,如果需要运行属于你自己软件包的程序,请务必加上前缀:如果该程序是作为 make 的一部分构建出来的,就用 ./;如果该文件是源代码中不会变化的一部分,就用 $(srcdir)/。如果这两个前缀都不加,就会使用当时的搜索路径。
区分 ./(构建目录)和 $(srcdir)/(源目录)是很重要的,因为用户可以通过 configure 的「--srcdir」选项在另一个目录中进行构建。形如下面这样的规则,
foo.1 : foo.man sedscript
sed -f sedscript foo.man > foo.1
在构建目录与源目录不同时会失败,因为 foo.man 和 sedscript 位于源目录中。
使用 GNU make 时,如果前置条件 (prerequisite) 文件只有一个,那么依赖「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.1 目标最好这样写:
foo.1 : foo.man sedscript
sed -f $(srcdir)/sedscript $(srcdir)/foo.man > $@
GNU 发行版中通常还包含一些并非源文件的文件,例如 Info 文件,以及 Autoconf、Automake、Bison、Flex 的输出。由于这些文件通常放在源目录中,因此它们应当始终放在源目录而非构建目录里。所以,用于更新它们的 Makefile 规则应当把更新后的文件放到源目录中。
不过,对于不包含在发行版中的文件,Makefile 不应把它放到源目录里,因为在通常情况下构建程序不应以任何方式改动源目录。
至少要让构建用和安装用的目标(以及它们的所有子目标)能够在并行 make 下正确工作。
请把 Makefile 中的命令(以及诸如 configure 之类的 shell 脚本)写成能在 sh(包括传统的 Bourne shell 和 POSIX shell)下运行,而不是 csh。不要使用 ksh 或 bash 的特殊功能,也不要使用在传统 Bourne sh 中没有得到广泛支持的 POSIX 功能。
在 configure 脚本以及构建和安装用的 Makefile 规则中,除以下实用工具外,不应直接使用其他工具:
awk cat cmp cp diff echo expr false grep install-info ln ls mkdir mv printf pwd rm rmdir sed sleep sort tar test touch tr true
诸如 gzip 之类的压缩程序可以在 dist 规则中使用。
基本上,即便是这些程序,也应只使用得到广泛支持(通常由 POSIX 规定)的选项和功能。例如,尽管「mkdir -p」很方便,也请不要使用它,因为有少数系统完全不支持它,而在其他系统上它在并行执行时也不安全。已知的不兼容情况清单,请参阅 Autoconf 手册中的 Portable Shell Programming 一节。
在 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 部分(或脚本)中,只要确知那些实用工具在该系统上存在,就可以使用其他工具。
Makefile 应当提供变量,以便能够覆盖特定的命令、选项等等。
尤其是,大多数实用工具程序都应通过变量来运行。例如,如果使用 Bison,就准备一个名为 BISON 的变量,用「BISON = bison」设置其默认值,然后在每次需要使用 Bison 时通过 $(BISON) 来引用它。
像 ln、rm、mv 这类文件管理用的实用工具,无需这样通过变量来引用,因为用户没有必要把它们替换成别的程序。
表示程序名的每个变量,都应当配有一个用于向该程序传递选项的选项变量。选项变量的名称是在程序名变量名后面加上「FLAGS」构成的,例如 BISONFLAGS。(C 编译器用的 CFLAGS、yacc 用的 YFLAGS、lex 用的 LFLAGS 是这一规则的例外,但因为它们是标准名称,所以沿用下来。)在运行预处理器的编译命令中请使用 CPPFLAGS,在进行链接的编译命令以及直接使用 ld 的场合请使用 LDFLAGS。
即使存在某些为了正确编译特定文件而必须使用的 C 编译器选项,也不要把它们放进 CFLAGS。因为用户期望能够自由地自行指定 CFLAGS。相反,应当独立于 CFLAGS 把所需的选项传给 C 编译器,办法是在编译命令中显式写出,或者像下面这样定义一条隐含规则:
CFLAGS = -g
ALL_CFLAGS = -I. $(CFLAGS)
.c.o:
$(CC) -c $(CPPFLAGS) $(ALL_CFLAGS) $<
「-g」选项可以放进 CFLAGS,因为它对于正确编译并非必需。可以把它视为只是推荐的默认值。如果软件包被设置成默认用 GCC 编译,那么不妨也把「-O」一并放进 CFLAGS 的默认值中。
在编译命令中,请把 CFLAGS 放在最后,排在其他包含编译器选项的变量之后。这样一来,用户就能用 CFLAGS 覆盖其他选项。
无论是进行编译还是进行链接,凡是调用 C 编译器的所有场合都应当使用 CFLAGS。
每个 Makefile 都应当定义 INSTALL 变量。这是把文件安装到系统中的基本命令。
每个 Makefile 还都应当定义 INSTALL_PROGRAM 和 INSTALL_DATA 这两个变量。(INSTALL_PROGRAM 的默认值应为 $(INSTALL),INSTALL_DATA 的默认值应为 ${INSTALL} -m 644。)然后,把这两个变量用作实际执行安装的命令:可执行文件用前者,不可执行文件用后者。这些变量的最简单用法如下:
$(INSTALL_PROGRAM) foo $(bindir)/foo $(INSTALL_DATA) libfoo.a $(libdir)/libfoo.a
不过,正如下一节所说明的那样,更理想的做法是让目标文件能够加上 DESTDIR 前缀。
也允许(但不要求)像下面这样,把最后一个参数设为目录,用一条命令安装多个文件:
$(INSTALL_PROGRAM) foo bar baz $(bindir)
DESTDIR:对分阶段安装的支持
DESTDIR 是一个会被加到每个被安装的目标文件开头的变量。用法如下:
$(INSTALL_PROGRAM) foo $(DESTDIR)$(bindir)/foo $(INSTALL_DATA) libfoo.a $(DESTDIR)$(libdir)/libfoo.a
DESTDIR 变量由用户在 make 的命令行上以绝对文件名的形式指定。例如可以这样写:
make DESTDIR=/tmp/stage install
应当支持 DESTDIR 的,只有 install* 和 uninstall* 目标,因为它们是 DESTDIR 唯一能派上用场的目标。
假设安装过程通常会安装 /usr/local/bin/foo 和 /usr/local/lib/libfoo.a,那么按上面那个例子启动的安装,改为安装 /tmp/stage/usr/local/bin/foo 和 /tmp/stage/usr/local/lib/libfoo.a。
像这样把 DESTDIR 变量加到每个目标的开头,就能实现分阶段安装(staged install)。这种方法不把被安装的文件直接放到它们本该所在的位置,而是先复制到一个临时位置(DESTDIR)。即便如此,被安装文件之间的相对目录结构仍会保持,文件内部嵌入的文件名也不会被改写。
绝不应在 Makefile 中设置 DESTDIR 的值。这样一来,默认情况下文件就会被安装到它们本该所在的位置。此外,指定 DESTDIR 不应以任何方式改变软件的行为,因此其值不得包含在任何文件的内容中。
对 DESTDIR 的支持在软件打包中经常用到。它对下列用户也有帮助:想要弄清某个特定软件包会把什么安装到哪里的用户,以及本来没有权限安装到受保护区域、但希望在获得该权限之前先完成构建和安装的用户。此外,它在 stow 之类的工具上也很方便——这类工具把代码安装到某个位置,却通过符号链接或特殊的挂载操作让它看起来像是安装在另一个位置。出于这些理由,尽管并非绝对的硬性要求,我们仍强烈推荐 GNU 软件包支持 DESTDIR。
安装目录应当始终用变量来命名,以便也能轻松安装到非标准的位置。这些变量的标准名称,以及在 GNU 软件包中应当设置的值,将在下面说明。它们基于标准的文件系统布局,该布局的各种派生形式被用于 GNU/Linux 及其他现代操作系统。
前提是安装者在调用 make 时(例如 make prefix=/usr install)或调用 configure 时(例如 configure --prefix=/usr)能够覆盖这些值。GNU 软件包不应试图猜测在安装目标系统上这些变量应取何值,而要使用这里指定的默认设置。这样一来,所有 GNU 软件包的行为都一致,安装者便能实现自己想要的布局。
安装目录及其父目录,都应当在向其中安装之前(如有必要)全部创建好。
最前面的两个变量设置安装的根(基点)。其他所有安装目录都应当是这两者之一的子目录,而且不应直接向这两个目录安装任何东西。
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@」。)
「libexecdir」的定义对所有软件包都相同,因此你应当把自己软件包的数据安装到它的子目录中。大多数软件包把数据安装在 $(libexecdir)/package-name/ 之下,有时还会放到更深一层的子目录中(例如 $(libexecdir)/package-name/machine/version)。
程序在运行时使用的数据文件,从两个角度进行分类。
这样就产生了六种可能。但是,除了目标文件(object)和库以外,我们并不愿意推荐使用与体系结构相关的文件。让其他数据文件与体系结构无关要清爽得多,而且通常也不太难。
为指定存放这些各种类型文件的位置,Makefile 应当使用的变量如下:
只读且与体系结构无关的数据文件所在目录树的根。通常是 /usr/local/share,但写成 $(prefix)/share。(如果使用 Autoconf,就写成「@datarootdir@」。)「datadir」的默认值基于这个变量,「infodir」「mandir」等也是如此。
用于安装本程序特有的、只读且与体系结构无关的数据文件的目录。通常与「datarootdir」是同一个位置,但分成两个变量。这样就能在不改变 Info 文件、man 页等位置的情况下,只移动本程序特有的文件。
通常是 /usr/local/share,但写成 $(datarootdir)。(如果使用 Autoconf,就写成「@datadir@」。)
「datadir」的定义对所有软件包都相同,因此你应当把自己的数据安装到它的子目录中。大多数软件包把数据安装在 $(datadir)/package-name/ 之下。
用于安装那些只关乎单台机器的只读数据文件、亦即用于配置主机的文件的目录。邮件程序和网络的配置文件、/etc/passwd 等都属于此处。该目录中的所有文件都应当是普通的 ASCII 文本文件。通常是 /usr/local/etc,但写成 $(prefix)/etc。(如果使用 Autoconf,就写成「@sysconfdir@」。)
不要把可执行文件安装到这个目录中(它们多半属于 $(libexecdir) 或 $(sbindir))。也不要安装那些在正常使用过程中会被修改的文件(以更改系统配置为目的的程序除外)。那类文件多半属于 $(localstatedir)。
用于安装与体系结构无关、且在程序运行期间会被程序修改的数据文件的目录。通常是 /usr/local/com,但写成 $(prefix)/com。(如果使用 Autoconf,就写成「@sharedstatedir@」。)
用于安装那些在程序运行期间会被程序修改、且只关乎某一台特定机器的数据文件的目录。绝不应让用户为了配置软件包的行为而需要修改该目录中的文件。那类配置信息应当另外写成单独的文件,放到 $(datadir) 或 $(sysconfdir) 中。$(localstatedir) 通常是 /usr/local/var,但写成 $(prefix)/var。(如果使用 Autoconf,就写成「@localstatedir@」。)
用于安装那些在程序运行期间会被程序修改、只关乎某一台特定机器、且无需存续得比程序运行期更久的数据文件的目录。放在这里的文件一般是较为长命的,例如会一直保留到下次重启。系统守护进程的 PID 文件是其典型用途。此外,这个目录(除重启时之外)不应被清理,而一般的 /tmp(TMPDIR)则可以在任意时刻被清理。通常是 /var/run,但写成 $(localstatedir)/run。之所以单独分成一个变量,是为了在希望时能够使用例如 /run。(如果使用 Autoconf 2.70 或更高版本,就写成「@runstatedir@」。)
下面这些变量,在程序拥有相应文件时,指定安装特定类型文件的目录。所有 GNU 软件包都应当有 Info 文件,因此每个程序都需要「infodir」,但并非每个都需要「libdir」或「lispdir」。
用于安装供用户程序通过 C 的「#include」预处理指令引入的头文件的目录。通常是 /usr/local/include,但写成 $(prefix)/include。(如果使用 Autoconf,就写成「@includedir@」。)
除 GCC 之外的大多数编译器不会从 /usr/local/include 目录查找头文件。因此,用这种方式安装头文件只有在使用 GCC 时才有用。有些库确实只打算配合 GCC 工作,所以这并不构成问题。但也有些库打算配合其他编译器工作。这类库应当把头文件安装到两个位置,即 includedir 指定的位置和 oldincludedir 指定的位置。
用于安装供 GCC 以外的编译器使用的「#include」头文件的目录。通常是 /usr/include。(如果使用 Autoconf,可以写成「@oldincludedir@」。)
Makefile 命令应当检查 oldincludedir 的值是否为空。若为空,就不应试图使用它,而应取消把头文件安装到第二个位置。
除非某个头文件来自同一软件包,否则软件包不应替换该目录中已有的头文件。因此,如果你的 Foo 软件包提供一个名为 foo.h 的头文件,那么只有在(1)oldincludedir 中不存在 foo.h,或(2)那里已存在的 foo.h 来自 Foo 软件包,这两种情况之一成立时,才应当把头文件安装到 oldincludedir 目录。
要判断 foo.h 是否来自 Foo 软件包,可以在该文件中(作为注释的一部分)放入一个作为标记的特殊字符串,然后用 grep 查找该字符串。
用于安装本软件包(Info 以外的)文档文件的目录。默认应为 /usr/local/share/doc/yourpkg,但写成 $(datarootdir)/doc/yourpkg。(如果使用 Autoconf,就写成「@docdir@」。)yourpkg 子目录(可以包含版本号)能防止 README 之类常见名称的文件彼此冲突。
用于安装本软件包 Info 文件的目录。默认应为 /usr/local/share/info,但写成 $(datarootdir)/info。(如果使用 Autoconf,就写成「@infodir@」。)infodir 之所以与 docdir 分开,是为了与既有惯例兼容。
分别用于安装特定格式文档文件的目录。默认应当全部设置为 $(docdir)。(如果使用 Autoconf,就写成「@htmldir@」「@dvidir@」等。)提供文档多种翻译的软件包,应当把它们安装到「$(htmldir)/」ll、「$(pdfdir)/」ll 等处,其中 ll 是诸如「en」或「pt_BR」这样的区域(locale)缩写。
用于存放目标文件(object)以及目标代码库的目录。不要把可执行文件安装到这里,它们多半应当改放到 $(libexecdir)。libdir 的值通常是 /usr/local/lib,但写成 $(exec_prefix)/lib。(如果使用 Autoconf,就写成「@libdir@」。)
用于安装本软件包中 Emacs Lisp 文件的目录。默认应为 /usr/local/share/emacs/site-lisp,但写成 $(datarootdir)/emacs/site-lisp。
如果使用 Autoconf,就把默认值写成「@lispdir@」。要让「@lispdir@」生效,configure.ac 文件中需要有下面这几行:
lispdir='${datarootdir}/emacs/site-lisp'
AC_SUBST(lispdir)
用于安装本软件包区域专用消息目录(message catalog)的目录。默认应为 /usr/local/share/locale,但写成 $(datarootdir)/locale。(如果使用 Autoconf,就写成「@localedir@」。)这个目录通常按区域(locale)各有一个子目录。
Unix 风格的 man 页安装到下列之一:
用于安装本软件包 man 页(若有)的最上层目录。通常是 /usr/local/share/man,但应当写成 $(datarootdir)/man。(如果使用 Autoconf,就写成「@mandir@」。)
用于安装第 1 节 man 页的目录。写成 $(mandir)/man1。
用于安装第 2 节 man 页的目录。写成 $(mandir)/man2。
不要把任何 GNU 软件的主要文档做成 man 页。请改用 Texinfo 来撰写手册。man 页只是给在 Unix 上运行 GNU 软件的人用的,而那只是次要用途。
被安装 man 页文件名的扩展名。它应当是一个句点后跟适当的数字,通常取「.1」。
被安装的第 1 节 man 页文件名的扩展名。
被安装的第 2 节 man 页文件名的扩展名。
如果软件包需要把 man 页安装到手册的多个节中,就使用这些名称来代替「manext」。
最后,你应当设置下面这个变量:
待编译源代码所在的目录。这个变量的值通常由 configure shell 脚本插入。(如果使用 Autoconf,就用「srcdir = @srcdir@」。)
举个例子:
# Common prefix for installation directories. # NOTE: This directory must exist when you start the install. prefix = /usr/local datarootdir = $(prefix)/share datadir = $(datarootdir) 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. libexecdir = $(exec_prefix)/libexec # Where to put the Info files. infodir = $(datarootdir)/info
如果程序要把大量文件安装到某个标准的用户指定目录中,把它们归拢到该程序专用的子目录里也许会很方便。这样做时,应当编写 install 规则来创建这些子目录。
不要指望用户会把子目录名包含进上面所列任何变量的值中。为安装目录采用一套统一变量名的想法,是为了让用户能对多个不同的 GNU 软件包指定完全相同的值。要让这一点有用,所有软件包都必须设计成在用户这样做时能合乎情理地工作。
在当前发布的 Autoconf 和/或 Automake 中,这些变量未必全部都已实现;不过从 Autoconf 2.60 起,我们认为它们全部都已实现。如果有缺失的,这里的说明便可作为 Autoconf 应当实现内容的规范。作为程序员,你可以选择使用开发版的 Autoconf,或者在支持这些变量的稳定版发布之前暂不使用它们。
所有 GNU 程序的 Makefile 中都应当具有下列目标:
编译整个程序。这应当是默认目标。这个目标不必重新生成任何文档文件;Info 文件通常应当包含在发行版中,而 DVI(及其他文档格式)文件应当只在被明确要求时才生成。
默认情况下,Make 的规则应当带「-g」进行编译和链接,使可执行程序中含有调试用符号。否则,一旦遇到崩溃,你基本上就束手无策,而且即使重新构建一遍也往往不容易重现。
编译程序,并把可执行文件、库等复制到它们在实际使用时应当所在的文件名位置。如果有简单的测试可以验证程序是否正确安装,这个目标就应当运行该测试。
安装时不要对可执行文件做 strip(删除符号)。这有助于日后可能需要的调试。而且如今磁盘空间也很便宜,动态加载器通常会确保调试用的节段在正常运行时不被加载。需要 strip 过的可执行文件的用户,调用 install-strip 目标即可。
如果可能,请把 install 目标的规则写成:只要刚做完「make all」,它就不会改动构建程序的目录中的任何东西。这在以某个用户名构建程序、再以另一个用户名安装时很方便。
命令应当创建用于安装文件的所有目录,如果它们尚不存在的话。这不仅包括作为 prefix 和 exec_prefix 变量值所指定的目录,也包括所有需要的子目录。做到这一点的一种方法,是使用后面所述的 installdirs 目标。
在安装 man 页的命令之前请加上「-」,让 make 忽略错误。这是为了应对没有安装 Unix man 页文档系统的系统。
安装 Info 文件的方法是:用 $(INSTALL_DATA) 把它们复制到 $(infodir)(请参阅用于指定命令的变量),然后,如果存在 install-info 程序就运行它。install-info 是一个会编辑 Info 的 dir 文件、为给定 Info 文件添加或更新菜单项的程序,它是 Texinfo 软件包的一部分。
下面给出一条安装 Info 文件的规则示例。它还试图应对若干额外情形,例如 install-info 不存在的情况。
do-install-info: foo.info installdirs
$(NORMAL_INSTALL)
# Prefer an info file in . to one in srcdir.
if test -f foo.info; then d=.; \
else d="$(srcdir)"; fi; \
$(INSTALL_DATA) $$d/foo.info \
"$(DESTDIR)$(infodir)/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.
# Use '$(SHELL) -c' because some shells do not
# fail gracefully when there is an unknown command.
$(POST_INSTALL)
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 目标时,你必须把所有命令归为三类,即普通命令、安装前(pre-installation)命令和安装后(post-installation)命令。请参阅安装命令的分类。
这些目标安装 Info 以外格式的文档。它们的设想是,在需要相应格式时,由安装软件包的人显式调用。GNU 偏爱 Info 文件,所以 Info 必须由 install 目标安装。
当要安装的文档文件很多时,我们建议你让这些目标安装到相应安装目录的子目录中(例如 htmldir 之下),以避免冲突和杂乱。举例来说,如果软件包有多本手册,并且你想安装由许多文件组成的 HTML 文档(例如 makeinfo --html 输出的「分割」模式),那么你几乎肯定会想使用子目录。否则,不同手册中两个同名的节点就会彼此覆盖。
请让这些 install-format 目标去调用 format 目标的命令,例如把 format 设为前置条件。
删除所有已安装的文件,即「install」和「install-*」目标所创建的副本。
这条规则不应改动进行编译的目录,而只应针对文件被安装到的目录。
卸载命令与安装命令一样分为三类。请参阅安装命令的分类。
与 install 相同,但在安装可执行文件时对其做 strip。在简单的情形下,这个目标可以像下面这样简单地利用 install 目标:
install-strip:
$(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' \
install
但如果软件包不仅安装真正的可执行文件,还安装脚本,那么 install-strip 目标就不能只是简单地引用 install 目标了,因为它必须对可执行文件做 strip,而对脚本不做。
install-strip 不应对构建目录中那些被复制去安装的可执行文件做 strip,只应对已安装的副本做 strip。
通常,除非你确信程序没有 bug,否则我们不推荐对可执行文件做 strip。不过,在为可能出现的 bug 着想、把未 strip 的可执行文件另存到别处的同时,把 strip 过的可执行文件安装供实际运行使用,这有时是合理的做法。
删除在构建程序过程中通常会生成的、位于当前目录中的所有文件。也删除本 makefile 在其他目录中生成的文件。但不要删除记录配置(configure)结果的文件。此外,对于虽然能由构建生成、但因为已随发行版附带而通常不会生成的文件,也请予以保留。无需删除用「mkdir -p」创建的父目录,因为那些目录反正原本就可能已经存在。
如果 .dvi 文件不属于发行版,就在这里删除它们。
删除因配置(configure)或构建程序而生成的、位于当前目录中(或由本 makefile 生成)的所有文件。如果你解压了源代码、在不生成任何其他文件的情况下构建了程序,那么执行「make distclean」之后,应当只剩下发行版中原有的文件。不过,无需删除用「mkdir -p」创建的父目录,因为那些目录反正原本就可能已经存在。
类似「clean」,但对于人们通常不愿重新编译的少数文件,可以不予删除。例如 GCC 的「mostlyclean」目标就不删除 libgcc.a,因为很少需要重新编译它,而且重新编译要花很多时间。
删除几乎所有能用本 Makefile 重新构建出来的东西。这通常包括 distclean 所删除的一切,外加更多东西,即 Bison 生成的 C 源文件、tags 表、Info 文件等等。
之所以说「几乎所有」,是有原因的。执行「make maintainer-clean」命令时,即使 Makefile 中的规则能够重新生成 configure,也不应删除 configure。更一般地说,「make maintainer-clean」不应删除任何为了运行 configure 进而开始构建程序而必须存在的东西。此外,也无需删除用「mkdir -p」创建的父目录,因为那些目录反正原本就可能已经存在。这些是仅有的例外;maintainer-clean 应当删除其他一切能够被重新构建的东西。
「maintainer-clean」目标的设想用户是软件包的维护者(maintainer),而非一般用户。要重新构建「make maintainer-clean」所删除的部分文件,可能需要特殊的工具。由于这些文件通常包含在发行版中,我们并没有费心让它们易于重新构建。如果你发现自己不得不重新解压一遍完整的发行版,请不要责怪我们。
为了让用户意识到这一点,特殊的 maintainer-clean 目标的命令应当以下面这两行开头:
@echo 'This command is intended for maintainers to use; it' @echo 'deletes files that may need special tools to rebuild.'
更新本程序的 tags 表。
生成所需的 Info 文件。编写规则的最佳方式如下:
info: foo.info
foo.info: foo.texi chap1.texi chap2.texi
$(MAKEINFO) $(srcdir)/foo.texi
在 Makefile 中必须定义变量 MAKEINFO。它应当运行 makeinfo 程序,makeinfo 是 Texinfo 发行版的一部分。
通常,GNU 的发行版会附带 Info 文件,也就是说 Info 文件存在于源目录中。因此,Info 文件的 Make 规则应当在源目录中更新它。当用户构建软件包时,由于 Info 文件已经是最新的,Make 通常不会更新 Info 文件。
生成指定格式的文档文件。这些目标应当始终存在,但如果无法生成相应的输出格式,它们中的任何一个(乃至全部)都可以是什么也不做的(no-op)。不应把这些目标设为 all 目标的前置条件,而要让用户手动调用。
下面给出一个从 Texinfo 生成 DVI 文件的规则示例:
dvi: foo.dvi
foo.dvi: foo.texi chap1.texi chap2.texi
$(TEXI2DVI) $(srcdir)/foo.texi
在 Makefile 中必须定义变量 TEXI2DVI。它应当运行 texi2dvi 程序,texi2dvi 是 Texinfo 发行版的一部分。(texi2dvi 用 TeX 来完成排版这项实际工作。TeX 并不随 Texinfo 一起分发。)或者,也可以只写出前置条件,让 GNU make 来提供命令。
再给出一个从 Texinfo 生成 HTML 的示例:
html: foo.html
foo.html: foo.texi chap1.texi chap2.texi
$(TEXI2HTML) $(srcdir)/foo.texi
同样,在 Makefile 中要定义变量 TEXI2HTML。例如让它运行 makeinfo --no-split --html(makeinfo 是 Texinfo 发行版的一部分)。
创建本程序的分发用 tar 文件。tar 文件应当做成:其中的文件名都以一个子目录名开头,而该子目录名就是这份发行所对应软件包的名称。这个名称可以包含版本号。
例如,GCC 1.40 版的分发用 tar 文件会展开到一个名为 gcc-1.40 的子目录中。
做到这一点最简单的方法,是创建一个名称恰当的子目录,用 ln 或 cp 把所需文件安装到其中,然后用 tar 把该子目录打包。
tar 文件用 gzip 压缩。例如,GCC 1.40 版的实际分发文件名为 gcc-1.40.tar.gz。也可以一并支持其他自由的压缩格式。
dist 目标应当显式依赖发行版中包含的所有非源代码文件,以确保它们在发行版中是最新的。请参阅 GNU Coding Standards 中的 Making Releases 一节。
执行自我测试(若有)。用户在运行测试之前必须构建程序,但不必安装程序;你应当把自我测试写成在程序已构建但未安装的情况下也能运行。
下面这些目标,是针对它们有用的程序而建议采用的惯用名称。
installcheck执行安装测试(若有)。用户在运行测试之前必须构建并安装程序。不要假定 $(bindir) 在搜索路径中。
installdirs添加一个名为「installdirs」的目标来创建文件被安装到的目录及其父目录,会很有用。有一个名为 mkinstalldirs 的便捷脚本可用于此,它在 Gnulib 软件包中。可以使用下面这样的规则:
# Make sure all installation directories (e.g. $(bindir))
# actually exist by making them if necessary.
installdirs: mkinstalldirs
$(srcdir)/mkinstalldirs $(bindir) $(datadir) \
$(libdir) $(infodir) \
$(mandir)
或者,如果你想支持 DESTDIR(强烈推荐),
# Make sure all installation directories (e.g. $(bindir))
# actually exist by making them if necessary.
installdirs: mkinstalldirs
$(srcdir)/mkinstalldirs \
$(DESTDIR)$(bindir) $(DESTDIR)$(datadir) \
$(DESTDIR)$(libdir) $(DESTDIR)$(infodir) \
$(DESTDIR)$(mandir)
这条规则不应改动进行编译的目录。除了创建安装目录之外,它什么都不应做。
编写 install 目标时,你必须把所有命令归为三类,即普通命令、安装前(pre-installation)命令和安装后(post-installation)命令。
普通命令把文件移动到它们本该所在的位置,并设置其模式(权限)。它们不得改动除完全来自其所属软件包的文件以外的任何文件。
安装前命令和安装后命令则可以改动其他文件。尤其是,它们可以编辑全局的配置文件或数据库。
安装前命令通常在普通命令之前执行,安装后命令通常在普通命令之后执行。
安装后命令最常见的用途是运行 install-info。这无法用普通命令完成,因为它改动的是一个并非完全且专一地来自被安装软件包的文件(Info 目录)。它之所以是安装后命令,是因为它需要在安装软件包 Info 文件的普通命令之后运行。
大多数程序都不需要安装前命令,但我们还是准备了这个功能,以备需要之时。
要把 install 规则中的命令归入这三类,就在它们之间插入分类行(category line)。分类行指定紧随其后命令的分类。
分类行由一个制表符、一个对特殊 Make 变量的引用,以及末尾可选的注释构成。可以使用三个变量,每个分类各一个;变量名指定分类。由于这三个 Make 变量通常未定义(而且你不应在 makefile 中定义它们),分类行在正常执行时什么也不做(no-op)。
下面给出三种可能的分类行,并各附一条说明其含义的注释:
$(PRE_INSTALL) # 安装前命令紧随其后。
$(POST_INSTALL) # 安装后命令紧随其后。
$(NORMAL_INSTALL) # 普通命令紧随其后。
如果你没有在 install 规则的开头放置分类行,那么在第一个分类行之前的所有命令都被归为普通命令。如果你完全没有使用分类行,那么所有命令都被归为普通命令。
用于 uninstall 的分类行如下:
$(PRE_UNINSTALL) # 卸载前命令紧随其后。
$(POST_UNINSTALL) # 卸载后命令紧随其后。
$(NORMAL_UNINSTALL) # 普通命令紧随其后。
典型情况下,卸载前命令用于从 Info 目录中删除条目。
如果 install 或 uninstall 目标拥有充当安装子例程的前置条件,那么你应当让每一个前置条件的命令都以分类行开头,并且主目标的命令也以分类行开头。这样一来,无论实际运行的是哪一个前置条件,都能保证每条命令都被放入正确的分类。
安装前命令和安装后命令不应运行下列程序以外的任何程序:
[ basename bash cat chgrp chmod chown cmp cp dd diff echo expand expr false 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 的 -s 选项用于抑制进入子目录的相关消息):
make -s -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 ~ /^(normal-install|post-install)[ \t]*$/ {on = 0}
on {print $0}
$0 ~ /^pre-install[ \t]*$/ {on = 1}