Makefile Conventions

Makefiles通用规范

每个Makefile都应该包含如下内容:

1
SHELL = /bin/sh

对SHELL变量进行赋值是为了避免在一些系统上SHELL环境变量设置错误导致出现问题。

不同的make程序可能有不兼容的后缀列表和隐含规则,因此有时可能造成误解甚至误用。为此,最好的方法是显式设置后缀列表,如下所示:

1
2
.SUFFIXES:
.SUFFIXES: .c .o

上面第一行清除后缀列表,第二行声明隐含规则可能处理的后缀列表。

在命令执行中,不要假设.是路径。当你需要在make编译期间运行一个程序时,如果那个程序是make构建出来的,请确保使用./作为前缀;如果该程序位于源码目录下,请使用$(srcdir)/作为前缀。如果不使用这些前缀,默认的搜索路径将是当前路径。

区分./(构建目录)与$(srcdir)/(源码目录)是十分重要的,因为用户可能在构建时使用’--srcdir’选项指定非当前目录的源码作为输入。如下例:

1
2
foo.1 : foo.man sedscript
sed -f sedscript foo.man > foo.1

如果构建目录与源码目录不同,则构建将失败,因为foo.man和sedscript位于源码目录。

当使用GUN make是,依赖于’VPATH’去指定源码文件搜索路径,这样在使用”$\<”这条隐含规则时,其所所代表的含义是指搜索目录下所有的源文件。一个Makefile的例子是:

1
2
foo.o : bar.c
$(CC) -I. -I$(srcdir) $(CFLAGS) -c bar.c -o foo.o

应该被这样改写:

1
2
foo.o : bar.c
$(CC) -I. -I$(srcdir) $(CFLAGS) -c $< -o $@

为了让”VPATH”正常工作,当构建目标有多个依赖项时,使用$(srcdir)去指定源码路径是一个更简单容易的方法,例如,对于如上的构建目标foo.1最好被写成如下这样:

1
2
foo.1 : foo.man sedscript
sed -f $(srcdir)/sedscript $(srcdir)/foo.man > $@

在GNU的源码分发标准下,通常不仅仅包含源码文件,可能还包含信息文件和Autoconf、Automake、Bison或者Flex的输出文件。这些文件通常都位于源码目录下,因此,Makefile规则在更新它们的时候也应将更新后的文件放到源码目录。

Makefiles中的常用工具

写Makefile文件,应该保证其中使用的命令是能在sh(包含传统的Bourne shell和POSIX shell)下运行的,而不是csh,不能使用ksh、bash所支持的特殊特性。

在写configure脚本和Makefile规则时,能够直接在其中使用的工具包含如下:

1
2
awk cat cmp cp diff echo egrep expr false grep install-info ln ls
mkdir mv printf pwd rm rmdir sed sleep sort tar test touch tr true

如gzip这样的压缩程序能够在dist规则中使用。

通常,不要使用具有平台差异性的命令,如’mkdir -p’,这样使用虽然很方便,但对于有些系统而言不支持,并且也是非多线程安全的。要了解更多这样的例子可参考 Portable Shell Programming

对于符号链接来说,也应该避免在Makefile中创建,因为有些文件系统不支持符号链接。

在Makefile规则中,可以方便地使用变量来表示如下这些程序:

1
2
ar bison cc flex install ld ldconfig lex
make makeinfo ranlib texi2dvi yacc

可表示为:

1
2
$(AR) $(BISON) $(CC) $(FLEX) $(INSTALL) $(LD) $(LDCONFIG) $(LEX)
$(MAKE) $(MAKEINFO) $(RANLIB) $(TEXI2DVI) $(YACC)

当你使用ranlib或ldconfig时,你应该确保系统中存在这些程序并且能正常工作。

DESTDIR

DESTDIR是一个为每个安装目标文件预先准备的变量,如下:

1
2
$(INSTALL_PROGRAM) foo $(DESTDIR)$(bindir)/foo
$(INSTALL_DATA) libfoo.a $(DESTDIR)$(libdir)/libfoo.a

make通过命令行传入DESTDIR变量,如下:

1
make DESTDIR=/tmp/stage install

在安装阶段,安装程序将会将目标文件复制到/tmp/stage/usr/local/bin/foo和/tmp/stage/usr/local/lib/libfoo.a位置,而非/usr/local/bin/foo和/usr/local/lib/libfoo.a。但/usr/local/bin/foo和/usr/local/lib/libfoo.a会成为符号链接,链接到被安装的实际位置。

安装目录相关的变量

安装目录应该总是以变量的形式设置,这样就可以很容易将目标文件安装到非标准目录。关于这些变量的标准名字和值将在下面给出,基于标准文件系统层次。

如下两个变量为设置根目录的变量。

  • prefix

    prefix默认值应是/usr/local。当构建完全的GNU系统时,prefix将为空,/usr也将成为一个指向/的符号链接。

  • exec_prefix

    exec_prefix的默认值为$(prefix)

可执行文件总是被安装到如下目录中。

  • bindir

    默认值是/usr/local/bin,被写成$(exec_prefix)/bin

  • sbindir

    默认值是/usr/local/sbin,被写成$(exec_prefix)/sbin

  • libexecdir

    默认值是/usr/local/libexec,被写成$(exec_prefix)/libexec

    所有包的libexecdir都是一样的,因此你应该将你的数据安装到其下的一个子目录,如$(libexecdir)/package-name/machine/version.

如下目录用于指定不同种类的数据应放的位置。

  • datarootdir

    平台无关的只读文件存放位置,通常为/usr/local/share,写成$(prefix)/share

  • datadir

    平台无关的只读文件存放位置,正常为/usr/local/share,写为$(datarootdir)

  • sysconfdir

    系统配置文件目录,/usr/local/etc,写成$(prefix)/etc

  • sharedstatedir

    程序运行时会修改的体系无关目录。/usr/local/com,写成$(prefix)/com

  • localstatedir

    程序运行时会修改,/usr/local/var,写成$(prefix)/var

  • runstatedir

    /var/run,写成$(localstatedir)/run

  • includedir

    头文件目录,/usr/local/include,写成$(prefix)/include

  • oldincludedir

    /usr/include

  • docdir

    文档安装目录,/usr/local/share/doc/yourpkg,写成$(datarootdir)/doc/yourpkg

  • infodir

    信息文件安装目录,/usr/local/share/info,写成$(datarootdir)/info

  • libdir

    库文件目录,/usr/local/lib,写成$(exec_prefix)/lib

  • srcdir

    被编译的s源码文件目录

标准目标

在Makefiles中,通常应该有如下这些目标。

  • all

    编译整个程序

  • install

    编译程序和复制可执行文件、库文件和其它文件到它们的安装位置

    ```
    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

    ```

  • uninstall

    删除安装文件

  • install-strip

    像install,但在安装时会strip可执行文件

    ```
    install-strip:

    $(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' \
            install

    ```

  • clean

    删除当前目录下的所有文件

  • dist

    为这个程序创建一个用于发布的tar文件

  • check

    自检,在编译后,安装前做检查

  • installdirs

    用于创建安装目录的目标


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 yxhlfx@163.com

文章标题:Makefile Conventions

本文作者:红尘追风

发布时间:2016-11-19, 23:32:46

原始链接:http://www.micernel.com/2016/11/19/MakefileConventions/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录