6.3. 软件包管理

软件包管理经常被请求加入到 LFS 手册中。软件包管理器可以追踪安装的文件,方便软件包的移除和升级。不仅是二进制执行文件和库文件,包管理器还会处理配置文件的安装。在你想太多之前,答案是不——本节不讨论也不安利任何特定的软件包管理器,只是总结了一下关于软件包管理的常用技术和工作原理。对你而言最完美的软件包管理器可能就在这些技术之中,也可能由这些技术的其中几个组合而成。本节还简要的提了一些在升级软件包时可能遇到的问题。

为什么 LFS 或 BLFS 手册里不采用任何软件包管理器的一些原因:

关于软件包管理有很多资料,可以访问 Hints Project 看看是否可以解决你的需求。

6.3.1. 升级问题

软件包管理器可以在软件新版本发布后轻松升级。一般来说 LFS 和 BLFS 手册里的指令是可以用来升级版本。下面是一些在你准备升级软件包时需要注意的事情,特别是运行中系统需更加注意。

  • 如果需要升级 Glibc 到新版本(比如,从 glibc-2.19 升级到 glibc-2.20),重新构建整个 LFS 会比较安全。虽然你 也许 能够按依赖关系重新编译所有的软件包,不过我们不建议这样做。

  • 如果某个包含的动态库的软件包升级了,而且库名字有所改变,那么所有动态链接到这个库的软件包都需要重新链接新的库。(请注意软件包版本和库名字并不存在相关性)。举个例子,说软件包 foo-1.2.3 安装了一个叫 libfoo.so.1 的动态库。然后你将软件包升级到了新版本 foo-1.2.4 安装的动态库叫 libfoo.so.2 了。在这种情况下,所有动态链接到 libfoo.so.1 的软件包都需要重新编译链接到 libfoo.so.2。注意在所有依赖软件包重新编译完成之前,请勿删除之前的库文件。

6.3.2. 软件包管理技术

下面介绍一些常见的软件包管理技巧。在决定用哪种包管理方式之前,先研究一下各种不同的技术,特别是某些方案的不足之处。

6.3.2.1. 所有一切都在我脑袋里!

是的,这也算一种软件包管理技术。有些人觉得不需要管理软件包,是因为他们非常熟悉软件包,知道每个包都安装了哪些文件。也有些用户不需要管理软件包,是因为他们会在某个软件包有更改后重建整个系统。

6.3.2.2. 在独立目录里安装

这是一种简单的软件包管理方式,不需要其他额外的软件来管理软件的安装。每一个软件包都被装到一个独立的目录里。例如,软件包 foo-1.1 安装到目录 /usr/pkg/foo-1.1 中并创建一个符号链接 /usr/pkg/foo 指向 /usr/pkg/foo-1.1。在安装新版本 foo-1.2 的时候,它会被装到目录 /usr/pkg/foo-1.2 中,然后用指向新版本的软链替代之前的符号链接。

类似 PATHLD_LIBRARY_PATHMANPATHINFOPATHCPPFLAGS 之类的环境变量变量需要包含 /usr/pkg/foo 目录。在管理大量软件包时,这种方式就不可行了。

6.3.2.3. 符号链接方式软件包管理

这是前一种软件包管理技术的变种。每个软件包的安装方式都和之前的方式类似。但不是建立目录的符号链接,而是把每个文件都链接到 /usr 目录结构里。这样就不需要扩展环境变量了。通过自动创建这些可由用户自行创建的软链,许多软件包管理器就采用了这种方式进行管理的。其中比较流行的有 Stow、Epkg、Graft 和 Depot。

这种安装方式需要伪装,这样软件包会认为自己被装到了 /usr 目录下,而实际上它被装到了 /usr/pkg 目录下。在这种方式下,安装并不是一件琐碎的小事。例如,假如你准备安装一个软件包 libfoo-1.1。下面的指令可能不会正确地安装:

./configure --prefix=/usr/pkg/libfoo/1.1
make
make install

安装本身倒是没有问题,但是可能一些依赖包不会像你期望的那样链接到 libfoo 库。如果你要编译一个链接 libfoo 的软件,你可能会注意到它实际上链接到的是 /usr/pkg/libfoo/1.1/lib/libfoo.so.1 而不是你所期望的 /usr/lib/libfoo.so.1。正确的方式是使用 DESTDIR 策略来伪装软件包的安装过程。这种方式需要像下面这样操作:

./configure --prefix=/usr
make
make DESTDIR=/usr/pkg/libfoo/1.1 install

大多数软件包支持这种方式,但也有一些例外。对于不兼容的软件包,你可能需要自己手动安装,或许你会发现将这些有问题的包安装到 /opt 目录下会更简单些。

6.3.2.4. 基于时间戳

在这种方式里,在安装之前会创建一个时间戳文件。在安装之后,用一行简单的 find 命令加上合适的参数就可以生成在时间戳文件创建之后所安装的所有文件列表。有一个采用这种方式的包管理器叫做 install-log。

这种方式的优点是非常简单,但是它有两个缺陷。比如,在安装过程中,所安装文件采用的是其它时间戳而不是当前时间,那这些文件将不能被软件包管理器跟踪到。还有,这种方式只能在一次安装一个软件包的情况下使用。如果在不同的终端里同时安装两个不同的软件包,此时的安装日志就不可靠了。

6.3.2.5. 追踪安装脚本

在这种方式里,安装脚本所使用的命令都会被记录下来。有两种技术,一种是:

设定环境变量 LD_PRELOAD 指向一个在安装前预加载的库。在安装过程中,这个库会追踪软件包安装脚本里所包含的各种执行文件比如 cpinstallmv,以及追踪会修改文件系统的系统调用。要让这种方式有效的话,所有的执行文件需要动态链接到没有 suid 或 sgid 标志位的库。预加载这个库可能会引起安装过程中一些意外的副作用。因此,建议做一些测试以保证软件包管理器不会造成破坏并且记录了所有适当的文件。

第二种技术是使用 strace 命令,它会记录下安装脚本执行过程中所有的系统调用。

6.3.2.6. 创建软件包存档

在这种方式里,像之前的符号链接软件包管理方式里所描述的那样,软件包被伪装安装到一个独立的目录树里。在安装完成后,会将已安装文件打包成一个软件包存档。然后这个存档会用来在本地机器或其他机器上安装软件包。

这种方式为商业发行版中的大多数包管理器所采用。例子有 RPM(顺带提一下,这也是 Linux 标准规范 中指定的包管理器),pkg-utils,Debian 的 apt,和 Gentoo 的 Portage 系统。如何在 LFS 系统里采用这种包管理方式的简单描述,请参看 http://www.linuxfromscratch.org/hints/downloads/files/fakeroot.txt.

创建带有依赖关系的软件包存档非常复杂,已经超出 LFS 手册范围了。

Slackware 使用一个基于 tar 的系统来创建软件包存档。这套系统不像那些更复杂的包管理器,有意地不处理包依赖关系。关于 Slackware 包管理器的详细信息,请参看 http://www.slackbook.org/html/package-management.html

6.3.2.7. 基于用户的软件包管理

这种方式,是 LFS 特有的,由 Matthias Benkmann 所设计,可以在 Hints Project 中能找到。在这种方式里,每个软件包都由一个单独的用户安装到标准的位置。文件属于某个软件包可以通过检查用户 ID 轻松识别出来。关于这种方式的利弊比较复杂,就不再本节中赘述了。详细的信息请参看 http://www.linuxfromscratch.org/hints/downloads/files/more_control_and_pkg_man.txt

6.3.3. 在多个系统上部署 LFS

LFS 系统的一个优点是没有那种需要依赖其在磁盘系统中的位置的文件。克隆一份 LFS 到和宿主机器相似配置的机器上,简单到只要对包含根目录的 LFS 分区(对于一个基本的 LFS 构建不压缩的话大概有 250MB)使用 tar 命令打包,然后通过网络传输或光盘拷贝到新机器上展开即可。在这之后,需要调整一些配置文件。需要更新的配置文件包括: /etc/hosts, /etc/fstab, /etc/passwd, /etc/group, /etc/shadow, /etc/ld.so.conf, /etc/sysconfig/rc.site, /etc/sysconfig/network, 和 /etc/sysconfig/ifconfig.eth0

根据系统硬件和原始内核配置文件的差异,可能还需要重新编译一下内核。

[注意]

注意

据报告,当这样的复制发生在两个相近却又不完全相同的架构时会发生问题。例如,Intel 系统的指令集就和 AMD 处理器的不同,还有一些较新版的处理器可能会有一些在较早版本中不能支持的指令。

最后,通过 第 8.4 节 「使用 GRUB 设置启动过程」 中介绍的方法来使新系统可以引导。