分析一段M4宏

一 背景

本来以为通过上周的学习,已经对整个流程已经比较熟悉了,但是没想到还是遇到undefined reference to问题,虽然很快通过添加依赖的类库解决了,但是为什么生成的Makefile没有引入库,还是需要分析下configure.ac,本着不能错误进步机会,留下知识盲点的原则,还是进一步学习下m4宏的一些基础语法.

GNU M4是传统UNIX宏处理器的一种实现方式,它还具有一些内置功能,包括文件,shell,运算等。
作为一个宏处理器,将输入复制到扩展的输出,它要么内置,要么用户定义,且可以接受参数。另外这个还有内置函数,包括命名文件、运行UNIX命令、执行整数运算、以各种方式操作文本、递归等。M4既可以作为编译器的前端使用,也可以作为自己的宏处理器使用。
GNU M4的最大用户之一是GNU AutoCOF项目。

二 AC_ARG_ENABLE

AC_ARG_ENABLE 是属于M4的宏代码中的,意思是做个标识,标识我们启用或者禁用某个功能,这样我们在调用configure的时候,可以根据实际的环境情况,可能禁止或启用特定功能。

多数库的引入都可以采用这个模板,稍微改下就可以,虽然我也改过,但是对含义不是特别清楚,梳理下,一段段贴出来解读下:

    AC_ARG_ENABLE(mysql,
            AS_HELP_STRING([--enable-mysql],[Enable MySql output support]),
            [ enable_mysql="yes"],
            [ enable_mysql="no"])

AC_ARG_ENABLE 语法说明如下:

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

AC_ARG_ENABLE 定义一个命令行选项,选项基本名字为option-name,完整名字为:
enable-option-name
帮助字符串为:
help-string,帮助字符串是./configure --help会打印出来的信息,通过AS_HELP_STRING将字符串格式化,截取一段如下显示:

AS_HELP_STRING 打印格式化字符串

action-if-present 为用户指定值执行的m4动作,action-if-not-present为用户未指定值的时候执行的动作。
上文的m4宏,只有两个参数,省略了,很多,我们看看一般的用法:

我们如果在调用configure时候指定:
--enable-mysql=yes 或者--enable-mysql 则宏变量:enable_mysql值为yes。
如果指定:
--enable_mysql=no 或者-disable_mysql 则宏变量enable_mysql值为no。

帮助字符串信息见上面截图。

三 AC_ARG_WITH

一般我们当启用特定的功能后,有时候需要引入特定的库来支持这个功能,而这些库或头文件的位置在不同的机器上安装位置不同,所以可以通过这个宏来定义下具体的位置,语法非常类似:
AC_ARG_ENABLE,只是使用的是with开头,如下,引入了mysql的库的头文件定义和库位置定义。

    AC_ARG_WITH(libmysql_includes,
            [  --with-libmysql-includes=DIR  libmysql include directory],
            [with_libmysql_includes="$withval"],[with_libmysql_includes="no"])
    AC_ARG_WITH(libmysql_libraries,
            [  --with-libmysql-libraries=DIR    libmysql library directory],
            [with_libmysql_libraries="$withval"],[with_libmysql_libraries="no"])

configure指定:

--enable-mysql   --with-libmysql-includes=/usr/include/mysql  --with-libmysql-libraries=/usr/lib64

四 判断和真正引入库

刚才通过enable来启用特定功能,用with指定库和头文件位置,那么这些位置是不是对的,是不是真的有需要依赖的库,如果判断没问题,则真正通过-I 引入库的头文件,-L库路径-lxx引入具体的库文件。

如下:

    if test "$enable_mysql" = "yes"; then
        if test "$with_libmysql_includes" != "no"; then
            CPPFLAGS="${CPPFLAGS} -I${with_libmysql_includes}"
        fi

        AC_CHECK_HEADER(mysql.h,MYSQL="yes",MYSQL="no")
        if test "$MYSQL" = "yes"; then
            if test "$with_libmysql_libraries" != "no"; then
                LDFLAGS="${LDFLAGS} -L${with_libmysql_libraries}"
            fi
            AC_CHECK_LIB(mysqlclient, mysql_thread_init,, MYSQL="no")
        
        fi
        if test "$MYSQL" = "no"; then
            echo
            echo "   ERROR!  libmysql library not found, go get it"
            echo "   from http://www.maxmind.com/en/geolite or your distribution:"
            echo
            echo "   Ubuntu: apt-get install mysql-devel"
            echo "   Fedora: yum install mysql-devel"
            echo
            exit 1
        fi

        AC_DEFINE([HAVE_MYSQL],[1],[libmysql available])
        enable_mysql="yes"
    fi

这里面的基本语法应该都看得懂,就是AC_CHECK_HEADER宏,比较简单,故名思意,就是检查头文件,如果mysql.h存在,则定义MYSQL=yes,不然就定义:MYSQL=no,后面如果真正有,则引入库。

AC_CHECK_LIB宏,也比较简单,判断库是否存在,
语法:
AC_CHECK_LIB(lib,function[,aciton_if_found[,action_if_not_found[,otherlibs]]]) ,判断lib中是否存在指定函数function,如果测试成功,执行action_if_found 如果判断库里面不包含 action_if_not_found中得命令。

五 pkg-config和PKG_CHECK_MODULES

5.1 pkg-config

刚才我们启动功能后,通过with 选项来指定库的位置和头文件位置,接着我们设置引入库和头文件。库的种类很多,库也可能也要依赖其他库,就造成去写执行库依赖很麻烦。
来看看pkg-config 一个命令的例子:

#pkg-config --cflags --libs htp
-I/usr/local/include -I/usr/local/lib/htp/include -L/usr/local/lib -lhtp

这样我们可以简化Makefile

CFLAGS := -O3 $(EXTRA_INC) -std=c++11 -I/usr/local/include -I/usr/local/lib/htp/include -L/usr/local/lib -lhtp

这部分就可以简化为:

CFLAGS := -O3 $(EXTRA_INC) -std=c++11 `pkg-config --cflags --libs htp`

pkg-config为什么可以知道这些头文件的位置和依赖库的选项那,那是因为通过安装库的时候有.pc文件来指明一些头文件,依赖库选项,举例如下:

#cat /home/peter/lib/pkgconfig/htp.pc
prefix=/home/peter
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include

Name: LibHTP
Description: A security-aware HTTP parser, designed for use in IDS/IPS and WAF products.
Version: 0.5.26
Libs: -L${libdir} -lhtp
Cflags: -I${includedir} -I${libdir}/htp/include

如果pkg-config 找不到库,则看看是不是pc文件的路径是不是在PKG_CONFIG_PATH环境变量中,如果不在添加进去即可。

5.2 PKG_CHECK_MODULES

PKG_CHECK_MODULES 为m4宏,作用是利用pkg-config来判断模块是否存在类似于通过:

pkg-config --libs --cflags --modversion nss

来检查错误。
具体的定义在pkg.m4中定义如下:

AC_DEFUN([PKG_CHECK_MODULES],
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl

pkg_failed=no
AC_MSG_CHECKING([for $1])

_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
_PKG_CONFIG([$1][_LIBS], [libs], [$2])

m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
and $1[]_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details.])

if test $pkg_failed = yes; then
    AC_MSG_RESULT([no])
        _PKG_SHORT_ERRORS_SUPPORTED
        if test $_pkg_short_errors_supported = yes; then
            $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1`
        else 
            $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1`
        fi
    # Put the nasty error message in config.log where it belongs
    echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD

    m4_default([$4], [AC_MSG_ERROR(
[Package requirements ($2) were not met:

$$1_PKG_ERRORS

Consider adjusting the PKG_CONFIG_PATH environment variable if you
installed software in a non-standard prefix.

_PKG_TEXT])[]dnl
        ])
elif test $pkg_failed = untried; then
        AC_MSG_RESULT([no])
    m4_default([$4], [AC_MSG_FAILURE(
[The pkg-config script could not be found or is too old.  Make sure it
is in your PATH or set the PKG_CONFIG environment variable to the full
path to pkg-config.

_PKG_TEXT

To get pkg-config, see <http://pkg-config.freedesktop.org/>.])[]dnl
        ])
else
    $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
    $1[]_LIBS=$pkg_cv_[]$1[]_LIBS
        AC_MSG_RESULT([yes])
    $3
fi[]dnl
])# PKG_CHECK_MODULES

它是在pkgconfig安装包中会安装到系统中,CentOS7下,缺省目录是:/usr/share/aclocal/pkg.m4,上次的文章即是pkg.m4里面文件找不到,所以宏没有展开,直接原样在configure里面了,所以报错,通过指定路径解决了这个问题:
autoreconf -I /usr/share/aclocal

六 问题

前几天遇到的问题是没有引入nss库,导致一堆没有定义符号的错误,为什么没能引入那,是通过PKG_CHECK_MODULES这个m4宏来检查的时候,nss.pc文件所在的路径,没有在配置的PKG_CONFIG_PATH 路径中,所以认为nss库不存在,所以没有引入相关的库,从而报找不到符号的错误,简单的解决办法:

pkg_config_path路径修改

七 诗词欣赏

燕歌行
[唐] [高适]

汉家烟尘在东北,汉将辞家破残贼。男儿本自重横行,

天子非常赐颜色。摐金伐鼓下榆关,旌旆逶迤碣石间。

校尉羽书飞瀚海,单于猎火照狼山。山川萧条极边土,

胡骑凭陵杂风雨。战士军前半死生,美人帐下犹歌舞。

大漠穷秋塞草腓,孤城落日斗兵稀。身当恩遇恒轻敌,

力尽关山未解围。铁衣远戍辛勤久,玉箸应啼别离后。

少妇城南欲断肠,征人蓟北空回首。边庭飘飖那可度,

绝域苍茫更何有。杀气三时作阵云,寒声一夜传刁斗。

相看白刃血纷纷,死节从来岂顾勋。君不见沙场征战苦,

至今犹忆李将军。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345