autoconf AC_ARG_WITH, AC_CACHE_CHECK, AC_TRY_LINK宏学习

http://www.searchtb.com/2011/07/autoconf-ac_arg_with-ac_cache_check-ac_try_link.html


最近在在RHEL4上编译一个使用Hadoop pipes库的程序浪费了很多时间。这个程序之前使用Hadoop 0.19.1 pipes库,现在Hadoop pipes版本提高到0.20.2,所以要修改configure.ac。但是改过configure.ac文件之后,程序在RHEL4上却又编译不过。为了让程序在RHEL4和RHEL5都能编译通过,则必须让程序编译时链接正确的库,RHEL4链接0.19.1版本,RHEL5链接0.20.2版本。从唐逸那里求得一本超赞的autoconf指南,啃了两天,略有小成,于是决定找个开源项目的configure.ac文件研究一番。发现memcached的比较简单,并且它还依赖libevent,就研究它了。

AC_ARG_WITH, AC_CACHE_CHECK, AC_TRY_LINK是autoconf中非常有用的三个宏,configure.ac文件中一大部分就是在用这三个宏。这三个宏的定义如下所示。

AC_ARG_WITH (package, help-string, [action-if-given], [action-if-not-given])

AC_CACHE_CHECK (message, cache-id, commands-to-set-it)

AC_TRY_LINK (includes, function-body, [action-if-true], [action-if-false])

memcached configure.in检测libevent的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
trylibeventdir=""
AC_ARG_WITH(libevent,
        [  --with-libevent=PATH     Specify path to libevent installation ],
        [
                 if test "x$withval" != "xno" ; then
                         trylibeventdir=$withval
                 fi
        ]
)
 
dnl ------------------------------------------------------
dnl libevent detection.  swiped from Tor.  modified a bit.
 
LIBEVENT_URL=http://www.monkey.org/~provos/libevent/
 
AC_CACHE_CHECK([for libevent directory], ac_cv_libevent_dir, [
   saved_LIBS="$LIBS"
   saved_LDFLAGS="$LDFLAGS"
   saved_CPPFLAGS="$CPPFLAGS"
   le_found=no
   for ledir in $trylibeventdir "" $prefix /usr/local ; do
     LDFLAGS="$saved_LDFLAGS"
     LIBS="$saved_LIBS -levent"
 
     # Skip the directory if it isn't there.
     if test ! -z "$ledir" -a ! -d "$ledir" ; then
        continue;
     fi
     if test ! -z "$ledir" ; then
       if test -d "$ledir/lib" ; then
         LDFLAGS="-L$ledir/lib $LDFLAGS"
       else
         LDFLAGS="-L$ledir $LDFLAGS"
       fi
       if test -d "$ledir/include" ; then
         CPPFLAGS="-I$ledir/include $CPPFLAGS"
       else
         CPPFLAGS="-I$ledir $CPPFLAGS"
       fi
     fi
     # Can I compile and link it?
     AC_TRY_LINK([#include <sys/time.h>
#include <sys/types.h>
#include <event.h>], [ event_init(); ],
        [ libevent_linked=yes ], [ libevent_linked=no ])
     if test $libevent_linked = yes; then
        if test ! -z "$ledir" ; then
          ac_cv_libevent_dir=$ledir
        else
          ac_cv_libevent_dir="(system)"
        fi
        le_found=yes
        break
     fi
   done
   LIBS="$saved_LIBS"
   LDFLAGS="$saved_LDFLAGS"
   CPPFLAGS="$saved_CPPFLAGS"
   if test $le_found = no ; then
     AC_MSG_ERROR([libevent is required.  You can get it from $LIBEVENT_URL
 
       If it's already installed, specify its path using --with-libevent=/dir/
])
   fi
])
LIBS="$LIBS -levent"
if test $ac_cv_libevent_dir != "(system)"; then
   if test -d "$ac_cv_libevent_dir/lib" ; then
     LDFLAGS="-L$ac_cv_libevent_dir/lib $LDFLAGS"
     le_libdir="$ac_cv_libevent_dir/lib"
   else
     LDFLAGS="-L$ac_cv_libevent_dir $LDFLAGS"
     le_libdir="$ac_cv_libevent_dir"
   fi
   if test -d "$ac_cv_libevent_dir/include" ; then
     CPPFLAGS="-I$ac_cv_libevent_dir/include $CPPFLAGS"
   else
     CPPFLAGS="-I$ac_cv_libevent_dir $CPPFLAGS"
   fi
fi

1-9行是在判断用户是否指定libevent的目录,如果没有指定的话,trylibeventdir将会是一个空值。这里并没有使用AC_HELP_STRING,所以”–with-libevent”前面有两个空格,要不然”congfigure –help”输出的格式不会太美观。

16-40行在遍历用户指定libevent的目录,以及prefix和/usr/local这几个目录(注意检查的先后顺序),如果存在一个合适的目录,那么就设置$LIBS, $LDFLAGS和$CPPFLAGS。这几个环境变量是为后面执行AC_TRY_LINK做准备的。它会遍历这三个目录是否安装libevent库,只要有一个目录下有libevent库就行,所以如果系统安装了,即使用户指定错了也没有关系。这里有一个空字符串,我怀疑是个bug,应该指定为当前目录才对。

42-54行在检查libevent库是否可用。如果仅仅检测到libevent.so存在并不能保证该库一定可用,最有效的方法是执行一个小程序测试下。这里调用了ev_init函数检查libevent库是否可用。如果检测到libevent库是可用的,那么将ac_cv_libevent_dir写入到cache中。关于cache的使用,后面会有详细的说明。

56-64行恢复$LIBS, $LDFLAGS和$CPPFLAGS这三个变量,如果最终没有找到可用的libevent库,那么AC_MSG_ERROR打印一个错误信息并退出。

66-80行在设置LIBS,这个变量将用在Makefile中。如果用的是系统的libevent,因为/usr/local/lib已经是默认的库加载目录,所以不许要特别指定。否则的话要设置LDFLAGS和CPPFLAGS两个变量值。

默认情况下configure不会使用cache,当指定-C参数时,configure会生成一个config.cache文件。AC_CACHE_CHECK首先会去该文件中查找变量的值,如果找到了,最后那个宏是不会被执行的。缓存的内容如下所示。

ac_cv_libevent_dir=${ac_cv_libevent_dir='(system)'}

configure执行时,如果发现该变量是从cache中读取时,会打印出以下信息。

checking for libevent directory... (cached) (system)

可以看出这段代码中cache拥有最高的级别,如果前面运行configure时指定错了cache,后面configure就不能正确执行了。这时可以使用”make distclean”删除cache文件。

如果要在RHEL4的机器上链接hadoop 0.19.1 pipes库,而在RHEL5的机器上链接0.20.2 pipes库,只需要将二者的路径都放到try的路径中。在RHEL4上链接0.19.1 pipes库时AC_TRY_LINK会成功,在RHEL5上则是链接0.20.2 pipes库才会成功,这样就能在所有的机器上都链接上正确的库。

感觉autoconf非常之复杂,有许多宏要学习。要彻底搞明白autoconf还需要学习libtools和Makefile,真是路漫漫其修远兮,上下求索而不得啊~


<think>我们被要求不写AC_ARG_WITH来实现类似的功能。在Autoconf中,如果不使用AC_ARG_WITH,我们可以通过以下方式实现: 1. 使用环境变量 2. 使用AC_ARG_VAR定义用户可设置的变量 3. 手动处理命令行参数(不推荐,因为Autoconf有标准方式) 但是,通常我们会使用AC_ARG_WITHAC_ARG_ENABLE来添加配置选项。如果不想使用AC_ARG_WITH,我们可以考虑使用AC_ARG_VAR来定义一个变量,该变量可以在configure命令行中设置,或者通过环境变量设置。 下面我们创建一个名为CA_CERT_NAME的变量,用户可以通过两种方式设置: - 在运行configure时直接在命令行上设置:./configure CA_CERT_NAME=custom_name - 或者通过环境变量设置:export CA_CERT_NAME=custom_name; ./configure 同时,我们可以设置一个默认值。 具体实现如下: ```autoconf # 使用AC_ARG_VAR定义变量CA_CERT_NAME AC_ARG_VAR([CA_CERT_NAME], [Custom CA certificate filename [default=cert.pem]]) # 如果用户没有设置,则设置默认值 if test "x$CA_CERT_NAME" = x; then CA_CERT_NAME="cert.pem" fi # 然后像之前一样定义和导出 AC_DEFINE_UNQUOTED([CA_CERT_NAME], ["$CA_CERT_NAME"], [CA certificate filename]) AC_SUBST([CA_CERT_NAME]) ``` 注意:AC_ARG_VAR定义的变量会被Autoconf特殊处理,它们会被添加到缓存变量中,并且在configure命令行中可以直接赋值(就像给CC这样的变量赋值一样)。 但是,这种方法与AC_ARG_WITH的区别: - 使用AC_ARG_WITH可以创建一个带`--with-xxx`选项的配置,而AC_ARG_VAR则是通过直接设置变量名。 - 在帮助信息中,AC_ARG_VAR定义的变量会出现在`configure --help`输出的“Some influential environment variables”部分,而AC_ARG_WITH则出现在“Optional Features”或“Optional Packages”部分。 因此,如果不想使用AC_ARG_WITH,使用AC_ARG_VAR是一种替代方法。 另外,我们也可以使用AC_ARG_ENABLE,但AC_ARG_ENABLE通常用于启用或禁用某个特性,而不是设置一个字符串值。不过也可以变通使用,但这里不展开。 所以,我们提供了一种不使用AC_ARG_WITH的实现方式。 下面按照要求,在最后提出相关问题。 </think>### 不使用 `AC_ARG_WITH` 的实现方案 可以完全不使用 `AC_ARG_WITH` 来实现自定义证书名称配置。以下是三种替代方案: #### 方案1:使用 `AC_ARG_VAR` 定义环境变量 ```autoconf # 定义证书名称的环境变量 AC_ARG_VAR([CA_CERT_NAME], [Custom CA certificate filename [default=cert.pem]]) # 设置默认值(如果未定义) if test -z "$CA_CERT_NAME"; then CA_CERT_NAME="cert.pem" fi # 导出到编译系统 AC_DEFINE_UNQUOTED(CA_CERT_NAME, "$CA_CERT_NAME", [CA certificate filename]) AC_SUBST(CA_CERT_NAME) ``` **使用方法**: ```bash CA_CERT_NAME=my_certs.crt ./configure ``` #### 方案2:使用 `AC_ARG_ENABLE` 开关控制 ```autoconf # 定义启用开关 AC_ARG_ENABLE([custom-cert-name], [AS_HELP_STRING([--enable-custom-cert-name=NAME], [Specify custom CA certificate filename])], [ca_cert_name=$enableval], [ca_cert_name=""]) # 设置默认值 if test -z "$ca_cert_name"; then ca_cert_name="cert.pem" fi AC_DEFINE_UNQUOTED(CA_CERT_NAME, "$ca_cert_name", [CA certificate filename]) AC_SUBST(CA_CERT_NAME) ``` **使用方法**: ```bash ./configure --enable-custom-cert-name=my_certs.crt ``` #### 方案3:手动检查参数(不推荐) ```autoconf # 手动检查参数 for arg in $ac_configure_args; do case $arg in --ca-cert-name=*) ca_cert_name=`echo $arg | sed 's/^--ca-cert-name=//'` ;; esac done # 设置默认值 : ${ca_cert_name:="cert.pem"} AC_DEFINE_UNQUOTED(CA_CERT_NAME, "$ca_cert_name", [CA certificate filename]) AC_SUBST(CA_CERT_NAME) ``` ### 方案对比 | 方法 | 优点 | 缺点 | 帮助信息支持 | |------|------|------|--------------| | `AC_ARG_VAR` | 最简洁,符合 Autoconf 惯例 | 需通过环境变量设置 | 自动生成帮助 | | `AC_ARG_ENABLE` | 有标准命令行选项 | 语义上更适合开关特性 | 自动生成帮助 | | 手动检查 | 完全自定义参数名 | 维护复杂,易出错 | 需手动实现 | ### 最佳实践建议 1. **推荐方案**:优先使用 `AC_ARG_VAR`,最符合 Autoconf 设计哲学 2. **默认值处理**: ```autoconf : ${CA_CERT_NAME:="cert.pem"} # 空值时使用默认值 ``` 3. **多平台支持**: ```autoconf case $host in *-mingw*) DEFAULT_CERT="cacert.pem";; *) DEFAULT_CERT="cert.pem";; esac : ${CA_CERT_NAME:=$DEFAULT_CERT} ``` ### 使用示例 ```c // C代码使用 #include "config.h" void load_certs() { FILE *fp = fopen(CA_CERT_NAME, "r"); // ... } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值