分类 技术志 下的文章

Hello,autoconf和automake

部门绝大多数的产品都运行在Sun的小型机上,底层的操作系统是Solaris。这两年公司开始主推刀片机(物美价廉^_^),不过刀片机上运行的也是Solaris 10 for x86版本。基于同种OS的前提下在Sparc和x86两种体系之间做移植比较简单,主要考虑字节序问题就可以了。不过对于可移植性的考虑不足还是让我们付出了较大的工作量。 在即将进行的新版本产品开发中,可移植性依旧没有被列入到必须要考虑实现的特性列表中,不过从未来产品演化和发展的角度考虑,现在就应该未雨绸缪,在可移植性方面多下工夫!

对于用C语言实现的服务器后端程序而言,GNU(/'gnu:/)的autoconfautomake(GNU autotools工具包的主要组成部分)显然已经成为这方面的事实标准!诸多知名开源应用和工具都采用autoconf和automake来生成相关的Build环境,"configure -> make -> make install"是采用这两个工具的应用软件的标准编译安装流程。不过autoconf和automake的学习门槛却不低,记得几年前曾尝试学习过这两个工具,结果被其纷繁芜杂的依赖关系搞得晕头转向,加之当时缺少些许耐心,也就放弃了(估计很多人都有与我类似的经历^_^)。

autoconf,顾名思义是用来做“自动配置”的,配置什么呢?配置软件代码包,目的是用来使软件包适应种类繁多的“Posix-like system"。automake,则是用来生成Makefile的原型-Makefile.in的。在大工程里,各种依赖关系纠结,如果采用纯手工编写Makefile方式,复杂度很高,而且Makefile的学习门槛也不低,想写出一份扩展性良好的通用Makefile也非易事。automake则试图将程序员从那些"扯不断理还乱"的依赖关系中解脱出来,你只需告诉automake你的工程里有哪些源文件、哪些目标文件、要连接什么库及各个文件的位置即可,automake帮你自动生成一份Makefile.in,Makefile.in则是configure脚本自动生成Makefile过程的输入。

重新学习使用autoconf和automake,最重要的就是迅速获得一个正反馈!这就好比学习一门新的计算机编程语言,如果能在3分钟之内迅速写出一个"Hello World"程序,编译运行成功,并在屏幕上看到有"Hello World"输出,那么有了这个正反馈后你八成才会有继续学习的动力,否则很多耐性不足的人就会止步于此(例如很多Java语言初学者就止步于配置JDK环境变量上)。

我将这个“正反馈”寄托在使用autoconf和automake来migrate一个几年前写的小工具库上了。这个工具库没比"hello world"复杂多少^_^,其大致组织结构是这样的:

lcut/
    – src/
        – apr_ring.h
        – lcut.h
        – lcut.c
        – example/
            – runtests.c

autoconf和automake虽然名字中都有一个"auto",但这并不意味着不需要任何“手工”操作。你起码需要手工完成两个文件:configure.in和Makefile.am。configure.in全工程只有一个,放置在工程最顶层目录下。这个文件包含一系列autoconf测试宏,用于告知autoconf生成的configure脚本需要在目标主机上做哪些check工作;至于Makefile.am,看文件后缀也可以知道这个文件是给automake使用的。Makefile.am可以有多个,简单来说只要你想在哪个目录下放置一个Makefile就需要在这个目录下编写一个对应的Makefile.am。Makefile.am的功用前面提到过,它要比Makefile简单许多,层次也更高,有些类似一种"声明式(declarative)语言”的脚本,你无须告知它怎么做,只需告知它要做的事情是什么样子的即可。

增加configure.in和Makefile.am后的源码包组织形式如下(新增文件用'+'标识):

lcut/
    + configure.in
    + Makefile.am
    – src/
        – apr_ring.h
        – lcut.h
        – lcut.c
        + Makefile.am
        – example/
            + Makefile.am
            – runtests.c

configure.in的生成是“半自动”的。GNU提供了一个名为autoscan的工具以帮助生成一个configure.in的模板。在lcut的目录下运行autoscan –verbose,我们得到一个名为configure.scan的文件,并得到autoscan的运行日志如下:
tonybai@tonybai-ubuntu-laptop:~/lcut$ autoscan –verbose
autoscan: srcdir = .
cfiles: src/apr_ring.h src/lcut.h src/lcut.c src/example/runtests.c
makefiles:
shfiles:

function:
malloc: src/lcut.c:132 src/lcut.c:203 src/lcut.c:241
memset: src/lcut.c:139 src/lcut.c:209 src/lcut.c:247

header:
stddef.h: src/apr_ring.h:40
stdlib.h: src/lcut.h:45
string.h: src/lcut.c:17

identifier:
size_t: src/lcut.h:214 src/lcut.c:78

program:
cc: src/apr_ring.h src/lcut.h src/lcut.c src/example/runtests.c

makevar:
librarie:

可以看出autoscan将lcut下的各类文件做了个全面扫描,根据其内部规则找出带有移植性问题的函数、头文件以及标识符等,并在configure.scan中放置了对应的autoconf测试宏,configure.scan的初始版本内容如下:
AC_PREREQ([2.65])
AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS])
AC_CONFIG_SRCDIR([src/apr_ring.h])
AC_CONFIG_HEADERS([config.h])

# Checks for programs.
AC_PROG_CC

# Checks for libraries.

# Checks for header files.
AC_CHECK_HEADERS([stddef.h stdlib.h string.h])

# Checks for typedefs, structures, and compiler characteristics.
AC_TYPE_SIZE_T

# Checks for library functions.
AC_FUNC_MALLOC
AC_CHECK_FUNCS([memset])

AC_OUTPUT

将configure.scan直接改名为configure.in(或configure.ac),我们就得到了configure.in的模板,剩下的工作就是根据需要修改该模板了。configure.in文件包含一组autoconf测试宏,关于这些测试宏的具体含义可参考GNU autoconf手册。这里仅针对某些宏做简要说明:
-> AC_PREREQ 这个宏是一个指示符,用于告知autoconf该configure.in需要的最低autoconf版本号是多少,上面例子中版本号是2.65,如果你用低于2.65版本的autoconf来处理configure.in,就会提示版本太低。
-> AC_INIT和AC_OUTPUT这两个宏标识着configure.in主体(body)内容的开始和结束,两个宏都可以接收一些options(参见manual)。
-> AC_CONFIG_SRCDIR 则是放置在configure脚本中的一个safety检测,用于检查特定文件的存在性,以确保软件包本身结构的正确性。
-> AC_CONFIG_HEADERS 指示需要输出config.h,config.h是configure过程输出的结果之一,其内容为一组#define directive,你的源件包代码可以包含config.h(在所有其他header files的前面)并使用这些directive来编写一些具有良好移植性的代码。

上述configure.in模板不完全能满足我们的要求,所以我们需要对configure.in内容进行修改,修改后的configure.in如下:

AC_PREREQ([2.65])
AC_INIT([lcut], [0.1], [bigwhite.cn@gmail.com])
AM_INIT_AUTOMAKE([foreign -Wall -Werror])
AC_CONFIG_SRCDIR([src/apr_ring.h])
AC_CONFIG_HEADERS([src/config.h])
AC_CONFIG_FILES([Makefile src/Makefile src/example/Makefile])

# Checks for programs.
AC_PROG_CC

# Checks for libraries.
AC_PROG_RANLIB

# Checks for header files.
AC_CHECK_HEADERS([stddef.h stdlib.h string.h])

# Checks for typedefs, structures, and compiler characteristics.
AC_TYPE_SIZE_T

# Checks for library functions.
AC_FUNC_MALLOC
AC_CHECK_FUNCS([memset])

AC_OUTPUT

以下对修改的地方做一些简要说明:
-> AM_INIT_AUTOMAKE 初始化automake并告知automake打开所有警告,另外将警告视为错误,注意:这里的-Wall -Werror虽和GCC的某些options名字相同,但这只是巧合,这里的-Wall -Werror并不是给gcc传递-Wall和-Werror选项,这两个选项是针对automake本身的。foreign选项则是告知automake此软件包非GNU软件包,不必严格遵循GNU软件包的标准,比如软件包必须包含ChangeLog、AUTHORS等文件。
-> AC_CONFIG_FILES 则是告知需要生成并输出相关文件,上面的AC_CONFIG_FILES([Makefile src/Makefile src/example/Makefile])与 AC_OUTPUT([Makefile src/Makefile src/example/Makefile])是等价的。
-> AC_PROG_RANLIB 在需要生成.a静态链接库的软件包中都要加上这个宏。

configure.in告一段落,下一个需要手工编辑的是Makefile.am。在这个lcut的例子里,我们需要三个Makefile.am(如上面的规划所描述)。 顶层Makefile.am内容如下:
SUBDIRS = src src/example

src/Makefile.am内容如下:
lib_LIBRARIES = liblcut.a
liblcut_a_SOURCES = lcut.c
include_HEADERS = apr_ring.h lcut.h

src/example/Makefile.am内容如下:
noinst_PROGRAMS = runtests
runtests_SOURCES = runtests.c
runtests_LDADD = $(top_srcdir)/src/liblcut.a

这里的Makefile.am相对比较简单,其语法格式在automake manual里有详尽说明,这里不重述。值得一提的是noinst_XX,对于不需要安装(install)的可执行程序、静态库等,都使用noinst_XX作为前缀描述。另外top_srcdir是内置的全局变量,可直接使用。

configure.in和Makefile.am都已经具备,只欠“东风”了,下面的Makefile生成操作都是“auto”的了。

首先在源码包顶层目录下使用autoheader生成config.h.in,在例子里我们的config.h.in放置在src下了;
然后同样在源码包顶层目录下运行aclocal,aclocal将根据configure.in生成aclocal.m4;
最后运行autoconf,configure脚本被自动生成; 运行automake,我们得到所有Makefile.in。

Build环境就此初步搭建完毕。运行"configure -> make -> make install",我们顺利的得到了安装后的库,example下的runtests程序运行也OK。以上脚本在Ubuntu 10.04和Solaris 10 for x86下均测试通过。另外Ubuntu linux默认自带了全部autotools工具,这让我的学习过程变得更加轻便顺畅了!

我们再按照顺序回顾一下使用autoconf和automake的步骤:
-> 策划你的项目目录结构,将编写好的第一版源码文件放到各个位置上;
-> 在顶层目录下运行autoscan获得configure.in模板 (configure.scan->configure.in)
-> 手工编辑configure.in,满足个性化需要
-> 手工建立并编辑各个层次的Makefile.am
-> 在顶层目录下运行autoheader,生成相应config.h.in
-> 在顶层目录下运行aclocal,生成aclocal.m4
-> 在顶层目录下运行autoconf,生成configure
-> 在顶层目录下运行automake,生成各级Makefile.in

autoconf和automake很是强大,以上也仅仅算是学到了这两个工具的一些皮毛。面对更加复杂的软件包,学习和实践还在继续!

做好个人代码备份与版本管理

今天下午花了一个小时分别和两位同事做了一些代码讨论,这两位同事正在编写的代码都具有一定的试验性质(暂不能进入项目代码库)。这里不谈代码如何如何,而是就我发现的一个问题谈谈我的看法。

问题其实也很简单:那就是两位同事“不约而同”的都没有对这类试验性质的代码进行很好的备份和版本管理

也许你看到这里会觉得这个芝麻粒儿大的问题不值得一提。没错,可能很多人都不以为然,不过有过以下经历的朋友们也许会与我产生共鸣:
- 主机掉电,磁盘损坏,无法恢复数据,绞尽脑汁辛苦编写的数百上千行代码“付之一炬”,一切待重来;
- 一个rm误操作使你和积累了多天的代码说了"永别",剩下的只有眼泪;
- 突然脑子中冒出一个想法,觉得应该如此重新设计并修改已有代码,加班+熬夜修改了数百行代码,横跨几个甚至是十几个文件,最终发现只是自己一时冲动,测试证明还是之前的设计是正确的。又加班+熬夜将代码恢复到修改前的模样。
- 前一天晚上新增了几百行代码、删除了若干行,修改了十几个文件,第二天醒来再打开这些代码,感觉有些陌生,记不清到底加了哪些代码,删了哪些代码,甚至为什么增加和删除也许都忘记了。

不知道上面的情景你亲历过几个?不管你是否亲历过,我想说都是:做好你的所有代码的备份和版本管理,哪怕这些代码仅仅是为自己所用,哪怕仅仅是用来做试验的,它们毕竟凝结了你的智慧和汗水。

前两种情况告知你应该做好代码的备份工作,只做本地备份还不够,还要进行多点备份。
第三种情况则提醒你做好版本管理,便于进行代码版本回溯。
第四种情况也是做好版本管理的一个好处:追踪你的修改记录以及回顾你的思维旅程(通过svn commit log)。

最好的方式就是你通过公司架设的版本控制Server管理你的代码,这样在版本管理的同时,代码也作了远程备份。如果是个人的非商业代码,你也可以尝试通过像Google Code这样的服务来管理和备份你的代码。如果你有条件的话,也完全可以在家里的另外一台机器上架设版本控制服务器,毕竟现在的硬件趋于白菜价。另外像Git这样的分布式版本控制工具既可以帮助你在本地做好代码版本管理,也方便你将代码与公司版本控制服务器之间做同步。

作为脑力劳动者,任何从你的大脑里流出的东西可能都是有价值的,最好都能像代码一样做好备份和版本管理。不以善小而不为,何况这是对你自己有益的事儿呢!

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats