字体: | 推荐给好友 上一篇 | 下一篇

GNU make中文手册 - 第十四章 Makefile的约定

发布: 2009-2-09 22:01 | 作者: webmaster | 来源: 本站原创 | 查看: 864次

第十四章Makefile的约定


本章讨论书写Makefile时需要遵循的约定。工具“Automake”可以帮助我们创建一个遵循这些约定的Makefile。所有GNU发布的软件包中的Makefile都是按照这些标准的约定来书写的。因此理解本章的内容,可帮助很快的熟悉那些开源代码的结构。而对于我们,在为一个工程书写Makefile时,也尽量遵循这些约定。虽然并没有强求你这么做,但是建议你还是按照已约定的规则来书写自己的Makefile

14.1     基本的约定

本节讨论书写Makefile时应遵循的一些基本约定,由于不同版本make之间的差异。可能在GNU make环境中正常工作的Makefile,换成其它版本的make执行时会发生错误。为了最大可能的兼容不同版本的make,这里给出了一些基本的约定。

1.       所有的Makefile中应该包含这样一行:

SHELL = /bin/sh

其目的是为了避免变量“SHELL”在有些系统上可能继承同名的系统环境变量而导致错误。虽然在GNU版本的make中不会出现这种问题(GNU make中变量“SHELL”的默认值是“/bin/sh”,它不同于系统环境变量“SHELL”)。

2.       小心处理后缀和隐含规则。不同make可识别后缀和隐含规则可能不同,它可能会导致混乱或者错误。因此在特定Makefile中明确限定可识别的后缀是一个不错的主意。在Makefile中应该这样做:

.SUFFIXES:

.SUFFIXES: .c .o

第一行首先取消掉make默认的可识别后缀列表,第二行重新指定可识别的后缀列表。

3.       小心处理规则中的路径。当需要处理指定目录的的文件时,应该明确给出路径。如“./”代表当前目录,“$(srcdir)”代表源代码目录。没有指定明确路径,那么就意味着是当前目录。

 

目录“./”(当前目录,GNU的发布软件包中的“build”目录)和“$(srcdir)”的区别和重要,我们可以通过“configure”脚本的选项“--srcdir”指定源代码所在的目录(可参考GNU发布的软件包中的configure脚本)。当源代码目录和build目录不同时,规则:

foo.1 : foo.man sedscript

  sed –e sedscript foo.man > $@

将执行失败,是因为“foo.man”和“sedscript”并不在当前目录(当然,处理这种错误的手段可能有很多,诸如使用变量“VPATH”等)。当前目录只是build目录,并不是软件包目录。

4.       使用GNU make的变量“VPATH”指定搜索目录。当规则只有一个依赖文件时。应该使用自动化变量$<”和“$@”代替出现在命令的依赖文件和目标文件(其它版本的make,只在隐含规则中设置自动化变量“$<”)。对于一个这样的规则:

foo.o : bar.c

  $(CC) –I. –I$(srcdir) $(CFLAGS) –c bar.o –o foo.o

 

我们在Makefile中应该以这种方式来书写:

 

foo.o : bar.c

  $(CC) –I. –I$(srcdir) $(CFLAGS) –c $< –o $@

 

另外,对于有多个依赖的规则,为了规则能被正确执行。应该在规则的命令行中明确的指定文件的完整路径名。例如第一个例子就可以这样写(需要在规则之前使用“VPATH”指定搜索目录):

 

foo.1 : foo.man sedscript

  sed –e $(srcdir)/sedscript $(srcdir)/foo.man > $@

 

GNU的发布软件包中,包括了很多非源代码的文件。诸如:“info”文件、“Autoconf”的输出文件、“Automake”、“Bison”或者“Flex”等文件。这些文件在发布时也存在于源代码的目录中。因此Makefile中对它们的重建也应该是在源代码目录下,而不应该在build目录。

相反的,对于那些本来就不存在于源代码目录下的文件,也不应该将它们创建在源代码的目录下。要记住,make的过程不应该以任何方式修改源代码,或者改变源代码目录的结构。

14.2     规则命令行的约定

本节讨论书写规则命令的一些约定,在书写多系统兼容的Makefile时,需要注意不同系统的命令存在不兼容。这里对规则命令行做出了一些书写的基本约定:

1.       书写Makefile时,规则的命令(包括其他的脚本文件,如:configure)应该是“sh”而不是“csh”所支持的。

2.       用于创建和安装的“configure”脚本以及Makefile中的命令,除使用下面所列出的之外,避免使用其它命令:

 

cat cmp cp diff echo egrep expr false grep install-info

ln ls mkdir mv pwd rm rmdir sed sleep sort tar test touch true

 

3.       在目标“dist”的命令行中可以使用压缩工具“gzip”。

4.       对于可使用的这些工具命令,尽量使用它的通用选项。不要使用那些只在特定系统上有效的选项。如:“mkdir -p”这个命令在Linux系统上能够很好的工作,但是其它很多系统却并不支持“mkdir”的“-p”选项。

5.       尽量不要在规则的命令行中创建符号连接文件(使用“ln”命令)。因为有些系统不支持符号连接文件(对于类Unix的系统我们基本上没有问题,可能这里所说的是MS-DOS系统的系统。我想大家也没有兴趣或者说没有必要在MS-DOS下写Makefile,所以这个限制基本可以不考虑)。

6.       重建或者安装目标(一般是伪目标)的命令行可使用编译器或者相关工具程序,这些命令使用一个变量来表示。这样做的好处是:当修改一个命令时,只需要更改代表命令的变量的值就可以了。对于以下的这些命令程序:

 

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”等这些工具时,需要考虑当前的系统是否支持这些工具。当在不支持它的系统中执行包含此命令的规则时,要能够给出提示信息(提示原因是告诉用户系统不支持此命令,但不应该出现错误而退出执行)。

对以上没有列出的其它命令,在规则的命令行使用时也应该都是通过变量的形式。例如如下的这些命令:

 

chgrp chmod chown mknod

 

我们可以在Makefile中为这些命令组件定义一个代表它的变量(如:CHRPCHMOD等,在命令行中就可以使用$(CHMOD)来引用)。

书写多系统兼容Makefile时需要遵循以上的约定。如果只考虑一种系统时,以上的这些约定中可以灵活处理,比如:在命令组件的使用上,我们就可以使用这个系统独具的那些命令组件;系统支持符号链接时,我们就可以在命令行重创建一个符号链接文件。对于上边的第6个约定,强烈建议大家都遵循。

14.3     代表命令变量

在我们书写的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 = -g

ALL_CFLAGS = -I $(CFLAGS)

.c.o:

$(CC) -c $(CPPFLAGS) $(ALL_CFLAGS) $<

 

例子中,变量“CFLAGS”指定编译选项为“-g”,在本例中它作为缺省的编译选项。对于所有源文件的编译都使用“-g”选项。双后缀规则的命令行中为编译生成“.o”文件指定了另外的选项“-I. -g

在所有编译命令行中,变量“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

 

另外,也可以使用变量“DESTDIR”来指定目标需要安装的目录。通常也可以不在Makefile定义变量“DESTDIR”,可通过make命令行参数的形式来指定。例如:“make DESTDIR=exec/ install”。因此上边的命令就可以这样实现:

 

$(INSTALL_PROGRAM) foo $(DESTDIR)$(bindir)/foo

$(INSTALL_DATA) libfoo.a $(DESTDIR)$(libdir)/libfoo.a

 

 

安装命令中使用文件名而不是目录名作为第二个参数。每一个需要安装的文件使用单独的命令(包括安装一个目录)。

14.4     安装目录变量

Makefile中,安装目录同样需要使用变量来指定,这样就可以很方便的修改文件的安装路径。安装目录的标准命名下边将一一介绍。这些变量基于标准的文件系统结构,这些变量的变种在SVR44.4BSDLinuxUltrix v4以及其它现代操作系统中都有使用。

Ø       以下所罗列的两个变量是指定安装文件的根目录。所有其它安装目录都是它们的子目录。注意:文件不能直接安装在这两个目录下。

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”,不会重建可执行程序(终极目标)。

 

Ø       文件(包括可执行程序、说明文档等)的安装目录:

 

评分:0

我来说两句

seccode