3. 二进制包

Debian发行版建立在Debian包管理系统之上,该系统被称为 dpkg 。因此,Debian发行版中的所有软件包都必须以 .deb 文件格式提供。

一个 .deb 包中包含了两组文件:一组文件会在包被安装时一同被安装在系统上,另一组文件要么提供了相关包的一些附加元数据,或是在包被安装或删除时一同被执行。第二组文件被称为 包的元数据文件 。这些文件中有包维护者脚本和 control ,即 binary package control file ,它包含了包的控制字段。其他的元数据文件包括用于存储共享库依赖信息的 symbolsshlibs ,以及列出包的配置文件的 conffiles 文件(在 Configuration files 中有所描述)。

不幸的是,控制信息文件和Debian控制文件格式的文件之间存在术语冲突。在本文档中, 控制文件 指的是Debian控制文件格式的文件。这些文件记录在 控制文件及其字段 中。只有专门被称为是 包元数据文件 的文件才是二进制包所用的 .deb 文件格式的包元数据文件成员(被称作 control.tar )中所包含的文件。大多数包元数据文件不具有Debian控制文件格式。

3.1. 软件包名

每个软件包都必须有一个Debian存档中独一无二的名称。

包名包含在控制字段 Package 中,其格式在 Package 中有描述。包名称也包含在 .deb 文件的文件名中。

3.1.1. 含潜在冒犯性内容的软件包

作为维护者,你应当判断一个包是否适于被接纳,是否需要某种类型的内容警告,以及该包的某些部分是否应被拆分成单独的包(方便那些想要避免接触该包中某些内容的用户)。在做出这些决定时,你应该考虑我们的多元化声明中表达出的Debian项目的种种观点。

如果你将(潜在的)冒犯性或干扰性内容拆分到一个单独的包中,那么通常情况下你应该通过在包名称中添加 -offensive 来对此进行标识。例如, cowsay 应当被标记为 cowsay-offensive 。在这种情况下, -offensive 包可以被核心软件包Suggested,但不应该被它们Recommended或被Depended on。

3.2. 软件包版本

每个包都有一个版本号,它记录在该包控制文件中的 Version 字段中,如 Version 中所描述。

软件包管理系统对版本号进行排序,以便判断包是否处在升级或降级过程中,从而使得包系统前端应用程序能够判断可用的软件包是否比系统上已安装的包更新。版本号格式最重要的部分(就比较而言)在开头。

如果上游包的版本号有问题,那么应将它们转换为合理的形式,以便在 Version 字段中使用。

3.2.1. 基于日期的版本号

一般来说,Debian软件包应该使用与上游源代码相同的版本号。然而,软件包管理软件无法对基于某些日期格式(有时用于开发或“快照”发布)的上游版本号进行正确排序。例如, dpkg 会将“96May01”视作是大于“96Dec24”的。

为避免必须为每个新的上游版本使用epoch,所有上游版本号中基于日期的部分都应该以正确排序的方式给出:首先是四位数年份,然后是两位数的月份,然后是两位数的日期,中间可以通过标点符号连接。

版本号包含日期的本土Debian软件包(即专门为Debian编写的软件包)也应遵循这些规则。如果需要用标点符号连接日期,请记住不能在本土版本号中使用连字符 (-)。 句点 (.) 通常是一个不错的选择。

3.2.2. 版本号的唯一性

一旦包被纳入存档中,版本号中epoch之后的部分不得再用于该包内容变更后的另一个版本,即使以前使用该版本号相关部分的那一个版本的软件包已经不在存档中出现。

这种对版本号的唯一性的要求既适用于源码包,也适用于二进制包,即使生成给定二进制包的源码包发生了变化。 因此,二进制包不得重复使用的版本号包括所有源码包给出的、被纳入存档中的二进制包的所有版本号。

此外,对于非本土包,上游版本不得重复用于不同的上游源代码,这意味着每个源码包名称和上游版本号在存档中仅有一个原始源码内容与其相对应(见 Files)。

做出这些限制的原因如下。 Epoch不包含在组成源码包的文件名中,也不包含在二进制包的文件名中,因此即使epoch不同,重复使用版本号也会导致存在具有不同内容的同名文件。 这会导致各种问题。

如果你想要重复使用版本号中epoch之后的部分,你可以增加Debian修订号,它不需要从1开始,也不需要是连续数字。

3.3. 软件包维护者

除了后文所述的那种被遗弃的包,每个包都必须有一个维护者。维护者可以是一个人,也可以是一组人,能通过一个共同的电子邮件地址被联系上,例如邮件列表。维护者负责维护Debian打包文件,对软件故障报告进行评估并给出恰当的回应,上传软件包的新版本(要么自行直接上传,要么通过推荐人上传),保证软件包被放进合适的存档分组中,并在维系该包稳定性和实用性的前提下将其纳入Debian发行版,在一个软件包不再有用或无法得到维护的时候,请求将其从Debian发行版中移除。

软件包维护者的信息必须在 Maintainer 控制字段中得以明确, 这其中必须包含其正确的姓名和正常使用的电子邮件地址。 Maintainer 控制字段中给出的电子邮件地址必须接收Debian中用于发送与软件包相关的自动邮件的那些角色帐户的邮件。这其中包括来自错误跟踪系统的非垃圾邮件、来自Debian存档维护软件的所有邮件,以及来自那些为Debian项目一致同意的其他角色帐户或自动化流程的邮件。[1] 如果一个人或团队维护多个包,他们应当使用在这些包的 Maintainer 字段中出现的相同形式的姓名和电子邮件地址。

Maintainer 控制字段的格式在 Maintainer 中有描述。

如果软件包的维护者是一组拥有共享电子邮件地址的人,那么 Uploaders 控制字段必须存在并且必须包含至少一个人及其个人电子邮件地址。有关该字段的语法,请参阅 Uploaders

被遗弃的包指的是那些当前没有维护者的包。遗弃包的 Maintainer 控制字段应被设置为 Debian QA Group <packages@qa.debian.org> 。这些软件包被视为由整个Debian项目维护,直到有人志愿接管对其进行维护。 [2]

3.4. 软件包描述

每个 Debian 软件包都必须有一个 Description 控制字段,其中包含软件包的概要和扩展性描述。有关 Description 字段格式的技术信息,见 Description

描述应该向以前从未接触过它的用户(系统管理员)描述该包(程序),以便他们有足够的信息来决定是否要安装它。这个描述不应该仅是从该程序文档逐字复制而来。

在概要和扩展性描述中,重要的信息放在前面。有时只有概要或描述的第一部分会被显示出来。但是你应该预设整个扩展性描述是可以被看到的。

描述中还应该给出有关此包与其他包之间的重要依赖关系和冲突的信息,以便用户知道为什么会存在对这些依赖关系和冲突的声明。

描述中不应包括对该包配置或使用的说明(这是安装脚本、手册页、信息文件等要做的)。版权声明和其他管理信息也不应包括在内(这就是版权文件要做的)。

3.4.1. 单行概要

单行概要应该保持简短 -- 不能超过80个字符。

不要在概要中包含软件包的名称。显示软件知道如何对此进行显示,不需要在描述中再次给出。请记住,在许多情况下,用户可能只会看到概要行 -- 因此,尽可能让它包含所有关键信息。

3.4.2. 扩展性描述

不要试图将单行概要延续到扩展描述中。这会使得完整描述在被显示的时候不明所以,也会使得只有总结(单行概要)被显示出来的时候不明所以。

扩展描述应该描述包的作用以及它与系统其余部分的关系(例如,它属于哪个子系统的哪个部分)。

描述字段需要对所有人而言都是一目了然的,即使是对该包所处理的事情一无所知的人也应如此。 [3]

3.5. 依赖关系

每个包都必须指明其正确运作所需的其他包的依赖信息。

例如,必须为包中动态链接的可执行二进制文件所需的所有共享库提供依赖项。

包不需要声明它与那些标记为 Essential (见下文)的包的依赖关系,除非它们依赖于该包的特定版本,否则不应该做这样的声明。 [4]

有时,解压一个包需要先解压 以及 配置另一个包。在这种情况下,有依赖的包必须在 Pre-Depends 控制字段中明确此依赖项。

debian-devel 邮件列表上进行相关讨论并就讨论结果达成共识之前,不应为软件包指定 Pre-Depends 条目。

包相互关系控制字段的格式在 Declaring relationships between packages 中有所描述。

3.6. 虚拟包

有的时候,存在几个包提供或多或少相同的功能的情况。在这种情况下,定义一个其名称描述了共同功能的 虚拟包 是有帮助的。(虚拟包仅在逻辑上存在,而非物理上存在;这就是它们被称为 虚拟 的原因。)具有此特定功能的包将 提供 虚拟包。因此,任何其他需要该功能的包都可以直接依赖于虚拟包,而不必单独明确所有可能存在的相关包。

所有包都应在适当的情况下使用虚拟包名称,并在必要时创建新虚拟包名称。除非对虚拟包名称的使用已得到共识并且该名称已经出现在虚拟包名称列表中,否则软件包不应该使用虚拟包名称(在一组合作的包间私下使用除外)。(另见 Virtual packages - Provides

具有权威性的最新版虚拟包名称列表可以在 debian-policy 包中找到。也可以从Debian网络镜像获得,网址为 https://www.debian.org/doc/packaging-manuals/virtual-package-names-list.yaml

列表的前言中描述了对列表进行更显所需的程序。

3.7. 基础系统

base system 是 Debian 系统的最小子集,在新系统所有其他部分安装之前被安装。为了保持所需的磁盘使用量非常小,只有很少的包得以构成基础系统的一部分。

基础系统由所有优先级为 requiredimportant 的包组成。其中许多包被标记为 essential (见下文)。

3.8. 核心包

核心被定义为在系统中必须存在,且始终可用的最小功能集,即使包处于“Unpacked“状态,也是如此。将软件包标记为 essential 需使用 Essential 控制字段。 Essential 控制字段的格式在 Essential 中有所描述。

由于这些包不能轻易删除(必须为 dpkg 指定一个额外的 强制选项 才能删除),除非绝对必要,否则不得使用此标志。共享库包不得被标记为 essential ;依赖关系会防止它被不恰当地删除,而我们需要能够在它被取代时删除它。

由于dpkg不会在 essential 包处于未配置状态时阻止其他包的升级,因此,对于所有 essential 包来说,即使它们当前处于未配置的状态,但只要这之前至少配置过一次,就必须提供其所有核心功能。如果包不能满足此要求,那么它不得被标记为是核心的,并且依赖于此包的所有包都必须适当地声明依赖字段。

维护人员在向 essential 包添加任何程序、接口或功能时应格外小心。其他包可能会假定 essential 包提供的功能始终可用,而无需声明依赖项,这意味着从核心包中删除功能非会变得非常困难,几乎从未有人成功过。因此,添加到 essential 包的任何功能都会产生一种义务,即永久支持该功能作为核心包的一部分。

debian-devel 邮件列表上对此进行讨论并达成共识之前,不得将任何包标记为 essential

3.9. 维护者脚本

软件包的安装脚本应该避免产生用户不需要看到的输出,并且应该依靠 dpkg 来避免让同时安装许多软件包的用户感到无聊。这意味着需要避免的事项之一是给 update-alternatives 提供 --verbose 选项。

如果安装脚本执行期间发生了错误,必须要对它进行检查,并且在出现错误后不得继续安装。

需要注意的是,通常情况下 Scripts 也适用于包维护者脚本。

在没有先咨询相关包的维护者之前,你不应该在属于另一个包的文件上使用 dpkg-divert 。在添加或删除转移时,包维护者脚本必须向 dpkg-divert 提供 --package 标志并且不得使用 --local

通常情况下,所有提供通用命令名(或一般情况下,文件名)的包都应使用 update-alternatives ,以便它们可以一起安装。如果不使用 update-alternatives ,那么每个包都必须使用 Conflicts 来确保其他包被删除。(在这种情况下,可以适当指明该包与其未使用 update-alternatives 的早期版本间的冲突;这是一个例外,因为通常的规则是避免出现一个包在其不同版本间的冲突。)

转移主要是用作让本地管理员和本地软件包能够推翻Debian的行为。虽然存在下面这样一些情况,其中,一个Debian软件包可能需要转移另一个Debian软件包所安装的文件,但是这样的情况非常少见。如果存在一些转移以外的其他推翻机制能够达成同一个目标,那么维护者应强烈偏向使用那些推翻机制。换言之,软件包中的转移应被视为是下下策。由一个Debian包对另一个Debian包的文件转移应由这些包的维护者们协同完成。

One specific case of this rule is that configuration files used by systemd components, such as units, udev rules, tmpfiles.d, modules-load.d, sysusers and other such files, including those specific to systemd daemons (e.g.: /etc/systemd/system.conf). must not be diverted by any Debian package. Instead, use masking and drop-ins.

替代系统不得用于 systemd 配置文件。替代系统不知道如何在更新该系统时应用出现的种种变更, 因此,随后出现的行为会是令人费解或不可预测的。相反, aliases 可以作为替代来实施同名称的unit。

3.9.1. 维护者脚本提示

如有必要,包维护者脚本可以提示用户。提示必须通过一个程序来实现沟通,例如 debconf ,它遵循Debian配置管理规范的第2版或更高版本。

如果在执行时没有相关的可用接口,核心包或依赖于核心包的包可以依靠其他提示方法。

Debian配置管理规范包含在debian-policy包中的 debconf_specification 文件中。它也可以从Debian网络镜像中获得,网址为 https://www.debian.org/doc/packaging-manuals/debconf_specification.html

使用Debian配置管理规范的软件包可能包含额外的控制信息文件 configtemplates 。 `` config`` 是用于配置包的附加维护者脚本, templates 包含用于用户提示的模板。config 脚本可能会在 preinst 脚本之前运行,并且在解包或其任何依赖项或前置依赖项得到满足之前运行。因此,它必须能在仅使用 核心 包中的工具的情况下正常工作。 [5]

使用Debian配置管理规范的软件包必须通过使用基于gettext的系统(例如po-debconf软件包提供的系统)来翻译其用户可见的消息。

软件包应该尽量最小化所需提示的数量,并且应该确保每个问题只问用户一次。这意味着包应该尝试使用适当的共享配置文件(例如 /etc/papersize/etc/news/server ),以及共享debconf变量,而不是每个包都提示自己所需的信息列表。

这也意味着包在升级时不应该再次询问已经问过的问题,除非用户使用 dpkg --purge 删除了包的配置。配置问题的答案应存储在 /etc 中适当的地方,以便用户对它们进行修改,并且,这些过程是如何完成的也应当被记录下来。

如果一个包有一条至关重要的信息要传递给用户(比如“不要原样运行我,你必须先编辑以下配置文件,否则你的系统可能会发出格式错误的消息”),它应该在 configpostinst 脚本中显示,并提示用户按回车键以确认消息。版权信息不算至关重要(它们存在于 /usr/share/doc/PACKAGE/copyright 中);关于如何使用程序的说明也不是至关重要的(它们应该在数据文档中,所有用户都可以看到它们)。

所有必要的提示几乎都应该局限于 configpostinst 脚本中。如果它是在 postinst 中完成的,那么它应该通过一个条件运算式得以保护,这样如果一个包安装失败并且 postinstabort-upgradeabort-removeabort-deconfigure 调用的话,不会出现不必要的提示。