使用autotools生成Makefile学习笔记

autotools使用流程

autotools 是一系列工具,它包含了aclocal,autoconf,autoheader和automake这些工具,使用autotools主要就是利用各个工具来生成最后的makefile文件。其具体流程如下图:

image

如果你的系统安装了autoreconf,则上面的工具调用可以通过autoreconf这一个命令来完成,如果有哪个源文件更新,autoreconf会检测到并重新调用上面的工具来生成新的Makefile. 命令如下:

autoreconf --install --force

基础例子

文件结构

geesun@geesun-OptiPlex-3010:~/automake$ tree 
.
├── configure.ac
├── Makefile.am
└── src
    └── helloworld.c

helloworld.c代码源文件:

#include <config.h>
#include <stdio.h>

int main (void)
{
    printf("Hello "PACKAGE_STRING);
    return 0;
}

configure.ac文件

AC_INIT([helloworld], [1.0], [geesun@gmail.com])

AC_PREREQ([2.68])

AC_CONFIG_SRCDIR([src/helloworld.c])

AC_CONFIG_AUX_DIR([build-aux])

AM_INIT_AUTOMAKE([silent-rules -Wall -Werror subdir-objects foreign])

AM_SILENT_RULES([yes])

AC_PROG_CC

AC_CONFIG_HEADERS([config.h])

AC_CONFIG_FILES([Makefile])

AC_OUTPUT

Makefile.am文件

bin_PROGRAMS = helloworld
helloworld_SOURCES = src/helloworld.c

常用功能分析

1. 增加configure的选项

如果要给configure生成像--enable-XXXX,--disable-XXXX,--with-XXXX,--without-XXXX这样的参数,就需在configure.ac里用到AC_ARG_ENABLE和AC_ARG_WITH这两个宏,AC_ARG_ENABLE和AC_ARG_WITH语法如下:

AC_ARG_ENABLE(option-name, help-string, action-if-present, action-if-not-present)
AC_ARG_WITH (package, help-string, [action-if-given], [action-if-not-given])


我们来看下的configure.ac例子文件:

......

AM_SILENT_RULES([yes])

dnl Example of default-enabled feature
AC_ARG_ENABLE([feature1],
              AS_HELP_STRING([--disable-feature1], [Disable feature1]))
AS_IF([test "x$enable_feature1" != "xno"], [
         AC_DEFINE([HAVE_FEATURE1], [1], [Enable feature1])
         ])

dnl Example of default-disabled feature
AC_ARG_ENABLE([feature2],
              AS_HELP_STRING([--enable-feature2], [Enable feature2]))
AS_IF([test "x$enable_feature2" = "xyes"], [
       AC_DEFINE([HAVE_FEATURE2], [1], [Enable feature2])
       ])

dnl feature3 has depended on feature2, if feature2 was not enabled, feature3 always disabled
AC_ARG_ENABLE([feature3],
              AS_HELP_STRING([--enable-feature3], [Enable feature3]),
              [
                AS_IF([test "x$enable_feature2" != "xyes"], [enable_feature3="no"])
              ],
              [
                enable_feature3="no"
              ])

AS_IF([test "x$enable_feature3" = "xyes"], [
       AC_DEFINE([HAVE_FEATURE3], [1], [Enable feature3])
       ])

AS_ECHO(["feature1 = $enable_feature1"])
AS_ECHO(["feature2 = $enable_feature2"])
AS_ECHO(["feature3 = $enable_feature3"])

AC_ARG_WITH([foo],
            AS_HELP_STRING([--without-foo], [Ignore presence of foo and disable it]))

AS_IF([test "x$with_foo" != "xno"], [
              AC_DEFINE([HAVE_FOO], [1], [With foo])
                     ])

AS_ECHO(["with_foo = $with_foo"])

AC_PROG_CC

......

有关Autoconf的语法规则,可以参考The Autoconf Language.

AS_IF就是一个if else的语句,比较好理解,AC_DEFINE就是在config.h里面来定义一个宏来给之后具体的代码使用。

helloworld.c代码文件:

    printf("Hello "PACKAGE_STRING"\n");
#ifdef HAVE_FEATURE1
    printf("Hello Feature1 \n");
#endif

#ifdef HAVE_FEATURE2
    printf("Hello Feature2 \n");
#endif
#ifdef HAVE_FEATURE3
    printf("Hello Feature3 \n");
#endif

#ifdef HAVE_FOO
    printf("Hello Foo \n");
#endif

2.条件编译

通常,我们针对不同平台,可能会编译不同源文件,还有就是在用户在使用configure增加或者减少某个功能时,需要编译出更多或者更少的可执行文件,这是怎么做到的呢?下面我们就用一个例子来说明这个在autoconf里面怎么做到。

首先在configure.ac中增加选项,让用户来决定是否开启指定功能:

AC_ARG_ENABLE([debug],
              AS_HELP_STRING([--enable-debug], [Enable debug feature]))
AM_CONDITIONAL([DEBUG], [test "x$enable_debug" = "xyes"])

AM_CONDITIONAL是在configure.ac里面定义一个条件,这样在Makefile.am里面就可以使用这个条件了,如果没有定义,是不可以使用的。

再在Makefile.am里面根据这个条件来指定文件进行编译:

if DEBUG
bin_PROGRAMS = helloworld debug 
debug_SOURCES=src/debug.c 
else
bin_PROGRAMS = helloworld
endif

if DEBUG
helloworld_SOURCES = src/helloworld.c  \
                     src/hello-debug.c
else
helloworld_SOURCES = src/helloworld.c 
endif

3.查找库以及头文件

AC_CHECK_LIB和AC_SEARCH_LIB是用来查找库,如果找到库,则会在编译可执行文件时把这个库增加到链接库里面。语法如下:

AC_CHECK_LIB (library, function, [action-if-found], [action-if-not-found], [other-libraries])
AC_SEARCH_LIBS (function, search-libs, [action-if-found], [action-if-not-found], [other-libraries])

AC_CHECK_HEADER和AC_CHECK_HEADERS是用来查找头文件的,如果找到,会在config.h里面增加相对应的宏。语法如下:

AC_CHECK_HEADER (header-file, [action-if-found], [action-if-not-found], [includes])
AC_CHECK_HEADERS (header-file..., [action-if-found], [action-if-not-found], [includes])

configure.ac例子如下:

AC_CHECK_LIB(m, abs,[],[AC_MSG_ERROR([unable to find the cos() function])])
AC_CHECK_HEADERS([pthread.h], [],[AC_MSG_ERROR([unable to find the pthread.h])])
AC_SEARCH_LIBS([pthread_create], [pthread])

4.自定义Makefile规则

在做工程的时候,可能会有一些比较特殊的文件需要通过Makefile产生,这个时候就需要用到在Makefile.am里面增加一些自己定义的规则来完成我们的目的。这些功能就是在Makefile.am里面增加一些带-local后缀的target来实现。目前支持-local后缀的targets有: all, install-exec, uninstall, clean 等。 例子:

scripts:
    mkdir -p scripts
    echo "#!/bin/sh " > scripts/hello.sh
    echo "echo ""hello world"" " >> scripts/hello.sh

all-local: scripts

clean-local:
    rm -rf scripts/

更多可以参考这里

5.自定义安装目录

大型工程里面会有许多文件,有一些要安装在特定的目录,不一定是系统默认目录,这个时候,就需要在Makefile.am中增加目录规则。系统默认目录可以参考这里。 要自己定义目录,首先要定义xxxdir变量,然后才可以用xxx来指定文件。如下例子:

mydir=$(prefix)/mybin
my_PROGRAMS = hello2
hello2_SOURCES = src/main2.c

6.其他文件安装

脚本

脚本默认不打包,如果要默认打包进去,则要用:

dist_bin_SCRIPTS = my_script

头文件

不保留原始目录结构:

include_HEADERS = foo.h bar/bar.h

保留原始目录结构:

nobase_include_HEADERS = foo.h bar/bar.h

Data文件

data文件可以安装在如下目录:datadir, sysconfdir, sharedstatedir, localstatedir, pkgdatadir.

不需要安装的文件

noinst_PROGRAMS=hello3
hello3_SOURCES = src/main3.c

7.更多可以参考

autotools.io

Program and Library Variables

Building a Shared Library

GNU Automake

Autoconf Macro Index

AutoMake Macro Index

Comments !

个人链接