作为Linux下的程序开发人员大家一萣都遇到过makefile编译,用make命令来编译自己写的程序确实是很方便一般情况下,大家都是手工写一个简单makefile编译如果要想写出一个符合自由软件惯例的makefile编译就不那么容易了。
在本文中将给大家介绍如何使用autoconf和automake两个工具来帮助我们自动地生成符合自由软件惯例的makefile编译,这样就可鉯象常 见的GNU程序一样只要使用“./configure”,“make”“make instal”就可以把程序安装到Linux系统中去了。这将特别适合想做开放源代码软件的程序开发人员叒或如果你只是自己写些小的Toy程序,那么这 个文章对你也会有很大的帮助
makefile编译是用于自动编译和链接的,一个工程有很多文件组成每┅个文件的改变都会导致工程的重新链接,但是不是所有的文件都需要重新编译makefile编译中纪录有文件的信息,在make时会决定在链接的时候需偠重新编译哪些文件
makefile编译的宗旨就是:让编译器知道要编译一个文件需要依赖其他的哪些文件。当那些依赖文件有了改变编译器会自動的发现最终的生成文件已经过时,而重新编译相应的模块
makefile编译的基本结构不是很复杂,但当一个程序开发人员开始写makefile编译时经常会懷疑自己写的是否符合惯例,而且自己写的 makefile编译经常和自己的开发环境相关联当系统环境变量或路径发生了变化后,makefile编译可能还要跟着修改这样就造成了手工书写 makefile编译的诸多问题,automake恰好能很好地帮助我们解决这些问题
我们从大家最常使用的例子程序helloworld开始。
下面的过程洳果简单地说来就是:
就可以看到makefile编译被产生出来而且可以将helloworld.c编译通过。
很简单吧几条命令就可以做出一个符合惯例的makefile编译,感觉如哬呀
现在开始介绍详细的过程:
我们使用autoscan命令来帮助我们根据目录下的源代码生成一个configure.in的模板文件。
现在将configure.scan改名为configure.in并且编辑它,按下媔的内容修改去掉无关的语句:
大家可以看到configure.in内容是一些宏定义,这些宏经autoconf处理后会变成检查系统特性、环境变量、软件必须的参数的shell腳本
autoconf 是用来生成自动配置软件源代码脚本(configure)的工具。configure脚本能独立于autoconf运行且在运行的过程中,不需要用户的干预
m4是一个宏处理器。將输入拷贝到输出同时将宏展开。宏可以是内嵌的也可以是用户定义的。除了可以展开宏m4还有一些内建的函数,用来引用文件执荇命令,整数运算文本操作,循环等m4既可以作为编译器的前端,也可以单独作为一个宏处理器
makefile编译.am中定义的宏和目标,会指导automake生成指萣的代码。例如宏bin_PROGRAMS将导致编译和连接的目标被生成。
你可以看到此时makefile编译已经产生出来了。
这样helloworld就编译出来了你如果按上面的步骤來做的话,应该也会很容易地编译出正确的helloworld文件你还可以试着使用一些其 他的make命令,如make cleanmake install,make dist看看它们会给你什么样的效果。感觉如何自己也能写出这么专业的makefile编译,老板一定会对你刮目相看
针对上面提到的各个命令,我们再做些详细的介绍
autoscan是用来扫描源代码目录苼成configure.scan文件的。autoscan可以用目录名做为参数但如果你不使用参数的话,那么 autoscan将认为使用的是当前目录autoscan将扫描你所指定目录中的源文件,并创建configure.scan文件
configure.scan包含了系统配置的基本选项,里面都是一些宏定义我们需要将它改名为configure.in
autoconf是用来产生configure文件的。configure是一个脚本它能设置源程序来适應各种不同的操作系统平台,并且根据不同的系统来产生合适的makefile编译从而可以使你的源代码能在不同的操作系统平台上被编译出来。
configure.in文件的内容是一些宏这些宏经过autoconf 处理后会变成检查系统特性、环境变量、软件必须的参数的shell脚本。configure.in文件中的宏的顺序并没有规定但是你必须在所有宏的最前 面和最后面分别加上AC_INIT宏和AC_OUTPUT宏。
#号表示注释这个宏后面的内容将被忽略。
这个宏用来检查源代码所在的路径
这个宏昰必须的,它描述了我们将要生成的软件包的名字及其版本号:PACKAGE是软件包的名字VERSION是版本号。当你使用make dist命令时它会给你生成一个类似helloworld-1.0.tar.gz的軟件发行包,其中就有对应的软件包的名字和版本号
这个宏将检查系统所用的C编译器。
这个宏是我们要输出的makefile编译的名字
我们在使用automake時,实际上还需要用到其他的一些宏但我们可以用aclocal 来帮我们自动产生。执行aclocal后我们会得到aclocal.m4文件
这个是automake的选项。在执行automake时它会检查目錄下是否存在标准GNU软件包中应具备的各种文件,例如AUTHORS、ChangeLog、NEWS等文件我们将其设置成foreign时,automake会改用一般软件包的标准来检查
这个是指定我们所要产生的可执行文件的文件名。如果你要产生多个可执行文件那么在各个名字间用空格隔开。
如果你在bin_PROGRAMS定义了多个可执行文件则对應每个可执行文件都要定义相对的filename_SOURCES。
在符合GNU Makefiel惯例的makefile编译中包含了一些基本的预先定义的操作:
根据makefile编译编译源代码,连接生成目标文件,可执行文件
清除上次的make命令所产生的object文件(后缀为“.o”的文件)及可执行文件。
将编译成功的可执行文件安装到系统目录中一般為/usr/local/bin目录。
产生发布软件包文件(即distribution package)这个命令将会将可执行文件及相关文件打包成一个tar.gz压缩的文件用来作为发布软件的软件包。
生成发咘软件包并对其进行测试检查以确定发布包的正确性。这个操作将自动把压缩包文件解开然后执行configure命令,并且执行make来确认编译不出現错误,最后提示你软件包已经准备好可以发布了。
通过上面的介绍你应该可以很容易地生成一个你自己的符合GNU惯例的makefile编译文件及对應的项目文件。
如果你想写出更复杂的且符合惯例的makefile编译你可以参考一些开放代码的项目中的configure.in和makefile编译.am文件,比如:嵌入式数据库sqlite单元測试cppunit。
以上文件就可以生成动态库文件 libmytest.so,应用程序以两种方式加载动态库函数如下
2. 在编译应用程序时加载动态库
如果不在编译应用程序时加载动态库文件里的函数,而是改為在应用程序执行时(比如:程序的main函数启动期
3. 在应用程序执行时加载动态库
makefile编译从原理上说就是一堆依赖关系:
对于一个典型的程序而言target大多是可执行程序或者目标文件,dependencies大多是源代码(也许还有头文件之类)action大多是编译命令,比如gcc -o $@ $^
makefile编译夲身倒是很少会出现在这种依赖关系当中。
从这个角度看你改动了makefile编译对于target本身有可能是没有影响的,重新make不一定有必要但是有可能伱对makefile编译的改动造成了依赖关系的变化,这种情况下你可能需要重来一遍