dpkg软件包管理工具往往部署于Debian系系统,像Ubuntu、Termux等发行版均使用dpkg来管理软件包。本文介绍dpkg软件包(*.deb包)的制作,包括各种控制脚本的介绍,以及debconf图形化界面配置工具的介绍。
准备
要打包一个二进制软件,你需要:
- 能运行dpkg的系统(Linux/WSL/MacOS/Android都行)
- dpkg软件、(可选:debconf软件)
- 一个文本编辑器
dpkg软件包的制作非常简单,将所有文件按照一定结构放到一个目录下,编写软件包信息,再使用dpkg --build
即可完成制作。
软件包目录结构
完全解压一个deb包,可以看到以下结构的文件:
可以看出,一个deb包内还有两个压缩包,一个是control.tar.gz
,一个是data.tar.gz
。显然,data.tar.gz
内的内容将被复制进系统。
而要实现上面的软件包,只需要对应建立以下文件夹:
也就是说,DEBIAN目录下所有文件将打包进control.tar.gz(xz)
,其他文件夹将打包进data.tar.gz(xz)
。debian-binary
由打包器自动生成。
打包命令如下:
$ dpkg-deb --build /path/to/previsat-4.0.8.1-bin-amd64-Linux-project /path/to/output.deb(可选输出文件)
dpkg-deb: 正在 '/path/to/output.deb' 中构建软件包 'previsat'。
软件包信息编写
通过文本编辑器打开DEBIAN
目录下的内容即可发现,这些文件描述了软件包信息,控制着安装过程中的行为。
control元数据
control
文件存储了该软件包的所有信息,这是必不可少的文件。以下为APT
软件包control
文件的内容:
Package: apt
Version: 1.8.2
Installed-Size: 4064
Maintainer: APT Development Team <deity@lists.debian.org>
Architecture: amd64
Replaces: apt-transport-https (<< 1.5~alpha4~), apt-utils (<< 1.3~exp2~)
Provides: apt-transport-https (= 1.8.2)
Depends: adduser, gpgv | gpgv2 | gpgv1, debian-archive-keyring, libapt-pkg5.0 (>= 1.7.0~alpha3~), libc6 (>= 2.15), libgcc1 (>= 1:3.0), libgnutls30 (>= 3.6.6), libseccomp2 (>= 1.0.1), libstdc++6 (>= 5.2)
Recommends: ca-certificates
Suggests: apt-doc, aptitude | synaptic | wajig, dpkg-dev (>= 1.17.2), gnupg | gnupg2 | gnupg1, powermgmt-base
Breaks: apt-transport-https (<< 1.5~alpha4~), apt-utils (<< 1.3~exp2~), aptitude (<< 0.8.10)
Description-en: commandline package manager
This package provides commandline tools for searching and
managing as well as querying information about packages
as a low-level access to all features of the libapt-pkg library.
.
These include:
* apt-get for retrieval of packages and information about them
from authenticated sources and for installation, upgrade and
removal of packages together with their dependencies
* apt-cache for querying available information about installed
as well as installable packages
* apt-cdrom to use removable media as a source for packages
* apt-config as an interface to the configuration settings
* apt-key as an interface to manage authentication keys
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,
scope::application, scope::utility, suite::debian, use::downloading,
use::organizing, use::playing, use::searching, works-with-format::html,
works-with::audio, works-with::software:package, works-with::text
Section: admin
Priority: required
Filename: pool/main/a/apt/apt_1.8.2_amd64.deb
Size: 1418108
MD5sum: 0e80dedab6ec1e66a8f6c15f1925d2d3
SHA256: 80e9600822c4943106593ca5b0ec75d5aafa74c6130ba1071b013c42c507475e
- Package: 软件包名称
- Version:软件包版本
- Architecture:软件包架构,如i386, amd64, aarch64等
- Maintainer:打包人信息
- Tags:软件包标签
- Depends:依赖项以及依赖版本
Pre-Depends:软件安装前就需要的依赖(比如preinst脚本中需要用的软件) - Recommends:强烈推荐的依赖来增强功能
Suggests:建议的依赖
Enhances:这个软件包的附加组件、插件等,一般被忽略 - Conflicts:相冲突的软件
Breaks:暂时不兼容/冲突 - Provides:提供的软件包,可以是“虚包”[1],也可以是旧版组件
- Replaces:被替代的软件包,若进行安装,列表中的软件包将被删除
注:[1]“虚包”指的是一类软件,比如mail-transport-agent可以表示postfix, sendmail等
软件包控制脚本
在DEBIAN目录下,除了保存软件包元数据的control文件,其余的均为软件包控制脚本。在软件包安装/升级/移除整个流程中,dpkg将按照不同步骤执行这些控制脚本。
需要提前说明的是,一些控制脚本不一定能与用户进行交互,比如说在Ubuntu桌面环境下,软件包可能由图形化的软件包安装管理器安装,因此用户可能根本无法看到安装时提示的消息。因此一般不建议在控制脚本中使用read之类的命令获取用户输入。若在安装时需要用户配置一些信息,需要使用debconf(下一节介绍)。
基础脚本
基础的脚本由dpkg支持,脚本文件分别为preinst,postinst,prerm,postrm。dpkg在调用这些控制脚本时还会附加一些参数比如abort-upgrade、upgrade等,以告诉软件包正在被执行什么操作。
以下为各种情况下安装软件包时执行的步骤。使用斜体字表示需要替换的参数。
软件包全新安装
- preinst install
- 解压软件包文件
- postinst configure
软件包升级/remove后再安装
- 运行旧软件包的prerm upgrade 新版本的版本字符串
- 运行新软件包的preinst upgrade 旧版本的版本字符串
- 解压软件包文件,如果系统中已存在会创建临时备份文件
- 运行旧软件包的postrm upgrade 新版本的版本字符串
- 删除备份文件,此时软件安装已无法撤销
- 处理配置文件(/etc目录下文件),可能提示用户选择配置文件版本
- 运行新软件包的postinst configure 最后完成配置的版本字符串
软件包移除
- 运行软件包的prerm remove
- 从系统删除相关文件,但留下配置文件和软件包安装控制脚本
- 运行软件包的postrm remove
- 删除除了postrm的所有软件包安装控制脚本
- 如果用户指定–purge来删除软件包,删除所有配置文件
- 运行软件包的postrm purge
使用debconf
软件包安装时,可能需要提示用户输入一些参数来初始化软件包。比如安装mysql时需要输入管理员密码。如果仅仅在软件包控制脚本中使用echo、read等命令来与用户交互的话,可能会由于各种环境不同而导致无法交互。最明显的例子就是当用户使用图形化的Ubuntu软件中心安装软件的话,很可能就会因为这个问题卡住。debconf则提供了统一的交互方式,避免了这一问题。
模板文件编写
模板文件为DEBIAN/templates,编辑其就能令debconf在必要的时候提示用户输入信息。其格式如下:
Template: mewwoof_dpkg_learning_deb/my_option_one
Type: select|multiselect|string|boolean|note|text|password
Default: xxx
[Choices: yes, no]
Description: Blah blah blah?
Blah blah blah. Blah blah. Blah blah blah. Blah blah? Blah
blah blah blah. Blah blah blah. Blah blah.
.
Blah blah blah. Blah blah. Blah blah blah. Blah blah. Blah blah blah.
blah.
Template: xxx
...
可用的参数类型:
string | 输入字符串 |
boolean | true | false |
select | Choices: yes, no |
multiselect | Choices: a, b, c |
note | 仅展示这个消息,但会记录于log中 |
text | 仅展示这个消息 |
password | 输入密码 |
在脚本中集成debconf
首先需要在脚本中source debconf的库。然后使用db_input、db_go、db_get、db_purge来控制。参数如下:
- db_input 优先级 名称:令debconf显示模板中定义的一个问题,会确保用户有输入
- db_go:令debconf不断询问完所有问题
- db_get question:从debconf数据库中获取一个问题的答案
以下是一个例子:
#!/bin/sh -e
# source debconf 库
. /usr/share/debconf/confmodule
db_input medium mewwoof_dpkg_learning_deb/my_option_one # 或直接db_go
db_get mewwoof_dpkg_learning_deb/my_option_one
if ["$RET" = "false"]; then
...
fi
触发器
在安装完一个主程序后,如果想在安装完其插件后,通知主程序一个新插件已被安装,则需使用触发器。插件的安装成功将触发dpkg执行主程序的postinst脚本,并且传递参数triggered。
要实现触发,主要需要配置主程序来听触发事件。即在主程序的DEBIAN目录下添加triggers
文件,内容如下:
interest /xxxx/xxx/xxx
同时在postinst脚本中提供参数为triggered的处理(参考后面#example2)
于是,当插件安装完成后,将运行相应脚本。
除了在主程序提供要监视的文件列表,还可以提供一个“名称”。如:
在主程序的triggers中写:
interest my-plugin-installed
在插件的triggers中写:
activate my-plugin-installed
也可完成触发操作。
例子和建议
常用的代码片段
写在postrm中,判断如果是purge操作,且系统中存在debconf,则删除debconf中相关数据:
if [ "$1" = "purge" && -e /usr/share/debconf/confmodule ]; then
# Source debconf library.
. /usr/share/debconf/confmodule
# Remove my changes to the db.
db_purge
fi
使用case判断操作:
#!/bin/sh -e
case "$1" in
configure)
;;
triggered)
#here is the handler
/etc/init.d/<serverPackageName> restart
;;
abort-upgrade|abort-remove|abort-deconfigure)
;;
*)
echo "postinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
#DEBHELPER#
exit 0
参考文献
https://www.debian.org/doc/debian-policy/ch-maintainerscripts.html
http://www.fifi.org/doc/debconf-doc/tutorial.html
https://stackoverflow.com/questions/15276535/dpkg-how-to-use-trigger