0%

Linux Automake 2 Autotools 简介

GNU Automake 版本(version 1.16.1, 26 February 2018)

Permission is granted to copy, distribute and/or modify this
document under the terms of the GNU Free Documentation License,
Version 1.3 or any later version published by the Free Software
Foundation; with no Invariant Sections, with no Front-Cover texts,
and with no Back-Cover Texts. A copy of the license is included in
the section entitled “GNU Free Documentation License.”

2 Autotools 简介


如果以前没有接触过Automake,你可能大概也许知道它是 Autotools 工具集的一部分。

如果你已经开始研究诸如 configure,configure.ac,Makefile.in,Makefile.am,aclocal.m等,那么你可能已经看到他们被声明为 generated by Autoconf or Automake。

整套生成过程异常复杂,本章准备一点点介绍讲解出来。

Alexandre Duret-Lutz 的 Autotools 教程特别好,供参考
(http://www.lrde.epita.fr/~adl/autotools.html)。

2.1 GNU Build System 简介


作为一个开发人员,有一套构建系统是相当有必要的。

在Unix的世界里,一般使用make。所有的规则都在文件Makefile中描述。

比如程序prog链接到文件 main.o,foo.o,bar.o,文件 main.omain.c编译而成。

每次执行 make的时候,它都会读取Makefile,检查文件的修改日期,来决定哪些文件需要重新构建。

在不同的平台上进行编译的时候,Makefile 可能需要微调。

1991年David J. MacKenzie在厌倦反复修改 Makefile后,编写了一个支持20个平台的脚本。
这个脚本的文件称为 configure,可以自动调整 Makefile

现在编译软件包简化为 ./configure && make.

目前GNU项目已经标准化这个过程,安装只需要解压软件包,然后执行 ./configure && make && make install,即可安装。

Autotools 工具可以帮助我们构建自己的工具包。

Autoconf 主要完成configure, Automake 完成Makefile

2.2 GNU Build System的用例


随automake发布的还有一个示例程序 amhello-1.0.tar.gz ,一般位于
PREFIX/share/doc/automake/amhello-1.0.tar.gz,其中PREFIX为安装路径的前缀,

默认为/usr/local,还有一些发行版位于/usr

2.2.1 基本安装


最常见的安装如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
~ % tar zxf amhello-1.0.tar.gz
~ % cd amhello-1.0
~/amhello-1.0 % ./configure
...
config.status: creating Makefile
config.status: creating src/Makefile
...
~/amhello-1.0 % make
...
~/amhello-1.0 % make check
...
~/amhello-1.0 % su
Password:
/home/adl/amhello-1.0 # make install
...
/home/adl/amhello-1.0 # exit
~/amhello-1.0 % make installcheck
...

我们首先解压文件,然后执行configure,将会创建Makefile文件,此时就可以执行make命令了。

make命令将编译程序、库和相关脚本。

make check会测试程序是否运行,不过需要编写测试用例。

当一切完成后,我们就可以使用make install来安装程序的,默认情况下降安装在目录/usr/local,比如:

  • 可执行程序在 /usr/local/bin,
  • 库文件在 /usr/local/lib

2.2.2 标准的 Makefile目标


前面我们接触了 make, make check, make install, 和 make installcheck.
这里的 check, installinstallcheck称为目标,而 make本身是 make all的缩写。
下面是一些常用的make命令:

  • make all :编译程序、库、文档等
  • make install :安装并拷贝相关文件到系统目录
  • make uninstall:清除安装的文件(需要在同一个目录执行)
  • make clean:清除由make生产的编译文件
  • make distclean:清除由configure创建的文件
  • make check:运行测试用例
  • make installcheck:检查安装的程序或库
  • make dist:从源码制作发布归档文件PACKAGE-VERSION.tar.gz

2.2.3 标准目录变量


GNU的编码标准也指定了安装的目录层,如下所示:

目录变量 默认值
prefix /usr/local
exec_prefix ${prefix}
bindir ${exec_prefix}/bin
libdir ${exec_prefix}/lib
includedir ${prefix}/include
datarootdir ${prefix}/share
datadir ${datarootdir}
mandir ${datarootdir}/man
infodir ${datarootdir}/info
docdir ${datarootdir}/doc/${PACKAGE}

安装的时候,安装的文件就会按照到上述变量的一个目录中。比如示例 amhello-1.0,可执行程序 hello将安装到BINDIRREADME安装到DOCDIR,默认路径为
/usr/local/share/doc/amhello.

如果希望安装到自己的目录,可以使用以下参数:

1
2
3
4
5
6
~/amhello-1.0 % ./configure --prefix ~/usr
...
~/amhello-1.0 % make
...
~/amhello-1.0 % make install
...

将安装为 ~/usr/bin/hello~/usr/share/doc/amhello/README.

更多参数可以使用 ./configure --help.

2.2.4 标准配置变量


GNU的编码标准也指定了编译中一系列的配置变量,如下所示:

变量 含义
CC C编译器
CFLAGS C编译器参数
CXX C++编译器
CXXFLAGS C++编译器参数
LDFLAGS 链接参数
CPPFLAGS C/C++预处理参数

configure一本都会设定好默认的参数值,不过如果希望覆盖默认参数,就需要修改这些值了比如通过 configure强制设定 gcc-3作为C编译器,使用 ~/usr/include的头文件编译,使用 ~/usr/lib的库进行连接。

1
~/amhello-1.0 % ./configure --prefix ~/usr CC=gcc-3 CPPFLAGS=-I$HOME/usr/include LDFLAGS=-L$HOME/usr/lib

这些变量均可以通过./configure --help查看

2.2.5 使用文件config.site重置默认的配置


在安装多个文件的时候,如果配置参数一致,那么创建一个通用的文件将十分简便。如果文件PREFIX/share/config.site存在,在configure的时候将自动导入这些参数。

重新调用上一节的命令:

1
2
~/amhello-1.0 % ./configure --prefix ~/usr CC=gcc-3 \
CPPFLAGS=-I$HOME/usr/include LDFLAGS=-L$HOME/usr/lib

此时我们就可以把下面的内容写入文件 ~/usr/share/config.site

 test -z "$CC" && CC=gcc-3
 test -z "$CPPFLAGS" && CPPFLAGS=-I$HOME/usr/include
 test -z "$LDFLAGS" && LDFLAGS=-L$HOME/usr/lib

然后,每次执行 configure都会load这个文件

1
2
3
~/amhello-1.0 % ./configure --prefix ~/usr
configure: loading site script /home/adl/usr/share/config.site
...

2.2.6 并行编译树(Parallel Build Trees) (又名VPATH Builds)


GNU Build System区分两个目录树:源码树和编译树。

源码树包含文件 configure及源码,编译树位于 configure运行的目录。一般编译树的布局与源码树类似。

正常情况下,我们可能会在源码树目录直接执行 configure命令,还有一种情况是如果希望保持源码树的整洁,在其他目录来进行编译,如下所示:

1
2
3
4
5
6
7
~ % tar zxf ~/amhello-1.0.tar.gz
~ % cd amhello-1.0
~/amhello-1.0 % mkdir build && cd build
~/amhello-1.0/build % ../configure
...
~/amhello-1.0/build % make
...

源码树与编译树的目录不同即被称为“parallel builds” 或 “VPATH builds”。

这个源码与编译不在同一个目录的方式对不同的配置参数进行编译特别方便,比如我们通过不同的配置参数来分别编译:

1
2
3
4
5
6
7
8
9
10
11
12
~ % tar zxf ~/amhello-1.0.tar.gz
~ % cd amhello-1.0
~/amhello-1.0 % mkdir debug optim && cd debug
~/amhello-1.0/debug % ../configure CFLAGS='-g -O0'
...
~/amhello-1.0/debug % make
...
~/amhello-1.0/debug % cd ../optim
~/amhello-1.0/optim % ../configure CFLAGS='-O3 -fomit-frame-pointer'
...
~/amhello-1.0/optim % make
...

对于网络文件系统,这种方式更方便了,比如我们在两个主机 HOST1HOST2上分别编译,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
~ % cd /nfs/src
/nfs/src % tar zxf ~/amhello-1.0.tar.gz
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
[HOST1] ~ % mkdir /tmp/amh && cd /tmp/amh
[HOST1] /tmp/amh % /nfs/src/amhello-1.0/configure
...
[HOST1] /tmp/amh % make && sudo make install
...
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
[HOST2] ~ % mkdir /tmp/amh && cd /tmp/amh
[HOST2] /tmp/amh % /nfs/src/amhello-1.0/configure
...
[HOST2] /tmp/amh % make && sudo make install
...

这种情况下,不会对源码目录有任何特殊需求,就算是只读也可编译,这个功能最开始主要是因为当时FSF使用CD-ROM发布,而CD只能读取。

2.2.7 两步安装


GNU编译系统还支持两步安装,什么意思的,也就是区分两种类型的文件,架构相关的和架构无关的,可以通过不同的 Makefile 来完成相关安装。

其实对于我们平时使用的make install是由两部分组成的:

  • 架构相关的 install-exec
  • 架构无关的install-data

从GNU编译系统的角度来看,架构相关与架构无关主要是安装环境及变量不同。所以架构相关的我们使用 make install-exec来安装,架构无关的直接使用make install-data来安装。

举个例子来看看:有两台主机需要安装,假定 (1) 程序安装到 /usr, (2) 目录 /usr/share共享。

在第一台host上,执行On the first host we would run

1
2
3
4
5
[HOST1] ~ % mkdir /tmp/amh && cd /tmp/amh
[HOST1] /tmp/amh % /nfs/src/amhello-1.0/configure --prefix /usr
...
[HOST1] /tmp/amh % make && sudo make install
...

On the second host, however, we need only install the
architecture-specific files.

1
2
3
4
5
[HOST2] ~ % mkdir /tmp/amh && cd /tmp/amh
[HOST2] /tmp/amh % /nfs/src/amhello-1.0/configure --prefix /usr
...
[HOST2] /tmp/amh % make && sudo make install-exec
...

In packages that have installation checks, it would make sense to run
make installcheck to verify that the
package works correctly despite the apparent partial installation.

2.2.8 交叉编译


这个功能特别有用,我们可以在一个平台编译,而在另外一个平台运行。

其中参数为:

  • --build=BUILD:软件包的编译系统
  • --host=HOST:软件和库将要运行的系统

再加上 --host参数的时候, configure就会搜索这个平台的交叉编译套件。交叉编译工具一般在名字前有目标架构作为前缀,比如 MinGW32 编译环境被称为i586-mingw32msvc-gcc, i586-mingw32msvc-ld, i586-mingw32msvc-as等。

接下来的操作介绍如何在GNU/Linux上为 i586-mingw32msvc编译 amhello-1.0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
~/amhello-1.0 % ./configure --build i686-pc-linux-gnu --host i586-mingw32msvc
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking for i586-mingw32msvc-strip... i586-mingw32msvc-strip
checking for i586-mingw32msvc-gcc... i586-mingw32msvc-gcc
checking for C compiler default output file name... a.exe
checking whether the C compiler works... yes
checking whether we are cross compiling... yes
checking for suffix of executables... .exe
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether i586-mingw32msvc-gcc accepts -g... yes
checking for i586-mingw32msvc-gcc option to accept ANSI C...
...
~/amhello-1.0 % make
...
~/amhello-1.0 % cd src; file hello.exe
hello.exe: MS Windows PE 32-bit Intel 80386 console executable not relocatable

2.2.9 安装时重命名程序


可以使用 configure的编译选项,在安装之前加上特定的前缀或后缀来避免与系统程序冲突。

  • --program-prefix=PREFIX:增加前缀
  • --program-suffix=SUFFIX:增加后缀
  • --program-transform-name=PROGRAM:安装时运行sed PROGRAM

比如下面的例子将安装 hello/usr/local/bin/test-hello

1
2
3
4
5
6
~/amhello-1.0 % ./configure --program-prefix test-
...
~/amhello-1.0 % make
...
~/amhello-1.0 % sudo make install
...

2.2.10 使用DESTDIR编译二进制包


单纯的GNU Build System的 make installmake uninstall接口并不能完全满足系统管理员的部署,比如我们希望在编译的时候安装指定路径,而在make的时候因为可能没有root权限暂时放在一个临时目录,然后再部署到其他机器。

比如像下面这个例子就很好的诠释了这个方法。

1
2
3
4
5
6
7
8
9
10
11
~/amhello-1.0 % ./configure --prefix /usr
...
~/amhello-1.0 % make
...
~/amhello-1.0 % make DESTDIR=$HOME/inst install
...
~/amhello-1.0 % cd ~/inst
~/inst % find . -type f -print > ../files.lst
~/inst % tar zcvf ~/amhello-1.0-i686.tar.gz `cat ../files.lst`
./usr/bin/hello
./usr/share/doc/amhello/README

然后,我们就可以把 amhello-1.0-i686.tar.gz解压到大部分主机的 /目录了。

注意使用cat ../files.lst而不是.是为了避免子目录的归档问题,保证在解压的时候按照预定的目录进行解压。

2.2.11 准备发布


make dist收集源码及必须的文件创建一个压缩包,名为 PACKAGE-VERSION.tar.gz

虽然也能满足大部分的功能,但是还有一个更有用的命令 make distcheck,这个命令也会像 make dist一样创建压缩包 PACKAGE-VERSION.tar.gz,并会做各种各样的检查以确保压缩包没有问题:

• 将运行所有的软件包命令,比如 make, make check, make installmake installcheck, 甚至是 make dist,来确保软件发布没有问题
• 测试只读源码树的VPATH编译
• 确保 make clean, make distclean, and make uninstall不忽略任何文件
• 检查 DESTDIR安装是否工作

所有的这些操作都在一个临时目录进行,所以并不需要root权限。

如果 make distcheck失败了,意味着其中的一个场景没有满足,可能不会影响程序的使用,不过最好根据失败的提示进一步改进得到一个完美的发布包。

2.2.12 自动依赖跟踪


Automake自动生成代码来解决依赖的问题。

configure执行的时候,我们可以看到这个过程:

1
2
3
4
~/amhello-1.0 % ./configure --prefix /usr
...
checking dependency style of gcc... gcc3
...

可以通过下面两个选项来使能或禁用依赖跟踪:

  • --disable-dependency-tracking :Speed up one-time builds.
  • --enable-dependency-tracking:Do not reject slow dependency extractors.

2.2.13 嵌套包


虽然并不推荐刚开始接触Autotools的时候就了解嵌套包,但是最为一个极具特性的功能还是需要了解一下的。

使用Autotools或者相关工具开发的时候可以任意潜逃子模块,比如包A的发布将需要字幕了的一个库,库B拥有自己的一套构建系统,那么包A的 configure脚本将先运行库B的 configure脚本,所以安装编译包A的同时也安装编译了库B。生成的包A的发型版也将包含库B。

当然还可以收集更多的包,GCC重度使用这个功能,这样就可以保证每个子模块均可以独立的开发了。

可以使用命令 configure --help=recursive来显示所有包含包的选项。

2.3 Autotools大有裨益


或许你只是希望完成一些可以通过自行编写 configure和 Makefile来完成的功能:

• 不需要特别多的功能
• 需要考虑移植的代价
• 随着GNU Coding Standards的改变需要及时升级配置文件

而 GNU Autotools 就把这些工作自己悄悄的做掉了,只需要关注你的代码即可。

2.4 经典Hello World小程序


本节中我们首先重新从头创建 amhello-1.0软件包,然后介绍下configure.acMakefile.am的含义。

2.4.1 创建 amhello-1.0.tar.gz


接下来我们从头创建 amhello-1.0.tar.gz ,这个压缩包很简单,我们只需要准备5个文件即可。
在一个空目录中创建以下文件:

src/main.c:程序 hello的源文件,我们把这个文件保存在 src/子目录,这样方便以后添加诸如man/data/的文件夹。

1
2
3
4
5
6
7
8
9
10
~/amhello % cat src/main.c
#include <config.h>
#include <stdio.h>
int
main (void)
{
puts ("Hello World!");
puts ("This is " PACKAGE_STRING ".");
return 0;
}

README:包含一些说明文档

1
2
3
~/amhello % cat README
This is a demonstration package for GNU Automake.
Type 'info Automake' to read the Automake manual.

Makefile.amsrc/Makefile.am包含Automake的指令

1
2
3
4
5
6
7
~/amhello % cat src/Makefile.am
bin_PROGRAMS = hello
hello_SOURCES = main.c
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
~/amhello % cat Makefile.am
SUBDIRS = src
dist_doc_DATA = README

configure.ac包含创建 configure脚本的Autoconf指令

1
2
3
4
5
6
7
8
9
10
~/amhello % cat 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

准备好这些文件就可以运行 Autotools 了,使用 autoreconf指令:

1
2
3
4
5
~/amhello % autoreconf --install
configure.ac: installing './install-sh'
configure.ac: installing './missing'
configure.ac: installing './compile'
src/Makefile.am: installing './depcomp'

搞定,此时就完成了编译工作。

除了提示的添加的文件, autoreconf还会创建另外4个文件: configure, config.h.in,
Makefile.insrc/Makefile.in. configure将使用后面的3个文件来创建 config.h, Makefilesrc/Makefile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
~/amhello % ./configure
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/Makefileconfig.h

1
2
3
4
5
6
7
8
9
10
11
~/amhello % make
...
~/amhello % src/hello
Hello World!
This is amhello 1.0.
~/amhello % make distcheck
...
=============================================
amhello-1.0 archives ready for distribution:
amhello-1.0.tar.gz
=============================================

autoreconf只需要运行一次即可,在构建后环境,后续更改了 Makefile.amconfigure.ac,在 make的时候会自动重建需要的依赖。

autoreconfautoconf, automake等一些列命令的集合

其中 autoconf负责从configure.ac创建 configure

automakeconfigure.acMakefile.am创建 Makefile.in

2.4.2 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.ins)读取。这个文件包含一系列的M4宏。

AC_开头的是Autoconf 宏,以 AM_开头的是Automake 宏。

configure.ac的前两行用于初始化 Autoconf 和Automake。 AC_INIT的参数为软件包,版本及联系邮箱(可以使用 ./configure --help来看到)。

记得邮箱要使用自己的

AM_INIT_AUTOMAKE的参数为automake的选项,比如 -Wall-Werror为打开所有的警告和错误报告。 foreign选项告诉Automake这个包不遵从GNU标准。GNU包需要包含ChangeLog, AUTHORS等文件。如果我们不希望
automake抱怨就需要加上这个选项。

AC_PROG_CC可以指定 configure脚本的C编译器。文件src/Makefile.in将使用这里定义的编译器来编译生成hello,当 configuresrc/Makefile.in创建src/Makefile的时候会用到。

选项 AC_CONFIG_HEADERS([config.h])将使 configure脚本创建一个文件config.h用于汇总configure.ac文件中所有定义的宏。在我们的例子中国呢,AC_INIT宏已经定义了一部分了。比如下面这个例子:

1
2
3
4
5
6
7
...
/* 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 and version of this package. */
#define PACKAGE_STRING "amhello 1.0"
...

我们可能注意到文件src/main.c 包含头文件config.h,这样就可以使用 PACKAGE_STRING

AC_CONFIG_FILES宏定义了configure需要从 *.in模版生成的文件。Automake将遍历需要的 Makefile.am文件,所以如果新增了一个文件夹,一定要记得添加 Makefile到这个列表。

AC_OUTPUT行为结束命令。

2.4.3 amhelloMakefile.am配置详解


接下来我们看看文件 src/Makefile.am,这个文件包含Automake编译安装 hello的指令。

bin_PROGRAMS = hello
hello_SOURCES = main.c

Makefile.amMakefile有相同的语法。automake在处理Makefile.am的时候,会拷贝整个文件到 Makefile.in

_PROGRAMS结尾的变量为 Makefiles将要编译的程序。还有其他后缀变量,比如 _SCRIPTS, _DATA, _LIBRARIES等,参考后面的章节。

bin_PROGRAMSbin部分表示 automake将把该程序安装到BINDIR。

注意此处省略了dir

程序需要从源码编译,所以对于每一个以 _PROGRAMS结尾的PROG都需要有一个变量为 PROG_SOURCES,后面将列出所有编译时需要的源文件。

Automake也会通过这个源码文件在打包 make dist的时候一起发布。

最后我们看看顶层目录的文件Makefile.am

SUBDIRS = src
dist_doc_DATA = README

SUBDIRS是一个特殊的变量,用来列出在处理当前目录之前 make需要遍历的目录。

dist_doc_DATA = README将会把文件 README发布和安装到DOCDIR。以 _DATA结尾的变量默认情况下在 make dist发布时是不包含的,所以我们新增了前缀dist_

处无为之事,行不言之教;作而弗始,生而弗有,为而弗恃,功成不居!

欢迎关注我的其它发布渠道