autotools
原文地址:https://gitee.com/simpost/autotools_tutorial
一、简介
1.1 介绍
我们知道在Linux下编译一个比较大型的项目,我们可以通过Makefile的方式来完成。但是,Makefile拥有复杂的语法结构,甚至让人难以领会,当我们项目非常大的时候,维护Makefile会成为一件非常头疼的事。Autotools工具就是专门用来生成Makefile的,这个工具让让我们很大程度上降低了开发的难道。
Autotools并不是一个工具,而是一系列工具:
autoscan
aclocal
autoconf
autoheader
automake
这一系列工具看着复杂,但我们只要记住:最终目标是生成Makefile。
一般情况下系统会默认安装这一系列工具,若未安装,在CentOS中可以使用下面命令安装:
sudo yum install automake
有关Autotools的详细完整的介绍,详见官网:https://www.lrde.epita.fr/~adl/autotools.html
1.2 不同视角的程序构建
1.2.1 用户视角
一般过程是configure,make,make install三部曲。这种方式成为一种习惯,被广泛使用。
在上图中,开发者在分发源码包时,除了源代码中的头文件(.h)和程序源文件(.c),还有许多支持软件构建的文件和工具。最重要的就是Makefile.in和config.h。
configure脚本执行时,将为每一个.in文件处理成对应的非.in文件,即生成:Makefile,src/Makefile,config.h 。大部分情况下,只有Makefile和config.h。Makefile用于被make程序识别并构建软件,而config.h中定义的宏,有助于软件通过预编译来改变自身代码,来适应目标平台某些特殊性。
有些软件在configure阶段,还可以生成其他文件,这完全取决于软件本身。
1.2.2 开发者视角
开发者除了编写软件本身的代码外,还需要负责生成构建软件所需要的文件和工具。因此对于开发者而言,要么自己编写构建用的脚本,要么选择部分依赖工具。Autotools就是这样的工具。Autotools包括了autoconf和automake等命令。
autoreconf命令
为了生成configure脚本和Makefile.in等文件,开发者需要创建并维护一个configure.ac文件,以及一些列的Makefile.am。autoreconf程序能够自动按照合理的顺序调用autoconf、automake、aclocal程序
configure.ac文件
configure.ac用于生成configure脚本,autoconf工具用来完成这一步。下面是一个简单的configure.ac例子:
AC_PREREQ
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])
# Checks for programs.
AC_PROG_CC
AC_PROG_LIBTOOL
# Checks for libraries.
# Checks for header files.
# Checks for typedefs, structures, and compiler characteristics.
# Checks for library functions.
AC_CONFIG_FILES([Makefile
src/Makefile
src/a/Makefile
src/b/Makefile])
AC_OUTPUT
其中以AC_开头的类似函数调用一样的代码,实际上时被称为“宏”的调用。这里的宏,与C语言中的宏概念类似,会被替换展开。configure.ac文件的一般布局是:
AC_INIT
测试程序
测试函数库
测试头文件
测试类型定义
测试结构
测试编译器特性
测试库函数
测试系统调用
AC_OUTPUT
m4是一个经典的宏工具。autoconf正是构建在m4之上,可以理解为autoconf预先定义了大量的、用户检查系统可移植性的宏,这些宏在展开就是大量的shell脚本。所以编写configure.ac就需要对这些宏掌握熟练,并且合理调用。
autoscan和configure.scan
通过调用autoscan命令,得到一个初始化的configure.scan文件。然后重命名为configure.ac后,在此基础上编辑configure.ac。
autoscan会扫描源码,并生成一些通用的宏调用,输入的声明,以及输出的声明。尽管autoscan十分方便,但是没人能够在构建之前,就把源码完全写好。因此,autoscan通常用于初始化configure.ac,即生成configure.ac的雏形文件configure.scan。
autoheader和configure.h
autoheader命令扫描configure.ac文件,并确定如何生成config.h.in。每当configure.ac变化时,都可以通过执行autoheader更新config.h.in。
在configure.ac通过AC_CONFIG_HEADERS([config.h])告诉autoheader应当生成config.h.in的路径。config.h包含了大量的宏定义,其中包括软件包的名字等信息,程序可以直接使用这些宏。更重要的是,程序可以根据其中的对目标平台的可移植相关的宏,通过条件编译,动态的调整编译行为。
automake和Makefile.am
手工编写Makefile是一件相当繁琐的事情,并且随着项目的复杂程序变大,编写难度越来越大。automake工具应运而生。可以编辑Makefile.am文件,并依靠automake来生成Makefile.in。
aclocal
configure.ac实际是依靠宏展开来得到configure。因此,能否成功生成,取决于宏定义是否能够找打。
autoconf会从自身安装路径下寻找事先定义好的宏。然而对于像automake、libtool、gettex等第三方扩展宏,autoconf便无从知晓。
因此,aclocal将在configure.ac同一个目录下生成aclocal.m4,在扫描configure.ac过程中,将第三方扩展和开发者自己编写的宏定义复制进去。
如此一来,autoconf遇到不认识的宏时,就会从aclocal.m4中查找。
上述命令与不同文件之间的关系如下图所示:
二、流程与规则
2.1 Autotools运行流程
执行autoscan命令,扫描工作目录并生成configure.scan文件;
修改configure.scan为configure.ac文件,并修改配置内容;
执行aclocal命令,扫描configure.ac文件并生成aclocal.m4文件;
执行autoconf命令,将configure.ac文件中的宏展开,生成configure脚本;
执行autoheader命令,生成config.h.in文件;
创建Makefile.am文件,修改配置内容;
执行automake --add-missing命令,生成Makefile.in文件;
执行./configure命令,生成Makefile文件;
执行make命令,生成需要的库或可执行程序;
执行make install/uninstall进行安装和卸载;
执行make dist对软件进行打包工作。
若是开发过程中,修改了部分文件(configure.ac、各目录的Makefile.am等),可以使用简化的autoreconf命令,它将自动按照合理的顺序调用aclocal、autoconf、automake命令。
2.2 configure.ac标签说明
标签说明
AC_PREREQ
声明autoconf要求的版本号
AC_INIT
定义软件名称、版本号、联系方式
AM_INIT_AUTOMAKE
必须要的,参数为软件名称和版本号
AC_CONFIG_SRCDIR
用来侦测所指定的源文件是否存在,来确定源码目录的有效性
AC_CONFIG_HEADERS
用于生成config.h文件,以便autoheader命令使用
AC_PROG_CC
指定编译器,默认为CC
AC_CHECK_HEADERS
autoscan侦测到的头文件
AC_CONFIG_FILES
生成相应的Makefile文件,不同文件夹下的Makefile通过空格分隔
AC_OUTPUT
指定configure所要产生的文件,如果是makefile,configure会把它检查出来的结果带入makefile.in文件产生合适的makefile
2.3 Makefile.am解读
2.3.1 可执行文件类型
规则说明
bin_PROGRAMS
指定生成可执行文件的名称,如果可执行文件为多个,则可以通过空格方式分割;当运行make install命令时,会被默认安装到/usr/local/bin目录下。
noinst_PROGRAMS
指定生成可执行文件的名称,如果可执行文件为多个,则可以通过空格方式分割;当运行make install命令时,不会被安装。
hello_SOURCES
编译可执行文件hello所依赖的*.c源文件,多个文件之间用空格分割。
hello_LDADD
编译可执行文件hello所依赖的*.so和*.a的库文件。
hello_CPPFLAGS
编译可执行文件hello所需要的编译选项。
hello_LDFLAGS
链接可执行文件时所需要的链接选项。
2.3.2 库文件类型
库文件类型,一般会将C源码放在不同的文件夹中,并且每个文件夹中都会有各自的Makefile.am文件,并且会被编译成动态库*.so或者静态库*.a格式的库文件。
如果使用静态库,只需要在configure.ac中加入AC_PROG_RANLIB定义;如果生成动态库,则使用AC_PROG_LIBTOOL。
规则说明
lib_LIBRARIES
指定生成静态库或动态库文件的名称,当运行make install命令时,会被默认安装到/usr/local/lib目录下。
noinst_LIBRARIES
指定生成静态库或动态库文件的名称,当运行make install命令时,不会被安装。
libsrc_a_SOURCES
编译libsrc.a/so库所依赖的*.c源文件,多个文件之间用空格分隔。
libsrc_a_LDADD
加载libsrc.a/so库时所依赖的库文件。
libsrc_a_CPPFLAGS
编译libsrc.a/so库所需要的编译选项。
libsrc_a_LDFLAGS
链接libsrc.a/so库文件时所需要的链接选项。
2.3.3 头文件
我们一般需要导入一些*.h的头文件,如果在Makefile.am中没有标识需要导入的头文件,那么在make dist打包的时候会出现头文件不被打包的问题。因此,建议都加上头文件标识。
include_HEADERS = xxx.h xxx.h xxx.h
在make install时,头文件默认会被安装到linux系统的/usr/local/include目录。
2.3.4 数据文件
data_DATA = data1, data2
2.3.5 常用变量
变量含义
INCLUDES
编译所需要的头文件
LDADD
链接时需要的库文件
LDFLAGS
连接时所需要的链接选项
SUBDIRS
处理本目录之前,先递归处理的子目录
EXTRA_DIST
源程序和一些默认的文件将自动打包入.tar.gz包,其他文件需要进入.tar.gz包可以使用这个变量指定,比如配置文件、数据文件等等
AM_V_AR
用于指定把目标文件打包成静态库时使用的ar命令
RANLIB
用于指定为静态库创建索引的ranlib命令
3.3.6 路径变量
在Makefile.am中尽量使用相对路径,系统预定义了两个基本路径:
$(top_srcdir):工程最顶层目录,用于引用源程序;
$(top_builddir):定义了生成目标文件最上层目录,用于引用.o等编译的中间文件。
3.3.7 安装目录
默认情况下,执行make install命令会将文件安装到/usr/local/bin、/usr/local/include、/usr/local/lib目录下面。我们可以通过./configure --prefix=指定安装路径。
我们也可以修改下面变量,指定安装的路径:
bindir = $(prefix)/bin
libdir = $(prefix)/bin
datadir = $(prefix)/share
sysconfdir = $(prefix)/etc
includedir = $(prefix)/include
三、使用实例
3.1 C源码在同一目录
如果你的C源文件放在同一个目录下面,那么使用Autotools的时候会相对简单很多。比较著名的开源软件Memcache也是放在同一目录下的。
3.1.1 源代码讲解
主程序main.c
#include
#include
#include
#include "sum.h"
#include "get.h"
int main(void)
{
int x = 10;
int y = 20;
int z = sum(x, y);
puts("This is main");
printf("Z:%d\n", z);
x = 20;
z = get(x, y);
printf("Z:%d\n", z);
return 0;
}
sum.c和sum.h
/* sum.h */
extern int sum(int x, int y);
/* sum.c */
#include
#include
#include "val.h"
int sum(int x, int y)
{
val(x);
printf("This is sum method!\n");
return (x + y);
}
val.c和val.h
/* val.h */
extern int val(int x);
/* val.c */
#include
int val(int x)
{
printf("This is val method, X:%d\n", x);
return x;
}
get.c和get.h
/* get.h */
extern int get(int x, int y);
/* get.c */
#include
int get(int x, int y)
{
printf("This is get method\n");
return (x*y);
}
目录文件结构如下:
$ ls
get.c get.h main.c sum.c sum.h val.c val.h
3.1.2 autoscan命令
第一步,我们使用autoscan命令扫描工作目录,并生成configure.scan文件,并将其重新命名为configure.ac。
$ autoscan
$ ls
autoscan.log configure.scan get.c get.h main.c sum.c sum.h val.c val.h
$ mv configure.scan configure.ac
$ cat configure.ac
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69])
AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS])
AC_CONFIG_SRCDIR([sum.h])
AC_CONFIG_HEADERS([config.h])
# Checks for programs.
AC_PROG_CC
# Checks for libraries.
# Checks for header files.
AC_CHECK_HEADERS([stdlib.h unistd.h])
# Checks for typedefs, structures, and compiler characteristics.
# Checks for library functions.
AC_OUTPUT
接着我们编辑configure.ac文件,将其修改为:
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69])
AC_INIT([hello], [1.0], [konishi5202@163.com])
AM_INIT_AUTOMAKE(hello, 1.0)
AC_CONFIG_SRCDIR([main.c])
AC_CONFIG_HEADERS([config.h])
# Checks for programs.
AC_PROG_CC
# Checks for libraries.
# Checks for header files.
AC_CHECK_HEADERS([stdlib.h unistd.h])
# Checks for typedefs, structures, and compiler characteristics.
# Checks for library functions.
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
3.1.3 aclocal命令
第二步,执行aclocal命令,扫描configure.ac文件生成aclocal.m4文件,该文件主要处理本地的宏定义,它根据已经安装的宏、用户定义宏和acinclude.m4文件中的宏,将configure.ac文件需要的宏集中定义到文件aclocal.m4中。
$ ls
autoscan.log configure.ac get.c get.h main.c sum.c sum.h val.c val.h
$ aclocal
$ ls
aclocal.m4 autom4te.cache autoscan.log configure.ac get.c get.h main.c sum.c sum.h val.c val.h
3.1.4 autoconf命令
第三步,执行autoconf命令,将configure.ac文件中的宏展开,生成configure脚本,这个过程需要用到第二步生成的aclocal.m4中定义的宏。
$ ls
aclocal.m4 autom4te.cache autoscan.log configure.ac get.c get.h main.c sum.c sum.h val.c val.h
$ autoconf
$ ls
aclocal.m4 autom4te.cache autoscan.log configure configure.ac get.c get.h main.c sum.c sum.h val.c val.h
3.1.5 autoheader命令
第四步,执行autoheader命令,生成config.h.in文件。该命令通常会从acconfig.h文件中复制用户附加的符号定义。本例中没有附加的符号定义,所以不需要创建acconfig.h文件。
$ ls
aclocal.m4 autom4te.cache autoscan.log configure configure.ac get.c get.h main.c sum.c sum.h val.c val.h
$ autoheader
$ ls
aclocal.m4 autom4te.cache autoscan.log config.h.in configure configure.ac get.c get.h main.c sum.c sum.h val.c val.h
3.1.6 创建Makefile.am文件
第五步,创建Makefile.am文件,aotumake工具会根据configure.in中的参量把Makefile.am转换成Makefile.in文件,最终通过Makefile.in生成Makefile文件。所以Makefile.am文件非常重要,它定义了一些生成Makefile的规则。
$ vim Makefile.am
$ cat Makefile.am
AUTOMAKE_OPTIONS = foreign
bin_PROGRAMS = hello
hello_SOURCES = main.c val.h val.c get.h get.c sum.h sum.c
3.1.7 automake命令
第六步,执行automake --add-missing命令,生成Makefile.in文件。选型‘--add-missing’可以让automake自动添加一些必须的脚本文件,如果发现一些文件不存在,可以通过手工touch创建:
$ automake --add-missing
configure.ac:6: warning: AM_INIT_AUTOMAKE: two- and three-arguments forms are deprecated. For more info, see:
configure.ac:6: http://www.gnu.org/software/automake/manual/automake.html#Modernize-AM_005fINIT_005fAUTOMAKE-invocation
configure.ac:6: installing './install-sh'
configure.ac:6: installing './missing'
Makefile.am: installing './INSTALL'
Makefile.am: error: required file './NEWS' not found
Makefile.am: error: required file './README' not found
Makefile.am: error: required file './AUTHORS' not found
Makefile.am: error: required file './ChangeLog' not found
Makefile.am: installing './COPYING' using GNU General Public License v3 file
Makefile.am: Consider adding the COPYING file to the version control system
Makefile.am: for your code, to avoid questions about which license your project uses
Makefile.am: installing './depcomp'
$ touch NEWS README AUTHORS ChangeLog
$ automake --add-missing
configure.ac:6: warning: AM_INIT_AUTOMAKE: two- and three-arguments forms are deprecated. For more info, see:
configure.ac:6: http://www.gnu.org/software/automake/manual/automake.html#Modernize-AM_005fINIT_005fAUTOMAKE-invocation
$ ls
aclocal.m4 autom4te.cache ChangeLog configure COPYING get.c INSTALL main.c Makefile.in NEWS sum.c val.c
AUTHORS autoscan.log config.h.in configure.ac depcomp get.h install-sh Makefile.am missing README sum.h val.h
3.1.8 软件三部曲
接下来,就是大家非常熟悉的三部曲了:configure、make、make install。
configure主要把Makefile.in变成最终的Makefile文件,同时configure会把一些配置参数配置到Makefile文件里面。
configure命令执行结果,生成Makefile文件:
$ ./configure
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /usr/bin/mkdir -p
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking whether make supports nested variables... yes
checking for gcc... gcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables...
checking whether we are cross compiling... no
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
checking how to run the C preprocessor... gcc -E
checking for grep that handles long lines and -e... /usr/bin/grep
checking for egrep... /usr/bin/grep -E
checking for ANSI C header files... yes
checking for sys/types.h... yes
checking for sys/stat.h... yes
checking for stdlib.h... yes
checking for string.h... yes
checking for memory.h... yes
checking for strings.h... yes
checking for inttypes.h... yes
checking for stdint.h... yes
checking for unistd.h... yes
checking for stdlib.h... (cached) yes
checking for unistd.h... (cached) yes
checking that generated files are newer than configure... done
configure: creating ./config.status
config.status: creating Makefile
config.status: creating config.h
config.status: executing depfiles commands
make命令执行结果,生成hello可执行程序:
$ make
make all-am
make[1]: Entering directory `/work/study/Module/autotools/test_c2'
gcc -DHAVE_CONFIG_H -I. -g -O2 -MT main.o -MD -MP -MF .deps/main.Tpo -c -o main.o main.c
mv -f .deps/main.Tpo .deps/main.Po
gcc -DHAVE_CONFIG_H -I. -g -O2 -MT val.o -MD -MP -MF .deps/val.Tpo -c -o val.o val.c
mv -f .deps/val.Tpo .deps/val.Po
gcc -DHAVE_CONFIG_H -I. -g -O2 -MT get.o -MD -MP -MF .deps/get.Tpo -c -o get.o get.c
mv -f .deps/get.Tpo .deps/get.Po
gcc -DHAVE_CONFIG_H -I. -g -O2 -MT sum.o -MD -MP -MF .deps/sum.Tpo -c -o sum.o sum.c
mv -f .deps/sum.Tpo .deps/sum.Po
gcc -g -O2 -o hello main.o val.o get.o sum.o
make[1]: Leaving directory `/work/study/Module/autotools/test_c2'
执行可执行程序hello:
$ ./hello
This is val method, X:10
This is sum method!
This is main
Z:30
This is get method
Z:400
3.1.9 软件打包发布
执行make dist命令可以对软件进行打包:
$ make dist
make dist-gzip am__post_remove_distdir='@:'
make[1]: Entering directory `/work/study/Module/autotools/test_c2'
if test -d "hello-1.0"; then find "hello-1.0" -type d ! -perm -200 -exec chmod u+w {} ';' && rm -rf "hello-1.0" || { sleep 5 && rm -rf "hello-1.0"; }; else :; fi
test -d "hello-1.0" || mkdir "hello-1.0"
test -n "" \
|| find "hello-1.0" -type d ! -perm -755 \
-exec chmod u+rwx,go+rx {} \; -o \
! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
! -type d ! -perm -400 -exec chmod a+r {} \; -o \
! -type d ! -perm -444 -exec /bin/sh /work/study/Module/autotools/test_c2/install-sh -c -m a+r {} {} \; \
|| chmod -R a+r "hello-1.0"
tardir=hello-1.0 && ${TAR-tar} chof - "$tardir" | GZIP=--best gzip -c >hello-1.0.tar.gz
make[1]: Leaving directory `/work/study/Module/autotools/test_c2'
if test -d "hello-1.0"; then find "hello-1.0" -type d ! -perm -200 -exec chmod u+w {} ';' && rm -rf "hello-1.0" || { sleep 5 && rm -rf "hello-1.0"; }; else :; fi
$ ls
aclocal.m4 ChangeLog config.status depcomp hello main.c Makefile.in stamp-h1 val.c
AUTHORS config.h configure get.c hello-1.0.tar.gz main.o missing sum.c val.h
autom4te.cache config.h.in configure.ac get.h INSTALL Makefile NEWS sum.h val.o
autoscan.log config.log COPYING get.o install-sh Makefile.am README sum.o
注意上面的hello-1.0.tar.gz文件即是打包发布的文件。使用发布文件的方法如下:
下载hello-1.0.tar.gz压缩包;
使用tar -zxvf hello-1.0.tar.gz命令解压;
使用./configure命令生成Makefile文件;
使用make命令编译源代码文件生成可执行程序;
使用make install或make uninstall来安装或卸载软件。
3.2 C源码在不同目录
如果你的入口文件main.c和依赖的文件不是在同一个目录中的,使用Autotools来管理项目的时候会稍微复杂一下。
在不同的目录下,项目会生成*.a文件的静态连接(静态连接相当于将多个.o目标文件合成一个),最外层的main.c会通过静态连接方式来实现连接。
3.2.1 源代码讲解
基于前面小节的源代码,这里还会加入math数学库的使用,让例子稍显复杂来介绍不同目录下的Autotools的使用。
我们首先创建include和source目录,并把相应的文件放进去:
$ ls
include main.c source
$ ls include/
get.h sum.h val.h
$ ls source/
get.c sum.c val.c
修改main.c文件,添加调用math的abs函数:
#include
#include
#include
#include
#include "sum.h"
#include "get.h"
int main(void)
{
int x = 10;
int y = 20;
int z = sum(x, y);
puts("This is main");
printf("Z:%d\n", z);
x = 20;
z = get(x, y);
printf("Z:%d\n", z);
z = abs(-3);
printf("Z:%d\n", z);
return 0;
}
其他源代码文件均不做修改。
3.2.2 autoscan命令
第一步,使用autoscan命令扫描工作目录,并生成configure.scan文件,并将其重新命名为configure.ac:
$ autoscan
$ ls
autoscan.log configure.scan include main.c source
$ mv configure.scan configure.ac
$ cat configure.ac
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69])
AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS])
AC_CONFIG_SRCDIR([main.c])
AC_CONFIG_HEADERS([config.h])
# Checks for programs.
AC_PROG_CC
# Checks for libraries.
# Checks for header files.
AC_CHECK_HEADERS([stdlib.h unistd.h])
# Checks for typedefs, structures, and compiler characteristics.
# Checks for library functions.
AC_OUTPUT
将其修改为:
$ vim configure.ac
[study@konishi test_c3]$ cat configure.ac
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69])
AC_INIT([hello], [1.0], [konishi5202@163.com])
AM_INIT_AUTOMAKE(hello, 1.0)
AC_CONFIG_SRCDIR([main.c])
AC_CONFIG_HEADERS([config.h])
# Generate static lib
AC_PROG_RANLIB
# Checks for programs.
AC_PROG_CC
# Checks for libraries.
# Checks for header files.
AC_CHECK_HEADERS([stdlib.h unistd.h])
# Checks for typedefs, structures, and compiler characteristics.
# Checks for library functions.
AC_CONFIG_FILES([Makefile
source/Makefile])
AC_OUTPUT
3.2.3 aclocal命令
第二步,执行aclocal命令,扫描configure.ac文件生成aclocal.m4文件。
$ ls
autoscan.log configure.ac include main.c source
$ aclocal
$ ls
aclocal.m4 autom4te.cache autoscan.log configure.ac include main.c source
3.2.4 autoconf命令
第三步,执行autoconf命令,将configure.ac文件中的宏展开生成configure脚本。
$ ls
aclocal.m4 autom4te.cache autoscan.log configure.ac include main.c source
$ autoconf
$ ls
aclocal.m4 autom4te.cache autoscan.log configure configure.ac include main.c source
3.2.5 autoheader命令
第四部,执行autoheader命令生成config.h.in文件。
$ ls
aclocal.m4 autom4te.cache autoscan.log configure configure.ac include main.c source
$ autoheader
$ ls
aclocal.m4 autom4te.cache autoscan.log config.h.in configure configure.ac include main.c source
3.2.6 创建Makefile.am文件
第五步,创建Makefile.am文件,Automake工具会根据configure.in中的参量把Makefile.am转换成Makefile.in文件,最终通过Makefile.in生成Makefile文件。
首先在根目录下创建Makefile.am文件:
$ vim Makefile.am
$ cat Makefile.am
AUTOMAKE_OPTIONS = foreign
SUBDIRS = source
bin_PROGRAMS = hello
hello_SOURCES = main.c
hello_LDADD = source/libsrc.a
hello_CPPFLAGS = -I./include/
LIBS = -l m
注意上面的hello_LDADD指定了链接文件,hello_CPPFLAGS通过编译选项指定了编译依赖的头文件路径,LIBS指定了链接依赖的系统库。
接着在source目录下创建Makefile.am文件:
$ vim source/Makefile.am
$ cat source/Makefile.am
noinst_LIBRARIES=libsrc.a
libsrc_a_SOURCES = get.c val.c sum.c
libsrc_a_CPPFLAGS = -I../include
include_HEADERS = ../include/get.h ../include/val.h ../include/sum.h
注意上面的libsrc_a_CPPFLAGS通过编译选型指定了编译依赖的头文件路径,include_HEADERS也可以不指定,但是后续make dist打包发布时,就不会将include文件夹打包进去了。当然,也可以在上一层目录的Makefile.am文件中添加该语句:
include_HEADERS = ./include/get.h ./include/val.h ./include/sum.h
3.2.7 automake命令
第六步,执行automake --add-missing命令,生成Makefile.in文件。
$ touch NEWS README AUTHORS ChangeLog
$ automake --add-missing
configure.ac:6: warning: AM_INIT_AUTOMAKE: two- and three-arguments forms are deprecated. For more info, see:
configure.ac:6: http://www.gnu.org/software/automake/manual/automake.html#Modernize-AM_005fINIT_005fAUTOMAKE-invocation
source/Makefile.am:2: warning: compiling 'get.c' with per-target flags requires 'AM_PROG_CC_C_O' in 'configure.ac'
$ ls
aclocal.m4 autom4te.cache ChangeLog configure depcomp install-sh Makefile.am missing README
AUTHORS autoscan.log config.h.in configure.ac include main.c Makefile.in NEWS source
3.2.8 软件三部曲
接下来,就是大家非常熟悉的三部曲了:configure、make、make install。
执行./configure脚本,生成Makefile:
$ ./configure
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /usr/bin/mkdir -p
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking whether make supports nested variables... yes
checking for ranlib... ranlib
checking for gcc... gcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables...
checking whether we are cross compiling... no
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
checking how to run the C preprocessor... gcc -E
checking for grep that handles long lines and -e... /usr/bin/grep
checking for egrep... /usr/bin/grep -E
checking for ANSI C header files... yes
checking for sys/types.h... yes
checking for sys/stat.h... yes
checking for stdlib.h... yes
checking for string.h... yes
checking for memory.h... yes
checking for strings.h... yes
checking for inttypes.h... yes
checking for stdint.h... yes
checking for unistd.h... yes
checking for stdlib.h... (cached) yes
checking for unistd.h... (cached) yes
checking that generated files are newer than configure... done
configure: creating ./config.status
config.status: creating Makefile
config.status: creating source/Makefile
config.status: creating config.h
config.status: executing depfiles commands
执行make命令,生成hello可执行文件:
$ make
make all-recursive
make[1]: Entering directory `/work/study/Module/autotools/test_c3'
Making all in source
make[2]: Entering directory `/work/study/Module/autotools/test_c3/source'
gcc -DHAVE_CONFIG_H -I. -I.. -I../include -g -O2 -MT libsrc_a-get.o -MD -MP -MF .deps/libsrc_a-get.Tpo -c -o libsrc_a-get.o `test -f 'get.c' || echo './'`get.c
mv -f .deps/libsrc_a-get.Tpo .deps/libsrc_a-get.Po
gcc -DHAVE_CONFIG_H -I. -I.. -I../include -g -O2 -MT libsrc_a-val.o -MD -MP -MF .deps/libsrc_a-val.Tpo -c -o libsrc_a-val.o `test -f 'val.c' || echo './'`val.c
mv -f .deps/libsrc_a-val.Tpo .deps/libsrc_a-val.Po
gcc -DHAVE_CONFIG_H -I. -I.. -I../include -g -O2 -MT libsrc_a-sum.o -MD -MP -MF .deps/libsrc_a-sum.Tpo -c -o libsrc_a-sum.o `test -f 'sum.c' || echo './'`sum.c
mv -f .deps/libsrc_a-sum.Tpo .deps/libsrc_a-sum.Po
rm -f libsrc.a
ar cru libsrc.a libsrc_a-get.o libsrc_a-val.o libsrc_a-sum.o
ranlib libsrc.a
make[2]: Leaving directory `/work/study/Module/autotools/test_c3/source'
make[2]: Entering directory `/work/study/Module/autotools/test_c3'
gcc -DHAVE_CONFIG_H -I. -I./include/ -g -O2 -MT hello-main.o -MD -MP -MF .deps/hello-main.Tpo -c -o hello-main.o `test -f 'main.c' || echo './'`main.c
mv -f .deps/hello-main.Tpo .deps/hello-main.Po
gcc -g -O2 -o hello hello-main.o source/libsrc.a -l m
make[2]: Leaving directory `/work/study/Module/autotools/test_c3'
make[1]: Leaving directory `/work/study/Module/autotools/test_c3'
运行hello可执行程序,输出:
$ ./hello
This is val method, X:10
This is sum method!
This is main
Z:30
This is get method
Z:400
Z:3
当然,你也可以通过make install/uninstall进行安装和卸载,还可以使用make dist对软件进行打包发布。
四、常见错误及解决办法
4.1 automake报错
当执行automake --add-missing报如下错误:
error: required file './ltmain.sh' not found
请使用libtoolize配置一下即可,运行如下命令:
libtoolize --automake --copy --debug --force
查看libtoolize版本方法为:
$ libtoolize --version
libtoolize (GNU libtool) 2.4.2
Written by Gary V. Vaughan
Copyright (C) 2011 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.