内容


对话 UNIX,第 12 部分

自己动手完成项目

从源代码构建软件

Comments

系列内容:

此内容是该系列 # 部分中的第 # 部分: 对话 UNIX,第 12 部分

敬请期待该系列的后续内容。

此内容是该系列的一部分:对话 UNIX,第 12 部分

敬请期待该系列的后续内容。

UNIX® 系统中提供了数百个实用工具应用程序或者命令。其中一些命令可以操作文件系统,而其他的命令则用于查询并控制操作系统本身。大量的命令提供了连接性,并且还有更丰富的命令可用于生成、交换、修改、筛选和分析数据。由于 UNIX 具有悠久而丰富的历史,所以您也许能够找到恰好合适的工具,以用于手头的任务。

此外,当一个实用工具不能满足需求时,您可以通过各种各样的方式结合任何数量的 UNIX 实用工具,以创建您自己的工具。正如您在前面的部分中所看到的,可以利用管道、重定向和条件,直接在命令行中构建即时可用的工具,并且 Shell 脚本将小型的、易于学习的编程语言的强大功能与 UNIX 命令结合在一起,以构建可重用工具。

当然,在很多情况下,仅依靠命令行和 Shell 脚本是不够的。例如,如果您必须部署一个新的守护进程以提供新的网络服务,那么您可以使用一种表达能力更强的语言,如 C 或者 Python,以便自己编写应用程序。并且,因为 Internet 上有许多应用程序是免费的(免费 意味着无需支付任何费用、得到自由条款的许可,或者两者都有),所以您还可以下载、编译并安装适当的、有效的解决方案,以满足您的需求。

UNIX(以及 Linux®)的许多版本都提供一种称为包管理器 的特殊工具,用以在系统中添加、删除和维护软件。包管理器通常可以维护本地安装的所有软件的详细目录,以及一个或者多个远程存储库 中所有可用软件的目录。您可以使用包管理器在存储库中搜索您所需要的软件。如果存储库中包含您正在寻找的软件,那么您只需要使用一个命令或者点击几下鼠标,就可以在您的系统中安装一个新的包。

包管理器是非常有价值的。使用它,您可以删除全部的包、更新现有的包,以及为任何包自动地检测并实现任何先决条件。例如,如果您选择了操作图像的软件,如可靠的 ImageMagick,但是您的系统中缺少处理 JPEG 图像的库,那么包管理器将在安装您所选择的包之前检测并安装缺少的内容。

然而,也可能存在这样的情况,即您所需要的软件是可获得的,但它却不包含于任何存储库中。由于包管理方式具有显著的优势,所以大多数软件都提供了可以使用包管理器进行下载并安装的形式。然而,因为 UNIX 的版本和风格非常之多,所以很难针对每种特定的变体,以各种包管理器的格式提供每个应用程序。如果您的 UNIX 安装是主流的,并受到大量拥护者的喜爱,那么您将更有可能找到预先构建的并且可供使用的软件。否则,您就需要挽起袖子准备自己动手构建软件了。

是的,年轻的绝地武士(《星球大战》中的武士),是使用源代码的时候了。

如同从沼泽中升起一架 X 翼战斗机一样,从源代码构建软件乍看起来可能是令人生畏的,特别当您不是软件开发人员的时候。事实上,在大多数情况下,整个构建过程仅仅只需要少数几条命令,其余的工作都是自动完成的。

当然,某些程序构建起来是非常复杂的(或者需要花费数小时来进行构建),并且在构建过程中需要进行人工介入。然而,即使这些程序通常是由一些容易构建的较小的块构造而得到的,依赖关系的数量和构造的顺序也会使构建过程变得复杂。一些程序还有许多您并不一定希望拥有的特性。例如,您可以构建 PHP,以便与新的网际协议版本 6 (IPv6) Internet 寻址方案进行互操作。如果您的网络尚未采用 IPv6,则不需要包括这个特性。对大量选项进行的审查将使构建过程变得更加麻烦。

这个月,让我们来研究如何构建一个典型的 UNIX 软件应用程序。在继续学习后面的内容之前,请确保系统中安装了 C 编译器,如 GNU 编译器套装(GNU Compiler Collection,GCC),以及常见的 UNIX 软件开发工具套装,包括 makem4pkg-configawk。此外,请确保在您的 PATH 环境变量中包含了所有的开发工具。

软件包中有价值的内容

作为一个说明性的和典型的示例,让我们配置、构建并安装 SQLite——一个实现结构化查询语言(Structured Query Language,SQL)数据库引擎的小型的库。SQLite 不需要进行任何配置即可使用,并且可以完整地嵌入到任何应用程序中,而数据库则包含在单个文件中。许多编程语言都可以调用 SQLite 以实现数据的持久化。SQLite 还包括一种用于管理 SQLite 数据库的、名为 sqlite3 的命令行实用工具。

要开始学习这部分内容,首先下载 SQLite(请参见参考资料)。选择最新的源代码包,并将其下载到您的计算机中。(在撰写本文时,SQLite 的最新版本是版本 3.3.17,于 2007 年4 月 25 日发布。)这个示例使用了 http://www.sqlite.org/sqlite-3.3.17.tar.gz 中存储的文件。

在您获得了该文件之后,请对其进行解压缩。.tar.gz 扩展反映了该存档文件是如何构造的。在这个示例中,它是一个压缩了的 tar 存档文件。后面的扩展 .gz,表示 gzip(压缩);前面的扩展 .tar,表示 tar(一种存档格式)。要提取该存档文件的内容,只需要对其进行反向处理即可,也就是首先解压缩,然后打开该存档文件:

$ gunzip sqlite-3.3.17.tar.gz
$ tar xvf sqlite-3.3.17.tar

这两个命令在一个名为 sqlite-3.3.17 的新目录中创建了原始源代码的一个副本。顺便说明一下,.tar.gz 文件格式是非常常见的(称为 tarball),并且您可以使用 tar 命令直接解压缩 tarball 文件:

$ tar xzvf sqlite-3.3.17.tar.gz

这一个命令和前面的两个命令是等价的。

接下来,将目录更改为 sqlite-3.3.17,并使用 ls,以列出其中的内容。您应该看到与清单 1 所示类似的清单:

清单 1. SQLite 包的清单
$ ls
Makefile.in             contrib                 publish.sh
Makefile.linux-gcc      doc                     spec.template
README                  ext                     sqlite.pc.in
VERSION                 install-sh              sqlite3.1
aclocal.m4              ltmain.sh               sqlite3.pc.in
addopcodes.awk          main.mk                 src
art                     mkdll.sh                tclinstaller.tcl
config.guess            mkopcodec.awk           test
config.sub              mkopcodeh.awk           tool
configure               mkso.sh                 www
configure.ac            notes

其中的源代码和 SQLite 补充文件经过了很好组织,并且模拟了大部分的软件项目分发源代码的方式:

  • README 文件对该项目进行了描述,并且通常用于说明如何构建该软件。(README 文件还详细地介绍了使用条款,或者许可证、适用情况。许多项目的许可证代码都符合 GNU 公共许可版本 2 中的条款,即所谓的“copyleft”许可证。在许可证与您打算如何使用该软件之间可能存在一定的冲突,如果您对此有任何疑问,最好请教一下合适的法律顾问。)
  • src 目录中包含了相关的代码。
  • test 目录中包含了一组测试,以验证该软件的操作是否正确。在开始构建或者进行了任何修改之后,请运行这些测试,这样可以增加对该软件的信心。
  • contrib 目录中包含核心 SQLite 开发团队所没有提供的附加软件。对于像 SQLite 这样的库,contrib 中可能包含一些常用语言(如 C、Perl、PHP 和 Python)的编程接口。它可能还包括图形用户界面(GUI)包装,以及更多的内容。
  • 在其他文件中,Makefile.in、configure、configure.ac 和 aclocal.m4 用于生成在您的 UNIX 版本中编译 SQLite 软件的脚本和规则。如果这个软件足够简单,那么要编译其代码,可能只需要一条简单的编译命令即可。但是,因为存在如此之多的 UNIX 变种(Mac OS X、Solaris、Linux、IBM® AIX® 和 HP/UX 等等),所以必须对宿主计算机进行分析,以确定它的功能及其实现。例如,邮件阅读应用程序可能会尝试确定本地系统是如何存储邮箱的,并包含对该格式的支持。

集中精力感受源代码

下一个步骤是探查系统并配置该软件,以便进行正确地构建。(您可以将这个步骤想象为裁剪一件衣服:这件衣服大小基本合适,但是需要进行一定的修改以使其更加时尚。)您需要进行自定义工作,并为使用 ./configure 本地脚本进行编译做好准备。在命令行提示处,键入:

$ ./configure

这个配置脚本进行了几项测试,以证明您的系统是合格的。例如,在一台 Apple MacBook 计算机上(其中运行着 FreeBSD® UNIX 的一个变种)运行 ./configure,将产生以下结果(请参见清单 2):

清单 2. 在 Mac OS X 上运行 ./configure 的结果
checking build system type... i386-apple-darwin8.9.1
checking host system type... i386-apple-darwin8.9.1
checking for gcc... gcc
checking for C compiler default output file name... a.out
checking whether the C compiler works... yes
checking whether we are cross compiling... no
checking for suffix of executables... 
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ISO C89... none needed
checking for a sed that does not truncate output... /usr/bin/sed
checking for grep that handles long lines and -e... /usr/bin/grep
checking for egrep... /usr/bin/grep -E
checking for ld used by gcc... /usr/bin/ld
...

这里,./configure 可以确定编译和主机系统的类型(如果您采用交叉编译的话,它可能不同),证实是否已经安装了 GNU C 编译器(GCC),并找到其余构建过程可能需要使用的实用工具的路径。您可以浏览一下其余的输出,但是您将看到一个较长的诊断信息列表,该列表从成功构造 SQLite 的角度分析了您的系统的特点。

注意:./configure 命令可能 会失败,特别是在无法找到一个先决条件的情况下(比如一个系统库或者关键的系统实用工具)。

浏览 ./configure 的输出,寻找其中不正常之处,如命令的专用或者本地版本,它可能并不适合于构建通用的应用程序,如 SQLite。作为一个示例,如果您的系统管理员安装了 GCC 的 alpha 版本,并且 configure 工具首选使用该版本,那么您可以选择手动地改写这个选择。要查看您可以改写的选项的列表(该列表通常很长),可以键入 ./configure --help,如清单 3 中所示:

清单 3. 用于 ./configure 脚本的通用选项
$ ./configure --help
...
By default, `make install' will install all the files in
`/usr/local/bin', `/usr/local/lib' etc. You can specify
an installation prefix other than `/usr/local' using `--prefix',
for instance `--prefix=$HOME'.

For better control, use the options below.

Fine tuning of the installation directories:
  --bindir=DIR           user executables [EPREFIX/bin]
  --sbindir=DIR          system admin executables [EPREFIX/sbin]
  --libexecdir=DIR       program executables [EPREFIX/libexec]
...

./configure --help 的输出中包括配置系统使用的通用选项,以及仅与您正在构建的软件相关的特定选项。要查看后者(较短)的列表,可以键入 ./configure --help=short(请参见清单 4):

清单 4. 要构建的软件包所特定的选项
$ ./configure --help=short
Optional Features:
  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
  --enable-shared[=PKGS]  build shared libraries [default=yes]
  --enable-static[=PKGS]  build static libraries [default=yes]
  --enable-fast-install[=PKGS]
                          optimize for fast installation [default=yes]
  --disable-libtool-lock  avoid locking (might break parallel builds)
  --enable-threadsafe     Support threadsafe operation
  --enable-cross-thread-connections
                          Allow connection sharing across threads
  --enable-threads-override-locks
                          Threads can override each others locks
  --enable-releasemode    Support libtool link to release mode
  --enable-tempstore      Use an in-ram database for temporary tables
                          (never,no,yes,always)
  --disable-tcl           do not build TCL extension
  --disable-readline      disable readline support [default=detect]
  --enable-debug          enable debugging & verbose explain

返回到 ./configure --help,最顶部的输出显示了可执行文件的缺省安装目录是 /usr/local/bin,库文件的缺省安装目录是 /usr/local/lib,等等。许多系统使用一个替代的层次结构来存储非核心软件。

例如,许多系统管理员选择使用 /opt 而不是 /usr/local 存储本地添加的或者在本地进行了修改的软件。如果您希望将 SQLite 安装到与缺省目录不同的其他目录中,可以使用 --prefix= 选项指定该目录。一种可行的方法(也是一种常见的方法,如果只有您一个人使用这个软件包,或者如果您没有 root 访问权限以便在全局的范围内安装该软件)是将该软件安装到您的 home 目录中的层次结构中:

$ ./configure --prefix=$HOME/sw

使用这个命令,构建过程的安装部分将在 $HOME/sw 中(比如 $HOME/sw/bin、$HOME/sw/lib、$HOME/sw/etc、$HOME/sw/man,以及其他所需的目录中)重新创建该软件的层次结构。为了简单起见,这个示例在缺省目标处安装其代码。

编译代码

./configure 的结果是一个与您的 UNIX 版本兼容的 Makefile。名为 make 的开发实用工具将使用这个 Makefile,以执行编译所需的步骤,并将代码链接到一个可执行文件。您可以打开这个 Makefile 对其进行检查,但不要对它进行编辑,因为如果您再次运行 ./configure,它将列出您所做的任何修改。

这个 Makefile 中包含需要编译的源文件的列表,并且它还包括启用或者禁用并选择 SQLite 包中的某些代码片段的常数。例如,如果 configure 工具检测到了系统中合适的芯片,那么它可能会启用 64位处理器特定的代码。这个 Makefile 还说明了源文件之间的依赖关系,因此在一个非常重要的头文件 (.h) 中进行的一项更改,可能会导致重新编译所有的 C 源代码。

您的下一个步骤是运行 make,以构建该软件(请参见清单 5):

清单 5. 运行 make
$ make 
sed -e s/--VERS--/3.3.17/ ./src/sqlite.h.in | \
  sed -e s/--VERSION-NUMBER--/3003017/ >sqlite3.h

gcc -g -O2 -o lemon ./tool/lemon.c

cp ./tool/lempar.c .

cp ./src/parse.y .

./lemon  parse.y

mv parse.h parse.h.temp

awk -f ./addopcodes.awk parse.h.temp >parse.h

cat parse.h ./src/vdbe.c | awk -f ./mkopcodeh.awk >opcodes.h

./libtool --mode=compile --tag=CC gcc -g -O2 -I. -I./src \
  -DNDEBUG  -I/System/Lib rary/Frameworks/Tcl.framework/Versions/8.4/Headers \
  -DTHREADSAFE=0 -DSQLITE_THREA D_OVERRIDE_LOCK=-1 \
  -DSQLITE_OMIT_LOAD_EXTENSION=1 -c ./src/alter.c

mkdir .libs

gcc -g -O2 -I. -I./src -DNDEBUG \
  -I/System/Library/Frameworks/Tcl.framework/Vers ions/8.4/Headers \
  -DTHREADSAFE=0 -DSQLITE_THREAD_OVERRIDE_LOCK=-1 \
  -DSQLITE_OMIT_L OAD_EXTENSION=1 -c ./src/alter.c  -fno-common \
  -DPIC -o .libs/alter.o
...
ranlib .libs/libtclsqlite3.a
creating libtclsqlite3.la

注意: 在上面的输出中,添加了一些空白行,以便更好地突出显示 make 发起的每个步骤。

make 实用工具检查文件(头文件、源代码、数据文件和目标文件)的修改日期,并编译合适的 C 源文件。最初,make 将重新编译所有内容,因为不存在任何目标文件或者编译目标。正如您可以看到的,用于编译目标的规则还包括一些中间步骤,其中使用了一些相关的工具,如 sedawk,以产生在后续的步骤中将要使用的头文件。

执行 make 命令所得到的结果是一个完成的库和 sqlite3 实用工具。

最好对您刚编译的软件进行测试,尽管这在每个包中并不是强制的,也没有提供相应的内容。即便成功地构建 了您的软件,也不一定就表示该软件能够正确地运行

要测试您的软件,可以再次运行 make,并使用 test 选项(请参见清单 6):

清单 6. 对软件进行测试
$ make test
...
alter-1.1... Ok
alter-1.2... Ok
alter-1.3... Ok
alter-1.3.1... Ok
alter-1.4... Ok
...
Thread-specific data deallocated properly
0 errors out of 28093 tests
Failures on these tests:

成功了!该软件构建成功,并且工作正常。如果其中一个或者多个测试用例失败了,那么底部的总结(这里,它是空白的)将向您报告哪一项测试或者哪几项测试需要进一步研究。

完成后的产品

如果您的软件工作正常,那么最后一个步骤是将它安装到您的系统中。同样,使用 make,并指定 install 目标。要将软件添加到 /usr/local,通常需要由 sudo 所提供的超级用户(root)权限(请参见清单 7):

清单 7. 在您的本地系统中安装软件
$ sudo make install 
tclsh ./tclinstaller.tcl 3.3
/usr/bin/install -c -d /usr/local/lib
./libtool --mode=install /usr/bin/install 
    -c libsqlite3.la /usr/local/lib /usr/bin/install 
    -c .libs/libsqlite3.0.8.6.dylib /usr/local/lib/libsqlite3.0.8.6 .dylib
...
/usr/bin/install -c .libs/libsqlite3.lai /usr/local/lib/libsqlite3.la
/usr/bin/install -c .libs/libsqlite3.a /usr/local/lib/libsqlite3.a
chmod 644 /usr/local/lib/libsqlite3.a
ranlib /usr/local/lib/libsqlite3.a
...
/usr/bin/install -c -d /usr/local/bin
./libtool --mode=install /usr/bin/install -c sqlite3 /usr/local/bin
/usr/bin/install -c .libs/sqlite3 /usr/local/bin/sqlite3
/usr/bin/install -c -d /usr/local/include
/usr/bin/install -c -m 0644 sqlite3.h /usr/local/include
/usr/bin/install -c -m 0644 ./src/sqlite3ext.h /usr/local/include
/usr/bin/install -c -d /usr/local/lib/pkgconfig; 
/usr/bin/install -c -m 0644 sqlite3.pc /usr/local/lib/pkgconfig;

make install 过程创建了所需的目录(如果它们都不存在的话),将文件复制到目标,并运行 ranlib,以便为应用程序准备好要使用的库。它还将 sqlite3 实用工具复制到 /usr/local/bin,复制开发人员使用 SQLite 库构建软件时所需的头文件,并将文档复制到该层次结构中适当的位置。

假定 /usr/local/bin 在您的 PATH 变量中,那么您现在就可以运行 sqlite3(请参见清单 8):

清单 8. SQLite 已经可以使用
$ which sqlite3
/usr/local/bin/sqlite3
$ sqlite3
SQLite version 3.3.17
Enter ".help" for instructions
sqlite>

对初学者的建议?

与 SQLite 一样,大多数软件包构建起来都非常容易。实际上,您通常只需要使用一个命令就可以配置、构建和安装该软件:

$ ./configure && make && sudo make install

仅当前面的命令正确完成时,&& 操作符才会运行后面的命令。因此,以上的命令相当于“运行 ./configure,如果它运行成功的话,那么运行 make,如果运行成功的话,运行 sudo make install。”这条命令构建了一个无人参与的包。启动它,然后去喝杯咖啡、来份三明治,或者享用一份套餐,这取决于您正在构建的包的大小和复杂程度。

对于从源代码构建软件,还有一些其他有价值的技巧:

  • 如果您正在构建的软件包使用典型的 ./configure && make && sudo make install 还不够,那么请记录下编译代码所采取的步骤。如果您必须重新编译相同的代码,或者编译该代码更新的版本,那么就可以借助您的记录,回忆起所需采取的步骤。将这个记录存储在与该包的 README 文件相同的目录中。您甚至可以为该记录的文件名采用一种约定,这将使您可以更容易地识别以前所编译的内容。
  • 然而更有价值的是,如果构建该软件所需的步骤是无需手工干预就可重复执行的,那么可以在 Shell 脚本描述这个过程。稍后,如果您必须重新编译相同的代码,只需要运行这个 Shell 脚本即可。如果该代码有更新的版本可供使用,那么您可以根据需要对这个脚本进行修改,以添加、更改或者删除其中的步骤。
  • 在安装完软件之后,通过使用 make clean,您就可以收回磁盘空间。这个规则通常会删除目标文件和任何中间文件,但是它将完整地保留重新启动该过程所需的文件。另一个规则 make distclean,可以删除 Makefile 和生成的其他文件。
  • 分开保存相同代码的不同版本源代码。这种方法允许您对不同的发布版本进行比较,除此之外,它还允许您恢复软件的特定版本。将源代码组织到一个本地存储库中,比如 $HOME/src 或者 /usr/local/src,这取决于您的使用范围(个人或者全局)以及您的本地约定。
  • 而且,您可以通过使得源代码在全局范围内成为只读的,来防止意外地删除或者覆盖。更改到您希望进行保护的源代码目录,并运行 chmod -R a-w * 命令(递归地运行 chmod,关闭所有的写权限)。

最后,可能出现这样的情况,源代码无法在您的系统中进行编译。如前所述,最常出现的问题是缺少某些先决条件。请仔细阅读错误消息或者相关的消息,可以清楚地看到究竟什么地方出了错。

如果您无法找到错误原因,那么可以在 Google 中键入确切的错误消息,以及您正在尝试编译的包的名字。其他的人很有可能也碰到并解决过相同的问题。(事实上,在 Internet 中搜索错误消息可能是非常具有启发性的,尽管您可能需要花费少许时间以挖掘出真正有用的信息。)

如果您遇到困难,可以查看该软件的主页,获取有关参考资料的链接,如 IRC 频道、新闻组、邮件列表或者 FAQ。本地系统管理员也同样具有丰富的经验。

源代码的功能是非常强大的

如果系统中缺少您所需要的某种工具,那么您可以随意地在命令行中创建一个,您可以编写 Shell 脚本,也可以编写您自己的程序,并且还可以借鉴联机查找的大量代码。您可以像我一样,不断地锻炼绝地武士的意识技能。

“这是我所读过的最好的文章。”


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=AIX and UNIX, Linux, Open source
ArticleID=257727
ArticleTitle=对话 UNIX,第 12 部分: 自己动手完成项目
publish-date=09242007