autoconf automake 进一步解读
author:hjjdebug
date: 2019年 12月 28日 星期六 22:06:34 CST
学习其工作原理
如何进一步控制Makefile 的生成,
例如 修改编译选项时,添加调试信息, 添加-D宏定义,添加第三方库包含路径,添加第三方库,
开源软件,一般只要configure 能通过,make 是没有什么问题的,可见configure 成为成功的关键.
1. 修改configure.ac 从而修改configure 文件
2. 修改Makefile.am,从而修改Makefile
configure 文件是一个脚本文件,主要用来检查目标文件的依赖,例如头文件是否存在,
链接的库文件是否存在等.
./configure 的传入参数将影响Makefile 的生成
例如你想修改CFLAGS, 添加调试信息,不优化,可用如下语法:
./configure CFLAGS=-g -O0
./configure --help 可以看到它的使用帮助,
前面一些选项用来修改默认的路径,下面一些选项则是用来说明如何影响源码的编译
Some influential environment variables:
CC C compiler command
CFLAGS C compiler flags
LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
nonstandard directory <lib dir>
LIBS libraries to pass to the linker, e.g. -l<library>
CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
you have headers in a nonstandard directory <include dir>
configure.ac 中的常见的语法.
AM_INIT_AUTOMAKE: 想生成Makefile, 必须要调用该宏
AC_SEARCH_LIBS:
Macro: AC_SEARCH_LIBS (FUNCTION, SEARCH-LIBS, [ACTION-IF-FOUND],
[ACTION-IF-NOT-FOUND], [OTHER-LIBRARIES])
configure.ac 中不叫函数调用,而叫宏调用, 也有传入参数, 例如该宏,
第一个参数,被检查的函数名称
第二个参数,被检查的库名称
第三个参数,发现了怎么办
第四个参数,未发现怎么办
第五个参数,其它的库
AC_CHECK_LIB:
Macro: AC_CHECK_LIB (LIBRARY, FUNCTION, [ACTION-IF-FOUND],
[ACTION-IF-NOT-FOUND], [OTHER-LIBRARIES])
这两个函数第一和第二参数调换了一下位置
AC_CHECK_LIB(nsl, t_open) //查libnsl.so 中的t_open()函数
AC_SEARCH_LIBS(socket, socket) //查libsochet.so 中的socket()函数
AC_CHECK_HEADER([stdio.h],
[AC_DEFINE([HAVE_STDIO_H], 1,
[Define to 1 if you have <stdio.h>.])],
[AC_MSG_ERROR([sorry, can't do anything for you])])
dnl: 该宏是一个注释宏, 查info m4知,
The builtin 'dnl' stands for "Discard to Next Line":
运行预处理命令:
Macro: AC_PREPROC_IFELSE (INPUT, [ACTION-IF-TRUE],
[ACTION-IF-FALSE])
For instance:
AC_INIT([Hello], [1.0], [bug-hello@example.org])
AC_DEFINE([HELLO_WORLD], ["Hello, World\n"],
[Greetings string.])
AC_PREPROC_IFELSE(
[AC_LANG_PROGRAM([[const char hw[] = "Hello, World\n";]],
[[fputs (hw, stdout);]])],
[AC_MSG_RESULT([OK])],
[AC_MSG_FAILURE([unexpected preprocessor failure])])
results in:
checking for gcc... gcc
checking for C compiler default output file name... a.out
checking whether the C compiler works... yes
checking whether we are cross compiling... no
checking for suffix of executables...
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ISO C89... none needed
checking how to run the C preprocessor... gcc -E
OK
常用的AC 宏举例: 望文生意,辅以练习,还是容易理解的. 我这里摘自unpv13e 项目,一个网络书的例子
AC_CACHE_VAL(ac_cv_ipv4,
AC_CANONICAL_HOST
AC_CHECK_FUNCS(bzero)
AC_CHECK_FUNCS(getaddrinfo)
AC_CHECK_FUNCS(inet_pton)
AC_CHECK_FUNC_PROTO(getnameinfo, netdb.h)
AC_CHECK_FUNC_PROTO(inet_aton, arpa/inet.h)
AC_CHECK_FUNC_PROTO(snprintf, stdio.h)
AC_CHECK_HEADERS(sys/types.h sys/socket.h sys/time.h time.h [], [
AC_CHECK_LIB(nsl, t_open)
AC_CHECK_LIB(pthread, pthread_create)
AC_CHECK_LIB(xti, t_open)
AC_CHECK_MEMBER([struct msghdr.msg_control],
AC_CHECK_MEMBER([struct sockaddr.sa_len],
AC_CHECK_TYPE([struct sockaddr_dl],
AC_CHECK_TYPES([struct sockaddr_storage],
AC_CONFIG_HEADER(config.h)
AC_DEFINE(HAVE_ADDRINFO_STRUCT, 1, Define to 1 if <netdb.h> defines struct addrinfo),,[
AC_DEFINE(HAVE_DEV_TCP, 1, Define to 1 if the /dev/tcp device exists)
AC_DEFINE(HAVE_DEV_XTI_TCP, 1, Define to 1 if the /dev/xti/tcp device exists)
AC_DEFINE(SA_FAMILY_T, uint16_t)
AC_DEFINE([ss_family],[__ss_family],[define to __ss_family if sockaddr_storage has that instead of ss_family]),
AC_HEADER_STDC
AC_HEADER_TIME
AC_INIT(lib/unp.h)
AC_MSG_CHECKING(for $HOME/libbind.a)
AC_MSG_CHECKING(for IPv4 support)
AC_MSG_ERROR([cannot find ss_family in sockaddr_storage]),[
AC_MSG_RESULT($ac_cv_ipv4)
AC_MSG_RESULT(no)
AC_MSG_RESULT(no, using ./libunp.a)
AC_MSG_RESULT(yes)
AC_OUTPUT(Makefile Make.defines)
AC_PROG_CC
AC_PROG_RANLIB
AC_REVISION($Revision: 1.13 $)
AC_SEARCH_LIBS(socket, socket)
AC_SUBST(LIBFREE_OBJS)
AC_TRY_RUN([
//自定义的宏
AC_UNPXTI_CHECK_TYPE(t_scalar_t, int32_t, scalar type)
AC_UNP_CHECK_TYPE(int16_t, short, 16 bit signed type)
AC_UNP_CHECK_TYPE(int32_t, int, 32 bit signed type)
2. 修改Makefile.am, 再调用./configure
看一下生成的Makefile, 原来它们使用了如下的宏!
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
我们可以在Makefile.am 中定义AM_CPPFLAGS 定义第三方包含路径,定义宏变量
用法:
AM_CPPFLAGS= -I ./lib #可以将包含路径定义在AM_CPPFLAGS中
在AM_CFLAGS 定义优化, 在AM_LDFLAGS 中定义第三方库路径
(注意:不要和configure中CFLAGS, CPPFLAGE重复定义)
INCLUDES 在Makefile.am 中已经不推荐使用了, 在Makefile.am 中最好使用AM_打头的宏
编译
.c.o:
$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
hello$(EXEEXT): $(hello_OBJECTS) $(hello_DEPENDENCIES) $(EXTRA_hello_DEPENDENCIES)
链接:
@rm -f hello$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(hello_OBJECTS) $(hello_LDADD) $(LIBS)
AM_V_CC 是空
AM_V_CCLD 是空
EXEEXT 为空
hello_OBJECTS 推导为hello.o ....
其它相关宏:
bin_PROGRAMS 是Makefile.am 中定义的, 后面会被Makefile使用
PROGRAMS = $(bin_PROGRAMS)
通过查看demo Makefile, 对编写Makefile.am 中使用的宏就心里有底了.
automake 目标:
bin_PROGRAMS (前面的bin表示安装到bin 目录)
noinst_LIBRARIES (noinst 是不安装)
lib_LTLIBRARIES (后缀名为.la,例如libmytest.la, lib是安装到lib目录,LT是libtool简写)
SUBDIRS (递归,层次调用时使用)
关键字只是后半部分, 初次相见感觉有点怪异,习惯就好了.
库文件举例: XXX_LTLIBRARIES, XXX_LIBRARIES
不安装XXX换成noinst, 安装到lib换成lib, 前者libtool生成的库,后者一般库
libtool编译的库对夸平台移植是个福音.
添加一个宏定义 例如:-DONPC=1
修改优化选项 :可修改CFLAGS, 或通过configure 传递
添加第三方包含路径 :可修改AM_CPPFLAGS
添加第三方库路径 :添加到_LDADD变量中
$cat Makefile.am
一个简单的例子,使用了关键的几个变量! 这也是最主要的几个宏了.
AUTOMAKE_OPTIONS=foreign
SUBDIRS=lib
bin_PROGRAMS=hello
hello_SOURCES=main.c #添加源文件 _SOURCES
hello_LDADD=lib/libhello.a #添加第三方库 _LDADD
AM_CPPFLAGS = -I ./lib #添加包含路径,直接修改_CPPFLAGS
除了自定义的宏,还有内部定义的宏调用, 多看多练就熟悉了, 也不多,就是Makefile 中常见的东西.
总之是为了make 出执行文件或库文件为目的, 查找头文件,库文件,等等功能
ubuntu下有一个check 包,里面有一个automake和cmake 的例子,可以参考下
/usr/share/doc/check/examples ,可以一阅.
想深入了解autoconf, automake, 可以读其info 文档.