GNU Make 中文手册v3.8 学习 ( 3/3 )
后缀规则是一种古老定义隐含规则的方式,在新版本的make中使用模式规则作为对它的替代,模式规则相比后缀规则更加清晰明了。在现在版本中保留它的原因是为了能够兼容旧的makefile文件。
判断一个后缀规则是单后缀还是双后缀的过程:判断后缀规则的目标,如果其中只存在一个可被make识别的后缀,则规则是一个"单后缀"规则;当规则目标中存在两个可被make识别的后缀时,这个规则就是一个"双后缀"规则。(尽管单后缀很少出现)
例如:".c"和".o"都是make可识别的后缀。因此当定义了一个目标是".c.o"的规则时。make会将它作为一个双后缀规则来处理,它的含义是所有".o"文件的依赖文件是对应的".c"文件。下边是使用后追规则定义的编译.c源文件的规则:
.c.o:
$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $兼容老版本的make。对于静态库的重建也可以使用后缀规则。目标后缀需要是".a"(在Linux(Unix)中、静态库的后缀为.a)。例如这样一个后缀规则:
.c.a:
$(CC) $(CFLAGS) $(CPPFLAGS) -c $兼容不同版本的make,这里给出了一些基本的约定。
1. 所有的Makefile中应该包含这样一行:
SHELL = /bin/sh
其目的是为了避免变量"SHELL"在有些系统上可能继承同名的系统环境变量而导致错误。虽然在GNU版本的make中不会出现这种问题(GNU make中变量"SHELL"的默认值是"/bin/sh",它不同于系统环境变量"SHELL")。
2. 小心处理后缀和隐含规则。不同make可识别后缀和隐含规则可能不同,它可能会导致混乱或者错误。因此在特定Makefile中明确限定可识别的后缀是一个不错的主意。在Makefile中应该这样做:
.SUFFIXES:
.SUFFIXES: .c .o
第一行首先取消掉make默认的可识别后缀列表,第二行重新指定可识别的后缀列表。
当规则只有一个依赖文件时。应该使用自动化变量"$ $@
重建或者安装目标(一般是伪目标)的命令行可使用编译器或者相关工具程序,这些命令使用一个变量来表示。这样做的好处是:当修改一个命令时,只需要更改代表命令的变量的值就可以了。对于以下的这些命令程序:
ar bison cc flex install ld ldconfig lex
make makeinfo ranlib texi2dvi yacc
在规则中的命令中,使用以下这些变量来代表它们:
$(AR) $(BISON) $(CC) $(FLEX) $(INSTALL) $(LD) $(LDCONFIG)$(LEX)
$(MAKE) $(MAKEINFO) $(RANLIB) $(TEXI2DVI) $(YACC)
如果规则的命令行需要使用"ranlib"或者"ldconfig"等这些工具时,需要考虑当前的系统是否支持这些工具。当在不支持它的系统中执行包含此命令的规则时,要能够给出提示信息(提示原因是告诉用户系统不支持此命令,但不应该出现错误而退出执行)。
在我们书写的Makefile中应该讲所有的命令、选项作为变量定义,方便后期对命令的修改和对选项的修改。就是说用户可以通过修改一个变量值来重新指定所要执行的命令,或者来控制命令执行的选项、参数等。
当使用变量来表示命令时,如果规则中需要使用此命令时,可通过引用代表此命令的变量来实现。例如:定义变量"CC = gcc",规则中就可使用"$(CC)"来引用"gcc"。对于一些件管理器工具如"ln","rm""mv"等,可以不为它们定义变量,而直接使用。
所有命令执行的参数选项也应该定义一个变量(可称为命令的选项变量)。在命令变量(代表一个命令的变量)后添加"FLAGS"来命名这个选项变量。例如:变量"CFLAGS"是c编译器(命令变量为"CC")的命令行选项变量;变量YFLAGS时命令"yacc"(命令变量为"YACC")选项变量;变量"LDFLAGS"是命令"ld"(命令变量为"LD")的选项变量等。在所有需要执行预处理的命令行应该使用变量"CCFLAGS"作为gcc的执行参数;同样任何需要执行链接的命令行中使用"LDFLAGS"作为命令的执行参数。
c编译器的编译选项变量"CFLAGS"在Makefile中通常是为编译所有的源文件提供选项变量。为编译一个特定文件增加的选项,不应包含在变量"CFLAGS"中。编译特定文件(或者一类特定文件)时,如果需要使用特定的选项参数,可以将这些选项写在编译它所执行规则的命令行中(也可以使用目标指定变量或者模式指定变量)。
在所有编译命令行中,变量"CFLAGS"应该放在编译选项列表的最后。这样可以保证当命令行参数出现重复时,"CFLAGS"始终效的。另外,在任何调用c编译器的命令行中都应该使用选项变量"CFLAGS",无论是进行编译还是连接。
如果需要在Makefile中实现文件安装的规则,那么就需要在Makefile中定义变量"INSTALL"。此变量代表安装命令(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
所有GNU发布的软件包的Makefile中,必须包含以下这些目标:
all
此目标的动作是编译整个软件包。"all"应该为Makefile的终极目标。该目标的动作不重建任何文档(只编译所有的源代码,生成可执行程序);Info文件应该作为发布文件的一部分,DVI文件只在明确指定的时候才应该被重建。
缺省情况下,对所有源程序的编译和连接应该使用选项"-g",是最终的可执行程序中包含调试信息。当最终的可执行程序不需要包含调试信息时,可使用"strip"去掉可执行程序中的调试符号以减小最终的程序大小。
install
此目标的动作是完成程序的编译并将最终的可执行程序、库文件等拷贝到安装的目录。如果只是验证这些程序是否可被正确安装,它的动作应该是一个测试安装动作。
安装时一般不要对可执行程序进行strip(去掉可执行程序内部的调试信息)。存在另外一个目标"install-strip",它实现安装的同时完成对可执行程序strip。
保证目标"install"的动作不更改程序创建目录(builid目录)下的任何文件,对这个目录下文件的修改(重建或者更新)是目标"all"所要定义的动作。
uninstall
删除所有已安装文件--由install创建的文件拷贝。规则所定义的命令不能修改编译目录下的文件,仅仅是删除安装目录下的文件。像install目标的命令一样,uninstall目标的命令也分为三类。
install-strip
和目标install的动作类似,但是install-strip指定的命令在安装时对可执行文件进行strip(去掉程序内部的调试信息)。它的定义如下:
install-strip:
$(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' install
如果软件包的存在安装脚本时,目标install-strip所定义的命令就不能是对目标"install"的引用,它仅仅完成对可执行文件的strip。 "install-strip"不应该直接在build目录下对可执行文件进行strip,应该是对安装目录下的可执行文件进行strip。就是说"install-strip"所定义的命令不能对build目录下的文件产生影响。
一般不建议安装时对可执行文件进行strip,因为去掉可执行文件的调试信息后,如果在程序中存在bug,就不能通过gdb对程序进行调试。
clean
清除当前目录下编译生成的所有文件,这些文件在make过程中产生。注意,clean动作不能删除软件包的配置文件,同时也不能删除build时创建的那些文件(诸如:目录、build生成的信息记录文件等)。因为这些文件都是发布版本的一部分。
对于.dvi文件,当它不作为发布版本的一部分时,可以删除。
distclean
类似于目标clean,但增加删除当前目录下的的配置文件、build过程产生的文件。目标"distclean"指定的删除命令应该删除软件包中所有非发布文件。
dist
此目标指定的命令创建发布程序的tar文件。创建的tar文件应该是这个软件包的目录,文件名中也可以包含版本号(就是说创建的tar文件在解包之后应该是一个目录)。例如,发布的gcc 1.40版的tar文件解包的目录为"gcc-1.40"。
通常的做法是是创建一个空目录,如使用ln或cp将所需要的文件加入到这个目录中,之后对这个目录使用tar进行打包。打包之后的tar文件使用gzip压缩。例如,实际的gcc 1.40版的发布文件叫"gcc-1.40.tar.gz"。
目标"dist"的依赖文件为软件包中所有的非源代码的文件,因此在使用目标进行发布软件打包压缩之前必须保证这些文件是最新的。
后缀规则是一种古老定义隐含规则的方式,在新版本的make中使用模式规则作为对它的替代,模式规则相比后缀规则更加清晰明了。在现在版本中保留它的原因是为了能够兼容旧的makefile文件。
判断一个后缀规则是单后缀还是双后缀的过程:判断后缀规则的目标,如果其中只存在一个可被make识别的后缀,则规则是一个"单后缀"规则;当规则目标中存在两个可被make识别的后缀时,这个规则就是一个"双后缀"规则。(尽管单后缀很少出现)
例如:".c"和".o"都是make可识别的后缀。因此当定义了一个目标是".c.o"的规则时。make会将它作为一个双后缀规则来处理,它的含义是所有".o"文件的依赖文件是对应的".c"文件。下边是使用后追规则定义的编译.c源文件的规则:
.c.o:
$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $兼容老版本的make。对于静态库的重建也可以使用后缀规则。目标后缀需要是".a"(在Linux(Unix)中、静态库的后缀为.a)。例如这样一个后缀规则:
.c.a:
$(CC) $(CFLAGS) $(CPPFLAGS) -c $兼容不同版本的make,这里给出了一些基本的约定。
1. 所有的Makefile中应该包含这样一行:
SHELL = /bin/sh
其目的是为了避免变量"SHELL"在有些系统上可能继承同名的系统环境变量而导致错误。虽然在GNU版本的make中不会出现这种问题(GNU make中变量"SHELL"的默认值是"/bin/sh",它不同于系统环境变量"SHELL")。
2. 小心处理后缀和隐含规则。不同make可识别后缀和隐含规则可能不同,它可能会导致混乱或者错误。因此在特定Makefile中明确限定可识别的后缀是一个不错的主意。在Makefile中应该这样做:
.SUFFIXES:
.SUFFIXES: .c .o
第一行首先取消掉make默认的可识别后缀列表,第二行重新指定可识别的后缀列表。
当规则只有一个依赖文件时。应该使用自动化变量"$ $@
重建或者安装目标(一般是伪目标)的命令行可使用编译器或者相关工具程序,这些命令使用一个变量来表示。这样做的好处是:当修改一个命令时,只需要更改代表命令的变量的值就可以了。对于以下的这些命令程序:
ar bison cc flex install ld ldconfig lex
make makeinfo ranlib texi2dvi yacc
在规则中的命令中,使用以下这些变量来代表它们:
$(AR) $(BISON) $(CC) $(FLEX) $(INSTALL) $(LD) $(LDCONFIG)$(LEX)
$(MAKE) $(MAKEINFO) $(RANLIB) $(TEXI2DVI) $(YACC)
如果规则的命令行需要使用"ranlib"或者"ldconfig"等这些工具时,需要考虑当前的系统是否支持这些工具。当在不支持它的系统中执行包含此命令的规则时,要能够给出提示信息(提示原因是告诉用户系统不支持此命令,但不应该出现错误而退出执行)。
在我们书写的Makefile中应该讲所有的命令、选项作为变量定义,方便后期对命令的修改和对选项的修改。就是说用户可以通过修改一个变量值来重新指定所要执行的命令,或者来控制命令执行的选项、参数等。
当使用变量来表示命令时,如果规则中需要使用此命令时,可通过引用代表此命令的变量来实现。例如:定义变量"CC = gcc",规则中就可使用"$(CC)"来引用"gcc"。对于一些件管理器工具如"ln","rm""mv"等,可以不为它们定义变量,而直接使用。
所有命令执行的参数选项也应该定义一个变量(可称为命令的选项变量)。在命令变量(代表一个命令的变量)后添加"FLAGS"来命名这个选项变量。例如:变量"CFLAGS"是c编译器(命令变量为"CC")的命令行选项变量;变量YFLAGS时命令"yacc"(命令变量为"YACC")选项变量;变量"LDFLAGS"是命令"ld"(命令变量为"LD")的选项变量等。在所有需要执行预处理的命令行应该使用变量"CCFLAGS"作为gcc的执行参数;同样任何需要执行链接的命令行中使用"LDFLAGS"作为命令的执行参数。
c编译器的编译选项变量"CFLAGS"在Makefile中通常是为编译所有的源文件提供选项变量。为编译一个特定文件增加的选项,不应包含在变量"CFLAGS"中。编译特定文件(或者一类特定文件)时,如果需要使用特定的选项参数,可以将这些选项写在编译它所执行规则的命令行中(也可以使用目标指定变量或者模式指定变量)。
在所有编译命令行中,变量"CFLAGS"应该放在编译选项列表的最后。这样可以保证当命令行参数出现重复时,"CFLAGS"始终效的。另外,在任何调用c编译器的命令行中都应该使用选项变量"CFLAGS",无论是进行编译还是连接。
如果需要在Makefile中实现文件安装的规则,那么就需要在Makefile中定义变量"INSTALL"。此变量代表安装命令(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
所有GNU发布的软件包的Makefile中,必须包含以下这些目标:
all
此目标的动作是编译整个软件包。"all"应该为Makefile的终极目标。该目标的动作不重建任何文档(只编译所有的源代码,生成可执行程序);Info文件应该作为发布文件的一部分,DVI文件只在明确指定的时候才应该被重建。
缺省情况下,对所有源程序的编译和连接应该使用选项"-g",是最终的可执行程序中包含调试信息。当最终的可执行程序不需要包含调试信息时,可使用"strip"去掉可执行程序中的调试符号以减小最终的程序大小。
install
此目标的动作是完成程序的编译并将最终的可执行程序、库文件等拷贝到安装的目录。如果只是验证这些程序是否可被正确安装,它的动作应该是一个测试安装动作。
安装时一般不要对可执行程序进行strip(去掉可执行程序内部的调试信息)。存在另外一个目标"install-strip",它实现安装的同时完成对可执行程序strip。
保证目标"install"的动作不更改程序创建目录(builid目录)下的任何文件,对这个目录下文件的修改(重建或者更新)是目标"all"所要定义的动作。
uninstall
删除所有已安装文件--由install创建的文件拷贝。规则所定义的命令不能修改编译目录下的文件,仅仅是删除安装目录下的文件。像install目标的命令一样,uninstall目标的命令也分为三类。
install-strip
和目标install的动作类似,但是install-strip指定的命令在安装时对可执行文件进行strip(去掉程序内部的调试信息)。它的定义如下:
install-strip:
$(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' install
如果软件包的存在安装脚本时,目标install-strip所定义的命令就不能是对目标"install"的引用,它仅仅完成对可执行文件的strip。 "install-strip"不应该直接在build目录下对可执行文件进行strip,应该是对安装目录下的可执行文件进行strip。就是说"install-strip"所定义的命令不能对build目录下的文件产生影响。
一般不建议安装时对可执行文件进行strip,因为去掉可执行文件的调试信息后,如果在程序中存在bug,就不能通过gdb对程序进行调试。
clean
清除当前目录下编译生成的所有文件,这些文件在make过程中产生。注意,clean动作不能删除软件包的配置文件,同时也不能删除build时创建的那些文件(诸如:目录、build生成的信息记录文件等)。因为这些文件都是发布版本的一部分。
对于.dvi文件,当它不作为发布版本的一部分时,可以删除。
distclean
类似于目标clean,但增加删除当前目录下的的配置文件、build过程产生的文件。目标"distclean"指定的删除命令应该删除软件包中所有非发布文件。
dist
此目标指定的命令创建发布程序的tar文件。创建的tar文件应该是这个软件包的目录,文件名中也可以包含版本号(就是说创建的tar文件在解包之后应该是一个目录)。例如,发布的gcc 1.40版的tar文件解包的目录为"gcc-1.40"。
通常的做法是是创建一个空目录,如使用ln或cp将所需要的文件加入到这个目录中,之后对这个目录使用tar进行打包。打包之后的tar文件使用gzip压缩。例如,实际的gcc 1.40版的发布文件叫"gcc-1.40.tar.gz"。
目标"dist"的依赖文件为软件包中所有的非源代码的文件,因此在使用目标进行发布软件打包压缩之前必须保证这些文件是最新的。