DPKG软件包制作

dpkg软件包管理工具往往部署于Debian系系统,像Ubuntu、Termux等发行版均使用dpkg来管理软件包。本文介绍dpkg软件包(*.deb包)的制作,包括各种控制脚本的介绍,以及debconf图形化界面配置工具的介绍。

准备

要打包一个二进制软件,你需要:

  • 能运行dpkg的系统(Linux/WSL/MacOS/Android都行)
  • dpkg软件、(可选:debconf软件)
  • 一个文本编辑器

dpkg软件包的制作非常简单,将所有文件按照一定结构放到一个目录下,编写软件包信息,再使用dpkg --build即可完成制作。

软件包目录结构

完全解压一个deb包,可以看到以下结构的文件:

一个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等

可以在https://www.debian.org/doc/manuals/debian-handbook/sect.package-meta-information.zh-cn.html#sect.control参考更多信息。

软件包控制脚本

在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输入字符串
booleantrue | false
selectChoices: yes, no
multiselectChoices: 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

发表评论