- 一个简单的例子
- 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
- _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.
- include
- if HAVE_UNISTD_H
- include
- endif
- if HAVE_IO_H
- include
- endif
- 使用aclocal管理自定义宏
- Libtool
- Makefile.am
- configure.ac
一个简单的例子
背景: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来动态指定编译的库类型。