#
怎樣才能使程序具有可移植性呢?
.盡可能避免針對特定系統的假定和方法.例如,不認定程序只在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
|