<!--
/* Font Definitions */
@font-face
{font-family:宋体;
panose-1:2 1 6 0 3 1 1 1 1 1;
mso-font-alt:SimSun;
mso-font-charset:134;
mso-generic-font-family:auto;
mso-font-pitch:variable;
mso-font-signature:3 135135232 16 0 262145 0;}
@font-face
{font-family:Verdana;
panose-1:2 11 6 4 3 5 4 4 2 4;
mso-font-charset:0;
mso-generic-font-family:swiss;
mso-font-pitch:variable;
mso-font-signature:536871559 0 0 0 415 0;}
@font-face
{font-family:"/@宋体";
panose-1:2 1 6 0 3 1 1 1 1 1;
mso-font-charset:134;
mso-generic-font-family:auto;
mso-font-pitch:variable;
mso-font-signature:3 135135232 16 0 262145 0;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
{mso-style-parent:"";
margin:0cm;
margin-bottom:.0001pt;
text-align:justify;
text-justify:inter-ideograph;
mso-pagination:none;
font-size:10.5pt;
mso-bidi-font-size:12.0pt;
font-family:"Times New Roman";
mso-fareast-font-family:宋体;
mso-font-kerning:1.0pt;}
p
{mso-margin-top-alt:auto;
margin-right:0cm;
mso-margin-bottom-alt:auto;
margin-left:0cm;
mso-pagination:widow-orphan;
font-size:12.0pt;
font-family:宋体;
mso-bidi-font-family:宋体;}
pre
{margin:0cm;
margin-bottom:.0001pt;
mso-pagination:widow-orphan;
tab-stops:45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;
font-size:12.0pt;
font-family:宋体;
mso-bidi-font-family:宋体;}
/* Page Definitions */
@page
{mso-page-border-surround-header:no;
mso-page-border-surround-footer:no;}
@page Section1
{size:612.0pt 792.0pt;
margin:72.0pt 90.0pt 72.0pt 90.0pt;
mso-header-margin:36.0pt;
mso-footer-margin:36.0pt;
mso-paper-source:0;}
div.Section1
{page:Section1;}
-->
HelloWorld
automake
比起
IDE
要复杂很多,这里我们先写一个
Hello World
例子,明白其中的基本概念后,再用它来管理实际的工程。
o
目录结构
最顶层目录名用模块名称,这里是
helloworld
。
源文件放在模块下的
src
子目录里,即
helloworld/src
。
这是惯例。有多个子模块时,各个子模块的源代码放在各自的目录里。
o
创建源文件
在
src
下创建源文件
main.c
,内容就是一个简单的
Hello World
程序。
o
创建
Makefile
模板
创建
helloworld/Makefile.am
,内容为:
SUBDIRS=src
这里只有简单的一行代码,表示其下有一个
src
的子目录,如果有多个子目录,用空格分开就行了。
创建
helloworld/src/Makefile.am
,内容为:
bin_PROGRAMS=helloworld
helloworld_SOURCES=main.c
这里表示有一个可执行文件
helloworld
,
helloworld
由源文件
main.c
编译而来。
PROGRAMS
表示要产生的可执行文件,有多个可执行文件时,用空格分开,而
bin
表示可执行文件要安装的目录。
SOURCES
表示生成可执行文件需要的源文件,有多个源文件时,也用空格分开。
.am
扩展名是
automake
的简称,它是
automake
用来产生
Makefile.in
文件的模板。
o
创建
autoconf
的模板。
在
helloworld
下运行
autoscan
,生成文件
configure.scan
,把它改名为
configure.in
。这是
autoconf
的模板文件,它的内容大概为:
#
-*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.61)
AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS)
AC_CONFIG_SRCDIR([src/main.c])
AC_CONFIG_HEADER([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
src/Makefile])
AC_OUTPUT
这个文件由一系列的宏组成,这些宏最终由命令
m4
展开,得到一个脚本文件
configure
。
configure
的主要功能是探测系统的配置,然后
根据这些配置来产生相应的
Makefile
文件。比如
AC_PROG_CC
是用来检测编译器的,
AC_CONFIG_FILES
和
AC_OUTPUT
是用来产生
Makefile
和其它数据文件的。
不过这个模板文件还不能直接使用,需要做下列修改:
把:
AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS)
改为:
AC_INIT(helloworld, 0.1, xianjimli@hotmail.com)
FULL-PACKAGE-NAME
是模块的名称。
VERSION
是模块的版本号,初始版本号都用
0.1
。对小模块来说用两级版本号就够了,小数点前的为主版本号,只有重大更新时才升级主版本号。小数点后的为次版本号,每次发布都应该升级它。一般升级到
0.9
后,可以继续升级到
0.10
、
0.11
等。
BUG-REPORT-ADDRESS
是作者或维护者的邮件地址。
再加上一行
automake
的初始化脚本:
AM_INIT_AUTOMAKE(helloworld, 0.1)
helloworld
是模块的名称。
0.1
是模块的版本号。
这里和前面的参数是重复的,
AC_INIT
是初始化
autoconf
的,
AM_INIT_AUTOMAKE
是初始
automake
的。在有的情况下,只是产生数据文件,而不需要编译文件时,那就不需要
AM_INIT_AUTOMAKE
了。
最后得到下面的文件:
#
-*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.61)
AC_INIT(helloworld, 0.1, xianjimli@hotmail.com)
AC_CONFIG_SRCDIR([src/main.c])
AC_CONFIG_HEADER([config.h])
AM_INIT_AUTOMAKE(helloworld, 0.1)
# 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
src/Makefile])
AC_OUTPUT
o
拷贝所用到宏。
运行:
aclocal
前面说了,
configure.in
里是一系列的宏,这些宏由命令
m4
负责展开。
m4
实际上就是
macro
的简称,
4
代表
m
后面省略了
4
个字母。类似的还有
I18n (Internationalization )
和
L10n(Localization)
,其中的数字都是代表所省略的字母个数。
AC_PROG_CC
之类的宏是标准的宏
(
或说是内置的宏
)
,不需要我们自己去写它,但我们需要运行命令
aclocal
,
aclocal
把
configure.in
中所用到的
宏全部拷贝到我们的工程里来。在
helloworld
目录下运行
aclocal
之后,当前目录下出现了:
autom4te.cache
这是一个临时目录,只是用来加快宏展开的。
aclocal.m4
是
configure.in
中用到的宏的定义,有兴趣的读者可以看看。
o
产生配置头文件的模板。
运行:
autoheader
配置头文件
(config.h)
是用来定义在
C/C++
程序中可以引用的宏,像模块的名称和版本号等等。这些宏由
configure
脚本产生,但我
们要提供一个模板文件。这个模板文件可以用命令
autoheader
产生出来。在
helloworld
目录下运行
autoheader
之后,当前目录下产
生
config.h.in
,一般情况不用修改它。
o
创建几个必要的文件。
README
:描述模块的功能、用法和注意事项等。
NEWS
:描述模块最新的动态。
AUTHORS
:模块的作者及联系方式。
ChangeLog
:记录模块的修改历史,它有固定的格式:
1.
最新修改放在最上面。
2.
对于每条记录,第一行写日期,修改者和联系方式。第二行开始以
tab
开头,再加一个星号,后面再写修改的原因和位置等。如:
2009-03-29 Li XianJing
* Created
o
生成
Makefile.in
和所需要的脚本。
运行:
automake -a
这个命令会建立
COPYING
depcomp INSTALL install-sh missing
几个文件的链接,这些文件指向系统中的文件。
automake
最重要的功能是以
Makefile.am
为模板产生
Makefile.in
文
件,
Makefile.in
相对于
Makefile.am
要复杂很多倍了,所幸的是我们不需要了解它。
o
产生
configure
脚本。
运行:
autoconf
autoconf
的功能是调用
m4
展开
configure.in
中的宏,生成
configure
脚本,这个脚本是最终运行的脚本。
o
产生最终的
Makefile
。
运行:
./configure
configure
有两个常用的参数:
–prefix
用来指定安装目录,
Linux
下默认的安装目录是
/usr/local
。
–host
用于交叉编译,比如
x86
的
PC
机上编译在
ARM
板上运行的程序。
如:
./configure
–prefix=/home/lixianjing/work/arm-root/usr –host=arm-linux
o
编译
运行:
make
o
安装
运行:
make install
o
发布软件包
运行:
make dist
或者
make
distcheck
make dist
用来生成一个发布软件包,这里会产生一个名为
helloworld-0.1.tar.gz
的文件。通常,源代码管理系统
(cvs/svn/git)
中的源代码是处于开发中的,是不稳定的,而发布的软件包则是稳定的,可供用户使用的。
怎样,是不是有点晕了?这里主要是想读者了解其中的原理,在实际操作中,我们可以把
make
之前的部分动作放到一个脚本文件中,这个脚本文件通常取名为
autogen.sh
或者
bootstrap
函数库
现在我们用
automake
来管理我们前面所建立的函数库,这是一个基础的函数库,我们就把它命名为
base
吧。
o
目录结构
base
根目录
base/src
源代码目录
o
创建
Makefile
模板
base/Makefile.am
内容为:
SUBDIRS=src
base/src/Makefile.am
内容为:
lib_LTLIBRARIES=libbase.la
libbase_la_SOURCES= darray.c /
darray.h /
dlist.c /
dlist.h /
darray_iterator.h /
dlist_iterator.h /
hash_table.c /
hash_table.h /
invert.c /
iterator.h /
linear_container_darray.c /
linear_container_darray.h /
linear_container_dlist.c /
linear_container_dlist.h /
linear_container.h /
queue.c /
queue.h /
sort.c /
sort.h /
stack.c /
stack.h /
typedef.h
libbase_la_LDFLAGS=-lpthread
noinst_PROGRAMS=darray_test dlist_test
darray_test_SOURCES=darray.c
darray_test_CFLAGS=-DDARRAY_TEST
dlist_test_SOURCES=dlist.c
dlist_test_CFLAGS=-DDLIST_TEST
basedir=$(includedir)/base
base_HEADERS=darray.h dlist.h iterator.h linear_container_dlist.h typedef.h /
darray_iterator.h
dlist_iterator.h
linear_container_darray.h
/
linear_container.h
EXTRA_DIST=/
linear_container_test.c /
invert_ng.c /
darray_iterator.c /
dlist_iterator.c /
test_helper.c
LTLIBRARIES
是关键字。
LT
代表
libtool
,
libtool
是用来封装共享库在不同平台上差异的脚本,其具体实现我们不用关心。
libbase.la
是函数库的名称,扩展名用
.la
而不是
.so
或
.a
,同时会生成共享库和静态库。
libbase_la_SOURCES
是生成
libbase.la
所需要的源文件。
LDFLAGS
是关键字,用来指定链接时需要的参数,
-lpthread
表示要链接
libpthread.so
。
noinst_PROGRAMS
是关键字,表示不需要安装的可执行文件,通常是测试程序。为了简单明了,这里没有写出全部的测试程序。
CFLAGS
是关键字,用来指定编译和预处理时的参数。
HEADERS
是关键字,列出所要安装的头文件。
xxx_HEADERS
和
xxxdir
要配套使用,后者表示要安装的位置。这里在
base_HEADERS
中列出的头文件会安装到
basedir
目录里。
o
创建
autoconf
的模板。
运行:
autoscan
mv configure.scan configure.in
然后按前面介绍的方法修改
configure.in
,得到下面的内容:
AC_PREREQ(2.61)
AC_INIT(base, 0.1, xianjimli@hotmail.com)
AC_CONFIG_SRCDIR([src/invert.c])
AC_CONFIG_HEADER([config.h])
AM_INIT_AUTOMAKE(base, 0.1)
# Checks for programs.
AC_PROG_CC
AC_PROG_LIBTOOL
# Checks for libraries.
# FIXME: Replace `main' with a function in `-lpthread':
AC_CHECK_LIB([pthread], [main])
# Checks for header files.
AC_HEADER_STDC
AC_CHECK_HEADERS([stdlib.h string.h unistd.h])
# Checks for typedefs, structures, and compiler characteristics.
AC_C_INLINE
AC_TYPE_SIZE_T
# Checks for library functions.
AC_FUNC_MALLOC
AC_FUNC_REALLOC
AC_CONFIG_FILES([Makefile
src/Makefile])
AC_OUTPUT
与前面不同的是:
AC_PROG_LIBTOOL
用来检查
libtool
脚本。
AC_CHECK_LIB
用来检查共享库是否存在。
AC_CHECK_HEADERS
用来检查头文件是否存在。
AC_FUNC_MALLOC
用来检查标准的
malloc
函数是否存在。
o
收集用到的
m4
宏。
运行:
aclocal
o
产生配置头文件的模板。
运行:
autoheader
o
创建
README
、
NEWS
、
ChangeLog
和
AUTHORS
几个文件。
o
生成
libtool
需要的文件。
运行:
libtoolize –force –copy
这个命令的主要功能是生成
ltmain.sh
,而
ltmain.sh
用来产生
libtool
脚本。
o
生成
Makefile.in
和需要的脚本。
运行:
automake -a
o
产生
configure
脚本。
运行:
autoconf
o
产生最终的
Makefile
。
运行:
./configure –prefix=$HOME/usr
o
编译运行:
make
o
安装
运行:
make install
o
发布软件包
运行:
make dist
我们编译好的文件安装到
/home/lixianjing/usr/lib/
目录下了:
libbase.a libbase.la libbase.so libbase.so.0 libbase.so.0.0.0
静态库:
libbase.a
动态库:
libbase.so
libtool
的包装:
libbase.la
头文件和库都安装好了,调用者还需要知道下列信息才能使用:
头文件和库安装在哪里?
还依赖哪些其它模块?
为了解决这个问题,我们需要借助另外一个名为
pkg-config
的工具。
pkg
是
package
的简写,
pkg-config
负责查询指定软件包
的配置信息,如软件包的名称、说明、版本号、头文件、库和依赖关系等等。为了让
pkg-config
能正常工作,软件包的实现者需要提供一个扩展名为
pc
的配置文件。
系统中的
pkg-config
配置文件通常放在
/usr/lib/pkgconfig/
和
/usr/local/lib/pkgconfig/
下,下面是
gtk+-2.0.pc
:
prefix=/usr
exec_prefix=/usr
libdir=/usr/lib
includedir=/usr/include
target=x11
gtk_binary_version=2.10.0
gtk_host=i386-redhat-linux-gnu
Name: GTK+
Description: GIMP Tool Kit (${target} target)
Version: 2.12.10
Requires: gdk-${target}-2.0 atk cairo
Libs: -L${libdir} -lgtk-${target}-2.0
Cflags: -I${includedir}/gtk-2.0
前面部分是定义的一些变量,后面是一些关键字:
Name:
名称
Description:
功能描述
Version:
版本号
Requires:
所依赖的软件包
Libs:
调用者的链接参数。
Cflags:
调用者的编译参数。
由于
prefix
之类的变量是在软件包
configure
时才决定的,不能直接写死在
pc
文件中。我们可以让
configure
根据模板文件来产生。
模板文件名为
base.pc.in
,内容为:
prefix=@prefix@
exec_prefix=${prefix}
libdir=${prefix}/lib
includedir=${prefix}/include
Name: @PACKAGE_NAME@
Description: a basic library.
Version: @VERSION@
Requires:
Libs: -L${libdir} -lbase
Cflags: -I${includedir}/base
这个模板文件和
Makefile.in
的替换规则一样,用两个
@@
括起来的变量会替换成
configure
检测出来的值,
@prefix@
等变量是标准的变量。
修改一下
base/Makefile.am
,增加下列两行代码:
pkgconfigdir=${libdir}/pkgconfig
pkgconfig_DATA=base.pc
这是安装数据文件的方法,
pkgconfig
不是关键字,取个描述性的名称就好了。
dir
和
_DATA
是关键字,它们有相同的前缀,前者表示安装的目录,后者表示要安装的文件。按照惯例,
pc
文件安装到
${libdir}/pkgconfig
下。
修改
configure.in
,增加输入出文件
base.pc
AC_OUTPUT([base.pc])
放到
AC_CONFIG_FILES
也可以,它告诉
configure
脚本要产生的文件。
重新运行
configure
后会生成
base.pc
,内容为:
prefix=/home/lixianjing/usr/local
exec_prefix=${prefix}
libdir=${prefix}/lib
includedir=${prefix}/include
Name: base
Description: a basic library.
Version: 0.1
Requires:
Libs: -L${libdir} -lbase
Cflags: -I${includedir}/base
(prefix
与
configure
时指定的
prefix
参数一致。
)
在下一节中,我们再学习调用者如何使用
pc
文件。
应用程序
前面我们创建的
helloworld
是一个应用程序工程,它很简单,只使用了标准
C
的函数。现在我们要建立一个应用程序工程,它将使用前面所写的
libbase
函数库。
o
目录结构
最顶层目录名用模块名称,这里用
appdemo
。
源文件放在模块下的
src
子目录里,即
appdemo/src
。
o
创建源文件
在
src
下创建源文件
main.c
,内容只是简单的调用一下
libbase
里的函数。
#include <dlist.h>
int main(int argc, char* argv[])
{
DList* dlist = dlist_create(NULL, NULL);
dlist_destroy(dlist);
return 0;
}
o
创建
Makefile
模板
创建
helloworld/Makefile.am
,内容为:
SUBDIRS=src
这里只有简单的一行代码,表示其下有一个
src
的子目录,如果有多个子目录,用空格分开就行了。
创建
helloworld/src/Makefile.am
,内容为:
bin_PROGRAMS=appdemo
appdemo_SOURCES=main.c
appdemo_CFLAGS=@BASE_CFLAGS@
appdemo_LDFLAGS=@BASE_LIBS@
appdemo_CFLAGS
指定了编译
appdemo
时需要的参数,
@BASE_CFLAGS@
将替换成实际
configure
时所得到的参数。
appdemo_LDFLAGS
是链接
appdemo
时需要的参数。
@BASE_LIBS@
将替换成实际
configure
时所得到的参数。
至于
@BASE_CFLAGS@
和
@BASE_LIBS@
是怎么得到的,后面我们再讲。
o
创建
autoconf
的模板。
在
appdemo
下运行
autoscan
,生成文件
configure.scan
,把它改名为
configure.in
。这是
autoconf
的模板文件,它的内容大概为:
#
-*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.61)
AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS)
AC_CONFIG_SRCDIR([src/main.c])
AC_CONFIG_HEADER([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
src/Makefile])
AC_OUTPUT
按照前面介绍的方法,把
configure.in
的内容修改为:
#
-*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.61)
AC_INIT(appdemo, 0.1, xianjimli@hotmail.com)
AC_CONFIG_SRCDIR([src/main.c])
AC_CONFIG_HEADER([config.h])
AM_INIT_AUTOMAKE(appdemo, 0.1)
# 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
src/Makefile])
AC_OUTPUT
与前面不同的是,我们还需要在
# Checks
for libraries.
之后加检查
libbase
参数的宏:
PKG_CHECK_MODULES(BASE, ["base"])
AC_SUBST(BASE_CFLAGS)
AC_SUBST(BASE_LIBS)
PKG_CHECK_MODULES(BASE,
["base"])
的功能是检查软件包
base
,通过调用前面所讲的
pkg-config
,生成
BASE_CFLAGS
和
BASE_LIBS
两个变量。
AC_SUBST(BASE_CFLAGS)
的功能是把所有对
BASE_CFLAGS
的引用替换成实际的参数。
o
拷贝所用到宏。
运行:
aclocal
o
产生配置头文件的模板。
运行:
autoheader
o
创建几个必要的文件。
README
:描述模块的功能、用法和注意事项等。
NEWS
:描述模块最新的动态。
AUTHORS
:模块的作者及联系方式。
ChangeLog
:记录模块的修改历史,它有固定的格式:
o
生成
Makefile.in
和所需要的脚本。
运行:
automake -a
o
产生
configure
脚本。
运行:
autoconf
o
产生最终的
Makefile
。
运行:
./configure –prefix=$HOME/usr
这时会出现错误:
No package 'base' found
Consider adjusting the PKG_CONFIG_PATH environment variable if you
installed software in a non-standard prefix.
在默认情况下,
pkg-config
只是在
/usr/lib/pkgconfig
下查找相关的
pc
文件。如果
pc
文件安装在其它目录,需要设置环境变量
PKG_CONFIG_PATH
。
export
PKG_CONFIG_PATH=$HOME/usr/lib/pkgconfig
重新
configure
,一切正常了。
o
编译
运行:
make
o
安装
运行:
make install
o
发布软件包
运行:
make dist
或者
make
distcheck
本章所讲的内容中可应付
90%
的情况了,如果需要使用更高级的特性,可以阅读相应的手册。