百分百源码网-让建站变得如此简单! 登录 注册 签到领金币!

主页 | 如何升级VIP | TAG标签

当前位置: 主页>网站教程>服务器> Gnu AutoMake/Autoconf完成编译配置详解
分享文章到:

Gnu AutoMake/Autoconf完成编译配置详解

发布时间:01/15 来源: 浏览: 关键词:
下面为各位总结一关于大型项目使用Gnu AutoMake/Autoconf完成编译配置的一篇完整的教程,希望此教程能够为各位带来帮助。

使用过开源C/C++项目的同学们都知道,标准的编译过程已经变成了简单的三部曲:configure/make/make install, 使用起来很方便,不像平时自己写代码,要手写一堆复杂的Makefile,而且换个编译环境,Makefile还需要修改(Eclipse也是这样)。

这么好的东东当然要拿来用了,但GNU的Autotool系列博大精深,工具数量又多,涉及的语言也多,要是自己从头看到尾,黄花菜都凉了,项目估计早就结束了;上网搜样例倒是有一大堆,但都是“hello world”的样例,离真正完成大型项目的目标还差得远。

Autotools使用流程

其实我们在大型项目中看到的Makefile,Makefile.in,configure基本都不是手写了,大部分都是由Autotool根据环境自动生成的。使用Autotools工具主要两部分:

**(1)按照顺序调用各个工具**
**(2)修改configure.ac,m4,Makefile.am三个文件***

流程图如下:

autotools

具体操作流程如下:

(1)源码根目录调用autoscan脚本,生成configure.scan文件,然后将此文件重命名为configure.ac(或configure.in,早期使用.in后缀)
(2)修改【configure.ac】,利用autoconf提供的各种M4宏,配置项目需要的各种自动化探测项目
(3)编写【自定义宏】,建议每个宏一个单独的*.m4文件;
(4)调用aclocal收集configure.ac中用到的各种非Autoconf的宏,包括自定义宏;
(5)调用autoheader,扫描configure.ac(configure.in)、acconfig.h(如果存在),生成config.h.in宏定义文件,里面主要是根据configure.ac中某些特定宏(如AC_DEFINE)生成的#define和#undefine宏,configure在将根据实际的探测结果决定这些宏是否定义(具体见后面例子)。
(6)按照automake规定的规则和项目的目录结构,编写一个或多个【Makefile.am】(Makefile.am数目和存放位置和源码目录结构相关),Makefile.am主要写的就是编译的目标及其源码组成。
(7)调用automake,将每个Makefile.am转化成Makefile.in,同时生成满足GNU编码规范的一系列文件(带-a选项自动添加缺少的文件,但有几个仍需要自己添加,在执行automake前需执行touch NEWS README AUTHORS ChangeLog)。如果configure.ac配置了使用libtool(定义了AC_PROG_LIBTOOL宏(老版本)或LT_INIT宏),需要在此步骤前先在项目根目录执行libtoolize --automake --copy --force,以生成ltmain.sh,供automake和config.status调用。
(8)调用autoconf,利用M4解析configure.ac,生成shell脚本configure。以上几步完成后,开发者的工作就算完成了,后面的定制就由开源软件的用户根据需要给configure输入不同的参数来完成。
(9)用户调用configure,生成Makefile,然后make && make install。

Gnu Build System


Autoconf 解决了一个重要的问题-发现系统构建和运行时的准确的信息,但是这仅仅只是在开发可移植软件过程中一小部分问题。为了这个目的,Gnu 工程开发了一套集成工具集来完成这个任务:Gnu Build System。这套工具中最重要的几个工具:Autoconf,Automake,Libtool。下面我们来看下这几个工具究竟是做什么的。

Automake

最开始make工具的存在意味着makefile几乎是提供自动构建规则的唯一的软件,我们通过编写Makefile来指定编译构建规则,但是很快遇到了很多限制,它缺乏自动依赖、递归构建、可靠的时间戳(比如网络文件系统)等其他方面的支持。这意味着开发者不得不痛苦的为每一个project来提供构建。工程的可移植性是非常重要的,而且主要归功于make工具在许多系统的均可以运行。这一切都是通过手动来实现不同的目标系统的依赖。尽管开始使用起Autoconf起,我们同样需要在Makefile.in中加入一些重复的代码来识别@CC@,@CFLAG@,并通过configure来做一些替换工作。正是这些混乱繁琐的步骤,Automake出现了。
Automake 允许我们在Makefile.am的文件中指定构建,通过一些比Makefile更加简单高校的语法来处理,然后通过Autoconf来读取Makefile.am来自动生成Makefile.in。比如:Makefile.am 来生成并安装一个“Hello World”程序,大概样子如下:

bin_PROGRAMS = hello
hello_SOURCES = hello.c

产生的Makefile.in(大约400行)会自动支持所有的标准目标平台,通过Autoconf提供了自动替换、自动解决依赖,虚拟路径(VPATH)建立等等。make 来产生 hello 程序,然后通过make install 来将这个程序安装到/usr/local/bin(或者是通过configure prefix来指定的路径)

这直接带来的使Automake可以构建尤其是大型的目标,但是对于对于小程序也带了许多方便和可移植性,当然好处不仅仅使这些。

Gnulib

Gnu 软件在运行不同类型的系统上非常有名,即使我们的只要目标使编写运行在Gnu 系统上的软件,但是还会有很多的使用者和开发者把我们的软件移植到他们正在使用的系统上。

Gnulib 是GNU 代码的核心,可以在免费软件包之间贡献,它的贡献是在源码级别的,而不是作为一个库,需要的时候来连接使用的。这就需要你需要把Gnulib拷贝到你的源码树中,而且没有发布的tar包,开发者只能从仓库中获取,源码文件在线可以拿到,并且遵循GNU GPL或者GNU LGPL协议。
Gnu 模块主要包含C源码,并且通过一系列的宏(Macro)来配置源代码。比如,Gnulib中 stdbool 模块实现了stdbool.h头文件中的近乎C99标准的功能,甚至一些主机上并不包含stdbool.h头文件,这个模块包含了替换的头文件,通过Autoconf宏还组织相关老式系统上的文件的替换。

Libtool

通常,我们想构建的并不仅仅使程序,还有库,这样一些程序可以依赖这些库,减少一些劳动。理想情况下,我们更希望这些库可以被多个程序贡献,而且不需要在磁盘之间复制,并且这些库可以独立的升级。然而这也带来了一些问题,每个系统都有自己的不兼容的工具集,编译标识,模数等等。幸运的使,Gnu 提供了解决方案:LIbtool

Libtool 为我们解决了共享库之间构建的需求,而且看来只有这唯一的一种途径。它解决了很多头疼的问题,比如共享库中不同变量前缀如何遵循Make规则,没有被超级管理员安装之前如何被链接、一致的版本系统,尽管Libtool可以想Autoconf一行,可以在没有Automake的情况下独立使用,但是它以最简单的方式和Automake交互,换句话说,Libtool通常在共享库需要的时候自动加载,我们不需要关注它的语法。

下面一节,我们会通过Autoconf手册中的amhello例子,来具体学习如何编写这些配置文件。

 

1、configure


由Autoconf创建的配置脚本通常写作configure,运行的时候,configure 创建几个文件,用适当的值替换配置中的参数,这些文件分别是:

 

 代码如下

(1) 一个或者多个Makefile文件,通常每个源码包及其子目录中都会包含一个。
(2) 一个可选、可配置的的C头文件,通常包含#define
(3) 一个config.status的文件,标识配置的状态。运行时,会重新创建上述几个文件
(4) 一个通常写为config.cache的shell脚本,(在调用configure --config-cache时生成),它保存了运行configure中多项测试结果(在configure中会使用test来检测各种配置环境)。
(5) 一个config.log文件,包含了多项编译器的log信息,来帮助调试configure时候会出错。

 

通过Autoconf来创建configure文件,我们需要编写一个辅助Autoconf运行的输入文件configure.ac(这个大家应该常见),然后再运行autoconf。如果我们需要编写自己的自定义的测试特征(自定义宏)来补充Autoconf,我们应该写一个文件aclocal.m4acsite.m4。如果我们使用了c头文件来包含这些#define的宏定义的话,我们还需要运行autoheader,然后就会生成一个config.h的头文件。在下面的例子中我们会比较明显的感受到,这个头文件基本包含在各个源码文件中供我们直接使用宏。


2、configure的运行过程

 
下面图表显示了配置文件使如何产生的,(图示中后缀为*的表示运行程序,可选文件使用闭合的[]),autoconfautoheader同样需要读取Autoconf的宏文件(通过阅读autoconf.m4)。

 

Autoconf1Autoconf1

 

如果我们使用了Automake,会有下面的过程

 

Autoconf2Autoconf2

 

下述文件是软件包配置过程中用到的文件(简介中提到的几个文件)
Autoconf3Autoconf3

 

看到这里我也晕了,文件太多了吧,但是这些并不都是我们写的,如果每一个都是手写的话不但浪费时间,而且很容易出错,我们看到上述的过程中,很多的文件我们都可以通过Autoconf,Automake,configure来生成,直接得到我们需要的Makefile文件。我们需要编写一个一般有:configure.ac,makfile.am,和自定义宏m4 。按照顺序,下一节,我们从configure.ac来说起。

自动生成configure.ac


1、configure.ac的标准结构


学习编写之前,我们首先了解下configure.ac中都有哪些内容吧。
在configure中的Autoconf宏的顺序并不是十分重要的,个别的例外(Autoconf中定义了大量的宏操作,比如定义、检查、输出等)。每个configure.ac开头必须包含一个称作AC_INIT的宏,并且末尾必须包含一个AC_OUTPUT的宏。另外,对于一些被其他宏依赖的宏,必须出现在前面先被调用,因为这些宏需要检查一些变量的值来确定接下来该怎么做。而且,如果顺序不正确的话在configure的过程中会有警告。

当然,为了保持书写过程中的一致性,这里有一个建议的Autoconf宏的顺序。通常来说,出现的后面的宏一般都可以依赖前面出现的宏。比如说,库函数会受到一些类型和库影响。


下面是一个例子:


AC_INIT(package, version, bug-report-address )
information on the package
checks for programs
checks for libraries
checks for header files
checks for types
checks for structures
checks for compiler characteristics
checks for library functions
checks for system services
AC_CONFIG_FILES([file...])
AC_OUTPUT
2、使用autosacn来创建configure.ac
autoscan程序可以帮助我们创建或者维护一个软件包中configure.ac的文件。autoscan自动扫描检查源文件根目录下的目录树(如果指定目录)或者是当前的目录(未指定)。autoscan通过搜索元文件查找常见的移植性文件,并且创建configure.scan文件,这个文件就是configure.ac的前身。并且检查已有的configure.ac是否完整。

当然,在使用了autoscan创建configure.scan文件后,我们在重命名为configure.ac前最好手动检查下,有时候会需要一个调整。autoscan偶尔也会输出一些错误顺序的宏,然后autoconf就会发出警告,这时候我们需要手动调整下顺序。如果我们想在源码包中使用配置头文件(通常为config.h),我们必须添加一个AC_CONFIG_HEADER宏,我们也可能会需要增加或修改一些#if条件定义,来帮助Autoconf完成工作。
在使用autoscan来生成configure.ac的时候,通常简单的增加一些建议的条件,在autoscan.log中包含了每个宏为什么是必须的。

autoscan在源码包中发现特殊的标识时会使用多个数据文件来决定输出哪些宏(macro),这些数据文件的格式使一致的:每行包含一个标识,一个

或者多个空格,如果遇到一个标识就输出相应的宏,行首#表示注释。

3、使用ifnames来输出条件
ifnames可以帮助我们为一些源码包编写configure.ac,它可以打印出这个包中已有的正在使用的C 预处理器条件,它可以帮助填写一些有autoscan生成的configure.ac的不足。ifnames扫描所有的c源文件,并按顺序以#if,#elif,#ifdef,#ifndef的形式输出。

4、使用autoconf创建configure


使用autoconf程序执行(不带参数)autoconf会使用m4宏处理器和Autoconf宏来处理configure.ac,如果加参数执行的话,autoconf会读取参数指定的文件而不是configure.ac,如果参数是 - 的话,会从标准输入中读取并输出到标准输出。

Autoconf宏定义在多个文件中,有些文件使Autoconf生成的,由autoconf优先读取,然后autoconf再检查acsite.m4文件查看包含Autoconf的其他文件。

5、使用autoreconf来更新configure脚本文件


为GNU Build System安装不同的组件却是很烦人,比如运行autopoint来安装Gettext,在各个目录下automake来生成Makefile.in等等,但是有时候这些都是必须的,如果因为一些改动比如说-更新configure.ac了,还得重新再来一次。

autoreconf可以看做是autoconf、autoheader、acloacl、automake、libtoolize、autopoint的组合体,而且会以合适的顺序来执行。

貌似到这里还是不知道configure怎么来的,基础概念及过程清楚了,我们下面一节就通过automake自带的例子amhello-1.0.tar.gz,来看看怎么实现自动化编译配置的。

Gnu Build System使用案例Hello World!

前言
前面???锣滤盗怂钠??菜贫际墙樯苄缘模?部床怀錾睹诺溃?暇?nu Build System涉及的东西太多了,更多的知识也只能通过阅读开发文档了。现在我们就通过一个具体的例子来看看,是不是有些期待呢?

这个例子来自于automake安装时自带的示例程序,在 /usr/share/doc/automake/amhello-1.0.tar.gz,使用linux系统的童鞋可以找找。我们也可以完全自己来创建。

准备文件

这个hello world 例子非常简单,只需要写五个文件:

首先创建源码目录cd && makdir amhello,作为源码根目录。
进入cd amhello, 创建源码目录mkdir src
此时目录结构为 ~/amhello/src ,在src目录下创建第一个文件main.c,源码如下


#include <config.h>
#include <stdio.h>
int
main (void)
{
 puts ("Hello World!");
 puts ("This is " PACKAGE_STRING ".");
 return 0;
}

在amhello目录下创建项目描述文件README,内容随意了….

Makefile.am和 src/Makefile.am包含了Automake在这两个目录的说明,所以在amhello目录下创建Makefile.am,在src/下创建Makefile.am.内容分别如下:

amhello/src/Makefile.am


bin_PROGRAMS = hello
hello_SOURCES = main.c
amhello/Makefile.am


SUBDIRS = src
dist_doc_DATA = README

最后创建configure.ac,来辅助Autoconf生成configure脚本。内容如下:

 

AC_INIT([amhello], [1.0], [bug-automake@gnu.org])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AC_PROG_CC
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_FILES([
Makefile
src/Makefile
])
AC_OUTPUT

执行编译安装过程

上述五个文件准备好之后,我们在amhello目录下执行 autoreconf --install (前面文章讲过autoreconf的作用,相当于aclocal,autoconf等程序的作用)。会看到如下结果:


~/amhello % autoreconf --install
configure.ac: installing ’./install-sh’
configure.ac: installing ’./missing’
configure.ac: installing ’./compile’
src/Makefile.am: installing ’./depcomp

这就表示构建完成。

除了看到在输出中三个脚本文件,我们可以看到autoreconf创建了另外四个文件:configure,config.h,Makefile.in和src/Maiefile.in,我们会发现有Makefile.am的目录下都会生成Makefile.in。后三个文件是configure脚本生成适应系统config.h,Makefile,src/Makefile的模版。

 

checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for gawk... no
checking for mawk... mawk
checking whether make sets $(MAKE)... yes
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 for style of include used by make... GNU
checking dependency style of gcc... gcc3
configure: creating ./config.status
config.status: creating Makefile
config.status: creating src/Makefile
config.status: creating config.h
config.status: executing depfiles commands
然后我们就会看到生成了Makefile,src/Makefile和config.h,现在我们就可以运行我们期望生成的目标了。

在amhello目录下执行make,然后会在src目录下按照配置的目标生成src/hello,执行src/hello

Hello World!
This is amhello 1.0.


不过请大家注意,只有在Gnu Build System不存在的情况下我们才需要执行autoreconf,原因前一篇提到了,autoreconf是一组调用了autoconf,automake和其他一系列命令的脚本。我们现在需要明白的是这些文件分别是怎么产生的。autoconf是用来通过configure.ac生成configure的,而automake是用来通过Makefile.ams和configure.ac来生成Makefile.ins的。我们至少应该知道如何通过手动以正确的顺序来创建了吧。再给大家看一个图来帮助理解下

autotool-order

amhello’s 中configure.ac配置解释

我们回顾下configure.ac的内容:


AC_INIT([amhello], [1.0], [bug-automake@gnu.org])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AC_PROG_CC
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_FILES([
Makefile
src/Makefile
])
AC_OUTPUT

这个文件需要被autoconf(创建configure)和 automake(创建不同的Makefile.am s)。它包含了一些列的M4宏,这些会被扩展为shell代码然后最终组织到configure脚本中,关于这个文件具体语法Autoconf手册都有。
我们可以看到这个文件中的宏的前缀都为 AC_,这些都是Autoconf宏,关于每个宏的作用可以查询Autoconf手册。

AC_INIT

前两行

AC_INIT([amhello], [1.0], [bug-automake@gnu.org])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])

作为Autoconf 和 Automake 的初始化。AC_INIT 需要的参数分别为,AC_INIT([包名],[版本号],[报告bug的邮箱]),这个邮箱地址我们可以在执行./configure --help命令查看到。版本号和包名在打包的时候会用到,通过执行 make dist可以把源码中主要的文件打包成 包名-版本号.tar.gz 。

AC_INIT_AUTOMAKE

AM_INIT_AUTOMAKW 参数是一组关于automake执行的选项。 -Wall 和 -Werror 让 automake 打开所有的警告和报告错误。这里说道的警告比如说在Makefile.am中的可疑指令,这些指令和将怎么调用编译器没任何关系,即使是可以通过类似命名的支持的选项。对于我们新手来说,通过使用 -Wall -Werror 是对待包构建的一个非常安全的设置,我们并不想错过任何的问题。熟练之后,我们可以适当放宽,知道哪些是可以留下的。foreign选项通知Automake程序这个包的构建不遵循Gnu 标准。因为在Gnu 标准中必须有这几个文件比如说:Changelog,AUTHORS等等,和我们平常使用的github项目类似,一般要求有一个README.md的文件来描述项目。我们不希望 automake 报告缺失这些文件。

AC_PROG_CC

AC_PORG_CC这行最终的作用是让configure脚本来在系统中搜索 c 编译器,并且把C编译器定义为变量CC ,然后src/Makefile.in文件就可以通过Automake使用CCa 来生成执行程序hello,所以当 configure 通过 src/Makefile.in 创建了 src/Makefile 文件的时候,它就会把找到的C编译器定义为CC , 如果要求Automake通过CC创建Makefile.in,而 configure.ac中并没有定义CC ,它就会提示我们增加一条 AC_PROC_CC.

AC_CONFIG_HEADER

AC_CONGIF_HEADER([config.h]),这个宏主要是用来创建config.h,它会把configure.ac中的被其它宏定义的宏以#define的形式集中到config.h中。在我们这个例子中,AC_INIT宏已经定义了一些宏了。这里是执行./configure后的config.h的内容:

/* config.h.  Generated from config.h.in by configure.  */
/* config.h.in.  Generated from configure.ac by autoheader.  */

/* Name of package */
#define PACKAGE "amhello"

/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT "bug-automake@gnu.org"

/* Define to the full name of this package. */
#define PACKAGE_NAME "amhello"

/* Define to the full name and version of this package. */
#define PACKAGE_STRING "amhello 1"

/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "amhello"

/* Define to the home page for this package. */
#define PACKAGE_URL ""

/* Define to the version of this package. */
#define PACKAGE_VERSION "1"

/* Version number of package */
#define VERSION "1"
我们会发现,src/main.c中头文件中包含了config.h,所以这个时候main.c就可以直接使用PACKAGE_STRING。在实际项目中,config.h会非常大,几乎系统每个特性都会有一个#define定义。

AC_CONFIG_FILES

AC_CONFIG_FILES宏声明了configure 通过Makefile.ins 需要生成的一系Makefile文件。Automake 同样要扫描这个列表来查找需要处理的Makefile.am。(特别强调:一旦我们在工程中增加了新的目录,我们应该把该目录下的Makefile增加到这个列表中,否则的话,即使我们在这个目录下谢了Makefile.am,Automake也 不会处理)。

AC_OUTPUT

AC_OUTPUT 是一个结束命令,实际上是AC_CONFIG_HEADERS和AC_CONFIG_FILES命令完成的结束,并将这俩处理后产生的文件输出。
对于一个新的工程的话,我们最好是以这样一个简单的的configure.ac文件开始,然后逐渐增加测试需求。autoscan命令同样可以帮助我们增加一些需要的测试需求。手动的话也可以先执行autoscan,然后再修改,注意文件中不同测试宏的顺序。

amhello 中 Makefile.am 配置解释
我们现在看下src/Makefile.am


bin_PROGRAMS = hello
hello_SOURCES = main.c

Makefile.am的语法和Makefile文件的语法是一样的。automake处理Makefile.am的时候它就把Makefile.am完全拷贝到Makefile.in中(Makefile.in文件会在之后被configure处理为Makefile),但是会根据一些构建规则和变量改变一些变量的定义。通常Makefile.ams 只包含一组上述例子中的变量定义,但是他们同样可以包含一些变量和规则定义,这些会通过automake不加解释地传递进来。

bin_PROGRAMS

以_PROGRAMS为后缀的在变量列表中属于特殊的变量,它们是Makefile 最终需要生成的。我们示例中最后的科执行文件就是hello。从Automake角度看,将bin_PROGRAMS分为两部分,_PROGRAMS后缀结束的变量为主要变量,Automake识别其他的主要变量比如说 _SCRIPTS,_DATA,_LIBRARIES等等,分别对应不同的文件。
而bin_PROGRAMS中的bin部分使用来告诉automake最终编译生成的程序应该安装到 bindir .
在GNU Build System中使用了一系列变量来提供目标目录的位置,并且允许用户自定义这些路径的位置。任何这样的变量都可以用这种形式来指定不同的文件放到哪些位置。
比如:

bindir 用于安装由用户运行的可执行文件的目录。
datadir 用于安装只读的与结构无关的数据的目录。
includedir 用于安装C头文件的目录。

可以参见GNU编码标准中这些变量的定义。
而且,Automake同样知道在创建打包文件时需要将不同类型的文件以分布式的形式放开,而hello_SOURCES的副作用就是在执行make dist是main.c成为tarball的一部分。

现在我们再看下工程目录下的Makefile.am。

SUBDIRS = src
dist_doc_DATA = README
SUBDIRS

SUBDIR 是一个特殊的变量,列出了make在处理当前目录之前递归处理的目录。所以在这个例子中,先用make处理src/,然后再处理amhello下的文件。同样在make install 的时候先安装src/README,然后再安装 src/hello。

dist_doc_DATA

dist_doc_DATA = README,因为README将要被安装到docdir中,由于_DATA后的文件在dist打包的时候不会被创建进去,所以我们在_DATA前加了一个dist 前缀,这个README并不是必须的。比较重要的影响是在make install 的时候把README安装进去。

打赏

打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

百分百源码网 建议打赏1~10元,土豪随意,感谢您的阅读!

共有10人阅读,期待你的评论!发表评论
昵称: 网址: 验证码: 点击我更换图片
最新评论

本文标签

广告赞助

能出一分力是一分吧!

订阅获得更多模板

本文标签

广告赞助

订阅获得更多模板