30
2024
10
09:14:08

automake/autoconf入门

automake/autoconf入门

 

一、Makefile介绍

Makefile是用于自动编译和链接的,一个工程有很多文件组成,每一个文件的改变都会导致工程的重新链接,但不是所有的文件都需重新编译,Makefile中纪录有文件的信息,在make时会决定在链接的时候需要重新编译哪些文件。
    Makefile的宗旨就是:让编译器知道要编译一个文件需要依赖其他的哪些文件。当那些依赖文件有了改变,编译器会自动的发现最终的生成文件已经过时,而重新编译相应的模块。
    使用automake,程序开发人员只需要写一些 简单的含有预定义宏的文件,由autoconf根据一个宏文件生成configure,由automake根据另一个宏文件生成Makefile.in, 再使用configure依据Makefile.in来生成一个符合惯例的Makefile。下面我们将详细介绍Makefile的automake生成方法。

 

二、从helloworld入手

下面的过程如果简单地说来就是:新建三个文件:
helloworld.c    configure.in       Makefile.am
    然后执行:
    aclocal;autoconf;automake --add-missing; . /configure;make; ./helloworld
就可以看到Makefile被产生出来,而且可以将helloworld.c编译通过。
    现在开始介绍详细的过程:
1、建目录:工作目录下建一个helloworld目录,用它来存放helloworld程序及相关文件,如在/home/my/build下:
$ mkdir helloword
$ cd helloworld
2、 helloworld.c
    然后编辑器写一个hellowrold.c文件,如命令:vi helloworld.c。使用下面的代码作为helloworld.c的内容。
int main(int argc, char** argv)
{
printf("Hello, Linux World!\n");
return 0;
}
完成后保存退出。
3、生成configure
    autoscan
:autoscan是用来扫描源代码目录生成configure.scan文件的。autoscan可以用目录名做为参数,但如果你不使用参数的话,那么 autoscan将认为使用的是当前目录。autoscan将扫描你所指定目录中的源文件,并创建configure.scan文件。configure.scan:configure.scan包含了系统配置的基本选项,里面都是一些宏定义。我们需要将它改名为configure.in

使用autoscan命令来帮助我们根据目录下的源代码生成一个configure.in的模板文件。命令:
$ autoscan
$ ls
configure.scan helloworld.c
    执行后在hellowrold目录下会生成一个文件:configure.scan,我们可以拿它作为configure.in的蓝本。
    现在将configure.scan改名为configure.in,并且编辑它,按下面的内容修改,去掉无关的语句:
============================configure.in内容开始=========================================
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.

AC_INIT(helloworld.c)
AM_INIT_AUTOMAKE(helloworld, 1.0)

# 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_OUTPUT(Makefile)
============================configure.in内容结束=========================================

然后执行命令aclocal和autoconf,分别会产生aclocal.m4及configure两个文件:
    aclocal:aclocal是一个perl 脚本程序。aclocal根据configure.in文件的内容,自动生成aclocal.m4文件。aclocal的定义是:“aclocal - create aclocal.m4 by scanning configure.ac”。
    autoconf:autoconf是用来产生configure文件的。configure是一个脚本,它能设置源程序来适应各种不同的操作系统平台,并且根据不同的系统来产生合适的Makefile,从而可以使你的源代码能在不同的操作系统平台上被编译出来。
    configure.in文件的内容是一些宏,这些宏经过autoconf 处理后会变成检查系统特性、环境变量、软件必须的参数的shell脚本。configure.in文件中的宏的顺序并没有规定,但是你必须在所有宏的最前面和最后面分别加上AC_INIT宏和AC_OUTPUT宏。在configure.ini中:#号表示注释,这个宏后面的内容将被忽略。
    AC_INIT(FILE):这个宏用来检查源代码所在的路径。
    AM_INIT_AUTOMAKE(PACKAGE, VERSION) :这个宏是必须的,它描述了我们将要生成的软件包的名字及其版本号:PACKAGE是软件包的名字,VERSION是版本号。当你使用make dist命令时,它会给你生成一个类似helloworld-1.0.tar.gz的软件发行包,其中就有对应的软件包的名字和版本号。
    AC_PROG_CC:这个宏将检查系统所用的C编译器。
    AC_OUTPUT(FILE):这个宏是我们要输出的Makefile的名字。
我们在使用automake时,实际上还需要用到其他的一些宏,但我们可以用aclocal 来帮我们自动产生。执行aclocal后我们会得到aclocal.m4文件。产生了configure.in和aclocal.m4 两个宏文件后,我们就可以使用autoconf来产生configure文件了。

$ aclocal
$ls
aclocal.m4 configure.in helloworld.c
$ autoconf
$ ls
aclocal.m4 autom4te.cache configure configure.in helloworld.c
    大家可以看到configure.in内容是一些宏定义,这些宏经autoconf处理后会变成检查系统特性、环境变量、软件必须的参数的shell脚本。autoconf 是用来生成自动配置软件源代码脚本(configure)的工具。configure脚本能独立于autoconf运行,且在运行的过程中,不需要用户的干预。要生成configure文件,必须告诉autoconf如何找到你所用的宏。方式是使用aclocal程序来生成你的aclocal.m4。aclocal根据configure.in文件的内容,自动生成aclocal.m4文件。aclocal是一个perl 脚本程序,它的定义是:“aclocal - create aclocal.m4 by scanning configure.ac”。
    autoconf从configure.in这个列举编译软件时所需要各种参数的模板文件中创建configure。autoconf需要GNU m4宏处理器来处理aclocal.m4,生成configure脚本。
    m4是一个宏处理器。将输入拷贝到输出,同时将宏展开。宏可以是内嵌的,也可以是用户定义的。除了可以展开宏,m4还有一些内建的函数,用来引用文件,执行命令,整数运算,文本操作,循环等。m4既可以作为编译器的前端,也可以单独作为一个宏处理器。
4、新建Makefile.am

Makefile.am:Makefile.am是用来生成Makefile.in的,需要你手工书写。Makefile.am中定义了一些内容:
    AUTOMAKE_OPTIONS:这个是automake的选项。在执行automake时,它会检查目录下是否存在标准GNU软件包中应具备的各种文件,例如AUTHORS、ChangeLog、NEWS等文件。我们将其设置成foreign时,automake会改用一般软件包的标准来检查。有foreign\gun\gnits三个软件等级,foreign只检测必须的文件。
    bin_PROGRAMS:这个是指定我们所要产生的可执行文件的文件名。如果你要产生多个可执行文件,那么在各个名字间用空格隔开。

helloworld_SOURCES:这个是指定产生“helloworld”时所需要的源代码。如果它用到了多个源文件,那么请使用空格符号将它们隔开。比如需要 helloworld.h,helloworld.c那么请写成helloworld_SOURCES= helloworld.h helloworld.c。
    如果你在bin_PROGRAMS定义了多个可执行文件,则对应每个可执行文件都要定义相对的filename_SOURCES。
命令:
$ vi Makefile.am
    内容如下:
AUTOMAKE_OPTIONS=foreign
bin_PROGRAMS=helloworld
helloworld_SOURCES=helloworld.c
    automake会根据你写的Makefile.am来自动生成Makefile.in。

Makefile.am中定义的宏和目标,会指导automake生成指定的代码。例如,宏bin_PROGRAMS将导致编译和连接的目标被生成。
5、运行automake

automake:我们使用automake --add-missing来产生Makefile.in。选项--add-missing的定义是“add missing standard files to package”,它会让automake加入一个标准的软件包所必须的一些文件。我们用automake产生出来的Makefile.in文件是符合GNU Makefile惯例的,接下来我们只要执行configure这个shell 脚本就可以产生合适的 Makefile 文件了。
命令:
$ automake --add-missing
configure.in: installing `./install-sh'
configure.in: installing `./mkinstalldirs'
configure.in: installing `./missing'
Makefile.am: installing `./depcomp'
    automake会根据Makefile.am文件产生一些文件,包含最重要的Makefile.in。
6、执行configure生成Makefile
$ ./configure
$ ls -l Makefile
-rw-rw-r-- 1 yutao yutao 15035 Oct 15 10:40 Makefile

此时Makefile已经产生出来了。Makefile:在符合GNU Makefiel惯例的Makefile中,包含了一些基本的预先定义的操作:
    make:根据Makefile编译源代码,连接,生成目标文件,可执行文件。
    make clean:清除上次的make命令所产生的object文件(后缀为“.o”的文件)及可执行文件。
    make install:将编译成功的可执行文件安装到系统目录中,一般为/usr/local/bin目录。
    make dist:产生发布软件包文件(即distribution package)。这个命令将会将可执行文件及相关文件打包成一个tar.gz压缩的文件用来作为发布软件的软件包。它会在当前目录下生成一个名字类似“PACKAGE-VERSION.tar.gz”的文件。PACKAGE和VERSION,是我们在configure.in中定义的AM_INIT_AUTOMAKE(PACKAGE, VERSION)。
    make distcheck:生成发布软件包并对其进行测试检查,以确定发布包的正确性。这个操作将自动把压缩包文件解开,然后执行configure命令,并且执行make,来确认编译不出现错误,最后提示你软件包已经准备好,可以发布了。
    make distclean:类似make clean,但同时也将configure生成的文件全部删除掉,包括Makefile。
7、使用Makefile编译代码
$ make

运行helloworld
$ ./helloworld
Hello, Linux World!

想写出更复杂的且符合惯例的Makefile,可参考一些开放代码的项目中的configure.in和Makefile.am文件,比如:嵌入式数据库sqlite,单元测试cppunit。

 

 

利用libtool自动生成动态库的Makefile的生成方法

 利用libtool自动生成动态库

1. autoscan命令在当前目录生成configure.scan文件, 内容为:


# -*- Autoconf -*-

# Process this file with autoconf to produce a configure script.

AC_PREREQ(2.57)

AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS)

AC_CONFIG_SRCDIR([src/bot.h])

AC_CONFIG_HEADER([config.h])

# Checks for programs.

AC_PROG_CXX

AC_PROG_CC

# Checks for libraries.

# Checks for header files.

AC_HEADER_STDC

AC_CHECK_HEADERS([limits.h malloc.h stdlib.h string.h unistd.h])

# Checks for typedefs, structures, and compiler characteristics.

AC_HEADER_STDBOOL

AC_C_CONST

AC_C_INLINE

# Checks for library functions.

AC_FUNC_MALLOC

AC_FUNC_REALLOC

AC_CHECK_FUNCS([memset strcasecmp strchr strdup])

AC_OUTPUT


将其该名为configure.ac 然后修改:

configure.ac 文件是 autoconf 的输入文件,经过 autoconf 处理,展开里面的 m4宏,输出的是 configure 脚本。
第 4 行声明本文件要求的 autoconf 版本,因为本例使用了新版本 2.57,所以在此注明。

第 5 行 AC_INIT 宏用来定义软件的名称和版本等信息

AC_INIT([test], 1.0, [email]linhanzu@gmail.com[/email])


增加版本信息(为生成lib库做准备)


lt_major=1

lt_age=1

lt_revision=12

dist_version=0.1.12

AM_INIT_AUTOMAKE(test, $dist_version) //自动生成Makefile文件


增加宏, 打开共享库


AC_PROG_LIBTOOL

# Check for dl  

DL_PRESENT=""

AC_CHECK_LIB( dl, dlopen, DL_PRESENT="yes",, $DL_LIBS -ldl )

if test "x$DL_PRESENT" = "xyes"; then

AC_DEFINE(HAVE_LIBDL, 1, [Define if DL lib is present])

DL_LIBS="-ldl"

AC_SUBST(DL_LIBS)

fi

# Check for libm

M_PRESENT=""

AC_CHECK_LIB( m, sin, M_PRESENT="yes",, $M_LIBS -lm )

if test "x$M_PRESENT" = "xyes"; then

AC_DEFINE(HAVE_LIBM, 1, [Define if libm is present])

M_LIBS="-lm"

AC_SUBST(M_LIBS)

fi


增加依赖库,这里就仅仅列举了pthread库,生成的Makefile会自动加上-pthread


# Check for pthread

PTHREAD_PRESENT=""

AC_CHECK_LIB( pthread, pthread_create, PTHREAD_PRESENT="yes",, $PTHREAD_LIBS-lpthread )

if test "x$PTHREAD_PRESENT" = "xyes"; then

AC_DEFINE(HAVE_LIBPTHREAD, 1, [Define if libpthread is present])

PTHREAD_LIBS="-lpthread"

AC_SUBST(PTHREAD_LIBS)

fi


要生成项目工程目录和其它目录下的Makefile 文件, 必需加入

AM_CONFIG_FILES的宏:

例如: AC_CONFIG_FILES([Makefile

                     src/Makefile

                     data/Makefile

                     docs/Makefile])


修改完后Makefile.ac如下:


# -*- Autoconf -*-

# Process this file with autoconf to produce a configure script.

AC_PREREQ(2.57)

AC_INIT([test],[1.0],[[email]arne_caspari@users.sourceforge.net[/email]])

AM_CONFIG_HEADER(config.h)

lt_major=1

lt_age=1

lt_revision=12

dist_version=0.1.12

AM_INIT_AUTOMAKE(test, $dist_version)

AC_SUBST(lt_major)

AC_SUBST(lt_revision)

AC_SUBST(lt_age)

# Checks for programs.

#AC_PROG_CC

#AC_PROG_INSTALL

#AC_PROG_LN_S

#AC_PROG_LIBTOOL

AM_PROG_LIBTOOL

# Checks for libraries.

pkg_modules="gtk+-2.0 >= 2.0.0"

PKG_CHECK_MODULES(GTK_PACKAGE, [$pkg_modules], HAVE_GTK2="yes", HAVE_GTK2="no" )

AC_SUBST(GTK_PACKAGE_CFLAGS)

AC_SUBST(GTK_PACKAGE_LIBS)

# Check for dl

DL_PRESENT=""

AC_CHECK_LIB( dl, dlopen, DL_PRESENT="yes",, $DL_LIBS -ldl )

if test "x$DL_PRESENT" = "xyes"; then

AC_DEFINE(HAVE_LIBDL, 1, [Define if DL lib is present])

DL_LIBS="-ldl"

AC_SUBST(DL_LIBS)

fi

# Check for libm

M_PRESENT=""

AC_CHECK_LIB( m, sin, M_PRESENT="yes",, $M_LIBS -lm )

if test "x$M_PRESENT" = "xyes"; then

AC_DEFINE(HAVE_LIBM, 1, [Define if libm is present])

M_LIBS="-lm"

AC_SUBST(M_LIBS)

fi

# Check for pthread 

PTHREAD_PRESENT=""

AC_CHECK_LIB( pthread, pthread_create, PTHREAD_PRESENT="yes",, $PTHREAD_LIBS

-lpthread )

if test "x$PTHREAD_PRESENT" = "xyes"; then

AC_DEFINE(HAVE_LIBPTHREAD, 1, [Define if libpthread is present])

PTHREAD_LIBS="-lpthread"

AC_SUBST(PTHREAD_LIBS)

fi


# Checks for header files.

#AC_HEADER_DIRENT

#AC_HEADER_STDC

#AC_CHECK_HEADERS([fcntl.h stdlib.h string.h sys/time.h unistd.h])

# Checks for typedefs, structures, and compiler characteristics.

#AC_TYPE_PID_T

#AC_TYPE_SIZE_T

#AC_HEADER_TIME

# Checks for library functions.

#AC_FUNC_CLOSEDIR_VOID

#AC_FUNC_MALLOC

#AC_CHECK_FUNCS([memset strstr])

AC_CONFIG_FILES([Makefile

             src/Makefile

             data/Makefile

             doc/Makefile])

AC_OUTPUT


2.生成各目录下的Makefile.am文件


./Makefile.am   //工程目录下

  SUBDIR = src data doc

../src/Makefile.am  //源码目录下

  MAINTAINERCLEANFILES = Makefile.in

   INCLUDES = -I../include  

   CPPFLAGS=-DINSTALL_PREFIX="\"$(prefix)\""
   lib_LTLIBRARIES = libtest.la
   libtest_la_LDFLAGS = -version-info @lt_major@:@lt_revision@:@lt_age@

   libtest_la_SOURCES = \

                      test.c \

                      test_private.h \

                      check_match.c \

                      check_match.h \

                      test_helpers.c \

                      test_helpers.h \

                      debug.h

    libtest_la_LIBADD = \

                      @DL_LIBS@ \

                      @M_LIBS@


3. 生成autogen.sh脚本, 内容


#! /bin/sh

set -x

aclocal

autoheader

automake --foreign --add-missing --copy

autoconf


保存后修改权限 chmod a+x autogen.sh

3.运行脚本./autogen.sh, 生成configure脚本. 这里可能会遇到错误, 可以根据错误提示作相应修改.(注意:如果您修改了Makefile.am中的项,那么就得重新执行这一步)

4.运行./configure脚本.自动生成src目录下的makefile文件

5. 切换到目录src, 运行make 自动在当前目录下建立.libs文件, 编程生成的库文件就保存在该目录下.

    make install 安装在默认目录 /usr/local/lib/下.

6.如果要生成其它的安装目录,Makefile.am就要这样写

 

MAINTAINERCLEANFILES = Makefile.in

INCLUDES = -I../include 

lib_LTLIBRARIES = libtt.la

libdir = $(prefix)/lib/test  //这个就是安装目录

libtt_la_LDFLAGS = -version-info @lt_major@:@lt_revision@:@lt_age@

libtt_la_LIBADD = @PTHREAD_LIBS@

libtt_la_SOURCES = \

                tt.c \

                video.c \

                video.h

当然,Makefile中的语法规则中还有很多宏定义,您可以在Makefile的官方网站找到说明。

 

借用Wei Mingzhi <whistler_wmz@users.sf.net> 开发的麻将游戏来进行演示autotools的应用

 

最终的 configure.ac 文件

 

# -*- Autoconf -*-

# Process this file with autoconf to produce a configure script.

 

AC_PREREQ(2.59)                    //显示autoconf 版本2.59

AC_INIT([majiang], [1.0])              // AC_INIT 宏必须放在开头位置,定义软件的名称和版本等信息

AC_CONFIG_SRCDIR([src/main.cpp])    //侦测所指定的源码文件是否存在,来确定源码目录的有效性。

AC_CONFIG_HEADER([config.h])       //生成 config.h 文件,里面存放 configure 脚本侦测到的信息。

 

AC_CANONICAL_HOST

AC_CANONICAL_TARGET

AM_INIT_AUTOMAKE              //使用 automake 产生Makefile.in文件。

 

# Checks for programs.

AC_PROG_CXX

AC_PROG_CC

 

AC_LANG(C++)

 

# Checks for libraries.

SDL_VERSION=1.2.0

AM_PATH_SDL($SDL_VERSION,

:,

AC_MSG_ERROR([*** SDL version $SDL_VERSION not found!])

)

 

# Check for SDL_image library                             //AC_CHECK_LIB可以用来检测库

AC_CHECK_LIB(SDL_image, IMG_LoadPNG_RW, , AC_MSG_ERROR([

*** Unable to find SDL_image libary with PNG support

(http://www.libsdl.org/projects/SDL_image/)

]), `sdl-config --libs`)

 

# Check for SDL_mixer library

AC_CHECK_LIB(SDL_mixer, Mix_LoadOGG_RW, , AC_MSG_ERROR([

*** Unable to find SDL_mixer libary with OGG support

(http://www.libsdl.org/projects/SDL_mixer/)

]), `sdl-config --libs`)

 

# Check for SDL_ttf library

AC_CHECK_LIB(SDL_ttf, TTF_OpenFont, , AC_MSG_ERROR([

*** Unable to find SDL_ttf libary

(http://www.libsdl.org/projects/SDL_ttf/)

]), `sdl-config --libs`)

 

# Checks for header files.

AC_HEADER_STDC

AC_CHECK_HEADERS([limits.h malloc.h stdlib.h string.h unistd.h])

 

# Checks for typedefs, structures, and compiler characteristics.

AC_HEADER_STDBOOL

AC_C_CONST

AC_C_INLINE

 

# Checks for library functions.

AC_FUNC_MALLOC

AC_FUNC_REALLOC

AC_CHECK_FUNCS([memset strcasecmp strchr strdup])

 

AC_ARG_ENABLE(debug,                             //定义debug 调试选项。

[ --enable-debug turn on debug],

CXXFLAGS="$CXXFLAGS -g3 -D_DEBUG=1")

 

AC_CONFIG_FILES([Makefile                         //指明要输出 Makefile

src/Makefile

data/Makefile

docs/Makefile

fonts/Makefile

images/Makefile

music/Makefile

sound/Makefile])

AC_OUTPUT

 

 

 

 

SDL 库的侦测

 

这个麻将游戏是基于 SDL 库开发的,一般系统默认不会安装,因此 configure 脚本的一

个任务就是检查用户的系统中是否有该软件包。

 

autoconf 提供了很多宏可以实现侦测功能,但首先应该查看 SDL 软件包是否已经提供相

应的宏。通过 pkgsrc 的工具可以看到:

 

$ pkg_info -L SDL|grep m4

/usr/pkg/share/aclocal/sdl.m4

 

即 SDL 软件包提供了一个 sdl.m4 宏,放在系统的 aclocal 目录下。

 

在这个宏文件的注释中说明了使用的方法:

 

dnl AM_PATH_SDL([MINIMUM-VERSION,[ACTION-IF-FOUND[,ACTION-IF-NOT-FOUND]]])

dnl Test for SDL, and define SDL_CFLAGS and SDL_LIBS

 

也就是说在 configure.ac 里面调用 AM_PATH_SDL 宏,就可以侦测 SDL。找到 SDL 库以

后,该宏还输出 SDL_CFLAGS 和 SDL_LIBS 编译连接选项,它们实际上就是调用

`sdl-config --cflags` 和 `sdl-config --libs`。

 

于是在 configure.ac 里面加入 AM_PATH_SDL 宏

 

# Checks for libraries.

SDL_VERSION=1.2.0

AM_PATH_SDL($SDL_VERSION,

:,

AC_MSG_ERROR([*** SDL version $SDL_VERSION not found!])

)

 

当前 SDL 的版本为 1.2.9,于是 MINIMUM-VERSION 就设为 1.2.0。如果在系统中侦测到

需要的库,没什么额外的操作,假如没有找到,则给出错误信息。

 

AM_PATH_SDL 输出 SDL_CFLAGS 和 SDL_LIBS 编译参数,需要添加到 src/Makefile.am 里面:

 

mj_CPPFLAGS = @SDL_CFLAGS@

mj_LDFLAGS = @SDL_LIBS@

 

用 `@' 包围的变量会在 configure 执行时被替换。

 

从 mahjong 的 Makefile 中看到,这个软件还要使用 SDL_image、SDL_mixser 和

SDL_ttf 库,但它们不属于 SDL 软件包,需要另外安装。由于这些库在 sdl.m4 中也没

有进行侦测,所以自己要写一些脚本。

 

autotools 提供了一个 AC_CHECK_LIB 宏可以用来检测库,现在就使用它来检测这几个SDL 库。该宏的语法为:

 

AC_CHECK_LIB (LIBRARY, FUNCTION, [ACTION-IF-FOUND],

[ACTION-IF-NOT-FOUND], [OTHER-LIBRARIES])

 

第一个参数是库名,第二个参数是库中的一个函数,第三个参数是检测到以后进行的动作

,第四个参数是未检测到以后的动作,第五个参数是其他的库。

 

对于 SDL_image、SDL_mixer 和 SDL_ttf 对应的使用方法如下:

 

# Check for SDL_image library

AC_CHECK_LIB(SDL_image, IMG_LoadPNG_RW, , AC_MSG_ERROR([

*** Unable to find SDL_image libary with PNG support

(http://www.libsdl.org/projects/SDL_image/)

]), `sdl-config --libs`)

 

# Check for SDL_mixer library

AC_CHECK_LIB(SDL_mixer, Mix_LoadOGG_RW, , AC_MSG_ERROR([

*** Unable to find SDL_mixer libary with OGG support

(http://www.libsdl.org/projects/SDL_mixer/)

]), `sdl-config --libs`)

 

# Check for SDL_ttf library

AC_CHECK_LIB(SDL_ttf, TTF_OpenFont, , AC_MSG_ERROR([

*** Unable to find SDL_ttf libary

(http://www.libsdl.org/projects/SDL_ttf/)

]), `sdl-config --libs`)

软件使用的数据文件

 

原来 mj 读取数据是从执行时目录的子目录中读取,但现在将数据放到 $prefix/share/majiang 目录下,需要通过一种途径让程序可以知道数据文件被安放的位置。

 

要达到这个目的有很多方法,这里采用最直接的一种:将数据文件安装目录变量通过CPPFLAGS 编译参数传递给程序。于是修改 src/Makefile.am 的 CPPFLAGS:

 

mj_CPPFLAGS = @SDL_CFLAGS@ -DDATA_DIR=\"${datadir}/majiang\"

 

相应地修改 src 目录下的源码,在读取数据文件的地方,将读取的路径改成 DATA_DIR里对应的子目录。例如,原先 config.cpp 中是:

 

void LoadCfg()

{

cfg.Load("data/mj.ini");

}

 

现改成:

 

void LoadCfg()

{

char ini_file[260];

sprintf(ini_file, "%s/data/mj.ini", DATA_DIR);

cfg.Load(ini_file);

}

 

 

configure 选项

 

原来 mahjong 的 Makefile 第 22 行定义了 debug 调试选项,虽然也可以照样放到 src/Makefile.am 的 CPPFLAGS 里面实现,但 autotools 提供了一种更灵活的机制。

 

configure 脚本可以通过选项来设置编译参数,现增加一个 --enable-debug 选项,需要DEBUG 时,在命令行上加上它来打开,默认则关闭。这项功能是使用 AC_ARG_ENABLE 宏实现:

 

AC_ARG_ENABLE (FEATURE, HELP-STRING, [ACTION-IF-GIVEN],

[ACTION-IF-NOT-GIVEN])

 

其中 FEATURE 是名称,HELP_STRING 为说明信息,在使用 ./configure --help 时可以看到。最后两个分别对应打开和关闭时的操作。

现在将 DEBUG 功能加入 configure.ac:

 

AC_ARG_ENABLE(debug,

[ --enable-debug turn on debug],

CXXFLAGS="$CXXFLAGS -g3 -D_DEBUG=1")

 

 

 

 

编写 Makefile.am

 

软件根目录 Makefile.am

由于该目录下面保存的是与 autotools 相关的文件,没有需要编译安装的文件,所以只注明需要进一步处理的子目录信息:

 

SUBDIRS = src data docs fonts images music sound

 

src/Makefile.am

 

此目录里是源代码,最终生成 mj 可执行文件,在其 Makefile.am 中写入

 


bin_PROGRAMS = mj

 

mj_SOURCES = bot.h \

bot.cpp \

config.cpp \

game.h \

game.cpp \

general.h \

general.cpp \

hand.h \

hand.cpp \

ini.h \

ini.cpp \

main.h \

main.cpp \

player.h \

player.cpp \

text.cpp \

tile.h \

tile.cpp \

util.cpp


 

am 文件里变量通过命名判断其含义,保留的字符串间用下划线分隔。

 

bin_PROGRAMS 表示列出二进制的程序,值为多个空格分开的程序列表,这里仅有一个 mj。

 

mj_SOURCES 列出的是组成 mj 程序的文件,文件比较多的时候,每个文件写成一行容易看清楚。

 

data/Makefile.am

 

本目录的文件是 mj 运行时读取的数据,它的 Makefile.am 可以这样写

 

mjdatadir = $(pkgdatadir)/data

mjdata_DATA = mj.ini titles.txt

EXTRA_DIST = $(mjdata_DATA)

 

因为 datadir 是保留的关键字,所以用 mjdatadir 代替,pkgdatadir 指向 $prefix/share/FULL-PACKAGE-NAME 目录,因为在 AC_INIT 中已经声明 FULL-PACKAGE-NAME 为majiang,pkgdatadir 就等于 $prefix/share/majiang 目录。

其中 mjdatadir 让 data 目录下的文件安装到 $prefix/share/majiang/data 目录里面。mjdata_DATA 列出此目录下需要安装的文件,然后用 EXTRA_DIST 变量注明。余下几个子目录都与 data 目录类似。

 


docs/Makefile.am

 

docsdir = $(pkgdatadir)/docs

docs_DATA = gkai00mp.txt gpl.html readme.txt

EXTRA_DIST = $(docs_DATA)

 

fonts/Makefile.am

 

fontsdir = $(pkgdatadir)/fonts

fonts_DATA = brush.ttf gkai00mp.ttf

EXTRA_DIST = $(fonts_DATA)

 

images/Makefile.am

 

imagesdir = $(pkgdatadir)/images

images_DATA = bgame.jpg \

mjgirl1a.jpg \

mjgirl2a.jpg \

mjgirl3a.jpg \

mjgirl4a.jpg \

tiles.jpg \

electron.jpg \

mjgirl1b.jpg \

mjgirl2b.jpg \

mjgirl3b.jpg \

mjgirl4b.jpg \

gameover.jpg \

mjgirl1c.jpg \

mjgirl2c.jpg \

mjgirl3c.jpg \

mjgirl4c.jpg

EXTRA_DIST = $(images_DATA)

 

music/Makefile.am

 

musicdir = $(pkgdatadir)/music

music_DATA = bet.ogg \

bonus.ogg \

music.ogg \

musicb.ogg \

musice.ogg \

win.ogg \

bgame.ogg \

gameover.ogg \

music1.ogg \

musicc.ogg \

musicp.ogg

EXTRA_DIST = $(music_DATA)

 

sound/Makefile.am

 

sounddir = $(pkgdatadir)/sound

sound_DATA = boom.wav \

ding.wav \

discard.wav \

discard2.wav \

flash.wav \

snd1.wav \

snd2.wav \

snd3.wav \

snd4.wav

EXTRA_DIST = $(sound_DATA)


 

autotools 脚本

 

每次修改了 configure.ac 或 Makefile.am 等 autotools 输入文件后都需要再次运行aclocal、automake、autoconf 这些命令,为了方便起见,可以将他们放到一个 shell脚本里面,例如:

 

#! /bin/sh

set -x

aclocal

autoheader

automake --foreign --add-missing --copy

autoconf

 

将上面内容保存到 autogen.sh 文件,并修改文件属性为 755。每次需要重新生成configure 脚本时,执行 ./autogen.sh 即可。

 

 

 

 

用qmake快速生成makefile

一个更加复杂的项目文件为例和你详细的讲诉qmake的高级技巧

  SOURCES += myqt.cpp

  SOURCES += main.cpp

  HEADERS += myqt.h

  FORMS += xsimform.ui

  TEMPLATE = lib

  CONFIG += debug warn_on qt thread x11 plugin

  TARGET = ../bin/panel_qt

  INCLUDEPATH = ../../../../xsim ../../../../xsim/IMdkit

  DEFINES = BDB_VERSION4 OS_LINUX

  从这个文件可以知道,SOURCES变量指向项目中的源文件,当项目中有多个源文件时,我们需对项目中的每一个源文件都这样做,直到结束:

  SOURCES += hello.cpp

  SOURCES += main.cpp

  当然,如果你喜欢使用像Make一样风格的语法,你也可以写成这样,一行写一个源文件,并用反斜线结尾,然后再起新的一行:

  SOURCES = hello.cpp main.cpp

  HEADERS变量指向项目中的头文件,多个头文件的时候,和多个源文件的解决方法一致。

  FORMS变量指向项目中使用到的窗体文件(qtdesign设计的.ui文件),qmake也注意了Qt的特殊需求,可以自动的包含moc和uic的连编规则。没有的话或者非qt程序可以不写。

  TEMPLATE变量告诉qmake为这个应用程序生成哪种makefile。下面是可供使用的选择:

  app - 建立一个应用程序的makefile。这是默认值,所以如果模板没有被指定,这个将被使用。

  lib - 建立一个链接库的makefile。

  vcapp - 建立一个应用程序的Visual Studio项目文件。

  vclib - 建立一个库的Visual Studio项目文件。

  subdirs - 这是一个特殊的模板,它可以创建一个能够进入特定目录并且为一个项目文件生成makefile并且为它调用make的mkefile。

  CONFIG变量变量指定了编译器所要使用的选项和所需要被连接的库。配置变量中可以添加任何东西,但只有下面这些选项可以被qmake识别。

  下面这些选项控制着使用哪些编译器标志:

  release - 应用程序将以release模式连编。如果"debug"被指定,它将被忽略。

  debug - 应用程序将以debug模式连编。

  warn_on - 编译器会输出尽可能多的警告信息。如果"warn_off"被指定,它将被忽略。

  warn_off - 编译器会输出尽可能少的警告信息。

  下面这些选项定义了所要连编的库/应用程序的类型:

  qt - 应用程序是一个Qt应用程序,并且Qt库将会被连接。

  thread - 应用程序是一个多线程应用程序。

  x11 - 应用程序是一个X11应用程序或库。

  windows - 只用于"app"模板:应用程序是一个Windows下的窗口应用程序。

  console - 只用于"app"模板:应用程序是一个Windows下的控制台应用程序。

  dll - 只用于"lib"模板:库是一个共享库(dll)。

  staticlib - 只用于"lib"模板:库是一个静态库。

  plugin - 只用于"lib"模板:库是一个插件,这将会使dll选项生效。

  TARGET变量指定生成的二进制代码的路径和文件名,如果建立的是一个链接库的话,

它会在文件名前面自动加上"lib"和在最后自动加上".so".

  我们在使用过程中可能会使用到另外的一些函数库,链接库等。函数库的头文件指定使用INCLUDEPATH变量,其它链接库的指定可以通过LIBS 变量来指定,例LIBS += -lmath -L/usr/local/lib

  DEFINES变量的指定就如同make的-D选项一样。




推荐本站淘宝优惠价购买喜欢的宝贝:

image.png

本文链接:https://hqyman.cn/post/8251.html 非本站原创文章欢迎转载,原创文章需保留本站地址!

分享到:
打赏





休息一下~~


« 上一篇 下一篇 »

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

请先 登录 再评论,若不是会员请先 注册

您的IP地址是: