automake中文手册(部分翻译)

个人水平有限,将就看吧,哈哈.觉得不确定的地方放了原文
本文档基于GNU automake官方手册进行翻译。在我开始的时候,官方文档是基于GNU Automake (version 1.16.5, 1 October 2021)进行编写的。
note:下述章节中从第三章开始打算以王立 1999.12.17翻译的内容为基础进行修改适配,因为新的automake文档已与原先不同,计划补充新增的内容,调整修改的内容,侵删。
另外最近在github找到了一个翻译项目,不过也并没有翻译完成

1.介绍

automake是一个根据Makefile.am文件自动生成Makefile.in的工具。每个Makefile.am文件都基本上是由一系列make变量定义组成(偶尔可能也会夹杂规则)。由Makefile.am生成的Makefile.in文件与GNU Makefile标准兼容。
Gun的Make标准文档很长,很复杂,又容易变化,而Automake的目标就是将Makefile维护的负担从GNU维护人员身上移除(把他放在automake维护者身上)。
最典型的automake输入文件就仅仅是一系列变量定义。每个这样的文件都会被处理并创建成一个Makefile.in文件。
Automake确实在某些方面对项目有要求(Automake does constrain a project in certain ways);例如,它假设项目使用Autoconf,并对configure.ac的内容 强制规定了某些限制。
Automake依赖perl来生成Makefile.in文件。然而,由automake创建的发行包完全兼容GUN标准,当编译时,并不需要依赖于perl。

2.autotools介绍

如果你刚刚接触automake,也许你听说过他是autotools系列工具的一部分。也许你已经研究过一个包含configure, configure.ac, Makefile.in, Makefile.am, aclocal.m4等文件组成的包,其中一些文件声称是由Autoconf或Automake生成的。但是这些文件的确切目的和它们之间的关系可能是模糊的。本章的目的是向你介绍这个机制,向你展示它是如何工作的,它有多么强大。如果你从未安装或见过这样的软件包,请不要担心:本章将带您完成它。
如果你需要一些更多插图,或者一个更少一点的以automake为中心的延伸的教学材料,在这里有一些介绍的幻灯片。本章是这个教程第一部分的书面版本。

2.1GUN编译系统的介绍

一个常识是,作为开发人员开发一个新的软件包,你一定需要一个构建系统。
在unix的世界里,这样一个构建系统通常是使用make命令来完成的。你会在Makefile中书写构建包的方法。这个文件 包含了一系列的规则,来用于编译包中的文件。比如程序prog可能是通过链接main.o,foo.o来生成的,而main.o有是通过main.c编译得来。每次执行make的时候,他都会去检查用到的文件是否存在,以及修改时间等等,来决定哪些文件需要去编译或者重新编译,并且运行相关的命令。
当一个包需要在与开发它的平台不同的平台上编译时,它的Makefile通常需要调整。比如他的编译器可能拥有不同的名字或者需要更多的参数。在1991年,David J. MacKenzie厌倦了为20个平台的Makefile进行手动适配。相反,他手工编写了一个名为configure的小shell脚本来自动调整Makefile。编译他的包现在就像运行./configure && make一样简单。
今天,这个过程已经在GNU工程中标准化了。GNU 编码标准声明了GNU 工程的每个软件包应该拥有的configure脚本,以及它应该具有的最小接口。 makefile也应该遵循一些既定的惯例。结果呢?一个统一的构建系统,使安装者几乎不用区分软件包就能安装。在最简单的场景中,安装者所要做的就是解压缩包,运行 ./configure && make && make install ,然后继续安装下一个包。
我们称这个构建系统为GNU构建系统,因为它是从GNU工程发展而来的。然而,它被大量的其他软件包使用:遵循任何现有的约定都有其益处。
autotools是用来为软件包创建GNU构建系统的工具。Autoconf主要用于configure,而Automake用来生成makefile。在没有这些工具的帮助下,创建一个GNU构建系统也是可行的。然而,这是相当繁琐和容易出错的。在对 GNU 构建系统的实际应用进行一些说明之后,我们将再次讨论这个问题。

2.2 GNU 构建系统的例子

在本节中,我们将探讨 GNU 构建系统的几个例子。您可以在随 Automake 分发的amhello-1.0.tar.gz包上重新尝试所有这些示例。如果您的系统上安装了 Automake,您应该在prefix/share/doc/automake/amhello-1.0.tar.gz中找到此文件的副本,其中prefix是配置期间指定的安装前缀(默认为 /usr/local,但是如果 Automake 是由某些 GNU/Linux 发行版安装的,则很可能已设置为/usr ,像我在macos上用brew安装automake后,这个压缩包在/opt/homebrew/share/doc/automake)。如果您没有安装 Automake 的副本,则可以在 Automake 程序包的doc目录中找到此文件的副本。
以下一些用例所呈现的功能实际上是GNU构建系统的扩展。注意:它们并不是GNU编码标准,但它们仍然是Autotools创建的构建系统的一部分。为了简单起见,我们不指出其中的区别。我们的目标是向您展示Autotools创建的构建系统将为您提供的许多功能。

2.2.1 基础安装

一个最常见的安装过程如下所示:

~ % 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
…

用户首先解压缩包。在这里和在下面的示例中,为了简单起见,我们将使用非可移植命令tar zxf。在未安装 GNU tar的系统上,此命令应为gunzip -c amhello-1.0.tar.gz | tar xf -
然后,用户进入新创建的目录以运行configure脚本。此脚本将探测系统的各种功能,并最终创建makefile。在这个示例中,只有两个makefile,但在实际项目中,可能还有更多,通常每个目录就会有一个。
然后可以执行make命令。这将构造需要为包构造的所有程序、库和脚本。在我们的示例中,这将编译hello程序。所有文件都是在源码树中就地构造的;我们稍后将看到如何改变这一点。
make check运行包的测试。此步骤不是强制性的,但在决定安装程序之前,确保已生成的程序按预期运行通常是个好主意。我们的示例不包含任何测试,因此运行make check是无操作的。
在构建所有内容并可能经过测试之后,是时候将其安装在系统上了。这意味着将程序、库、头文件、脚本和其他数据文件从源目录复制到系统上的最终目标。make install命令将执行此操作。但是,默认情况下,所有内容都将安装在/usr/local子目录中:二进制文件将进入/usr/local/bin,库将最终进入/usr/local/lib等。这些目录通常不能被任何用户写入,因此我们假设我们必须成为root才能运行 make install 。在我们的示例中,运行make install会将程序hello复制到/usr/local/bin 中,将README复制到/usr/local/share/doc/amhello中。
最后一个可选步骤是运行make installcheck 。此命令可能会对已安装的文件运行测试。 make check测试源代码树中的文件,而make installcheck测试其已安装的副本。后者运行的测试可能与前者运行的测试不同。例如,有些测试无法在源代码树中运行。反过来,某些包的设置使得make installcheck在不同的文件(未安装与已安装)上运行与 make check相同的测试,当源树的布局与安装的布局不同时这个操作就会很有用。此外,它可能有助于诊断不完整的安装。
目前大多数软件包没有任何install check,因为installcheck的存在鲜为人知,它的用处被忽视了。我们的小示例也好不到哪里去:make install check什么也不做。

2.2.2 标准makefile目标

到目前为止,我们已经遇到了在 GNU 构建系统中运行的四种方法:make, make check, make install, 和 make installcheck 。作为参数传递给make的单词check, install和 installcheck称为目标。make 是make all的简写,是 GNU 构建系统中的默认目标。
以下是 GNU 编码标准指定的最有用的目标的列表。

命令解释
make all构建程序、库、文档等(与make相同)(ps:按我的理解,make执行的是第一个目标,而第一个目标并不一定必定是all)
make install安装需要安装的内容,将文件从包的构建树复制到系统目录。
make install-strip与make install相同,但是将会去除调试符号。有些用户喜欢用空间换取有用的错误报告…(Some users like to trade space for useful bug reports…)
make uninstall与make install相反:删除已安装的文件。(这需要从当时安装时的同一构建树中运行)
make clean从构建树中删除 由make all生成的文件。
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程序将安装在bindir目录中。此目录的默认值为/usr/local/bin,但用户可以在调用configure时提供不同的值。此外, README 将安装到docdir中,默认为 /usr/local/share/doc/amhello.

作为用户,如果您希望在自己的帐户上安装软件包,可以按以下步骤操作:

~/amhello-1.0 % ./configure --prefix ~/usr
…
~/amhello-1.0 % make
…
~/amhello-1.0 % make install

这样所有的文件都会被复制到~/usr下,但是这样可能可执行文件不在PATH中

2.2.4 标准配置变量

GNU 编码标准还定义了一组在构建期间使用的标准配置变量。以下是一些:

命令解释
CCC 编译器命令
CFLAGSC 编译器标志
CXXC++编译器命令
CXXFLAGSC++编译器标志
LDFLAGS链接器标志
CPPFLAGSC/C++ 预处理器标志

configure通常能够为这些变量设置适当的值,但在某些情况下,您可能希望覆盖它们。例如,你可能安装了多个版本的编译器,并希望使用另一个版本,还可能将头文件安装在了编译器的默认搜索路径之外,或者可能把库安装在了链接器之外。
下面介绍如何调用configure以强制将gcc-3用作 C 编译器,在编译时使用来自~/usr/include头文件,在链接时使用来自~/usr/lib 库。

~/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则会在其执行开始时先应用这个文件
回想一下上一节中的命令:

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

假设我们要在~/usr中安装许多软件包,并且总是希望使用CC, CPPFLAGS和 LDFLAGS的这些定义,我们可以通过创建~/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脚本使用~/usr前缀时,它将执行上述config.site的内容并定义这三个变量。

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

需要注意的是,config.site并不是固定位置的–他是根据prefix而决定使用哪一个的

2.2.6 并行构建树(又名VPATH构建)

GNU 构建系统分为了两个树:源代码树和构建树。这两个目录可能相同,也可能不同。
源码树根植于包含了configure脚本的目录中。它包含所有源文件(分发的文件),并且可能分成了多个子目录。
构建树植根于运行configure时所在的目录,这个目录也会保存所有对象文件、程序、库以及从源码构建生成的其他派生文件(因此未分发)。构建树通常具有与源树相同的子目录布局;其子目录由构建系统自动创建。
如果configure在它自己的目录中执行,则源代码树和构建树将混合在一起:派生文件在其源代码相同的目录中构建。在我们的第一个安装示例中就是这种情况(请参阅2.2.1 基础安装)。
用户的一个常见需求是,他们希望将所有生成的文件限制在单个目录中,以保持其源代码目录整洁。以下是我们如何在名为 build/的构建树(即子目录)中运行configure,以保存其创建所有内容。

~ % 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

这种源树和构建树不同的构建方式通常称为并行构建或 VPATH 构建。并行构建这种说法具有误导性:“并行”一词代表了构建树投影于的源码树的方式,它并不代表构建命令的并发运行(the word parallel is a reference to the way the build tree shadows the source tree, it is not about some concurrency in the way build commands are run.)。出于这个原因,我们在下面使用VPATH构建的名称来描述此类配置。VPATH 是make的一个特性的名字(请参阅 GNU Make Manual 中的 VPATH Search Path for All Prerequisites)。

VPATH 构建还有其他有趣的用途。一种是使用多个配置构建相同的代码。例如:

~ % 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

对于网络文件系统,可以使用类似的方法在不同的计算机上构建相同的源。例如,假设源代码存在于一个共享目录中由两个不同的平台的主机HOST1 和HOST2访问。

~ % 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
...

(这里我们假设安装者已经配置sudo,因此可以使用root权限执行)。

在第二台主机上,你可能同时执行完全相同的操作:

[HOST2] ~ % mkdir /tmp/amh && cd /tmp/amh
[HOST2] /tmp/amh % /nfs/src/amhello-1.0/configure
...
[HOST2] /tmp/amh % make && sudo make install
...

在这种情况下,/nfs/src/amhello-1.0目录完全可以为只读。事实上,VPATH 构建也是从只读介质(如 CD-ROM)构建软件包的一种手段。(在GNU工程发展到如此之大之前,FSF曾经销售带有未打包源代码的CD-ROM。)

2.2.7 分为两部分的安装

在我们的上一个示例中(请参阅并行构建树(又名 VPATH 构建)),源代码树由两个主机共享,但编译和安装是在每个主机上分别完成的。
GNU 构建系统还支持网络设置,其中哪些安装文件应在多个主机之间共享(The GNU Build System also supports networked setups where part of the installed files should be shared amongst multiple hosts.这句咋翻译的…)。它通过将体系结构相关文件从体系结构无关的文件中区分开来,并提供两个Makefile目标来分别安装他们的文件来实现此目的。
对依赖于体系结构的文件,对应目标是install-exec。对于体系结构无关的文件,对应目标是install-data。到目前为止我们使用的命令make install可视为make install-exec install-data的简写。
从 GNU 构建系统的角度来看,依赖于体系结构的文件和与体系结构无关的文件之间的区别完全基于指定其安装位置的目录变量。在我们之前提供的目录变量列表中(请参阅2.2.3 标准的目录变量),所有基于exec-prefix的变量都指定为与体系结构相关的目录,这些目录的文件将通过make install-exec安装。而其他的则指定为独立于体系结构的目录,并将通过make install-data来安装。有关更多详细信息,请参阅The Two Parts of Install

让我们再来看下之前的双主机安装的例子,假设(1)我们要直接在/usr中安装软件包,并且(2)/usr/share目录由两台主机共享。

在第一台主机上,我们将运行

[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
...

但是,在第二台主机上,我们只需要安装特定于体系结构的文件。(因为与体系结构无关的文件只需一份)

[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
...

ps:这里的意思应该是说,在第二台主机,与架构相关的部分通过make install-exec安装了,而与架构无关的部分(比如帮助手册等等)将共享第一台主机安装的。
在具有安装检查的软件包中,运行make installcheck以验证软件包是否正常工作(尽管明显是部分安装)是有意义的。

2.2.8 交叉编译

交叉编译是在一个平台上构建一个将在另一个平台上运行的二进制文件。说到交叉编译,区分执行编译的构建平台(build)和生成的可执行文件运行的主机平台(host)非常重要。以下configure选项用于指定他们:

字段含义
--build=build构建包的系统。
--host=host将运行被构建的程序和库的系统。

当使用--host时,configure将搜索此平台的交叉编译套件。交叉编译工具通常将其目标体系结构作为其名称的前缀。例如,我的 MinGW32 交叉编译器包含i586-mingw32msvc-gcc 、i586-mingw32msvc-ld 、i586-mingw32msvc-as 等编译工具。

以下是我们如何在 i586-mingw32msvc架构的GNU/Linux PC 上进行构建amhello-1.0。

~/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

--host-和 --build选项通常是我们进行交叉编译时需要指定全部内容。唯一的例外是,如果正在构建的包本身就是一个交叉编译器:我们需要第三个选项来指定其目标体系结构。比如在x86平台编译一个在arm平台上运行的编译器,这个(刚刚编译的)编译器编译的程序运行于powerpc。

字段含义
--target=target构建编译器工具时:编译的工具创建的程序运行的平台

例如,在安装GCC(GNU编译器集合)时,我们可以用–target=target来指定我们要将GCC构建为target的交叉编译器。同时使用–build和–target,我们可以交叉编译一个交叉编译器;这种三向交叉汇编被称为Canadian cross.

有关这些configure选项的详细信息,请参阅《autoconf手册》中的Specifying the System Type

2.2.9 在安装时重命名程序

GNU 构建系统提供了在安装可执行文件和man手册之前自动重命名它们的方法。当你想安装的命令已经存在,你不想覆盖他时,这种方法会很方便。例如,您可能希望安装 GNU的tar为gtar,这样你就可以将其与供应商的tar命令区分开来。
这可以使用这三个configure选项之一来完成。

字段含义
–program-prefix=prefix在已安装的程序名称前面附加prefix。
–program-suffix=suffix追加suffix到已安装的程序名称后面。
–program-transform-name=program在已安装的程序名称上运行sed program。

例如,以下命令将hello安装为/usr/local/bin/test-hello :

~/amhello-1.0 % ./configure --program-prefix test-
…
~/amhello-1.0 % make
…
~/amhello-1.0 % sudo make install

2.2.10 使用 DESTDIR 构建二进制包

当系统管理员必须在许多主机上部署和升级软件包时,GNU构建系统的make install和make uninstall接口可能不能完全符合他们的需求。换句话说,GNU构建系统并不能取代软件包管理器。
这些包管理器通常需要知道包安装了哪些文件,因此仅仅一个make install是不够的。
可以使用DESTDIR变量执行分段安装。当执行configure时,这个包应当被配置为最终安装位置(例如--prefix=/usr),但是当运行make install时,你可以使用DESTDIR 参数,指定真正要安装目录的绝对路径。从这个目录中,很容易查看哪些文件安装在何处,最后通过某种方式将它们复制到最终位置。

下面的例子展示了,如何创建一份所有要安装的文件的快照。

~/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而不是.作为tar的参数可以避免存档中每个子目录的条目:我们不希望恢复/ 、/usr等的修改时间。)(Using cat ../files.lst instead of ‘.’ as argument for tar avoids entries for each subdirectory in the archive: we would not like tar to restore the modification time of /, /usr/, etc.)
请注意,在为多个体系结构生成包时,使用make install-data和make install-exec(请参阅2.2.7 分为两部分的安装)在单个包中收集与体系结构无关的文件可能会很有用。
有关详细信息,请参阅 What Gets Installed

2.2.11 准备发行版

我们已经提到过make dist。此目标收集所有源文件和构建系统的必要部分,以创建名为package-version.tar.gz
的压缩包。
另一个更有用的命令是make distcheck 。distcheck目标在构造package-version.tar.gz时与dist一样好,但它另外还执行了到目前为止提出的大多数用例的测试:

  • 它尝试对软件包进行完整编译(请参阅基本安装):解压缩新构造的压缩包,运行make 、make check 、 make install以及 make installcheck,甚至是make dist。
  • 它使用只读源代码树测试VPATH构建(请参阅并行构建树(又名VPATH构建)),
  • 它确保make clean 、make distclean 、make uninstall 没忽略任何应当删除的文件(标准makefile目标)
  • 检查DESTDIR安装是否正常工作(请参见使用 DESTDIR 构建二进制包)。

所有这些操作都是在临时目录中执行的,因此不需要 root 权限。这种子目录的确切位置和确切结构(提取的源代码的位置,临时构建和安装目录的命名方式以及它们嵌套的深度等)将被视为可以随时更改的实现细节;所以不要依赖它。

发布执行make distcheck失败的包意味着我们提供的编译目标中的某些将不起作用,这会使用户会感到失望。因此,最好仅在make distcheck成功后才发布包。当然,这并不意味着该软件包将完美无缺,但至少它可以帮你防止犯那些从未听说过distcheck的人所发布的软件包中所犯的一些令人尴尬的错误(例如由于拼写错误而使得DESTDIR无法正常工作,或者包文件被make clean删除,甚至VPATH构建不起作用)。

请参阅 Creating amhello-1.0.tar.gz,以使用make distcheck重新创建amhello-1.0.tar.gz 。有关 distcheck的详细信息,请参阅See Checking the Distribution

2.2.12 自动依赖关系跟踪

依赖关系跟踪是作为编译的副产物执行的。每次构建系统编译源文件时,它都会计算其依赖项列表(在C语言中,这些依赖项是正在编译的源代码中所包含的头文件)。每当运行make时,如果依赖项发生了更改,则将重新构建依赖项文件。
默认情况下,Automake会生成用于自动依赖关系跟踪的代码,除非开发人员选择覆盖它;有关详细信息,请参阅 Automatic dependency tracking

当configure执行时,您可以看到它探测每个编译器的依赖关系机制(有好几种机制可以被使用):

~/amhello-1.0 % ./configure --prefix /usr
…
checking dependency style of gcc... gcc3
…

由于依赖项仅作为编译的副产物进行计算,因此在首次生成包时不存在依赖项信息。这没关系,因为无论如何所有文件都需要被构建:make不必决定需要重新编译哪些文件。实际上,依赖项跟踪对于一次性生成是完全无用的,并且configure有一个选项可以禁用此功能:

字段含义
--disable-dependency-tracking加快一次性构建速度

一些编译器不提供任何适用的方法来派生依赖项列表,而需要单独的运行(可能是另一个工具)来计算这些依赖项。这些方法带来的性能损失可能很多,因此需要在默认情况下禁用它们。。必须为configure传递 --enable-dependency-tracking选项才能激活它们。

字段含义
--enable-dependency-tracking不要拒绝慢速依赖关系提取程序。

参见Automake简史中的 Dependency Tracking Evolution,了解Automake多年来使用的不同依赖跟踪方案的一些讨论。

2.2.13 嵌套软件包

尽管我们不推荐刚使用Autotools的人使用嵌套包,但在这个小型推广教程之旅中,这是一个值得一提的不错功能。
自动解析的包(这意味着其构建系统已由 Autoconf 创建的包)可以嵌套到任意深度。

一个典型的设置是,包 A 将在子目录中分发它需要的库之一。这个库B是一个完整的软件包,有自己的GNU构建系统。A的脚本将运行B的脚本作为其执行的一部分;构建和安装 A 也将构建和安装 B.为 A 生成分发包时也将包括 B。

嵌套可以像这样包含多个包。GCC对嵌套的功能使用很多。这为安装者提供了单独的一个用于配置,构建和安装的软件包,与此同时还允许开发人员独立处理子包。

配置嵌套包时,提供给顶层configure的选项将以递归方式传递给嵌套的包的configure。当每个包不支持某个选项时,将会忽略这个选项,并认为这个选项是用于配置别的包的。

configure --help=recursive可用于显示所有包含的包的支持的选项

有关示例设置,请参阅 Nesting Packages

2.3 automake能帮助你干什么

你可能有很多原因不想自己实现 GNU 构建系统(自己编写configure脚本和Makefile文件)。

  • 正如我们所看到的,GNU构建系统有很多功能(参见2.2 GNU 构建系统的例子)。有些用户可能会期望那些因为你不需要它们所以未添加的功能。
  • 以可移植方式实现这些功能是既困难又费劲的。想想为那些可能不熟练的系统编写可移植shell脚本和可移植的Makefile。请参阅autoconf手册中的Portable Shell Programming,以说服自己。
  • 你必须升级你的设置以遵循GNU编码标准的变更。

GNU Autotools将所有负担从你身上移走,并提供:

  • 通过简单的指令就能创建可移植,完整和自包含的GNU构建系统的工具。自包含意味着生成的构建系统不需要 GNU Autotools就能运行。
  • 集中的进行修复和改进更改(A central place where fixes and improvements are made):可移植性问题的错误修复将使每个包都被修复。

然而,也许也存在一些原因,使你可能不想使用autotools…例如,您可能已经在使用(或习惯于)另一个不兼容的构建系统。autotools只有在您接受 GNU 构建系统的概念时才有用。对于想完全控制自己编译系统的人来说,使用Autotools会感到沮丧。

2.4 helloworld的例子

在本节中,我们将从头开始重新创建amhello-1.0。第一小节说明如何调用 Autotools 来实例化 GNU 构建系统,而第二小节则解释了 Autotools 读取的configure.ac及Makefile.am的含义。

2.4.1 创建amhello-1.0.tar.gz

以下是我们如何从头开始重新创建amhello-1.0.tar.gz。该软件包非常简单,因此我们只需要写入5个文件。(如果您不想编写它们,则可以从随 Automake 一起分发的最终版本中复制它们。)

在空目录中创建以下文件。

  • src/main.c是hello程序的源文件。我们将其存储在src/子目录中,因为在之后,当软件包扩展时,还有为存放man手册的man/,存放数据文件的data/目录等。
~/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包含一些非常有限的文档,用于我们的小软件包。

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

Makefile.am和src/Makefile.am包含了这两个目录的Automake指令。

~/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 指令。

~/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命令执行此操作,如下所示:

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

此时,构建系统已完成。
执行完命令后文件的变化情况如下:

~/amhello %  ls
Makefile.am    aclocal.m4     config.h.in    depcomp        src
Makefile.in    autom4te.cache configure      install-sh
README         compile        configure.ac   missing

除了其输出中提到的三个脚本之外,您还可以看到autoreconf创建了其他四个文件:configure、config.h.in、Makefile.in和 src/Makefile.in 。后面三个文件是在执行configure时将会适配系统的模板文件,并会生成config.h,Makefile和src/Makefile(The latter three files are templates that will be adapted to the system by configure under the names config.h, Makefile, and src/Makefile. ).

~/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/Makefile 和config.h 是在configure探测完系统后在最后创建的。现在可以运行我们想要的所有目标(请参阅 Standard Makefile Targets)。例如:

~/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
=============================================

请注意,只有当 GNU 构建系统不存在时,才需要运行autoreconf。当您稍后更改Makefile.am或 configure.ac中的一些指令时,构建系统的相关部分将在您执行make时自动重新生成。
autoreconf是一个脚本,它以正确的顺序调用 autoconf、automake 和一堆其他命令。如果你是刚开始用这些工具,那么没必要一定弄清楚所有这些工具应以何种顺序调用以及他们的作用。但是,由于 Autoconf 和 Automake 都有单独的手册,因此需要了解的重要一点是,autoconf负责从configure.ac创建configure ,而automake负责从Makefile.am 和configure.ac创建Makefile.in。在寻求答案时,这至少应该引导您到正确的手册。
autoconf:configure.ac->configure,config.h.in
automake:Makefile.am,configure.ac->Makefile.in
configure:config.h.in->config.h;Makefile.in->Makefile

2.4.2 amhello的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.in) 读取。它包含一系列M4宏,这些宏将作为shell代码扩展以最终形成configure脚本。我们不会详细介绍此文件的语法,因为 Autoconf 手册中有一整节介绍它(请参阅 Autoconf 手册 中的 Writing configure.ac)。

前缀为AC_前缀的宏是 Autoconf 宏,记录在 Autoconf 手册中(请参见《 Autoconf手册》中的 Autoconf Macro Index )。以AM_开头的宏是Automake宏,本手册后面将对此进行介绍(请参见 Macro Index)。

configure.ac的最前面两行初始化了 Autoconf 和 Automake 。 AC_INIT将包的名称、版本号和有关包的错误报告的联系人地址作为参数(例如,此地址在./configure --help的末尾输出)。在将此设置调整为您自己的软件包时,请不要盲目复制Automake的地址:使用软件包的邮件列表或您自己的邮件地址。

AM_INIT_AUTOMAKE的参数是传递给automake的一系列参数(请参见 Changing Automake’s Behavior)。-Wall和-Werror选项使automake打开所有告警并将其报告为错误。我们在这里说的是Automake的警告,例如Makefile.am中的可疑说明。这与编译器如何调用完全无关,即使它可能支持具有相似名称的选项。在开始处理软件包时,使用-Wall -Werror是一个安全的设置:您不想错过任何问题(-Wall将开启所有告警,-Werror将把所有告警都当作错误)。之后你可能会决定放松一下。foreign选项告诉Automake,此软件包将不遵循GNU标准。GNU 软件包应始终分发其他文件,如ChangeLog、AUTHORS等。我们不想automake在我们的小示例中提示这些丢失的文件。

AC_PROG_CC行使configure脚本搜索 C 编译器并使用其名称定义CC变量。Automake 生成的src/Makefile.in文件使用CC变量来构建hello ,因此当configure从src/Makefile.in创建src/Makefile时,它将使用找到的值来定义CC。如果当Automake创建Makefile.in时,要使用的CC变量在configure.ac中没被定义,它将建议您添加对AC_PROG_CC的调用。

AC_CONFIG_HEADERS([config.h])调用将使configure脚本创建一个config.h文件,该文件收集configure. ac中其他宏定义的’ #define '。在我们的例子中,AC_INIT宏已经定义了其中的一些。下面是configure运行后config.h的摘要:

…
/* 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。在真实的项目中,config.h可能会变得非常大,在系统中探测每个特性都需要一个’ #define '。

AC_CONFIG_FILES宏声明了一个文件列表,这个列表里的文件就是configure从那些*.in模板创建出的文件的列表。Automake还扫描这个列表以找到必须处理的Makefile.am文件。(记住这一点很重要:当添加一个新目录到你的项目,你应该添加它的Makefile到这个列表,否则Automake将永远不会处理你新写在那个目录里的Makefile.am)

最后,AC_OUTPUT行是一个关闭命令,它实际生成脚本的一部分,即负责创建用AC_CONFIG_HEADERS和AC_CONFIG_FILES注册的文件的那部分。(Finally, the AC_OUTPUT line is a closing command that actually produces the part of the script in charge of creating the files registered with AC_CONFIG_HEADERS and AC_CONFIG_FILES.)

当开始一个新项目时,我们建议您从这样一个简单的 configure.ac开始,并逐渐添加它需要的其他测试。autoscan命令也可以建议包可能需要的一些测试(请参阅使 Using autoscan to Create configure.ac)。

2.4.3 amhello的Makefile.am设置说明

我们现在转到src/Makefile.am .此文件包含了用来构建和安装hello的Automake指令。

bin_PROGRAMS = hello
hello_SOURCES = main.c

一个Makefile.am具有与普通Makefile相同的语法。当automake处理Makefile.am时,它复制整个文件到Makefile.in(稍后此文件将会被configure转为Makefile),但是某些变量定义将生成一些构建规则和其他变量。通常Makefile.am只包含如上所述的变量定义列表,但它们还可以包含其他可以automake不会解析直接传递的变量和规则定义。

以_PROGRAMS结尾的变量是特殊的变量,它列出了生成的Makefile应该构建的程序。在Automake语言中,这个_PROGRAMS后缀被称为primary;Automake可以识别其他的primary,如_SCRIPTS, _DATA,_LIBRARIES等,它们对应于不同类型的文件。

bin_PROGRAMS的’ bin '部分告诉automake,生成的程序应该安装在bindr中。回想一下,GNU构建系统使用一组变量来表示目标目录,并允许用户自定义这些位置(参见 Standard Directory Variables)。任何这样的目录变量都可以放在主目录(省略dir后缀)前面,告诉automake将列出的文件安装在哪里。

程序需要从源文件构建,因此对于_PROGRAMS变量中列出的每个程序prog, automake将查找另一个名为prog_SOURCES的变量,这个变量将列出它的源文件。可能有多个源文件:它们都将被编译并链接在一起。

Automake还知道在创建tarball时需要分发的源文件(不同于构建的程序(unlike built programs))。因此hello_SOURCES声明的一个副作用是,main.c将成为make dist创建的tarball的一部分。

最后,这里有一些关于顶层Makefile.am的解释。

SUBDIRS = src
dist_doc_DATA = README

SUBDIRS是一个特殊变量,他列出了make在处理当前目录之前应递归处理的所有目录。因此,即使我们从顶层运行它,这行也使用make构建src/hello。此行还会导致make install在安装README之前进行安装src/hello(虽然说这个顺序并不是很重要)。

dist_doc_DATA = README行导致README被分发并安装在docdir中。列出_DATA primary的文件不会自动成为使用make dist构建的tarball的一部分,所以我们添加了dist_前缀,以便分发它们。然而,对于README来说就没有这个必要了:automake会自动分发它遇到的任何README文件(自动分发的其他文件列表由automake——help提供)。因此,第二行代码的唯一重要作用是在make安装期间安装README。

本例中没有涉及的一件事是从程序代码访问安装目录值(请参阅 Standard Directory Variables),也就是说,将它们转换为已定义的宏。关于这个,请参见在Autoconf手册中的Defining Directories

3.通用性概念

4.一些实例软件包

5.创建Makefile.in

6.扫描configure.in

7.目录相关

8.创建程序和库

Automake的大部分功能的目的是使创建C程序和库变得容易些。

8.1创建一个程序

为了构建一个程序,你需要告诉automake这个程序包含哪些源文件,需要链接哪些库。
这节也包括了源码或进程的条件编译。这里的许多注解也适应于静态库编译章节和动态库编译章节

8.1.1 定义程序源文件

在一个含有将被创建成一个程序(而不是创建成一个库)的源代码的目录中,要使用主变量PROGRAMS。程序可以安装到bindirsbindirlibexecdirpkglibdir中,或者根本不安装(noinst)。他们也可以只被编译成make check,在这种情况下前缀是check_

例如:

bin_PROGRAMS = hello

在这种简单的情况下,最终的`Makefile.in’将含有代码以生成名为hello的一个程序。
与每个程序相关联的是几个以程序命名的辅助变量。这些变量都是可选的,并且具有合理的默认值。每个变量、它的用法和默认值将在下面详细说明;我们始终使用“hello”示例。
变量hello_SOURCES 用于确定哪些源代码应该被创建到可执行文件中去:

hello_SOURCES = hello.c version.c getopt.c getopt1.c getopt.h system.h 

它将导致所有提及的.c文件被编译成对应的.o文件。而后被一同连接以生成hello
如果没指定prog_SOURCES,那么它被缺省地设置成单个文件hello.c
可以把多个程序创建到一个目录中。多个程序可以共享一个源代码文件,这个源代码文件必须在需要它的每个_SOURCES中列出。
出现在_SOURCES中的头文件将被包含在发布版本中,其它的头文件将被忽略。因为它并不明显,你不应该把由configure生成的头文件包含在变量_SOURCES中;这个文件不应该被发布。lex(.l)和yacc(.y)文件也可以放在变量中;参见对Yacc和Lex的支持。

8.1.2链接程序

如果需要链接不通过configure寻找的库,可以使用LDADD。此变量用于指定要链接的其他对象或库;它不适合指定特定的链接器标志;如果想指定,您应该使用AM_LDFLAGS。
有时,多个程序在一个目录中构建,但不共享相同的链接时依赖。在这种情况下,您可以使用prog_LDADD变量(其中prog是程序的名称,即组成_PROGRAMS变量的名字,并且通常以小写形式书写)来覆盖LDADD。如果给定程序存在此变量,则不会使用LDADD链接该程序。
例如,在GNU cpio中,pax、cpio和mt是指向库libcpio.a链接的。然而,rmt构建在同一个目录中,并且没有这样的链接需求。此外,mt和rmt仅构建在特定的体系结构上。这是cpio的src/Makefile:(只列出部分):

bin_PROGRAMS = cpio pax $(MT)
libexec_PROGRAMS = $(RMT)
EXTRA_PROGRAMS = mt rmt

LDADD = ../lib/libcpio.a $(INTLLIBS)
rmt_LDADD =

cpio_SOURCES = …
pax_SOURCES = …
mt_SOURCES = …
rmt_SOURCES = …

prog_LDADD不适合传递特定于程序的链接器标志(-l、-L、-dlopen和-dlpreopen除外)。因此,明确使用prog_LDFLAGS变量的意图。
有时,让一个程序依赖于实际上不属于该程序的其他目标也是有用的。这可以使用prog_DEPENDENCIES或EXTRA_prog_DEPENDENCIES变量来完成。每个程序都依赖于这两个变量的内容,但这里不做进一步的解释。
由于这些依赖关系与用于创建程序的链接规则相关联,因此它们通常应该列出link命令使用的文件。即*.$(OBJEXT), *.a,或*.la的文件。在极少数情况下,您可能需要添加其他类型的文件,例如链接器脚本,但是在_DEPENDENCIES中列出源文件是错误的。如果需要在构建程序的所有组件之前构建一些源文件,请考虑使用BUILT_SOURCES变量
如果未提供prog_DEPENDENCIES,则由Automake计算。自动分配的值是prog_LDADD的内容,大多数配置将被替换,-l、-l、-dlopen和-dlpreopen选项将被删除。configure替换后剩下的只有$(LIBOBJS)$(ALLOCA);留下这些是因为已知它们不会导致生成prog_DEPENDENCIES的无效值。当您只想增加自动生成的prog_DEPENDENCIES而不是替换它时,EXTRA_prog_DEPENDENCIES可能很有用。
我们建议您在引用包构建的库时避免在LDADD或prog_LDADD中使用-l选项。相反,应该像上面的cpio示例那样显式地编写库的文件名。使用-l仅用于列出第三方库。如果您遵循此规则,prog_DEPENDENCIES的默认值将列出所有本地库并忽略其他库。

8.1.3 源码条件编译

您不能将配置替换(例如’ @FOO@ ‘或’ $(FOO) ',其中FOO是通过AC_SUBST定义的)放入_SOURCES变量中。这样做的原因有点难以解释,但足以说明它根本行不通。如果您尝试这样做,Automake将给出一个错误。

幸运的是,还有另外两种方法可以达到相同的结果。一种是在_LDADD变量中使用configure替换,另一种是使用Automake条件。

使用_LDADD 替换来条件编译
Automake必须知道所有可能进入程序的源文件,即使并非所有文件都是在每种情况下构建的。任何仅按条件构建的文件都应该在适当的EXTRA_变量中列出。例如,如果hello-linux.c或hello-generic.c被有条件地包含在hello中,那么Makefile.am的内容将包含:

bin_PROGRAMS = hello
hello_SOURCES = hello-common.c
EXTRA_hello_SOURCES = hello-linux.c hello-generic.c
hello_LDADD = $(HELLO_SYSTEM)
hello_DEPENDENCIES = $(HELLO_SYSTEM)

接下来你可以在configure.ac中设置$(HELLO_SYSTEM)替换。

…
case $host in
  *linux*) HELLO_SYSTEM='hello-linux.$(OBJEXT)' ;;
  *)       HELLO_SYSTEM='hello-generic.$(OBJEXT)' ;;
esac
AC_SUBST([HELLO_SYSTEM])
…

在这种情况下,变量HELLO_SYSTEM应该替换为hello-linux.o或hello-generic.o,并添加到hello_DEPENDENCIES和hello_LDADD中,以便构建和链接。

使用automake条件来条件编译

有条件地编译源文件的一种通常更简单的方法是使用Automake条件。例如,您可以使用这个Makefile.am构造来构建相同的hello示例:

bin_PROGRAMS = hello
if LINUX
hello_SOURCES = hello-linux.c hello-common.c
else
hello_SOURCES = hello-generic.c hello-common.c
endif

在这种情况下,configure.ac应该使用AM_CONDITIONAL设置LINUX条件。
当使用这样的条件时,您不需要使用EXTRA_变量,因为Automake将检查每个变量的内容以构造完整的源文件列表。
如果你的程序使用了大量的文件,你可能更喜欢有条件的’ += '。

bin_PROGRAMS = hello
hello_SOURCES = hello-common.c
if LINUX
hello_SOURCES += hello-linux.c
else
hello_SOURCES += hello-generic.c
endif
8.1.4 条件编译进程

有时,确定要在配置时构建的程序是有用的。例如,GNU cpio只在特殊情况下构建mt和rmt。实现程序条件编译的方法与有条件地编译源文件的方法相同:替换或条件。
使用configure替换来条件编译

在这种情况下,你必须把可能创建的所有程序都告诉automake,但同时使生成的Makefile.in使用由configure 指明的程序。这可以通过在EXTRA_PROGRAMS中列出所有可能创建的程序的同时,把configure提供的值的替换到每个_PROGRAMS变量的定义中,来完成。

bin_PROGRAMS = cpio pax $(MT)
libexec_PROGRAMS = $(RMT)
EXTRA_PROGRAMS = mt rmt

正如在支持可执行扩展中所解释的那样,Automake将重写bin_PROGRAMS, libexec_PROGRAMS和EXTRA_PROGRAMS,并在每个二进制文件中附加’ $(exext) ‘。显然,它不能重写在运行时通过configure替换获得的值,因此您应该自己注意追加’ $(exexext) ‘,如’ AC_SUBST([MT], [’ MT ${exexext}']) '。

使用automake条件来条件编译
您还可以使用automake条件(参见条件)来选择要构建的程序。在这种情况下,您不必担心’ $(exext) '或EXTRA_PROGRAMS。

bin_PROGRAMS = cpio pax
if WANT_MT
  bin_PROGRAMS += mt
endif
if WANT_RMT
  libexec_PROGRAMS = rmt
endif

8.2 编译一个库

创建库与创建程序十分类似。在这种情况下,主变量的名字是LIBRARIES。库可以安装到libdir或pkglibdir之中。
关于如何使用Libtool和主变量LTLIBRARIES创建共享库的详情,请参见创建共享库。

每个_LIBRARIES变量都是需要被创建的库的列表。例如创建一个名为libcpio.a的库,但并不安装它,你可以写:

noinst_LIBRARIES = libcpio.a
libcpio_a_SOURCES = …

确定那些源代码应该被创建到库中的方式与创建程序的情况完全相同,是通过变量_SOURCES。需要指出的是,库的名字是规范化的(参见派生变量是如何命名的),所以对应与libcpio.a_SOURCES变量对应的变量名为libcpio_a_SOURCES,而不是libcpio.a_SOURCES
通过使用变量`library_LIBADD’,可以把额外的对象添加到库中。这应该由configure确定的对象使用。再看看cpio:

libcpio_a_LIBADD = $(LIBOBJS) $(ALLOCA)

此外,在配置时之前不存在的额外对象的源必须添加到BUILT_SOURCES变量中(参见build sources)。
构建静态库是通过编译所有对象文件,然后调用’ $(AR) $(ARFLAGS) ‘,后面跟着库的名称和对象列表,最后在该库上调用’ $(RANLIB) '来完成的。您应该从configure.ac中调用AC_PROG_RANLIB来定义RANLIB(否则Automake会报错)。您还应该调用AM_PROG_AR来定义AR,以便支持不常见的归档器,如Microsoft lib.ARFLAGS默认为cru;您可以通过在Makefile中设置它来覆盖该变量,或者从你的configure.ac中替换它。您可以通过定义每个库的maude_AR变量来覆盖AR变量(参见程序和库变量)。
在有条件地选择库组件时要小心。因为构建空库是不可移植的,所以您应该确保任何库总是包含至少一个对象。
要在构建程序时使用静态库,请将其添加到此程序的LDADD中。在下面的示例中,程序cpio与库libcpio.a静态链接。

noinst_LIBRARIES = libcpio.a
libcpio_a_SOURCES = …

bin_PROGRAMS = cpio
cpio_SOURCES = cpio.c …
cpio_LDADD = libcpio.a

8.3创建一个共享库

创建共享库是一件相对复杂的事情。为此,提供了GNU Libtool以使我们可以按照与平台无关的方式创建共享库(参见Libtool手册)。

8.3.1 libtool思想

Libtool将共享库和静态库抽象为一个统一的概念,从此称为Libtool库。Libtool库是使用.la后缀的文件,可以指定静态库、共享库,或者两者都指定。在运行./configure之前,无法确定它们的确切性质:并非所有平台都支持所有类型的库,用户可以显式选择应该构建哪些库。(但是,包的维护者可以调节优默认值;参见Libtool手册中的LT_INIT宏。)
因为共享库和静态库的目标文件必须以不同的方式编译,所以在编译过程中也使用libtool。libtool构建的对象文件称为libtool对象:这些文件使用.lo后缀。Libtool库是由这些Libtool对象构建的。
您不应该对.la或.lo文件的结构以及libtool如何构造它们进行任何假设:这是libtool关心的问题,人们最不希望了解的是libtool的内部结构。然而,这些文件的存在很重要,因为在构建libtool库时,它们被用作Makefiles规则中的目标和依赖项。在某些情况下,您可能不得不引用这些,例如,在有条件地表示构建源文件的依赖关系时(请参阅有条件源的Libtool Libraries)。
考虑编写带有动态加载模块的插件系统的人,应该查看libltdl: libtool的dlopen接口(参见libtool手册中的使用libltdl)。这提供了一个可移植的dlopen工具来动态加载libtool库,并且还可以在不可避免的情况下实现静态链接。
在我们详细讨论如何使用libtool和Automake之前,应该注意的是,libtool手册中也有一节关于如何使用Automake和libtool(参见libtool手册中的Using Automake with libtool)。

构建libtool库

Automake使用Libtool来创建在主变量LTLIBRARIES中声明的库。
要使用LIBTOOL需要在configure.ac中添加LT_INIT宏。可选项为–enable-shared, --disable-shared, --enable-static, --disable-static, --with-pic, 和–without-pic .可以参考Libtool手册
需要注意的是,如果你加了宏之后还一直报错,那可能是没装libtool:

sudo apt-get install libtool

每个_LTLIBRARIES变量都是一个需要创建的共享库的列表。例如,为了创建一个名为libgettext.a的库和它对应的共享库,并且把它安装到libdir,可以写:

lib_LTLIBRARIES = libgettext.la
libgettext_la_SOURCES = gettext.c gettext.h …

Automake预先定义了变量pkglibdir,所以你可以使用pkglib_LTLIBRARIES来安装’ $(libdir)/@PACKAGE@/ '中的库。
如果gettext.h是一个公共头文件,为了让人们使用库需要安装它,它应该使用_HEADERS变量来声明,而不是在libgettext_la_SOURCES中。后者中列出的头应该是不属于公共接口的内部头。

lib_LTLIBRARIES = libgettext.la
libgettext_la_SOURCES = gettext.c …
include_HEADERS = gettext.h …

一个包可以与使用它的其他程序一起构建和安装这样的库。应该使用LDADD来指定这个依赖项。下面的示例构建了一个名为hello的程序,该程序与libgettext.la有链接。

lib_LTLIBRARIES = libgettext.la
libgettext_la_SOURCES = gettext.c …

bin_PROGRAMS = hello
hello_SOURCES = hello.c …
hello_LDADD = libgettext.la

hello是静态链接还是动态链接到libgettext.La是未知的:这将取决于libtool的配置和主机的能力。
需要注意的是,在main.c目录里编译出的并不是最终的进程文件,make install的才是。
以下是开源代码klish的automake文件内容节选:

#定义libclish.so或libclish.a
lib_LTLIBRARIES += libclish.la

#定义用于编译这个库的源文件
libclish_la_SOURCES = \
	clish/plugin_builtin.c \
	clish/private.h

#定义用于编译库的flag
libclish_la_LDFLAGS = $(AM_LDFLAGS) $(VERSION_INFO) @XML_LDFLAGS@
libclish_la_CFLAGS = @XML_CFLAGS@ $(DEBUG_CFLAGS) $(LEGACY_CFLAGS) $(NSS_CFLAGS)

#定义编译这个库所需的依赖
libclish_la_DEPENDENCIES = \
	liblub.la \
	libtinyrl.la \
	libkonf.la
libclish_la_LIBADD = \
	$(libclish_la_DEPENDENCIES) \
	@XML_LIBS@
	
#定义编译的进程
bin_PROGRAMS += \
	bin/clish

#定义编译进程所需的源文件
bin_clish_SOURCES = bin/clish.c

#定义源文件依赖的库
EXTRA_bin_clish_DEPENDENCIES = \
	@CLISH_PLUGIN_BUILTIN_LIBS@
bin_clish_LDADD = \
	libclish.la \
	libkonf.la \
	libtinyrl.la \
	liblub.la \
	@CLISH_PLUGIN_BUILTIN_LIBS@ \
	$(LIBOBJS)

Q:automake中的lib_LTLIBRARIES和lib_LIBRARIES有什么区别
chatgpt A:在Automake中,lib_LTLIBRARIESlib_LIBRARIES是用于定义库文件的变量,但它们有一些区别。

lib_LTLIBRARIES用于定义链接时加载的动态库(shared library)。动态库是在运行时动态加载的库文件,可以在多个程序之间共享,并且可以通过运行时链接器进行符号解析。lib_LTLIBRARIES变量通常用于构建动态库,并且在生成Makefile时,Automake会自动为这些库生成适当的链接规则。

示例:

lib_LTLIBRARIES = libexample.la
libexample_la_SOURCES = example.c

lib_LIBRARIES用于定义静态库(static library)。静态库是在编译时链接到目标程序的库文件,它们会被完整地复制到目标程序中,因此目标程序在运行时不需要依赖外部的库文件。lib_LIBRARIES变量通常用于构建静态库,并且在生成Makefile时,Automake会自动为这些库生成适当的链接规则。

示例:

lib_LIBRARIES = libexample.a
libexample_a_SOURCES = example.c

总结:

  • lib_LTLIBRARIES用于定义动态库,会生成动态链接库文件(.so文件)。
  • lib_LIBRARIES用于定义静态库,会生成静态库文件(.a文件)。

需要注意的是,lib_LTLIBRARIESlib_LIBRARIES变量的值是Makefile规则的目标文件列表。这些变量还可以包含其他属性,例如源文件列表、编译选项等,以便在构建库时进行自定义配置。

8.3.3 条件构建libtool库

和条件构建进程相似,有两个主要的方法来条件的构建库:使用automake条件和autoconf AC_SUBSTitutuions.

您必须了解的重要实现细节是,库的安装位置与libtool有关:需要在链接时使用-rpath选项指出它。对于目标目录在Automake运行时已知的库,Automake将自动向libtool提供适当的-rpath选项。对于在一些可安装的_LTLIBRARIES变量(如lib_LTLIBRARIES)中显式列出的库,情况就是如此。
但是,对于在配置时确定的库(因此在EXTRA_LTLIBRARIES中提到),Automake不知道最终的安装目录。对于这样的库,必须手动将-rpath选项添加到适当的_LDFLAGS变量中。
下面的示例说明了这两种方法之间的区别。
下面是一个示例,其中WANTEDLIBS是一个AC_SUBSTed变量,在./configure-time处设置为libfoo.la,libbar.la,要么都有,要么没有。虽然’ $(WANTEDLIBS) '出现在lib_LTLIBRARIES中,但Automake无法猜测它与libfoo.la或libbar.la有关,此时,它为这两个库创建链接规则。因此,必须显式提供-rpath参数。

EXTRA_LTLIBRARIES = libfoo.la libbar.la
lib_LTLIBRARIES = $(WANTEDLIBS)
libfoo_la_SOURCES = foo.c …
libfoo_la_LDFLAGS = -rpath '$(libdir)'
libbar_la_SOURCES = bar.c …
libbar_la_LDFLAGS = -rpath '$(libdir)'

下面是使用名为WANT_LIBFOO和WANT_LIBBAR的automake条件时相同的Makefile.am的样子。(Here is how the same Makefile.am would look using Automake conditionals named WANT_LIBFOO and WANT_LIBBAR. )现在Automake能够自己计算-rpath设置,因为很明显,如果安装了这两个库,它们最终都会在’ $(libdir) '中结束。

lib_LTLIBRARIES =
if WANT_LIBFOO
lib_LTLIBRARIES += libfoo.la
endif
if WANT_LIBBAR
lib_LTLIBRARIES += libbar.la
endif
libfoo_la_SOURCES = foo.c …
libbar_la_SOURCES = bar.c …
8.3.4条件编译源码的Libtool库

9.其他派生对象

10.其他gnu工具

11.创建文档

12.安装了些什么

13.清除了些什么

14.需要发布哪些文件

15.对测试套件(test suites)的支持

16.重构Makefiles

17.改变Automake的行为

18.其它规则

19.包含(include)

20.条件(Conditionals)

21.静默构建(Silencing make)

22.什么时候Automake不够用

23.发布`Makefile.in’

24.其他补充

24.1注释

在 configure.ac 文件中,以 dnl 开头的行是注释行。dnl 是 autoconf 的注释标记,用于告诉 autoconf 预处理器忽略该行以及该行以下的内容。

dnl 是 “Discard to New Line” 的缩写,它被用于在 configure.ac 文件中注释掉一行或多行的内容,而不会对 autoconf 的预处理和生成配置脚本产生任何影响。

在 configure.ac 文件中使用 dnl 注释行的目的是增加代码的可读性,删除无用的注释内容或临时调试代码,使配置文件更简洁明了。

库文件包含

链接了静态库后,只会把进程中调用了的函数所在的.o从.a中抽取出来编译到进程,不会直接把整个静态库全链接进去
如果想要全部链接,可以使用-Wl,–whole-archive选项
在Makefile或编译命令中,-Wl,–whole-archive选项用于告诉链接器将指定的目标文件库中的所有目标文件都包含在最终的可执行文件中,而不仅仅是被引用的目标文件。

默认情况下,链接器只会将库中包含未解析的目标文件添加到最终的可执行文件中。然而,有时候我们希望将整个库中的所有目标文件都包含进去,即使它们没有被直接引用。

例如,假设有一个静态库libexample.a,其中包含了一些未被引用的目标文件。在正常情况下,链接器会忽略这些未被引用的目标文件,导致它们不会出现在最终的可执行文件中。但是,如果我们使用-Wl,–whole-archive选项来编译链接,链接器将会包含整个库中的所有目标文件。

指定外部依赖

当需要指定编译选项,以及头文件件和库的路径时可以按如下方式

dhcpd_CFLAGS = -Wall -O2
AM_CPPFLAGS = -I/path/to/library/headers
dhcpd_LDADD = -L/path/to/library -lmylibrary

dhcpd_CFLAGS 是用来指定编译 dhcpd 程序时的编译选项,比如 -Wall, -O2 等。它一般用来设置编译参数而不是头文件路径。
如果需要包含特定的头文件,推荐使用 AM_CPPFLAGS 变量来指定头文件路径。AM_CPPFLAGS 是 automake 提供的一个变量,用来指定 C/C++ 编译器的预处理器选项。
将编译选项放在 dhcpd_CFLAGS 中,将头文件路径放在 AM_CPPFLAGS 中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值