一个简单的例子

背景:POSIX系统提供了两个参数的mkdir命令,Mingw32提供了单个参数的mkdir命令,Win32提供了单个参数的_mkdir命令。
假如我们希望在C源文件中通过宏判断是否存在合适的mkdir并对不同系统提供的mkdir进行统一定义:

if HAVE_MKDIR

if MKDIR_ONE_ARG

define mkdir(a,b) mkdir(a)

endif

else

if HAVE__MKDIR

define mkdir(a,b) _mkdir(a)

else

error “Don’t know how to create a directory.”

endif

endif

为此,需要定义这样的一个宏:

AC_DEFUN([AX_FUNC_MKDIR], [AC_CHECK_FUNCS([mkdir _mkdir]) AC_CHECK_HEADERS([io.h]) AX_FUNC_MKDIR_ONE_ARG ])

以上,AX_FUNC_MKDIR是我们定义的宏名,按照autotools的建议,自定义的宏名以AX开头;
AX_FUNC_MKDIR中间按照AC_FUNC_XXX宏的习惯,以FUNC表示该宏是一个检查函数的宏;
AC_CHECK_FUNCS宏会定义HAVE_MKDIR和HAVE__MKDIR;
AC_CHECK_HEADERS宏会定义HAVE_IO_H;
AX_FUNC_MKDIR_ONE_ARG是接下来要定义的又一个宏,它会定义MKDIR_ONE_ARG,该宏定义如下:

_AX_FUNC_MKDIR_ONE_ARG(IF-ONE-ARG, IF-TWO-ARGS)

———————————————————————-

Execute IF-TWO-ARGS if mkdir() accepts two

arguments; execute IF-ONE-ARG otherwise.

AC_DEFUN([_AX_FUNC_MKDIR_ONE_ARG], [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[

include

if HAVE_UNISTD_H

include

endif

if HAVE_IO_H

include

endif

]], [[mkdir (“.”, 0700);]])], [$2], [$1])])

以上AC_DEFUN定义了_AX_FUNC_MKDIR_ONE_ARG宏,该宏使用AC_COMPILE_IFELSE来尝试编译一段程序,根据编译结果来决定展开哪段参数;被编译的程序使用宏AC_LANG_PROGRAM生成,该宏的第一个参数是main函数前的代码,第二个参数是main函数体中的代码。
_AX_FUNC_MKDIR_ONE_ARG宏以下划线开头,表示它是另一个名字类似的宏(AX_FUNC_MKDIR_ONE_ARG)的内部实现细节,AX_FUNC_MKDIR_ONE_ARG宏定义如下:

AC_DEFUN([AX_FUNC_MKDIR_ONE_ARG], [AC_CACHE_CHECK([whether mkdir takes one argument], [ax_cv_mkdir_one_arg], [_AX_FUNC_MKDIR_ONE_ARG([ax_cv_mkdir_one_arg=yes], [ax_cv_mkdir_one_arg=no])]) if test x”$ax_cv_mkdir_one_arg” = xyes; then AC_DEFINE([MKDIR_ONE_ARG], 1, [Define if mkdir takes only one argument.]) fi]) # AX_FUNC_MKDIR_ONE_ARG

AX_FUNC_MKDIR_ONE_ARG这样被另一个更高层的AC_FUNC_MKDIR宏所使用的宏被称作low level宏,low level宏一般需要利用AC_CACHE_CHECK宏定义检查结果,提供对检查的简单描述,提供检查的具体代码,以及对检查结果进行缓存。
这个AX_FUNC_MKDIR_ONE_ARG的实现:

  • 首先利用AC_CACHE_CHECK宏,打印一段提示文字(whether mkdir takes one argument),然后检查ax_cv_mkdir_one_arg宏是否已定义,若已定义就跳过检查部分,若未定义就执行检查部分对其进行定义。
  • 然后对上一步定义的ax_cv_mkdir_one_arg宏进行判断,利用AC_DEFINE或者AC_SUBST定义接下来会在config.h或Makefile.am中用到的变量。

使用aclocal管理自定义宏

autoconf只认识它提供的宏(m4*, AS, AH_, AC*, AT),Automake所需的AM_宏不是由autoconf提供的。
为了处理configure.ac,autoconf还会读取aclocal.m4中的宏定义,而acloca就是自动根据宏定义文件生成aclocal.m4的工具。
aclocal会在以下地点查找宏定义文件:

  • 用-I指定的目录
  • aclocal系统目录,通常是/usr/local/aclocal/,第三方包通常会把它们提供的宏定义存放在这里
  • Automake自己的宏定义目录

可以在项目中维护一个自己的宏定义目录:

  • 创建一个m4目录
  • 然后将你的宏定义存放在这个目录下以.m4结尾的文件中
  • 在顶层的Makefile.am中添加ACLOCAL_AMFLAGS = -I m4
  • 在configure.ac中添加AC_CONFIG_MACRO_DIR([m4])

要使用Gettext和Libtool,也需要进行类似的配置。

Libtool

因为每个操作系统都有自己的库文件格式,如dll、so、dylib。。。
因此,autotools定义了一种特殊的格式:.la(libtool archive)格式,在构建库文件时,构建目标需使用Libtool提供的PRIMAR:_LTLIBRARIES进行声明,最后,Libtool会负责把这些.la文件翻译成系统的库文件格式。
例如:

Makefile.am

lib_LTLIBRARIES = libfoo.la libfoo_la_SOURCES = foo.c foo.h etc.c

bin_PROGRAMS = runme runme_SOURCES = main.c runme_LDADD = libfoo.la

但是,Libtool并不是autoconf的一部分,要使用Libtool提供的功能,需要在configure.ac中增加一些配置:

configure.ac

AC_INIT([amhello], [2.0], [bug-report@address]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([foreign -Wall -Werror]) LT_INIT AC_PROG_CC AC_CONFIG_HEADERS([config.h]) AC_CONFIG_FILES([Makefile lib/Makefile src/Makefile]) AC_OUTPUT

默认情况下,会构建静态和动态库,可以在LT_INIT宏中指定参数:disable-shared/disable-static来改变默认行为,运行configure时也可以显式指定—enable-xxx或—disable-xxx来动态指定编译的库类型。