现在的位置: 首页 > 综合 > 正文

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

2017年12月21日 ⁄ 综合 ⁄ 共 12244字 ⁄ 字号 评论关闭


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

抱歉!评论已关闭.