Debian 软件包不仅是一个待安装文件的归档。它是一个更大结构的一部分,它描述与其它 Debian 软件包的关系(依赖、冲突、建议)。在软件包生命周期的不同阶段(安装、删除、升级),它还提供执行命令的脚本。软件包中的这些数据供包管理工具使用,但不是打包软件的一部分,称为“元信息”(关于其它信息的信息)。
该文件使用一个类似电子邮件头(由 RFC_2822 定义)的结构。例如,apt 包的 control
文件看起来是这样的:
$
apt-cache show apt
Package: apt
Version: 1.0.9.6
Installed-Size: 3788
Maintainer: APT Development Team <deity@lists.debian.org>
Architecture: amd64
Replaces: manpages-it (<< 2.80-4~), manpages-pl (<< 20060617-3~), openjdk-6-jdk (<< 6b24-1.11-0ubuntu1~), sun-java5-jdk (>> 0), sun-java6-jdk (>> 0)
Depends: libapt-pkg4.12 (>= 1.0.9.6), libc6 (>= 2.15), libgcc1 (>= 1:4.1.1), libstdc++6 (>= 4.9), debian-archive-keyring, gnupg
Suggests: aptitude | synaptic | wajig, dpkg-dev (>= 1.17.2), apt-doc, python-apt
Conflicts: python-apt (<< 0.7.93.2~)
Breaks: manpages-it (<< 2.80-4~), manpages-pl (<< 20060617-3~), openjdk-6-jdk (<< 6b24-1.11-0ubuntu1~), sun-java5-jdk (>> 0), sun-java6-jdk (>> 0)
Description-en: commandline package manager
该软件包提供了用于
搜索、管理以及以低层次访问
libapt-pkg 库的全部功能来查询
软件包信息的命令行工具。
.
它包括:
* apt-get,用于从已验证的源获取软件包
和相关信息并对软件包及其依赖进行安
装、升级和删除
* apt-cache,用于查询已安装和可安
装软件包的可用信息
* apt-cdrom,用于将可移除式介质作为软件包的源
* apt-config,作为设置配置的一个界面
* apt-key,作为管理验证密钥的一个界面
Description-md5: 9fb97a88cb7383934ef963352b53b4a7
Tag: admin::package-management, devel::lang:ruby, hardware::storage,
hardware::storage:cd, implemented-in::c++, implemented-in::perl,
implemented-in::ruby, interface::commandline, network::client,
protocol::ftp, protocol::http, protocol::ipv6, role::program,
role::shared-lib, scope::application, scope::utility, sound::player,
suite::debian, use::downloading, use::organizing, use::searching,
works-with::audio, works-with::software:package, works-with::text
Section: admin
Priority: important
Filename: pool/main/a/apt/apt_1.0.9.6_amd64.deb
Size: 1107560
MD5sum: a325ccb14e69fef2c50da54e035a4df4
SHA1: 635d09fcb600ec12810e3136d51e696bcfa636a6
SHA256: 371a559ce741394b59dbc6460470a9399be5245356a9183bbeea0f89ecaabb03
依赖关系是在软件包头部的 Depends
域中定义的,它是软件包能够正常运行所需要满足的一系列条件。包管理工具诸如 apt
依靠这个信息来安装该软件所依赖的恰当版本的库。对于每一项依赖,可以限定依赖的版本范围来满足条件。换句话说,可以这样表达:我们需要版本号等于或者大于“2.15”的 libc6 软件包(写作 “libc6 (>= 2.15)
”)。版本比较操作符使用如下:
碰到条件时,以逗点做为交集 “and” 符号。以直线 (“|”) 为联集 “or” 符号 (它是包容性的或 “or”、不是排它性的非此即彼 “either/or”)。优先级比 “and” 高,可以无限次使用。因此,相依性 “(A or B) and C” 可写成
A | B, C
。另个例子,“A or (B and C)” 应写成 “(A or B) and (A or C)”,因为
Depends
字段不接受以括号改变逻辑操作数 “or” 与 “and”的优先次序。所以须写成
A | B, A | C
。
相依性系统是保证程序顺利运作的良好机制,但它也有另个用法 「元软件包」。这些空的软件软件包内容只有相依性的说明。由元软件包维护者把一群程序的相依性描述在其中;例如,apt install meta-package
将自动安装所有用到元软件包相依的文件。gnome、kde-full 与 linux-image-amd64 软件包就是元软件包之一。
Conflicts 冲突
字段指示不能与其共存的其它软件包。最常见的理由是两个软件包使用相同的名称,或在同个 TCP 端口提供相同的服务,或会隐藏彼此的运作。
dpkg
不会安装冲突的软件包,除非新软件包指明 “取代” 被冲突的软件包,dpkg
才会以新的软件包取代旧的软件包。apt
总是遵循您的指示:若选择安装新软件包,则自动移除造成问题的旧软件包。
中断 Breaks
字段有一个影响和冲突
字段类似,但它具有特殊的意义。它标志着一个包的安装会将另外一个包(或者是特定版本)中断掉。通常而言,两个包之间的不兼容是短暂的,中断
关系会特别指出那些不兼容的版本。
已经中断现有软件包时,dpkg
将拒绝安装并且 apt
将更新软件包至新的版本试图解决此问题 (通常可解决此问题,并再度兼容)。
不向后兼容的更新可能会发生这种情况:如果新版本与旧版本的功能不在另一个程序中做特别规定,这将导致故障。中断
字段会防止用户继续运行从而遇到这些问题。
这个字段引入了一个很有意思的“虚拟包”的概念。它有很多的角色,但有两个特别重要。第一个是由使用虚拟包关联到的一个通用的服务(包“预备”了这些服务)。第二个表示一个包完全取代了另外一个,所以它也能满足依赖性要求。因此,可以创建一个替换而不必使用相同的包名称的包。
让我们以范例详述第一个案子:postfix 或 sendmail 之类的邮件服务器都 “提供” mail-transport-agent 虚拟软件包。因此需要启用该等服务的软件包 (如:smartlist 或 sympa 之类的邮件列表管理器) 只要在其相依性里叙明需要 mail-transport-agent 而不是指明还不兼容的可能解决方案清单 (如 postfix | sendmail | exim4 | …
)。更进一步来说,在同个机器安装两个邮件服务器是没有用的,因此每个软件包都声明与 mail-transport-agent 虚拟软件包冲突。系统忽略冲突的两个软件包,但技术上可以禁止同时安装两个邮件服务器。
软件包内容被置于另个较大的软件包内时,Provides
字段就很有意思。例如,libdigest-md5-perl Perl 模块是 Perl 5.6 的选项之一,已被集成入标准的 Perl 5.8 (及其以后的版本,如 5.20 并纳入 Jessie)。所以,软件包 perl 5.8 版之后已被声明 Provides: libdigest-md5-perl
所以只要使用 Perl 5.8 (或更新版) 就已满足其相依性。libdigest-md5-perl 软件包本身早就被删除,因为移除 Perl 旧版之后就没有任何意义。
这个功能非常有用,因为它是永远不可能预料到变化莫测的发展,它不能够调整重命名,或者其他自动替换过的软件,所以它是必要的。
虚拟软件包曾有很多限制,最麻烦的是没有版本编号。从上例可知,这种 Depends: libdigest-md5-perl (>= 1.6)
相依性,尽管在 Perl 5.10 里存在,但包管理系统仍认为不满足其相依性 — 虽然实际上已满足了。但是包管理系统不知道相依性没问题,只好选择风险最小的做法,假设其版本是不匹配的。
该限制已经在 dpkg 1.17.11 中解除,在 Jessie 中不再相关。软件包可以给提供依赖的虚拟包指定一个版本,如 Provides: libdigest-md5-perl (= 1.8)
。
Replaces
字段指出在其他软件包的文件,但该软件包也合理地取代他们。如果没有指明,dpkg
失败,说明它不能覆写另个软件包的文件 (技术上来说,虽可以强迫使用 --force-overwrite
选项,但这不是标准选项)。因此在加入该字段之前,先允许维护者确认潜在可能的问题,并要求维护者先研究它。
当软件包名称变更时,或者当一个软件包包含在另外一个的时候,此字段的使用是合理的。这也发生在维护者决定从来自相同的源码包的大量二进制包里分发出文件:一个替换的文件不再属于旧的软件包,只有新的才属于。
如果已安装软件包的所有文件都被替换了,那么该软件包将认为将被删除掉。最后,这个字段会鼓励dpkg
命令去删除替换掉有冲突的包。
除了控制
文件之外,每个Debian软件包可能在control.tar.gz
的存档中包含了一些脚本,这些脚本在软件包处理的不同的阶段被dpkg
调用。Debian政策描述了一些可能的细节,在可能的情况下,指定脚本调用它们所收到的参数。这些顺序可能比较复杂,因为如果脚本中任何一步出现问题的时候,dpkg
进程会通过取消安装或者卸载来尝试恢复到一个令人满意的状态(只要是可能的)。
在一般情况下,preinst
脚本会在安装软件包前执行,而postinst
会稍候执行。同样,prerm
会在移除一个软件包的之前被调用,postrm
则随后执行。更新软件包相当于是清除掉以前的旧版本并且安装新的软件包。所以在这里我们不可能来详细描述所有的可能的方案,但我们将讨论最常见的两种:安装/更新和移除。
这里告诉你,安装的时候发生了什么(或者是更新)
对于升级,dpkg
会调用old-prerm 升级 新版本
.
还是对于升级,dpkg
执行new-preinst 升级 旧版本
;对于第一个安装,它会执行 new-preinst 安装
。它可能会增加旧版本的最后一个参数,如果软件包已经被安装或者是删除了(但没有清除合并掉旧版本,那么这个配置文件会被保留)。
新的软件包文件被解压。如果文件已经存在,就被会替换,同时会产生一个临时的备份副本。
对于更新,dpkg
执行old-postrm 升级 新版本
。
dpkg
更新所有的内部数据(文件列表,配置脚本等),并删除被替换文件的备份。这是一条不归路: ) dpkg
将不能够再访问回退到之前状态所需要的所有元素。
最后,dpkg
通过执行 new-postinst configure 最近一次配置的版本号
对软件包进行配置。
这里是当移除一个软件包的时候发生了什么:
dpkg
调用prerm 移除
。
dpkg
移除了所有的软件包文件,只剩下了配置文件和配置的脚本。
dpkg
运行 postrm remove
。移除所有配置脚本,保留 postrm
。若用户未选择 “清除” 选项,则工作完成。
对于一个完整的清除包(由dpkg --purge
或dpkg -P
发出的),配置文件也将会被删除,同时一些副本(*.dpkg-tmp
,*.dpkg-old
,*.dpkg-new
)和缓存文件也会被删除掉;dpkg
这时会执行postrm purge
。
config
脚本补充前述的 4 个脚本,软件包以 debconf
取得配置用的信息。安装过程中,此脚本以 debconf
指令询问用户详细的问题。把回应记录在 debconf
数据库供未来的参考。在安装之前先由 apt
逐一运行该等脚本,归纳问题与回答。事前与事后安装脚本可使用该等信息回应用户的期望。
conffiles
列出必须当配置文件处理的软件包文件。管理员可修改配置文件,升级软件包时 dpkg
将保留这些变动。
实际上,在这种情况下,dpkg
的行为会尽可能地智能:如果两个版本之间并没有改变标准的配置文件,那么dpkg
就什么也不会作。但是,如果文件已经被修改,它会尝试更新此文件。这会产生两种可能的情况:一是管理员没有碰配置文件,在这种情况下dpkg
会自动安装新版本;二是文件已经被修改了,在这种情况下dpkg
会询问管理员希望使用哪个版本(旧版本或者新的)。为了帮助用户作出选择,dpkg
会提供“差异
”,这会显示两个版本之间的差异。如果用户选择保留旧的版本,新的将被存储在文件夹的同一个位置并以.dpkg-dist
为后缀名的文件中。另外一个可能的操作是由暂时中断的dpkg
来编辑该文件,并试图重新恢复相关的修改(之前被验证的差异
)。