如何自动产生makefile(例子分析)

<!-- /* 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% 的情况了,如果需要使用更高级的特性,可以阅读相应的手册。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值