#
怎样才能使程序具有可移植性呢?
.尽可能避免针对特定系统的假定和方法.例如,不认定程序只在OpenLinux系统上运行,或者只在使用RPM包管理系统的系统上使用.
.隔离依赖于系统的部分.如果说有了一种特殊的GUI环境,例如ncurses,那么要把GUI部分的代码单独放在它自己的模块中.这样做可以使移植任务变得简单,
比如让程序使用基于X的GUI环境或者另一种基于文本的GUI
S-Lang而不是ncurses.
.尽可能复用已有的接口.为什么一定要重新创造已经存在的东西呢?从常用的数据库管理库,比如Berkeley DB或GNU
DBM中选出一种代替你自己的数据库管理库.
.使用标准接口,比如多种POSIX标准;标准语言,比如C和C++;以及标准库,比如标准C库,NAG数学库和terminfo等常用库.
http://www.cs.wvu.edu/~jdm/research/portability/portbib.html 上提供的参考书目是个很好的资源.
最好的一点是,autoconf生产的脚本是自包含的,因此在目标系统上编译软件时不需要在其系统上安装autoconf,使用者所需要做的只是在软件发布版本的源程序目录中键入./configure.
为了生产configure脚本,需要在源文件树的根目录下创建名为configure.in的文件.configure.in调用一系列autoconf宏来测试程序需要的或用到的特性是否存在,以及这些特性的功能.
每个configure.in文件必须在开始所有测试前调用AC_INIT,并且在结束所有测试后调用AC_OUTPUT.而事实上,也只有这两个宏是必须的.AC_INIT的语法如下:
AC_INIT(unique_file_in_source_dir)
unique_file_in_source_dir是在源代码目录下的一个文件,对AC_INIT的调用在所产生的配置脚本文
件中生产一条shell命令,通过检查unique_file_in_source_dir是否存在来验证当前目录是否正确.
AC_OUTPUT创建名为makefile或其他名字(可选)的输出文件,其语法如下:
AC_OUPUT([file...[,extra_cmds[,init_cmds]]])
其中file是用空格分隔的输出文件列表,通过复制file.in到file来生成这些文件.extra_cmds是一个命令列
表,附加在config.status之后,在重新生成配置脚本时会用到它,init_cmds也将插入到config.status中,但其位置正好在
extra_cmds之前.
下面的调用次序只是建议性质的,而非必须:
AC_INIT
测试程序
测试函数库
测试头文件
测试类型定义
测试结构
测试编译器行为
测试库函数
测试系统调用
AC_OUTPUT
在这里有必要注意一下configure.in的写法.每一个宏调用应该占据单独的一行,这是因为多数autoconf宏都需要一个新行来结束命令.
一个多参数的单宏调用可以超过这个每宏一行的规则.这时应该使用\来续行并且用m4所能识别的括号[]来括起所有参数.下面的两个宏调用是等价的:
AC_CHECK_HEADERS([unistd.h
termios.h termio.h sgtty.h alloca.h
\
sys/iteimer.h)
AC_CHECK_HEADERS(unistd.h termios.h termio.h sgtty.h
alloca.h
sys/timer.h)
最后,可以使用,m4的注释符号dnl在cofigure.in中插入注释.例如:
dnl
dnl
This is an utterly gratuitous comment
dnl
AC_INIT(some_darn_fie)
在开始测试之前,可以用Perl脚本autoscan从源文件中抽取与函数调用和头文件有关的信息,并将其输出到cofigure.scan文件中.
autoscan的完整语法为:
autoscan
[ --macrodir=dir ] [ --help ] [ --verbose ] [ --version ] [srcdir
]
它没有必须要求的参数.srcdir指定了扫描的目录.在大多数情况下,只在源代码树的根目录下执行autoscan就足够了.
但是,在将该文件重命名或复制到configure.in之前,需要手工检查一下以确认是否遗漏了需要抽取的特性.ifnames工具的功能与
autoscan类似,它查找源文件中的预处理指令#if,#elif,#ifdef和#ifndef.可以用它来增加autoscan的输出,
ifname的语法是:
Usage:
ifnames [ -h ] [ --help ] [ -m dir ] [ --macrodir=dir ] [ --version ] [ file ...
]
关键的参数是file ...,它是要
代码清单 bogusapp.c
#include <stdio.h>
#include
<stdlib.h>
#ifdef HAVE_RESOLV_H
#include <resolv.h>
#endif
/* HAVE_RESOLV_H */
#include "config.h"
int main(void)
{
int
reval;
#ifdef HAVE_MMAP
fprintf(stdout, "have
mmap()\n");
#else
fprintf(stderr, "no
mmap()\n");
#endif
if(HAVE_UTIME_NULL)
fprintf(stdout, "utime()
allows NULL\n");
else
fprintf(stderr, "utime doesn't allow
NULL\n");
if(SYS_SIGLIST_DECLARED)
fprintf(stdout, "sys_siglist()
declared\n");
else
fprinf(stderr, "sys_siglist() not
declared\n");
#ifdef HAVE_NCURSES_H
fprintf(stdout, "ncurses.h
found\n");
#else
fprintf(stderr, "ncurses.h not
found\n);
if(HAVE_FCNTL_H)
fprintf(stdout, "fcntl.h
found\n");
else
fprintf(stderr, "fcntl.h not
found\n");
if(HAVE_SYS_FCNTL_H)
fprintf(stdout, "sys/fcntl.h
found\n");
else
fprintf(stderr, "sys/fcntl.h not
found\n");
#ifdef NLIST_NAME_UNION
fprintf(stdout, "nlist.n_un
member found\n");
#else
fprintf(stdout, "nlist.n_un member not
found\n");
#endif
if(HAVE_VOID_POINTER)
fprintf(stdout, "Yep, we
have a usable void pointer type\n");
else
fprintf(stderr, "Nope, no
usable void pointer
type\n");
exit(EXIT_SUCCESS);
}
.Makefiel.in--用于创建真正的makefile文件的模板
.acconfig.h--与特定系统相关的宏的集合,它随autoconf软件一起提供
.bogusapp.c--bogusapp的源代码,这是个示例程序
.config.h--包含bogusapp.c中用到的所有宏的头文件
.configure.in--创建最终的configure脚本的模板
.install.sh--安装脚本,用在不带兼容BSD的install程序的系统上
在目录下运行autoscan产生的configure.scan如下:
$autoscan
$cat
configure.scan
dnl Process this file with autoconf to produce a configure
script.
AC_INIT(acconfig.h)
dnl Checks for programs.
dnl Checks for
libraries.
dnl Checks for header files.
AC_HEADER_STDC
dnl Checks for
typedefs, structures, and compiler characteristics.
dnl Checks for library
functions.
AC_OUTPUT(Makefile)
ifnames的输出如下:
HAVE_MMAP
bogusapp.c
HAVE_NCURSES_H bogusapp.c
HAVE_RESOLV_H
bogusapp.c
HAVE_NAME_UNION bogusapp.c
候选程序测试集
测试
说明
AC_PROG_AWK
顺序检查mawk,gawk,nawk和awk是否存在,将输出变量AWK设置为所找到的第一个程序名
AC_PROG_CC
决定使用哪个C编译器,并设置输出变量CC
AC_PROG_CC_C_O
决定编译器是否接受-c或-o选项,如果不接受,定义NO_MINUS_C_MINUS_O
AC_PROG_CPP 把输出变量CPP设置为执行C预处理的命令
AC_PROG_INSTALL 把输出变量INSTALL设置为BSD兼容的install程序,或者是install
-sh
AC_PROG_LEX 查找flex或lex,并把输出变量LEX设为结果
AC_PROG_LN_S
如果系统支持符号链接,则设置变量LN_S设为ln -s,否则设置为ln
AC_PROG_RANLIB
如果ranlib存在,则设置输出变量RANLIB为ranlib,否则设置为":"
AC_PROG_YACC
顺序查找bison,byacc和yacc,并根据它找到结果把输出变量YACC设为bison
-y,byacc或yacc
库函数测试集
测试
说明
AC_CHECK_LIB(lib,function[
通过把一个C程序链接到函数库lib来判断在
,action_if_found[ lib库中是否存在指定的函数.在测试成功是
,action_if_not_found, 执行shell命令action_if_found或者在
[,other_libs]]])
ation_if_found为空时,在输出变量LIB中添
加-llib.action_if_not_found把
lother_libs选项传给link命令
AC_FUNC_GETLOADAVG
如果系统支持getloadavg函数,把获得该函数所必须的函数库添加到LIBS变量
AC_FUNC_GETPGRP
测试getprgrp是否需要参数,如果不需要,定义GETPGRP_VOID,否则,getpgrp
需要一个进程ID作为其参数
AC_FUNC_MEMCMP
如果memcmp函数不存在,把memcmp.o添加到LIBOBJS中
AC_FUNC_MMAP
如果存在mmap函数,设置HAVE_MMAP
AC_FUNC_SETPGRP
测试setprgrp是否需要参数,如果不需要,定义SETPGRP_VOID,否则,setpgrp
需要两个进程ID作为其参数
AC_FUNC_UTIME_NULL
如果utime(file,NULL)函数能把文件的时间戳设置为当前时间,定义HAVE_UTIME_NULL
AC_FUNC_VFORK
如果vfork.h文件不存在,定义vfork为fork
AC_FUNC_VPRINTF 如果存在vprintf函数,定义HAVE_VPRINTF
头文件测试
测试 说明
AC_DECL_SYS_SIGLIST
如果signal.h或unistd.h定义了sys_syglist,定义
SYS_SIGLIST_DECLARED
AC_HEADER_DIRENT 顺序检查头文件dirent.h,
sysdir/ndir.h,sys/dir.h和ndir.h中是否定义了DIR,并根据结果定义HAVE_DIRENT_H,
HAVE_SYS_NDIR_H,HAVE_SYS_DIR_H或HAVE_NDIR_H
AC_HEADER_STDC 如果系统支持ANSI/ISO
C头文件,定义STDC_HEADERS
AC_HEADER_SYS_WAIT 如果系统支持有POSIX兼容的头文件sys/wait.h,定义输出变量HAVE_SYS_WAIT
结构测试
测试 说明
AC_HEADER_TIME 如果time.h和sys/time.h在一个程序中都存在,定义输出变量TIME_WITH_SYS_TIME
AC_STRUCT_ST_BLKSIZE 如果stat结构有成员st_blksize,定义输出变量
HAVE_ST_BLKSIZE
AC_STRUCT_ST_BLOCKS
如果stat结构有成员st_blocks,定义输出变量
HAVE_ST_BLOCKS
AC_STRUCT_TIMEZONE 指出如何取得时区值,如果tm结构有成员tm_zone,定义输出变量HAVE_TM_ZONE:如果找到tzname数组,定义输出变量HAVE_TZNAME
类型定义测试
测试
说明
AC_TYPE_GETGROUPS 根据传递给
getgroups()的数组的基本类型来设置
GETGROUPS_T为gid_t或int
AC_TYPE_MODE_T
如果mode_t没有定义,定义mode_t为int
AC_TYPE_PID_T 如果pid_t没有定义,定义pid_t为int
AC_TYPE_SIGNAL
如果signal.h中没有将signal定义为(void*),定义RETSIGTYPE为int
AC_TYPE_SIZE_T
如果size_t没有定义,定义size_t为unsigned
AC_TYPE_UID_T 如果uid_t没有定义,定义uid_t和gid_t为int
|