您必须在makefile文件中定义变量TEXI2DVI。它将运行程序texi2dvi,该程序是发布的Texinfo一部分。要么仅仅编写依靠文件,要么允许GNU make提供命令,二者必选其一。
`dist'
为程序创建一个tar文件。创建tar文件可以将其中的文件名以子目录名开始,这些子目录名可以是用于发布的软件包名。另外,这些文件名中也可以包含版本号,例如,发布的GCC 1.40版的tar文件解包的子目录为‘gcc-1.40'。最方便的方法是创建合适的子目录名,如使用in或cp等作为子目录,在它们的下面安装适当的文件,然后把tar文件解包到这些子目录中。使用gzip压缩这些tar文件,例如,实际的GCC
1.40版的发布文件叫‘gcc-1.40.tar.gz'。目标dist明显的依靠所有的发布文件中不是源文件的文件,所以你应确保发布中的这些文件已经更新。参阅GNU标准编码中创建发布文件。
`check'
执行自我检查。用户应该在运行测试之前,应该先建立程序,但不必安装这些程序;您应该编写一个自我测试程序,在程序已建立但没有安装时执行。
以下目标建议使用习惯名,对于各种程序它们很有用:
installcheck
执行自我检查。用户应该在运行测试之前,应该先建立、安装这些程序。您不因该假设‘$(bindir)'在搜寻路径中。
installdirs
添加名为‘installdirs'目标对于创建文件要安装的目录以及它们的父目录十分有用。脚本‘mkinstalldirs'是专为这样处理方便而编写的;您可以在Texinfo软件包中找到它,您可以象这样使用规则:
# 确保所有安装目录(例如
$(bindir))
# 都实际存在,如果没有则创建它们。
installdirs: mkinstalldirs
$(srcdir)/mkinstalldirs $(bindir) $(datadir)
$(libdir)
$(infodir)
$(mandir)
该规则并不更改编译时创建的目录,它仅仅创建安装目录。
14.6 安装命令分类
编写已安装目标,您必须将所有命令分为三类:正常的命令、安装前命令和安装后命令。
正常情况下,命令把文件移动到合适的地方,并设置它们的模式。它们不会改变任何文件,仅仅把它们从软件包中完整地抽取出来。
安装前命令和安装后命令可能更改一些文件,如,它们编辑配置文件后数据库文件。
安装前命令在正常命令之前执行,安装后命令在正常命令执行后执行。
安装后命令最普通的用途是运行install-info程序。 这种工作不能由正常命令完成,因为它更改了一个文件(Info 目录),该文件不能全部、单独从软件包中安装。它是一个安装后命令,因为它需要在正常命令安装软件包中的Info文件后才能执行。
许多程序不需要安装前命令,但是我们提供这个特点,以便在需要时可以使用。
要将安装规则的命令分为这三类,应在命令中间插入category lines(分类行)。 分类行指定了下面叙述的命令的类别。
分类行包含一个Tab、一个特殊的make变量引用,以及行结尾的随机注释。您可以使用三个变量,每一个变量对应一个类别;变量名指定了类别。分类行不能出现在普通的执行文件中,因为这些make变量被由正常的定义(您也不应在makefile文件中定义)。
这里有三种分类行,后面的注释解释了它的含义:
$(PRE_INSTALL) # 以下是安装前命令
$(POST_INSTALL) # 以下是安装后命令
$(NORMAL_INSTALL) # 以下是正常命令
如果在安装规则开始您没有使用分类行,则在第一个分类行出现之前的所有命令都是正常命令。如果您没有使用任何分类行,则所有命令都是正常命令。
这是反安装的分类行
$(PRE_UNINSTALL) #以下是反安装前命令
$(POST_UNINSTALL) #以下是反安装后命令
$(NORMAL_UNINSTALL) #以下是正常命令
反安装前命令的典型用法是从Info目录删除全部内容。
如果目标install或 uninstall 有依赖作为安装程序的子程序,那么您应该使用分类行先启动每一个依赖的命令,再使用分类行启动主目标的命令。无论哪一个依赖实际执行,这种方式都能保证每一条命令都放置到了正确的分类中。
安装前命令和安装后命令除了对于下述命令外,不能运行其它程序:
basename bash cat chgrp chmod chown cmp cp
dd diff echo
egrep expand expr false fgrep 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 -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 ~ /^\t[
\t]*(normal_install|post_install)[ \t]*$/ {on = 0}
on {print $0}
$0 ~ /^\t[ \t]*pre_install[ \t]*$/ {on =
1}
安装前命令的结果文件是象安装二进制软件包的一部分shell脚本一样执行。
15 快速参考
这是对指令、文本操作函数以及GNU make能够理解的变量等的汇总。对于其他方面的总结参阅特殊的内建目标名,隐含规则目录,选项概要。
这里是GNU make是别的指令的总结:
define variable
endef
定义多行递归调用扩展型变量。参阅定义固定次序的命令。
ifdef variable
ifndef variable
ifeq (a,b)
ifeq "a" "b"
ifeq 'a' 'b'
ifneq (a,b)
ifneq "a" "b"
ifneq 'a' 'b'
else
endif
makefile文件中的条件扩展,参阅makefile文件中的条件语句。
include file
-include file
sinclude file
包含其它makefile文件,参阅包含其它makefile文件。
override variable = value
override variable := value
override variable += value
override variable ?= value
override define variable
endef
定义变量、对以前的定义重载、以及对在命令行中定义的变量重载。参阅override指令。
export
告诉make缺省向子过程输出所有变量,参阅与子make通讯的变量。
export variable
export variable = value
export variable := value
export variable += value
export variable ?= value
unexport variable
告诉make是否向子过程输出一个特殊的变量。参业与子make通讯的变量。
vpath pattern path
制定搜寻匹配‘%’格式的文件的路径。参阅vpath指令。
vpath pattern
去除以前为‘pattern’指定的所有搜寻路径。
vpath
去除以前用vpath指令指定的所有搜寻路径。
这里是操作文本函数的总结,参阅文本转换函数:
$(subst from,to,text)
在‘text’中用‘to’代替‘from’,参阅字符串替换与分析函数。
$(patsubst pattern,replacement,text)
在‘text’中用‘replacement’代替匹配‘pattern’字,参阅字符串替换与分析函数。
$(strip string)
从字符串中移去多余的空格。参阅字符串替换与分析函数。
$(findstring find,text)
确定‘find’在‘text’中的位置。参阅字符串替换与分析函数。
$(filter pattern...,text)
在‘text’中选择匹配‘pattern’的字。参阅字符串替换与分析函数。
$(filter-out pattern...,text)
在‘text’中选择不匹配‘pattern’的字。参阅字符串替换与分析函数。
$(sort list)
将‘list’中的字按字母顺序排序,并删除重复的字。参阅字符串替换与分析函数。
$(dir names...)
从文件名中抽取路径名。参阅文件名函数。
$(notdir names...)
从文件名中抽取路径部分。参阅文件名函数。
$(suffix names...)
从文件名中抽取非路径部分。参阅文件名函数。
$(basename names...)
从文件名中抽取基本文件名。参阅文件名函数。
$(addsuffix suffix,names...)
为‘names’中的每个字添加后缀。参阅文件名函数。
$(addprefix prefix,names...)
为‘names’中的每个字添加前缀。参阅文件名函数。
$(join list1,list2)
连接两个并行的字列表。参阅文件名函数。
$(word n,text)
从‘text’中抽取第n个字。参阅文件名函数。
$(words text)
计算‘text’中字的数目。参阅文件名函数。
$(wordlist s,e,text)
返回‘text’中s到e之间的字。参阅文件名函数。
$(firstword names...)
在‘names…’中的第一个字。参阅文件名函数。
$(wildcard pattern...)
寻找匹配shell文件名格式的文件名。参阅wildcard函数。
$(error text...)
该函数执行时,make产生信息为‘text’的致命错误。参阅控制make的函数。
$(warning text...)
该函数执行时,make产生信息为‘text’的警告。参阅控制make的函数。
$(shell command)
执行shell命令并返回它的输出。参阅函数shell。
$(origin variable)
返回make变量‘variable’的定义信息。参阅函数origin。
$(foreach var,words,text)
将列表列表words中的每一个字对应后接var中的每一个字,将结果放在text中。参阅函数foreach。
$(call var,param,...)
使用对$(1), $(2)...对变量计算变量 var ,变量$(1), $(2)...分别代替参数 param 第一个、第二个…的值。参阅函数call。
这里是对自动变量的总结,完整的描述参阅自动变量。
$@
目标文件名。
$%
当目标是档案成员时,表示目标成员名。
$<
第一个依赖名。
$?
所有比目标‘新’的依赖的名字,名字之间用空格隔开。对于为档案成员的依赖,只能使用命名的成员。参阅使用make更新档案文件。
$^
$+
所有依赖的名字,名字之间用空格隔开。对于为档案成员的依赖,只能使用命名的成员。参阅使用make更新档案文件。变量 $^ 省略了重复的依赖,而变量 $+ 则按照原来次序保留重复项,
$*
和隐含规则匹配的stem(径)。参阅格式匹配。
$(@D)
$(@F)
变量$@.中的路径部分和文件名部分。
$(*D)
$(*F)
变量$*中的路径部分和文件名部分。
$(%D)
$(%F)
变量$%中的路径部分和文件名部分。
$(<D)
$(<F)
变量$<中的路径部分和文件名部分。
$(^D)
$(^F)
变量$^中的路径部分和文件名部分。
$(+D)
$(+F)
变量$+中的路径部分和文件名部分。
$(?D)
$(?F)
变量$?中的路径部分和文件名部分。
以下是GNU make使用变量:
MAKEFILES
每次调用make要读入的Makefiles文件。参阅变量MAKEFILES。
VPATH
对在当前目录下不能找到的文件搜索的路径。参阅VPATH: 所有依赖的搜寻路径。
SHELL
系统缺省命令解释程序名,通常是`/bin/sh'。您可以在makefile文件中设值变量SHELL改变运行程序使用的shell。参阅执行命令。
MAKESHELL
改变量仅用于MS-DOS,make使用的命令解释程序名,该变量的值比变量SHELL的值优先。参阅执行命令。
MAKE
调用的make名。在命令行中使用该变量有特殊的意义。参阅变量MAKE的工作方式。
MAKELEVEL
递归调用的层数(子makes)。参阅与子make通讯的变量。
MAKEFLAGS
向make提供标志。您可以在环境或makefile文件中使用该变量设置标志。参阅与子make通讯的变量。在命令行中不能直接使用该变量,应为它的内容不能在shell中正确引用,但总是允许递归调用make时通过环境传递给子make。
MAKECMDGOALS
该目标是在命令行中提供给make的。设置该变量对make的行为没有任何影响。参阅特别目标的参数。
CURDIR
设置当前工作目录的路径名,参阅递归调用make。
SUFFIXES
在读入任何makefile文件之前的后缀列表。
.LIBPATTERNS
定义make搜寻的库文件名,以及搜寻次序。参阅连接库搜寻目录。
16 make产生的错误
这里是您可以看到的由make产生绝大多数普通错误列表,以及它们的含义和修正它们信息。
有时make产生的错误不是致命的,如一般在命令脚本行前面存在前缀的情况下,和在命令行使用选向‘-k’的情况下产生的错误几乎都不是致命错误。使用字符串***作前缀将产生致命的错误。
错误信息前面都使用前缀,前缀的内容是产生错误的程序名或makefile文件中存在错误的文件名和包含该错误的行的行号和。
在下述的错误列表中,省略了普通的前缀:
`[foo] Error NN'
`[foo] signal description'
这些错误并不是真的make的错误。它们意味着make调用的程序返回非零状态值,错误码(Error NN),这种情况make解释为失败,或非正常方式退出(一些类型信号),参阅命令错误。如果信息中没有附加***,则是子过程失败,但在makefile文件中的这条规则有特殊前缀,因此make忽略该错误。
`missing separator. Stop.'
`missing separator (did you mean TAB
instead of 8 spaces?). Stop.'
这意味着make在读取命令行时遇到不能理解的内容。GNU make 检查各种分隔符(:, =, 字符TAB,等) 从而帮助确定它在命令行中遇到了什么类型的错误。这意味着,make不能发现一个合法的分隔符。出现该信息的最可能的原因是您(或许您的编辑器,绝大部分是ms-windows的编辑器)在命令行缩进使用了空格代替了字符Tab。这种情况下,make将使用上述的第二种形式产生错误信息。一定切记,任何命令行都以字符Tab开始,八个空格也不算数。参阅规则的语法。
`commands commence before first target.
Stop.'
`missing rule before commands. Stop.'
这意味着在makefile中似乎以命令行开始:以Tab字符开始,但不是一个合法的命令行(例如,一个变量的赋值)。任何命令行必须和一定的目标相联系。产生第二种的错误信息是一行的第一个非空白字符为分号,make对此的解释是您遗漏了规则中的"target:
prerequisite" 部分,参阅规则的语法。
`No rule to make target `xxx'.'
`No rule to make target `xxx', needed by `yyy'.'
这意味着make决定必须建立一个目标,但却不能在makefile文件中发现任何用于创建该目标的指令,包括具体规则和隐含规则。如果您希望创建该目标,您需要另外为改目标编写规则。其它关于该问题产生原因可能是makefile文件是草稿(如文件名错)或破坏了源文件树(一个文件不能按照计划重建,仅仅由于一个依赖的问题)。
`No targets specified and no makefile
found. Stop.'
`No targets. Stop.'
前者意味着您没有为命令行提供要创建的目标,make不能读入任何makefile文件。后者意味着一些makefile文件被找到,但没有包含缺省目标以及命令行等。GNU make在这种情况下无事可做。参阅指定makefile文件的参数。
`Makefile `xxx' was not found.'
`Included makefile `xxx' was not found.'
在命令行中指定一个makefile文件(前者)或包含的makefile 文件(后者)没有找到。
`warning: overriding commands for
target `xxx''
`warning: ignoring old commands for
target `xxx''
GNU make允许命令在一个规则中只能对一个命令使用一次(双冒号规则除外)。如果您为一个目标指定一个命令,而该命令在目标定义是已经定义过,这种警告就会产生;第二个信息表明后来设置的命令将改写以前对该命令的设置。参阅具有多条规则的目标。
`Circular xxx <- yyy dependency dropped.'
这意味着make检测到一个相互依靠一个循环:在跟踪目标xxx的依赖yyy 时发现,依赖yyy的依赖中一个又以xxx为依赖。
`Recursive variable `xxx' references itself (eventually).
Stop.'
这意味着您定义一个正常(递归调用性)make变量xxx,当它扩展时,它将引用它自身。无论对于简单扩展型变量(:=)或追加定义(+=),这也都是不能允许的,参阅使用变量。
`Unterminated variable reference.
Stop.'
这意味着您在变量引用或函数调用时忘记写右括号。
`insufficient arguments to function `xxx'. Stop.'
这意味着您在调用函数是您密友提供需要数目的参数。关于函数参数的详细描述参阅文本转换函数。
`missing target pattern. Stop.'
`multiple target patterns. Stop.'
`target pattern contains no `%'. Stop.'
这些错误信息是畸形的静态格式规则引起的。第一条意味着在规则的目标部分没有规则,第二条意味着在目标部分有多个规则,第三条意味着没有包含格式符%。参阅静态格式规则语法。
`warning: -jN forced in submake:
disabling jobserver mode.'
该条警告和下条警告是在make检测到在能与子make通讯的系统中包含并行处理的错误(参阅与子make通讯选项)。该警告信息是如果递归调用一个make过程,而且还使用了‘-jn’选项(这里n大于1)。这种情况很可能发生,例如,如果您设置环境变量MAKE为‘make –j2’。这种情况下,子make不能与其它make过程通讯, 而且还简单假装它由两个任务。
`warning: jobserver unavailable: using
-j1. Add `+' to parent make rule.'
为了保证make过程之间通讯,父层make将传递信息给子make。这可能导致问题,因为子过程有可能不是实际的一个make,而父过程仅仅认为子过程是一个make而将所有信息传递给子过程。父过程是采用正常的算法决定这些的(参阅变量MAKE的工作方式)。如果makefile文件构建了这样的父过程,它并不知道子过程是否为make,那么,子过程将拒收那些没有用的信息。这种情况下,子过程就会产生该警告信息,然后按照它内建的次序方式进行处理。
17 复杂的makfile文件例子
这是一个用于GNU tar程序的makefile文件;这是一个中等复杂的makefile文件。
因为‘all’是第一个目标,所以它是缺省目标。该makefile文件一个有趣的地方是‘testpad.h'是由testpad程序创建的源文件,而且该程序自身由‘testpad.c'编译得到的。
如果您键入‘make'或`make all',则make创建名为‘tar'可执行文件, 提供远程访问磁带的进程‘rmt',和名为‘tar.info'的Info文件。
如果您键入‘make install',则make不但创建‘tar',‘rmt',和‘tar.info',而且安装它们。
如果您键入‘make clean', 则make删除所有‘.o'文件,以及‘tar',‘rmt',‘testpad', ‘testpad.h',和‘core’文件。
如果您键入‘make distclean', 则make不仅删除‘make clean'删除的所有文件,而且包括文件‘TAGS', ‘Makefile', 和‘config.status' 文件。(虽然不明显,但该 makefile (和‘config.status')是用户用configure程序产生的,该程序是由发布的tar文件提供,但这里不进行说明。)
如果您键入‘make realclean', 则make删除‘make distclean
'删除的所有文件,而且包括由‘tar.texinfo'产生的Info文件。
另外,目标shar和dist创造了发布文件的核心。
# 自动从makefile.in产生
# 用于GNU tar 程序的Unix
Makefile
# Copyright (C) 1991 Free Software Foundation, Inc.
# 本程序是自由软件;在遵照GNU条款的情况下
# 您可以重新发布它或更改它
# 普通公众许可证 ...
...
...
SHELL = /bin/sh
#### 启动系统配置部分 ####
srcdir = .
# 如果您使用gcc, 您应该在运行
# 和它一起创建的固定包含的脚本程序以及
# 使用-traditional选项运行gcc中间选择其一。
# 另外的ioctl调用在一些系统上不能正确编译
CC = gcc -O
YACC = bison -y
INSTALL = /usr/local/bin/install -c
INSTALLDATA = /usr/local/bin/install -c -m 644
# 您应该在DEFS中添加的内容:
# -DSTDC_HEADERS
如果您有标准C的头文件和
# 库文件。
# -DPOSIX
如果您有POSIX.1的头文件和
#
库文件。
# -DBSD42
如果您有sys/dir.h (除非
#
您使用-DPOSIX), sys/file.h,
#
和st_blocks在`struct stat'中。
# -DUSG
如果您有System V/ANSI C
#
字符串和内存控制函数
#
和头文件, sys/sysmacros.h,
#
fcntl.h, getcwd, no valloc,
#
和 ndir.h (除非
#
您使用-DDIRENT)。
# -DNO_MEMORY_H
如果USG或STDC_HEADERS 但是不
#
包括memory.h.
# -DDIRENT
如果USG而且您又用dirent.h
#
代替ndir.h。
# -DSIGTYPE=int
如果您的信号控制器
#
返回int,非void.
# -DNO_MTIO
如果您缺少sys/mtio.h
# (magtape ioctls).
# -DNO_REMOTE
如果您没有一个远程shell
#
或rexec.
# -DUSE_REXEC
对远程磁带使用rexec
#
操作代替
#
分支rsh或remsh.
# -DVPRINTF_MISSING
如果您缺少函数vprintf
#
(但是有_doprnt).
# -DDOPRNT_MISSING
如果您缺少函数 _doprnt.
#
同样需要定义
#
-DVPRINTF_MISSING.