经常使用Linux的开发者或运维人员可能对configure->make->makeinstall相当熟悉。实际上,这就是所谓的GNU构建系统,它使用脚本和make程序在特定平台上构建软件。这种方法已经成为一种习惯,被广泛使用。本文从用户和开发者的角度,详细解释了这种构建方式的细节,以及开发者如何使用autoconf和automake(autotools)等工具来创建兼容GNU构建系统的项目。为了简化便携构建的难度,早期有一套autotools工具来帮助程序员构建软件。我们熟悉的configure->make->makeinstall三部曲大部分都是基于autotools构建的。autotools是GNU程序的标准构建系统,所以我们实际上经常使用trilogy。有些程序虽然也是三部曲,但不是用autotools实现的。比如nginx的源码就是作者自己写的构建程序。从用户的角度,用户可以通过configure->make->makeinstall来安装基于源代码的软件。但是,大多数用户可能不知道这个过程是做什么的。配置脚本是由软件开发人员维护并分发给用户的shell脚本。这个脚本的作用是检测系统环境,最终目的是生成Makefile和config.h。make通过读取Makefile开始构建软件。而makeinstall可以将软件安装到需要安装的位置。如上图所示,开发者在分发源代码包时,除了源代码(.c.h...)外,还有很多用于支持软件构建的文件和工具。最重要的文件是Makefile.in和config.h.in。configure脚本执行成功后,会将每个*.in文件处理成对应的非*.in文件。大多数情况下,只生成Makefile和config.h,因为Makefile是make程序用来识别和构建软件的,而config.h中定义的宏帮助软件通过预编译改变自己的代码以适应某些目标平台。特殊性。有些软件在configure阶段还可以生成其他文件,这完全取决于软件本身。configure运行configure时,您将看到类似于以下的系统检查。这些检查的多少取决于软件本身的需要,即由软件开发人员定义和编写。检查BSD兼容安装.../usr/bin/install-ccheckingwhetherbuildenvironmentissane...yescheckingforathread-safemkdir-p.../bin/mkdir-pcheckingforgawk...gawkcheckingwhethermakesets$(MAKE)...yescheckingforgcc...gcccheckingforCcompilerdefaultoutputfilename...a.out...一般来说,configure主要检查当前目标平台的程序、库、头文件、函数等的兼容性。这些检查结果将作用于config.h和Makefile文件的生成。从而影响最终的编译。用户还可以通过configure配置参数,自定义软件需要包含或不包含的组件行为、安装路径等。这些参数分为5组,可以通过执行./configure--help查看,软件提供了哪些配置参数:*安装路径相关配置。最常见的是--prefix。*程序名称配置。例如--program-suffix可以用来为生成的程序添加后缀。*跨平台编译。不是很常用。*动态库静态库选项。用于控制是否生成某种类型的库文件。程序组件选项。用于配置程序是否将某个函数编译到程序中,一般为--with-xxx的形式。这可能是最常见的配置,由软件开发人员定义。(*表示这是几乎所有软件都支持的配置,因为默认情况下autotool生成的configure脚本都支持这些配置。)configure在执行过程中,除了生成Makefile外,还会生成包括但不包括在内的文件限于:配置。log日志文件config.cache被缓存,以提高下次configure的速度。生成config.status需要用-C指定。实际调用编译工具构建软件的shell脚本。如果软件是通过libtool构建的,也会生成一个libtool脚本。如何生成libtool脚本,请看开发者视角。configure经常会中途失败,这通常是因为当前平台没有构建软件所必需的依赖(库、函数、头文件、程序……)。此时,不要惊慌,仔细查看输出,并解决这些依赖关系。开发人员的视角除了编写软件本身的代码外,开发人员还负责生成构建软件所需的文件和工具。当我接触到autotools时,我发现尽管有了工具的帮助,这件事还是很复杂。对于C或C++程序员来说,构建跨平台应用程序在早期是相当繁琐的,对于没有经验的程序员来说甚至是困难的。因为构建可移植程序的必要前提是对各个平台有足够的了解,而这往往需要长时间的积累。Unix系统的分支复杂度非常高,不同的商业版本或开源版本或多或少会有不同。这些差异主要体现在:系统组件、系统调用。我们主要把Unix分为以下几类:IBM-AIXHP-UXApple-DARWINSolarisLinuxFreeBSD。Unix分支百科全书因此,对于开发者来说,不得不自己编写脚本进行构建,这往往需要极其扎实的shell能力和平台熟悉度。另一种选择是部分依赖工具。autoconf和automake就是这样的工具。为了让autoreconf生成configure脚本和Makefile.in文件,开发者需要创建和维护一个configure.ac文件(早期一般叫configure.in文件,虽然没有区别,但是强烈推荐使用.ac,因为.in文件往往意味着configure脚本将其识别为模板文件并生成直接参与最终构建的文件,configure.in在命名上有歧义),以及一系列的Makefile。是。autoreconf程序可以按合理顺序自动调用autoconfautomakeaclocal等程序。configure.acconfigure.ac用于生成配置脚本。autoconf工具用于执行此操作。下面是configure.ac的例子:AC_PREREQ([2.63])AC_INIT([st],[1.0],[zhoupingtkbjb@163.com])AC_CONFIG_SRCDIR([src/main.c])AC_CONFIG_HEADERS([src/config.h])AM_INIT_AUTOMAKE([foreign])#Checksforprograms.AC_PROG_CCAC_PROG_LIBTOOL#Checksforlibraries.#Checksforheaderfiles.#Checksfortypedefs,structures,andcompilercharacteristics.#Checksforlibraryfunctions.AC_CONFIG_FILES([Makefilesrc/Makefilesrc/a/Makefilesrc/b/Makefile])AC_OUTPUTAC_全部开头看起来像是函数调用的代码,其实是一些叫做“宏”的调用。这里的宏类似于C中的宏概念,会被替换和扩展。m4是一个经典的宏工具,而autoconf是建立在m4之上的。可以理解为autoconf预先实现了大量用于检测系统可移植性的宏。这些宏是大量扩展后的shell脚本。因此,编写configure.ac需要熟练掌握这些宏并合理调用。有时,您甚至可以自己实现自己的宏。autoscan和configure.scan可以通过调用autoscan命令得到一个初始化的configure.scan文件,然后重命名为configure.ac,然后在此基础上编辑configure.ac。autoscan扫描源代码并生成一些常用的宏调用、输入声明和输出声明。autoscan虽然很方便,但是没有人能完全在build之前写好代码,所以通常使用autoscan来初始化configure.ac。autoheader和config.hautoheader命令扫描configure.ac的内容并确定需要如何生成config.h.in。每当configure.ac发生变化时,可以通过再次执行autoheader来更新config.h.in。在configure.ac中通过AC_CONFIG_HEADERS([config.h])告诉autoheader应该生成config.h.in的路径。在实际编译阶段,生成的编译命令会加上-DHAVE_CONFIG_H来定义宏,所以在代码中,我们可以通过下面的代码放心的引用config.h。/bin/sh../../libtool--tag=CC--mode=compilegcc-DHAVE_CONFIG_H...#ifdefHAVE_CONFIG_H#include
