在构建大型的C/C++项目时,往往需要借助Makefile. 然而手动编写makefile是相当复杂的,这一点在程序需要跨平台的时候表现地更为明显。在此背景下,我们需要借助于一些自动化的工具来生成Makefile和configure。本文主要介绍autoconf, automake等自动化工具,默认读者已经具备linux shell脚本和makefile的基础知识。
autoconf,automake,libtool等工具
目前automake支持三种目录层次:flat、shallow和deep。
* flat(平),指的是所有文件都位于同一个目录中,就是所有源文件、头文件以及其他库文件都位于当前目录中,且没有子目录。Termutils就是这一类。
* shallow(浅),指的是主要的源代码都储存在顶层目录,其他各个部分则储存在子目录中,就是主要源文件在当前目录中,而其它一些实现各部分功能的源文件位于各自不同的目录。automake本身就是这一类。
* deep(深),指的是所有源代码都被储存在子目录中;顶层目录主要包含配置信息
先来看看整个project的构成:其中源程序文件有三个test.c , test.h, main.c ,其他的文件是我们在编译过程中自动和手动生成的。
|-- Makefile.am
|-- configure.ac
|-- lib
| |-- test.c
| `-- test.h
`-- main
|-- Makefile.am
`-- main.c
与Makefile和configure有关的配置文件我们放在后面介绍,这里给出工程文件的相关code。
/* project/main.c */
#include
#include "test.h"
int main()
{
printf("main entrance.\n");
test_method();
return 0;
}
/* project/lib/test.c */
#include
#include "../include/test.h"
void test_method()
{
printf("test method.\n");
}
//test.h
void test_method();
现在我们的目的是根据模板来生成Makefile文件;这个流程,我们可以用这样一张图表示
从这张图,我们可以看到,我们需要手动编写的有两个文件:Makefile.am && configure.in;
$ autoscan
这条命令,会根据当前目录下地源程序结构,生成对应的configure.scan模板,根据这个文件,修改成configure文件模板,configure.in,这个文件是生成configure文件的基础。如果我们需要定义一些configure的具体参数,那么我们需要更改这个模板。实际上在大型项目的构建过程中,这个文件正是初始的几个需要手动编写的文件之一。如何编写configure.in文件,我们将在其他地方说明。
#config.scan -*- 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([lib/test.c])
AC_CONFIG_HEADERS([config.h])
# Checks for programs.
AC_PROG_CC
# Checks for libraries.
# Checks for header files.
# Checks for typedefs, structures, and compiler characteristics.
# Checks for library functions.
AC_CONFIG_FILES([Makefile
main/Makefile])
AC_OUTPUT
将上述文件进行改动如下:
//config.in
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69])
AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS])
AM_INIT_AUTOMAKE
AC_CONFIG_SRCDIR([lib/test.c])
AC_CONFIG_HEADERS([config.h])
# Checks for programs.
AC_PROG_CC
# Checks for libraries.
# Checks for header files.
# Checks for typedefs, structures, and compiler characteristics.
# Checks for library functions.
AC_CONFIG_FILES([Makefile
main/Makefile])
AC_OUTPUT
执行命令aclocal和autoconf,分别会产生aclocal.m4及configure两个文件。
$aclocal
$autoconf
automake将根据Makefile.am生成Makefile.in.从上面的目录结构中,我们可以看到,这个项目中含有两个Makefile.am,第一个在root目录,第二个在main目录。内容分别如下:
ykhuang@ubuntu:/tmp/project$ cat Makefile.am
AUTOMAKE_OPTIONS = foreign
SUBDIRS = main
ykhuang@ubuntu:/tmp/project$ cat main/Makefile.am
AM_CPPFLAGS = -I$(top_srcdir)/lib
noinst_PROGRAMS= main
main_SOURCES = $(top_builddir)/lib/test.c\
$(top_builddir)/main/main.c
Makefile.am是整个流程中的关键,详细可以参考GNU 相关文档,本文也会在后续的章节中进行介绍。
$automake
$configure
configure.in的模板形式如下:
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69])
AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS])
AM_INIT_AUTOMAKE
AC_CONFIG_SRCDIR([config.h.in])
AC_CONFIG_HEADERS([config.h])
# Checks for programs.
AC_PROG_CC
# Checks for libraries.
# Checks for header files.
# Checks for typedefs, structures, and compiler characteristics.
# Checks for library functions.
AC_CONFIG_FILES([Makefile
main/Makefile])
AC_OUTPUT
Autoconf提供了各种内置的宏,用于检测;AM_INIT_AUTOMAKE && AC_CONFIG_FILES
是两个我们需要的手动加入和编写的宏定义。
Makefile.am是一种比Makefile更高层次的规则。只需指定要生成什么目标,它由什么源文件生成,要安装到什么目录等构成。
Makefile.am的一般形式:
|文件|格式|
|可执行文件|test|
文件 | 格式 |
---|---|
可执行文件 | bin_PROGRAMS = foo |
所有*.c文件将被编译成一个目标文件,最后链接成一个可执行文件 | foo_SOURCES = foo.c func.c |
foo_LDADD = | |
foo_LDFLAGS = | |
foo_DEPENDENCIES = | |
静态库 | lib_LIBRARIES = foo.a |
foo_a_SOURCES = foo.c func.c | |
foo_a_LDADD = | |
foo_a_LDFLAGS = | |
foo_a_DEPENDENCIES |
对于可执行文件和静态库类型,如果只想编译,不想安装到系统中,可以用noinst_PROGRAMS
代替bin_PROGRAMS
,noinst_LIBRARIES
代替lib_LIBRARIES
。同时,这些书写规则都是局部的,仅仅对一个目标起作用。Makefile.am还有一些全局变量,能够作用于所有规则.
全局变量 | 含义 |
---|---|
AM_CPPFLAGS | 编译器的-I参数 |
SUBDIRS | 在处理顶层目录之前,先递归处理子目录 |
系统预定义的两个基本路径
路径名称 | 含义 |
---|---|
$(top_srcdir) | 源代码所在目录 |
$(top_builddir) | 编译顶层目录 |
在很多情况下,我们并不需要源代码目录和编译目录相同,假设编译顶层目录是build,那么此时我们在build目录下运行configure脚本,就能生成对应Makefile文件,然后运行相关命令即可。具体可以参考GNU相关文档的VPATH章节。