第 7 章 Debian 软件包管理系统基础

目录

7.1. 什么是 Debian 软件包?
7.2. Debian 二进制软件包的格式是什么?
7.3. 为什么 Debian 软件包的文件名这么长?
7.4. 什么是 Debian 控制文件?
7.5. 什么是 Debian conffile?
7.6. 什么是 Debian preinst、postinst、prerm,和 postrm 脚本?
7.7. 什么是必备必需重要标准可选额外软件包?
7.8. 什么是虚拟软件包?
7.9. 一个软件包依赖推荐建议冲突替代破坏或者提供另一个软件包是什么意思?
7.10. “预依赖”是什么意思?
7.11. 软件包状态中的未知安装卸载清除保留都是什么意思?
7.12. 如何将软件包设置为“保留”?
7.13. 如何安装源码包?
7.14. 如何从源码包构建二进制包?
7.15. 我应该如何创建自己的 Debian 软件包?

本章涉及 Debian 包管理系统的一些底层细节。如果您主要是想了解相关工具的用法,请跳过本章,并阅读第 8 章 Debian 软件包管理工具和/或第 9 章 使您的 Debian 系统保持最新状态

7.1. 什么是 Debian 软件包?

软件包通常包括了实现一组互相关联的命令或功能所需要的一切文件。Debian 软件包有两种类型:

  • 二进制包,包含了可执行文件、配置文件、man/info 页面、版权信息,以及其他文档。这些软件包使用一种 Debian 特有的存储格式进行分发(参见第 7.2 节 “Debian 二进制软件包的格式是什么?”);它们的扩展名通常是“.deb”。二进制包可以使用 Debian 的 dpkg 工具进行解包(也许会通过 aptitude 等前端进行调用);更多细节请阅读手册页。

  • 源码包,包含了一个 .dsc 文件描述该源码包(包括下述文件的文件名),一个 .orig.tar.gz 文件,使用经过 gzip 压缩的 tar 档案保存未经修改的源代码,通常还包括一个 .diff.gz 文件,保存了 Debian 对源代码的修改。dpkg-source 工具可以打包和解包 Debian 源码包;更多细节请阅读手册页。(apt-get 程序可以用作 dpkg-source 的前端。)

软件包管理系统使用“依赖关系”安装软件,这些依赖由软件包的维护者小心地指定。依赖关系在每个软件包含有的 control 文件里给出。例如,包含 GNU C 编译器的软件包(gcc)“依赖”软件包 binutils,后者包含了链接器和汇编器。如果用户在没有安装 binutils 的情况下尝试安装 gcc,包管理系统(dpkg)会给出一条错误信息,提示它依赖 binutils,并停止安装 gcc。(不过,如果用户坚持,这个功能可以被覆盖,参见 dpkg(8))。阅读下方的第 7.9 节 “一个软件包依赖推荐建议冲突替代破坏或者提供另一个软件包是什么意思?”以了解更多信息。

Debian 的包管理工具可以用于:

  • 操作和管理软件包或其组成部分,

  • 管理软件包在本地覆盖的文件,

  • 帮助开发者建立软件包档案库,以及

  • 帮助用户从远程档案库站点安装软件包。

7.2. Debian 二进制软件包的格式是什么?

Debian“软件包”,或称作 Debian 档案文件,包含了与特定的程序套件或一组关联的程序有关的可执行文件、库和文档。正常情况下,Debian 档案文件的文件名以 .deb 结尾。

Debian 二进制包格式的内部细节在 deb(5) 手册页中有描述。这一内部格式可能会发生变化(在 Debian GNU/Linux 的各个主要版本之间),所以如果您需要对 .deb 文件进行底层操作,请总是使用 dpkg-deb(1)

7.3. 为什么 Debian 软件包的文件名这么长?

Debian 二进制包的文件名遵循以下约定:<foo>_<版本号>-<Debian 修订版本号>_<Debian 架构>.deb

注意 foo 应当是软件包的名称。要检查特定的 Debian 档案文件(.deb 文件)的包名,可以使用以下方法之一:

  • 在 Debian 档案站点存储它的目录中检查“Packages”文件。该文件对每个软件包都有一节简要的信息;每节的第一行就是正式的包名。

  • 使用命令 dpkg --info foo_VVV-RRR_AAA.deb(其中 VVV、RRR 和 AAA 分别是该软件包的版本号、修订版本号和架构)。这将显示被解包的档案文件的包名和其他一些信息。

VVV 部分是上游开发者指定的版本号。版本号没有固定的标准,所以可能出现诸如“19990513”和“1.3.8pre1”之类迥异的格式。

RRR 部分是 Debian 修订版本号,由 Debian 开发者指定(如果用户自己构建软件包,则由用户指定)。这一数字对应了 Debian 软件包的修订级别,所以,一个新的修订级别通常意味着对该软件包的 Debian Makefile(debian/rules)、Debian 控制文件(debian/control)、安装或卸载脚本(debian/p*),或者配置文件的修改。

AAA 部分给出了该软件包针对的处理器。常见的是 amd64,也就是 AMD64、Intel 64 或者 VIA Nano 处理器。欲了解其他可能的值,请阅读第 6.7 节 “Debian 档案库中的那些目录都是干什么用的?”重温 Debian 档案库的目录结构。欲了解详细信息,请阅读 dpkg-architecture(1) 手册页中关于“Debian 架构”的描述。

7.4. 什么是 Debian 控制文件?

有关 Debian 控制文件的内容的细节,请阅读 Debian 政策手册的第 5 节,参见第 12.1 节 “Debian 系统中还能获得哪些有针对性的文档?”

简短地说,Debian 软件包 hello 的控制文件样例如下:

Package: hello
Version: 2.9-2+deb8u1
Architecture: amd64
Maintainer: Santiago Vila <sanvila@debian.org>
Installed-Size: 145
Depends: libc6 (>= 2.14)
Conflicts: hello-traditional
Breaks: hello-debhelper (<< 2.9)
Replaces: hello-debhelper (<< 2.9), hello-traditional
Section: devel
Priority: optional
Homepage: https://www.gnu.org/software/hello/
Description: example package based on GNU hello
 The GNU hello program produces a familiar, friendly greeting.  It
 allows non-programmers to use a classic computer science tool which
 would otherwise be unavailable to them.
 .
 Seriously, though: this is an example of how to do a Debian package.
 It is the Debian version of the GNU Project's `hello world' program
 (which is itself an example for the GNU Project).

Package 字段给出了软件包的名称。这个名字可以传递给软件包管理工具,以操作该软件包,且它通常与 Debian 档案文件的文件名的第一部分相似,但不一定完全相同。

Version 字段给出了上游开发者的版本号以及(在最后一部分)Debian 软件包的修订级别,在第 7.3 节 “为什么 Debian 软件包的文件名这么长?”中有详细解释。

Architecture 字段指明了该二进制软件包编译时针对的处理器。

Depends 字段给出了安装该软件包前必须安装的软件包列表。

Installed-Size 指出了该软件包安装后会消耗多少磁盘空间。设置该字段的目的是给软件包安装前端使用,以判断是否有足够的磁盘空间安装该程序。

Section 行给出了该 Debian 软件包在 Debian 档案站点所存储的“分区”。

Priority 指出了该软件包在安装时有多重要,这使得半智能的软件,如 apt 或 aptitude,能够将软件包分为各种类别,例如,可选择安装的软件包。参见第 7.7 节 “什么是必备必需重要标准可选额外软件包?”

Maintainer 字段给出了当前负责维护该软件包的人的电子邮件地址。

Description 字段给出了该软件包功能的简短介绍。

要了解一个软件包可能拥有的全部字段的信息,请阅读 Debian 政策手册,第 5 节,“控制文件及其字段”,参见第 12.1 节 “Debian 系统中还能获得哪些有针对性的文档?”

7.5. 什么是 Debian conffile?

conffile 是软件包管理系统在升级该软件包时不会覆盖的配置文件(通常在 /etc 中)的列表。这确保了对这些文件的本地修改会被保留,因此是一个非常重要的功能,使得正在运行中的系统的软件包可以被原地升级。

要准确得到升级时将会保留哪些文件的内容,请运行:

dpkg --status package

并查看“Conffiles:”的内容。

7.6. 什么是 Debian preinst、postinst、prerm,和 postrm 脚本?

这些文件是在软件包安装或卸载前后自动执行的可执行脚本。这些文件和名为 control 的文件都属于 Debian 档案文件的“控制”部分。

各文件分别是:

preinst

该脚本在所属的软件包从它的 Debian 档案文件(“.deb”)中被解压之前执行。许多“preinst”脚本会停止和正在升级的软件包有关的服务,直至安装或升级完成(即在“postinst”脚本成功执行之后)。

postinst

该脚本在 foo 从它的 Debian 档案文件(“.deb”)中被解压之后执行,通常用于完成软件包 foo 所必需的配置。“postinst”脚本常常请求用户输入,且/或在用户接受默认值的时候警告他们,应记得在需要时回来重新配置软件包。许多“postinst”脚本会在新软件包的安装或升级完成之后执行必要的命令,以启动或重新启动服务。

prerm

该脚本通常停止与软件包有关联的服务。它在删除与软件包相关的文件之前执行。

postrm

该脚本通常修改与 foo 相关的链接或其他文件,且/或删除该软件包创建的文件。(另请参见第 7.8 节 “什么是虚拟软件包?”。)

目前所有控制文件都可以在 /var/lib/dpkg/info 目录中找到。与软件包 foo 有关的文件会以“foo”打头,并且,如果适用的话,扩展名会是“preinst”、“postinst”等。该目录中的 foo.list 文件列出了软件包 foo 安装的所有文件。(注意,这些文件的路径是一个 dpkg 内部变量;您不应当依赖它。)

7.7. 什么是必备必需重要标准可选额外软件包?

每一个 Debian 软件包都被发行版的维护者赋予了一个优先级,以辅助软件包管理系统。优先级有:

  • 必需(Required):系统正常运行必需的软件包。

    这包括了所有修复系统故障所必需的软件包。您不可以卸载这些软件包,否则您的系统可能完全损坏,而您甚至可能无法使用 dpkg 来修复它。只包含“必需”软件包的系统或许无法使用,但却足以让系统管理员引导系统并安装更多软件。

  • 重要(Important) 软件包应当能在任何类 Unix 系统上找到。

    其他能使系统正常运行或可以正常使用的软件包会出现在这里。这包括 Emacs 或 X 或 TeX,或任何其他的大型应用程序。这些软件包仅能构成最基本的基础架构。

  • 标准(Standard) 软件包是任何 Linux 系统的标准组成部分,包含了一个较小的,但功能不会太受限的字符模式的系统。它包含了可以用来发送电子邮件(使用 mutt)以及从档案服务器下载文件的工具。

    这是用户在其他选项都不选的情况下默认会安装的软件包。它不包括很多大型应用程序,但确实包含了 Python 解释器和一些服务器软件,例如 OpenSSH(用于远程管理)和 Exim(用于发送邮件,虽然可以配置为只发送本地邮件)。它也包含了一些许多用户觉得有用的通用文档。

  • 可选(Optional) 软件包包括了所有在您不认识它们的情况下可能想要安装的软件包,或者没有特殊系统需求的软件包。

    这包括 X,完整的 TeX 发行版,以及很多应用程序。

  • 额外(Extra):这里的软件包和其他更高优先级的软件包冲突,或者只在您知道它们是什么的情况下可能有用,或者有其他特殊的系统需求,因此不适合放在“可选”。

如果您进行一次 Debian 默认安装,所有优先级为标准或更高的软件包都会被安装在您的系统上,如果您选择了预定义的任务软件包,您还会同时获得更低优先级的软件包。

另外,有些软件包被标记为 必备(Essential),因为系统必须要有它们才能正常运行。软件包管理工具会拒绝卸载这些软件包。

7.8. 什么是虚拟软件包?

虚拟软件包是一个通用的名称,适合称呼一组软件包中的任意一个,所有软件包都提供相似的基本功能。例如,konquerorfirefox-esr 都是网页浏览器,所以应该能满足系统中所有需要安装浏览器才能工作或好用的程序的依赖关系。所以我们说它们两个都提供了名为 www-browser 的“虚包”。

类似地,exim4sendmail 都提供了邮件客户端的功能。所以我们说它们都提供了虚包“mail-transport-agent”。如果已经安装了其中的任意一个,那么任何依赖 mail-transport-agent 的程序都会因为这一虚包的存在而满足了依赖关系。

Debian 提供了一种机制,如果有提供同一虚包的多个软件包安装在系统上,系统管理员可以选择一个作为偏好的软件包。相关的命令是 update-alternatives,在第 11.11 节 “有些用户喜欢 mawk,有些喜欢 gawk;有些喜欢 vim,有些喜欢 elvis;有些喜欢 trn,有些喜欢 tin;Debian 如何支持这种多样性?”中有更详细的描述。

7.9. 一个软件包依赖推荐建议冲突替代破坏或者提供另一个软件包是什么意思?

Debian 软件包管理系统有一系列的软件包“依赖关系”,设计目的是(用单一的标志)表明在给定系统中程序 A 能够脱离程序 B 独立运行的程度:

  • 如果运行软件包 A 前必须先安装软件包 B,那么 A 依赖(depend)B。某些情况下,A 不仅依赖于 B,还依赖 B 的某个版本。这种情况下,依赖的版本号通常是一个下限,也就是 A 依赖于 B 的比某一指定的版本更新的任何版本。

  • 如果软件包维护者认为大多数用户在没有软件包 B 提供的功能的情况下,就不会愿意安装软件包 A,那么 A 推荐(recommend)B。

  • 如果软件包 B 包含的文件和软件包 A 的功能有关(并且往往会增强该功能),那么 A 建议(suggest)B。

  • 如果软件包 B 安装于系统上,软件包 A 就无法工作,那么 A 冲突(conflict)B。最常见的情况是,A 包含的某些文件是对 B 的文件的改进。“冲突”通常和“替代”同时出现。

  • 如果软件包 B 的文件被软件包 A 删除或者(在某些情况下)覆盖,那么 A 替代(replace)B。

  • 如果软件包 A 和软件包 B 无法同时配置在同一系统上,那么 A 破坏(break)B。如果其中一个软件包在系统中已经处于已安装且已配置的状态,软件包管理系统会拒绝安装另外一个。

  • 如果软件包 B 的所有文件和功能都已经整合进了软件包 A,那么 A 提供(provide)B。这个机制帮助磁盘空间有限的用户只安装软件包 A 中他们真正需要的部分。

关于以上术语的使用方式的更多细节,请阅读 Debian 政策手册,第 7.2 节,“Binary Dependencies”,并请参见第 12.1 节 “Debian 系统中还能获得哪些有针对性的文档?”

7.10. “预依赖”是什么意思?

“预依赖(Pre-Depend)”是一种特殊的依赖关系。在多数情况下,dpkg 不管软件包档案文件(即 .deb 文件)依赖的软件包存不存在,都会解压该档案。简单地说,解压缩意味着 dpkg 将准备安装的软件包里的各个文件解压到您的文件系统中,并放在正确的位置。如果该软件包依赖于您系统中没有的其他软件包,dpkg 会拒绝完成安装进程(也就是拒绝执行“配置”步骤),直到它依赖的软件包被安装。

但是,对于某些软件包,dpkg 甚至会在依赖关系解决前拒绝解压缩它们。我们称这类软件包“预依赖”某些其他的软件包。Debian 利用这种机制完成从 a.out 格式到 ELF 格式的升级,因为在这种情况下,软件包解压缩的顺序是至关重要的。这一方法还能用于其他重大的升级,例如优先级为“必需”且依赖 LibC 的软件包。

和以前一样,关于这一机制的更多细节可以在政策手册中找到。

7.11. 软件包状态中的未知安装卸载清除保留都是什么意思?

这些“想要”标志表示用户想要对软件包做什么(通过用户对 dpkg/apt/ aptitude 的直接调用表达出来)。

他们的意思分别是:

  • 未知(unknown):用户从未表示是否需要该软件包。

  • 安装(install):用户希望该软件包被安装或升级。

  • 卸载(remove):用户希望该软件包被卸载,但不希望删除任何已存在的配置文件。

  • 清除(purge):用户希望该软件包被完全卸载,包括配置文件。

  • 保留(hold):用户不希望处理该软件包,也就是希望保持当前的版本和当前的状态,无论是什么。

7.12. 如何将软件包设置为“保留”?

有三种方法将软件包设置为“保留”,分别是用 dpkg、apt 和 aptitude。

如果使用 dpkg,您需要先使用如下命令导出已选择的软件包:

dpkg --get-selections \* > selections.txt

然后编辑生成的文件 selections.txt,将您想要设为“保留”的软件包,例如 libc6 所在的行,由:

libc6                                           install

改为:

libc6                                           hold

然后保存文件,并使用以下命令将它加载到 dpkg 数据库:

dpkg --set-selections < selections.txt

如果使用 apt,您可以使用以下命令将软件包设为“保留”:

apt-mark hold package_name

或使用以下命令取消“保留”:

apt-mark unhold package_name

如果使用 aptitude,您可以使用以下命令将软件包设为“保留”:

aptitude hold package_name

或使用以下命令取消“保留”:

aptitude unhold package_name

7.13. 如何安装源码包?

事实上,无法真正地“安装”Debian 源码包,它们仅仅是被解压到了您指定的目录,用于构建它们可以产生的二进制包。

绝大多数分发二进制包的镜像站也同时分发源码包。如果您已经修改了 APT 的 sources.list(5) 并加入了适当的“deb-src”条目的话,您可以运行以下命令轻松下载任何源码包:

apt-get source foo

为了帮助您构建源码包,Debian 源码包提供了所谓的“编译依赖(build-dependency)”机制。这意味着源码包的维护者维护了一个构建该软件包所必需的其他软件包的列表。您可以在编译源码包之前执行

apt-get build-dep foo

来看看它有多实用。

7.14. 如何从源码包构建二进制包?

推荐的方式是使用各种包装器工具。我们将用 devscripts 工具来演示如何操作。如果您还没有安装它的话,请安装该软件包。

首先获取源码包:

apt-get source foo

并 cd 到源码树中:

cd foo-*

然后安装所需的编译依赖(如果有的话):

sudo apt-get build-dep foo

然后为您构建的版本指定一个独一无二的版本号(这样当 Debian 发布一个新版本时,您就不会感到混乱):

dch -l local 'Blah blah blah'

最后构建您的软件包:

debuild -us -uc

如果一切顺利的话,您可以执行以下命令安装您的软件包:

sudo dpkg -i ../*.deb

如果您喜欢手动操作,而不想使用 devscripts,请遵循以下步骤:

您需要准备好 foo_*.dsc、foo_*.tar.gz 以及 foo_*.diff.gz 才能编译源码(注意:有些 Debian 原生的软件包没有 .diff.gz 文件)。

准备好以上文件(第 7.13 节 “如何安装源码包?”)且安装 dpkg-dev 软件包之后,执行以下命令:

dpkg-source -x foo_版本号-修订版本号.dsc

这会将源码包解压到一个名为 foo-版本号 的目录。

如果您只想编译软件包,您可以 cd 到 foo-版本号 目录,然后执行

dpkg-buildpackage -rfakeroot -b

以编译源码包(注意该命令需要 fakeroot 软件包),然后执行

dpkg -i ../foo_version-revision_arch.deb

以安装刚编译好的软件包。

7.15. 我应该如何创建自己的 Debian 软件包?

欲了解关于这一问题的详细信息,请阅读新维护者指南,可在 maint-guide 软件包或 https://www.debian.org/doc/devel-manuals#maint-guide 找到,或者 Debian 维护者指南,可在 debmake-doc 软件包或 https://www.debian.org/doc/devel-manuals#debmake-doc 找到。