目录
- 2.2 Use Cases for the GNU Build System
- 2.2.1 Basic Installation
- 2.2.2 Standard Makefile Targets
- 2.2.3 Standard Directory Variables
- 2.2.4 Standard Configuration Variables
- 2.2.5 Overriding Default Configuration Setting with config.site
- 2.2.6 Parallel Build Trees (a.k.a. VPATH Builds)
- 2.2.7 Two-Part Installation
- 2.2.8 Cross-Compilation
- 2.2.9 Renaming Programs at Install Time
- 2.2.10 Building Binary Packages Using DESTDIR
- 2.2.11 Preparing Distributions
- 2.2.12 Automatic Dependency Tracking
- 2.2.13 Nested Packages
- 2.3 How Autotools Help
- 2.4 A Small Hello World
2.2 Use Cases for the GNU Build System
2.2.1 Basic Installation
automake 源码包提供了一个amhello-1.0.tar.gz示例tar包,在automake-1.16.1/doc/路径中有解压缩的amhello源码包。
进入amhello目录中,依次执行 ./configure、make、make check、sudo make install、make installcheck命令完成安装。
- configure命令探测系统的各种特性,最终生成一系列Makefile文件。amhello示例只生成了两个Makefile。
- make命令编译生成安装该软件需要的所有可执行文件、库文件和脚本文件。amhello示例只生成了hello可执行文件。
- make check命令运行软件的测试样例,该步不是必须的。但在安装程序前运行该步可保证编译过程产生的软件是想要的。amhhello示例没有测试样例,所以运行make check什么都不做。
- make install命令安装编译的软件到系统中。这会拷贝编译生成的可执行文件、库文件、脚本及其他一些来自源码包的数据文件到各类型文件指定的目录中。默认情况下,所有文件都安装到 /usr/local目录中,其中二进制可执行文件安装到 /usr/local/bin/ 目录,库文件安装到 /usr/local/lib文件等。这些目标目录一般用户没有可写权限,所以运行make install前需通过su命令切换root用户,或直接通过sudo make install获取root权限安装。在amhello示例中,hello可执行文件被安装到 /usr/local/bin目录,README文件被安装到 /usr/local/share/doc/amhello目录。
- make installcheck命令在已安装的程序文件上运行测试样例。make check在源码包中测试要安装的程序文件,make installcheck在程序安装目录中测试已经安装的程序文件。有些软件安装时可能无法在源码包中运行make check,此时可运行make installcheck检查程序。有些软件make check和make installcheck执行相同的测试,只不过一个测试未安装程序文件,一个测试已安装程序文件。当源码包中未安装程序文件路径分布与已安装程序文件路径分布不同时,可分别执行两个check命令测试程序文件。make installcheck还可以用于检测安装不完全问题。amhello示例执行make installcheck什么都不做。
2.2.2 Standard Makefile Targets
在GNU Build System中,check、install和installcheck都是传给make命令的参数,称为targets。make命令是make all的缩写,all是GNU Build System中的默认target。
GNU Coding Standards中常用的targets如下:
- make all
- make install
- make install-strip
与make install相同,只是剥离了debugging symbols - make uninstall
make install的反操作,删除安装的程序文件。(需要与执行安装操作有相同build tree,可以是同一源码包重新构建的build tree,源码包的不同版本未测试) - make clean
删除build tree中所有make all命令生成的文件 - make distclean
在make clean的基础上,再删除由 ./configure命令生成的文件 - make check
- make installcheck
- make dist
从源码包中重新生成package-version.tar.gz发布包
2.2.3 Standard Directory Variables
GNU Coding Standards指定一组层次变量来指定安装目录,部分如下:
| Directory variable | Default value |
| 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} |
...
用户可以在执行 ./configure时根据需要修改某些目录变量,如:
lgd@ubuntu:~/Workspace/amhello$ ./configure --prefix ~/usr
或
lgd@ubuntu:~/Workspace/amhello$ ./configure --prefix=$HOME/usr
第二种形式不可以使用 –prefix=~/usr,会报如下错误:
configure: error: expected an absolute directory name for --prefix: ~/usr
对于amhello示例,修改后程序文件安装为 ~/usr/bin/hello和 ~/usr/share/doc/amhello/README
其他目录选项可以通过 ./configure --help查看
2.2.4 Standard Configuration Variables
GNU Coding Standards定义了一组标准配置变量用于编译过程,部分变量为:
CC C compiler command
CFLAGS C compiler flags
CXX C++ compiler command
CXXFLAGS C++ compiler flags
LDFLAGS linker flags
CPPFLAGS C/C++ preprocessor flags
. . .
configure脚本执行时一般会为这些变量配置合适的值。用户可能需要覆盖某些变量的值,例如有多个版本的编译器时需要使用非默认的那个、需要引用默认路径外的头文件、需要引用默认链接路径外的库等,此时可以通过传递给configure脚本相应选项参数来覆盖某些变量的配置,如:
lgd@ubuntu:~/Workspace/amhello$./configure --prefix ~/usr CC=gcc-3 \
CPPFLAGS=-I$HOME/usr/include LDFLAGS=-L$HOME/usr/lib
可通过 ./configure --help查看所有的变量配置选项。
2.2.5 Overriding Default Configuration Setting with config.site
当要使用相同的configure脚本配置选项来安装多个软件时,可以通过创建一个文件来保存安装这些软件共同需要的配置选项。如果prefix/share/config.site文件存在(路径的 prefix 前缀在执行 ./configure脚本时可能会指定,此时要到指定的路径下寻找config.site文件),configure脚本执行时会先引用该文件中的配置,对于configure命令:
lgd@ubuntu:~/Workspace/amhello$./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
此时,任何时候通过 –prefix ~/usr选项调用configure脚本,configure脚本都会寻找 ~/usr/share/config.site文件并定义相应的配置选项:
lgd@ubuntu:~/Workspace/amhello$./configure --prefix ~/usr
configure: loading site script /home/lgd/usr/share/config.site
...
2.2.6 Parallel Build Trees (a.k.a. VPATH Builds)
GNU Build System区分两种树:source tree和build tree。(tree指的是目录层级分布,不包含任何文件)
source tree的根是包含configure脚本的目录。tree目录中包含所有可被发布的文件,并可能被组织为多个子目录。
build tree的根是执行configure脚本时的当前目录。tree目录中通常包含目标文件、可执行文件、库文件及其他从源文件编译生成的各种文件,这些文件在发布时不会被包含。build tree通常与source tree有相同的子目录分布。build tree中的子目录一般是由构建系统自动创建的。
如果configure脚本在其所在目录中被执行,则source tree和build tree就合并到了一起:编译生成的文件都放置在编译它时需要的源文件所在的目录中。
VPATH builds使用户在编译过程中保证其源码包的整洁。用户可在其源码包根目录下创建一个build/ 目录,在该目录下调用 configure脚本。
这种source tree和build tree不同的机制,通常被称为parallel builds或VPATH builds。parallel builds的释意带有一定误导:parallel是指build tree是source tree的一个映射,并不是指编译命令并发运行。
VPATH builds的一个有趣功能是,对同一source tree,可以创建多个build tree,并在每个build tree使用不同的配置选项调用configure脚本:
~ % 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
注意:如果在source tree根目录amhello-1.0中执行了 ./configure,则在创建amhello-1.0/build目录作为build tree的根目录并在其中执行 configure脚本 时,会报下面的错误:
lgd@ubuntu:~/Workspace/amhello/build$ ../configure
configure: error: source directory already configured; run "make distclean" there first
此时需要在source tree根目录amhello-1.0中执行make distclean来清除所有生成文件(由 ./configure和 make 命令生成)。
如果在amhello-1.0/build目录中执行了configure后,又创建了amhello-1.0/debug目录作为另一个build tree的根目录,在amhello-1.0/debug目录中执行configure脚本时不会报上述错误,所以source tree映射的各build tree(即不将source tree根目录作为执行configure脚本的根目录的build tree)之间执行configure脚本是不影响的。(make distclean在映射的build tree中执行时,只删除文件,不删除configure脚本执行时生成的映射自source tree的层次结构目录)
在网络文件系统中,用一种类似的方法用于在不同主机上编译同一个源码包。例如某个源码包在一个目录中,该目录被两台主机共享: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
...
在第二台主机中可以执行完全相同的编译操作,甚至是与第一台主机同时执行。在该示例中,/nfs/src/amhello-1.0完全可以是只读的。实际上,VPATH builds也是一种从只读介质(例如CD-ROM)中编译源码包的方式。
2.2.7 Two-Part Installation
在上一小节的示例中,两台主机共享一个source tree,但是两台主机的编译和安装动作都是各自独立进行的。
GNU Build System还支持通过网络方式部分安装需要被多台主机共享的文件。这是通过区分架构依赖文件和非架构依赖文件实现的,并给Makefile文件提供了两个不同的targets来分别安装相应的文件。其中install-exec targets用于安装架构依赖文件,install-data targets用于安装非架构依赖文件。目前我们使用的make install可以认为是make install-exec install-data的缩写。
对于GNU Build System来说,架构依赖文件和非架构依赖文件的区别只是在于指定它们安装位置的目录变量的不同。所有基于exec-prefix的变量指明了通过make install-exec安装架构依赖文件的目标目录,其它变量指明了通过make install-data安装非架构依赖文件的目标目录。
现在重新看网络文件系统中两台主机上安装源码包的示例,假设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
...
如果包中有安装检测样例,可以通过执行make installcheck来验证部分安装的包也是正常运行的。
2.2.8 Cross-Compilation
交叉编译是在某个平台上编译一个二进制可执行文件,而该文件在另一个平台上运行。在交叉编译中,区分哪个是编译平台build platform来编译源码包及哪个是主机平台host platform来运行可执行文件是很重要的。下面的configure选项用于指定它们:
--build=build
指定源码包在哪个系统上编译
--host=host
指定程序和库在哪个系统上运行
当使用了 –host选项时,configure脚本会查找选项平台对应的交叉编译组件。交叉编译工具通常使用目标架构的名称作为工具的命名前缀。
在交叉编译过程中上面两个选项已经够用了。有个例外是当编译的源码包本身是一个交叉编译器的源码包时,需要通过另一个选项来指定将要编译的交叉编译器对应的目标架构:
--target=target
当构建一个编译工具时,指定使用该编译工具得到的结果文件在哪个系统上运行。
通过使用 –build和 –target选项,可以交叉编译一个交叉编译器,这样的三方交叉编译称为Canadian cross。
2.2.9 Renaming Programs at Install Time
GNU Build System提供在安装时重命名可执行文件和man pages的方法。这在再次安装某个系统中已经存在但又不想被覆盖的包时很有用。这可以通过三个configure选项来实现:
--program-prefix=prefix
在将要安装的程序名字上追加前缀
--program-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 Building Binary Packages Using DESTDIR
对于需要在多台主机上部署和升级软件的系统管理员来说,make install和make uninstall并不能安全满足需求。类似的软件管理需要知道软件安装时都有哪些文件被安装了。
DESTDIR变量可用于实现一种暂存式安装。在执行configure脚本时按正常安装动作执行来指定安装位置(如 –prefix /usr),但在make install阶段,需要使用DESTDIR变量来指定一个用于实现安装转移的目录的绝对路径,在该目录中可以很方便的回顾都有哪些文件被安装了,并且后续可以通过其他方式很方便的将要安装的文件拷贝到最终安装目录。
对amhello示例操作如下:
lgd@ubuntu:~/Workspace/amhello$ ./configure --prefix ~/usr
...
lgd@ubuntu:~/Workspace/amhello$ make
...
lgd@ubuntu:~/Workspace/amhello$ make DESTDIR=$HOME/inst install
...
lgd@ubuntu:~/Workspace/amhello$ % cd ~/inst
lgd@ubuntu:~/inst % find . -type f -print > ../files.lst
lgd@ubuntu:~/inst % tar zcvf ~/amhello-1.0-i686.tar.gz `cat ../files.lst`
./home/lgd/usr/share/doc/amhello/README
./home/lgd/usr/bin/hello
find命令和tar命令会将安装文件以与make install正常安装时相同的目录层级结构打包.(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.)。这样就可以将tar包直接复制解压到相同平台主机的根目录 / 完成安装,而不用在每个主机都编译源码包。
amhello示例通过make DESTDIR=$HOME/inst install安装软件时,会将安装目录转移到 $HOME/inst目录,分别安装了 $HOME/inst/home/lgd/usr/bin/hello文件和 $HOME/inst/home/lgd/usr/share/doc/amhello/README文件。
lgd@ubuntu:~/Workspace/amhello$ ll ~/inst/home/lgd/usr/
bin/ share/
当安装软件到不同架构时,可以在执行make install-data和make install-exec时使用上述方式将非架构依赖文件打包。
2.2.11 Preparing Distributions
之前已经提到过make dist,这个target会收集所有的源文件和构建系统需要的文件,然后生成一个名为 package-version.tar.gz 的tar包。(发布的tar包解压后可直接./configure && make && make install进行安装,所以包含aclocal.m4、compile、config.h.in、configure、depcomp、install-sh、Makefile.in、missing等一系列autotools生成的build system需要的文件。)
另一个有用的命令是make distcheck。distcheck target就像dist一样构造一个package-version.tar.gz,但它额外保证在这之前的大部分构建动作正常工作:
- 它试图对源码包执行一次完整的编译流程:解压缩make dist构造的tar包,执行configure、make、make check、make install和make installcheck,甚至会执行make dist。
- 它会通过只读source tree来测试VPATH builds功能正常。
- 它保证make clean、make distclean和make uninstall不会遗漏任何文件。
- 它检查DESTDIR安装可正常执行。
所有这些动作都在一个临时目录中执行,所以不需要root权限。注意一点,make distcheck在临时目录中执行的上述检查涉及到的文件位置及子目录层级结构等相关变量都是临时使用的默认值,在实际操作中可能会发生变化。
发布一个源码包失败意味着make distcheck执行的上述检查中有某个或某些无法正常执行。所以在发布源码包前成功执行make distcheck是一个好习惯。
2.2.12 Automatic Dependency Tracking
依赖关系跟踪是编译过程中的附带功能。当build system编译一个源文件时,它计算该源文件一系列的依赖关系(在C中是正在编译的源文件include的头文件)。当make执行时,如果源文件的某些依赖文件发生的变化,源文件会被重新编译。
Automake在执行过程中自动进行依赖关系跟踪,开发者可以选择关闭。
configure脚本执行过程中会探测每一个编译器支持的依赖关系跟踪。
~/amhello-1.0 % ./configure --prefix /usr
...
checking dependency style of gcc... gcc3
...
因为依赖关系是在编译过程中附带生成的,所以第一次编译源码包时没有依赖关系信息。这并不会影响源码包编译,因为第一次编译时会编译所有源文件,make不需要决定哪些源文件需要重新编译。实际上,在只编译一次时依赖关系跟踪并没有什么用,有一个configure选项可以关闭该功能:
--disable-dependency-tracking
关闭依赖关系跟踪,在只构建一次时可提高编译速度。
某些编译器在编译过程中不提供依赖关系跟踪功能,需要单独执行专门的依赖关系跟踪以生成依赖关系信息(也可能是使用其他工具生成依赖关系)。此时性能损失使这种方式下的依赖关系跟踪功能是默认关闭的。可以传递 –enable-dependency-tracking选项给configure脚本来使能依赖关系跟踪功能:
--enable-dependency-tracking
使能依赖关系跟踪(Do not reject slow dependency extractors)
2.2.13 Nested Packages
已经通过Autotools构建了build system的包可以嵌套任意层数。
例如,源码包A要发布其中某个库文件,该库文件在某个子目录(库B)中。库B有完整的源码包,包含自己的一套GNU Build System。源码包A的configure脚本执行时调用库B的configure脚本来作为它的一部分执行,编译和安装A也意味着编译和安装B。生成的A的发布包也会包含B。
(Nested Packages指一个Package A在其源码根目录rootA下有自己的configure.ac,即可以构建自己的build tree,这里Package A想添加一个library B,这个library B在其源码根目录rootB下也有configure.ac,这时可以将library B的整个源码直接拷贝到Package A某个子目录下,假设是rootA/dynamic-plugin目录,然后只需要:
1、在Package A根目录configure.ac中添加AC_CONFIG_SUBDIRS([dynam ic-plugin/rootB]),中括号中是library B根目录(library B configure.ac所在目录)相对于Pachage A根目录(Package A configure.ac所在目录)的相对路径,
2、在dynamic-plugin(library B源码拷贝目录)中的Makefile.am中的SUBDIRS += rootB
执行这两步后,在rootA中执行autotools某些操作时,rootB中也会执行相应操作;在rootA中执行make命令时,也会编译library B的源码 。可以认为是library B build tree是Package A build tree的一个子树。)
通过这种方式可以集合多个源码包到一起。安装者只需要在一个源码包中执行configure、build和install动作,而开发者可以在子源码包独立性的基础上进行开发。
当配置嵌套源码包时,传递给顶层configure脚本的参数会被递归传递给嵌套的configure脚本。某个源码包如果不理解某个选项时会忽略它,认为该选项是用于其他源码包的。
configure --help=recursive命令列出了被所有嵌套源码包支持的configure选项。
2.3 How Autotools Help
2.4 A Small Hello World
2.4.1 Creating amhello-1.0.tar.gz
2.2章节中已经提到了amhello示例,也可以自己重新创建一个amhello源码包。包含如下所示的几个文件:
lgd@ubuntu:~/Workspace/automake-1.16.1/doc/amhello$ ll
total 24
drwx------ 3 lgd lgd 4096 Nov 28 22:18 ./
drwx------ 3 lgd lgd 4096 Nov 28 22:18 ../
-rwx------ 1 lgd lgd 384 Mar 11 2018 configure.ac*
-rwx------ 1 lgd lgd 225 Mar 11 2018 Makefile.am*
-rwx------ 1 lgd lgd 100 Mar 11 2018 README*
drwx------ 2 lgd lgd 4096 Nov 28 22:18 src/
lgd@ubuntu:~/Workspace/automake-1.16.1/doc/amhello$ ll src
total 16
drwx------ 2 lgd lgd 4096 Nov 28 22:18 ./
drwx------ 3 lgd lgd 4096 Nov 28 22:18 ../
-rwx------ 1 lgd lgd 328 Mar 11 2018 main.c*
-rwx------ 1 lgd lgd 232 Mar 11 2018 Makefile.am*
lgd@ubuntu:~/Workspace/automake-1.16.1/doc/amhello$
main.c源文件在src子目录中,这样在后续学习中扩展amhello包内容时,可以很容易的创建用于man pages的man子目录和用于data files的data子目录,main.c源文件内容如下:
#include <config.h>
#include <stdio.h>
int main (void)
{
puts ("Hello World!");
puts ("This is " PACKAGE_STRING ".");
return 0;
}
README包含一些amhello的描述信息
This is a demonstration package for GNU Automake.
Type ’info Automake’ to read the Automake manual.
Makefile.am和src/Makefile.am分别包含对应于amhello和amhello/src目录的Automake指令
SUBDIRS = src
dist_doc_DATA = README
bin_PROGRAMS = hello
hello_SOURCES = main.c
configure.ac包含用于创建configure脚本的Autoconf指令
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
lgd@ubuntu:~/Prac/amhello$ autoreconf --install
configure.ac:7: installing './compile'
configure.ac:6: installing './install-sh'
configure.ac:6: installing './missing'
src/Makefile.am: installing './depcomp'
此时amhello源码包的build system就创建完成了。
除了上边命令行输出提到到几个脚本文件,autoreconf命令还创建了configure, config.h.in, Makefile.in和src/Makefile.in四个文件,此时amhello源码包中包含的文件:
lgd@ubuntu:~/Prac/amhello$ ll
total 304
drwx------ 4 lgd lgd 4096 Nov 28 22:48 ./
drwxrwxr-x 6 lgd lgd 4096 Nov 28 22:41 ../
-rw-rw-r-- 1 lgd lgd 42147 Nov 28 22:41 aclocal.m4
drwxr-xr-x 2 lgd lgd 4096 Nov 28 22:41 autom4te.cache/
-rwxr-xr-x 1 lgd lgd 7333 Nov 28 22:41 compile*
-rw-rw-r-- 1 lgd lgd 625 Nov 28 22:41 config.h.in
-rwxrwxr-x 1 lgd lgd 145965 Nov 28 22:41 configure*
-rwx------ 1 lgd lgd 384 Nov 28 22:41 configure.ac*
-rwxr-xr-x 1 lgd lgd 23567 Nov 28 22:41 depcomp*
-rwxr-xr-x 1 lgd lgd 15155 Nov 28 22:41 install-sh*
-rwx------ 1 lgd lgd 225 Nov 28 22:41 Makefile.am*
-rw-rw-r-- 1 lgd lgd 26639 Nov 28 22:41 Makefile.in
-rwxr-xr-x 1 lgd lgd 6872 Nov 28 22:41 missing*
-rwx------ 1 lgd lgd 100 Nov 28 22:41 README*
drwx------ 2 lgd lgd 4096 Nov 28 22:41 src/
lgd@ubuntu:~/Prac/amhello/src$ ll
total 36
drwx------ 2 lgd lgd 4096 Nov 28 22:41 ./
drwx------ 4 lgd lgd 4096 Nov 28 22:52 ../
-rwx------ 1 lgd lgd 328 Nov 28 22:41 main.c*
-rwx------ 1 lgd lgd 232 Nov 28 22:41 Makefile.am*
-rw-rw-r-- 1 lgd lgd 17212 Nov 28 22:41 Makefile.in
configure脚本会以config.h.in, Makefile.in和src/Makefile.in文件为模板生成适应当前系统的config.h, Makefile, and src/Makefile文件:
lgd@ubuntu:~/Prac/amhello$ ./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... /bin/mkdir -p
checking for gawk... no
checking for mawk... mawk
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 whether gcc understands -c and -o together... yes
checking for style of include used by make... GNU
checking dependency style of gcc... gcc3
checking that generated files are newer than configure... done
configure: creating ./config.status
config.status: creating Makefile
config.status: creating src/Makefile
config.status: creating config.h
config.status: executing depfiles commands
后续就可以在amhello目录下执行make、make install、make distcheck、make dist等命令。
注意:只有在源码包中没有构建GNU Build System时才需要运行autoreconf命令,当后续修改了Makefile.am和configure.ac文件中的指令时,build system中被相关修改涉及到的内容会在执行make时自动重新构建。
autoreconf是一个脚本,以正确的顺序执行autoconf、automake和一些其他命令。autoconf通过configure.ac文件生成configure脚本,automake通过Makefile.am和configure.ac文件生成一系列Makefile.in文件。
2.4.2 amhello’s configure.ac Setup Explained
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
configure.ac被autoconf和automake读取。文件中包含一些M4宏,这些宏会被展开为shell code并最终组成configure脚本。以 AC_ 为前缀的宏是Autoconf宏,以 AM_ 为前缀的宏是Automake宏。configure.ac的简单分析如下:
- 1、2行用于初始化Autoconf和Automake。AC_INIT宏的参数分别为源码包名称、源码包版本号、用于提交BUG的联系地址,其中联系地址会在 ./configure --help 输出内容的最后一行显示。AM_INIT_AUTOMAKE 的参数是一些automake 要使用的选项的集合。-Wall和-Werror选项告诉automake打开所有警告并上报为错误。在开始创建源码包时打开这些选项是安全的。foreign选项告诉automake这个源码包不遵守GNU标准。GNU标准源码包总是会发布像ChangeLog、AUTHORS等额外的文件,在当前amhello这样小的示例中,foreign选项使automake不会因为缺少这些文件而上报错误。
- AC_PROG_CC宏使configure脚本查找C编译器,并定义变量CC为查找到的编译器名字。Automake生成的src/Makefile.in文件使用CC变量来编译hello,所以当configure脚本通过src/Makefile.in生成src/Makefile时,它会使用找到的值来定义CC变量。如果Automake生成src/Makefile.in用到CC变量时发现configure.ac中没有使用定义,它会建议在configure.ac中添加AC_PROG_CC宏。
- AC_CONFIG_HEADERS([config.h]) 宏使configure脚本生成一个config.h文件,该文件集合了configure.ac中其他宏定义的一系列 #define宏。在amhello的示例中,只有AC_INIT定义了一些 #define宏,下面是示例中 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.0"
/* 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.0"
/* Version number of package */
#define VERSION "1.0"
- src/main.c中包含了config.h头文件,所以可以使用PACKAGE_STRING等宏。在实际的工程中,随着对每个探测到的系统特性定义一个 #define,config.h头文件会变的很大。
- AC_CONFIG_HEADERS宏声明了一个文件列表,在configure脚本执行时会根据列表中各文件对应的 .in模板来生各文件。Automake也会浏览该文件列表来确定它必须处理的Makefile.am文件。(重点:当在工程中添加一个新目录时,必须在AC_CONFIG_HEADERS文件列表中添加新目录对应的Makefile,否则Automake将不会处理新目录下的Makefile.am文件)。
- AC_OUTPUT宏是一个关闭命令,会生成configure脚本中一部分内容,该部分内容负责生成AC_CONFIG_HEADERS宏和AC_CONFIG_FILES宏声明的文件
2.4.3 amhello’s Makefile.am Setup Explained
src/Makefile.am文件包含用于编译和安装hello的Automake指令:
bin_PROGRAMS = hello
hello_SOURCES = main.c
一个Makefile.am文件跟一个普通Makefile的有相同的语法。当automake处理一个Makefile.am文件时,它会将Makefile.am文件中的所有内容拷贝到结果文件Makefile.in(用于后续configure脚本生成Makefile)中,但会执行其中的某些变量定义来产生一些编译规则及其他变量。通常Makefile.am文件中只包含类似于上述的变量定义,但也可能包含一些automake不会解释的变量和规则。
以 _PROGRAMS结尾的变量定义了一系列需要 Makefile编译生成的可执行文件名称。在Automake中,还有其他关键字如 _SCRIPTS、_DATA、_LIBRARIES等对应不同的文件类型。
bin_PROGRAMS宏的的bin部分告诉automake生成的可执行文件应该安装到bindir目录中。前文提到GNU Build System使用一组变量来指定一系列目标目录,并且允许用户根据需要修改这些变量。这组变量中任意一个都可以添加到某个关键字前边(变量省略了dir后续)来告诉automake该关键字对应类型的文件应该安装到哪个目录。
_PROGRAMS变量指定了一系列最终需要生成的可执行文件。对于某个可执行文件prog,prog_SOURCES变量指定了编译生成可执行文件prog需要的所有源文件,这些源文件最终将被编译链接成可执行文件prog。_SOURCES变量的另一个作用是告诉Automake在执行make dist发布软件tar包时,哪些文件需要被包含在生成的tar包中(还指明了在发布时从源码包的什么位置获取源文件,例如有prog_SOURCES = xxx.h,如果xxx.h头文件在上级目录中,make dist会找不到xxx.h头文件,需要修改为prog_SOURCES = …/xxx.h),示例中hello_SOURCES指明了main.c需要被包含在make dist生成的tar包中。
对于最顶层的Makefile.am:
SUBDIRS = src
dist_doc_DATA = README
SUBDIRS变量列出了一系列目录,在当前目录执行make命令时,make先递归处理SUBDIRS变量列出的目录,再处理当前目录。并且SUBDIRS还使make install命令在安装README之前先安装src/hello(不是因为上述Makefile.am文件中SUBDIRS变量和dist_doc_DATA变量定义时的前后顺序原因)。
dist_doc_DATA变量使README文件可被发布,并且make install时会被安装到docdir目录中(发布时README仍在源码包对应位置,对于amhello示例即在amhello目录中)。在执行make dist命令时 _DATA关键字指定的文件并不会自动包含在发布的tar包中,所以需要添加dist_ 前缀来指明发布时需要包含这些文件。但对于README文件来说这些不是必须的,不管指定README文件的 _DATA关键字是否有dist__前缀,甚至README文件是否被 _DATA关键字指定,automake会自动指定源码包中的README文件可发布(其他可自动发布的文件可通过automake --help命令查看)。所以dist_doc_DATA = README的唯一作用就是使README文件在make install时被安装。