Debian 软件包制作小记 (1)

IT, 其他
没有评论

Debian 的软件包以 .deb 作为后缀,它是一个Unixar的标准归档,将包文件信息以及包内容,经过gziptar打包而成。通常来说这个包里面含有 data.tar.* 实际的软件内容; control.tar.gz 记录了包名称,版本号,维护者,需要什么依赖,与什么冲突的信息; debian-binary deb 格式版本号码.

对于我们平时在 Debian/Ubuntu 平台编译的软件来说,通常只会通过 make 之类的编译指令生成 deb 包中 data.tar.* 的内容,即使我们使用 checkinstall 之类的软件打包成 deb 包, 也不会自动填充 control.tar.gz,这就会导致这个生成的 deb 包并不是适合分发的形式。这个包在使用例如 dpkg 包管理器的时候,既不会检查依赖,冲突,也不能正确显示软件包的具体信息 (例如 dpkg -l, 你会看到这种软件包上面写着 Package created with checkinstall 1.6.2 而不是相应的软件描述)。

因此这里就需要用到一个完整的 Debian 软件包打包流程。经过对前人留下的 Blog 文章以及官方文档的了解,我尝试了这个流程,并且整理成这几篇记录,内容仅仅是摸索过程中的体验和一些技巧,并不代表任何官方性的规则,具体文档请直接参考Debian 新维护者手册


写在在打包之前:

在制作软件包之前,首先应该清楚打包是什么,需要哪些流程,需要什么工具:

手工安装软件包的流程

如果要手工从源代码开始将这个软件安装到系统中,常见的步骤包括:

  1. 获取源代码

  2. 准备软件依赖以及工具链

  3. 从源码构建软件

  4. 测试

  5. 安装

打包者的工作:

相对于这些需要手动编译安装的软件,作为用户一般会更喜欢有一个一条指令就能完成的安装操作,并且会希望这些软件在安装或者清理时都尽可能的干净,不会造成冲突。那么打包者就需要完成上述除去安装以外的步骤。

  1. 获取源代码:打包者通常需要通过可靠的途径获取源代码,比如从官方 Git/SVN 库获取源码,或者官方的发布网页。(当开发者同时提供 Git 仓库和源代码包时,需要小心版本区别,例如 deluge 官网提供的 1.3.15 代码包和 Git 仓库里 Tag 为 1.3.15 的源码有细微差别)

  2. 处理源代码:打包者需要对获取到的源代码进行处理,具体包括检查源文件的版权信息、构建需求(有什么依赖,什么冲突) 。有必要的时候还需要制作补丁,与上游开发者沟通等。

  3. 尝试构建软件包:根据开发者文档对打过补丁的源码包进行构建,如果在此过程发现问题,则重复上一步直到问题解决。

  4. 检查软件包:即便软件包成功构建,打包者也需要对其进行检查,确保质量可靠、符合要求。有问题则需要返工重新调整。

  5. 发布软件包:打包者在确定软件包适合发布后,会通过某种途径将其向外界发布出去,供用户使用。比较常见的是在Debian.org 或者 ubuntu 的 launchpad 发布。也可以直接将打包好的 deb 直接通过 http 下载的方式提供给用户。

  6. 跟踪并修复软件缺陷:有些时候你会在你的发布页看到用户反馈的 issue,在确认到问题后,打包者应该尝试修复,或者与上游开发者沟通。

  7. 跟踪并发布新版本:软件的原作者可能会不时地发布这个软件的新版本。打包者需要跟踪开发流程,并在新版本发布时检查变化,为新版本再次打包并发布。

而对于用户而言,他将会得到一个可以安装直接使用的 deb 包,并且可以通过 deb 包里面的信息搞定所有剩余的依赖库。


准备工作

首先安装称为 packaging-dev 的工具链,这里面包含了大部分打包需要使用的工具:


sudo apt-get install packaging-dev

如果你希望为多个 debian 版本以及衍生平台(甚至不同构架,使用 qemu 即可达到此目的)提供本地编译,那么可以选择安装 pbuilder

sudo apt-get install pbuilder ubuntu-dev-tools debootstrap devscripts

pbuilder 使用 chroot 也就是所谓净室环境在不污染宿主系统的前提下,构建不同平台的包。

除去这些打包必备的工具以外,如果你还需要发布到 launchpad 之类的平台,那么还需要对你的安装包使用 gpg 签名,因此,需要安装 gpg:

sudo apt install gnupg

具体创建 gpg 密钥的方式可以参考这里

与 Lanuchpad 相关的操作也将在后续文章里说明。

设置 dh_make 的方法如下:首先设置两个 环境变量,$DEBEMAIL$DEBFULLNAME,这样大多数 Debian 维护工具就能够正确识别你用于维护软件包的姓名和电子邮件地址。然后修改并写入如下内容:

$ cat >>~/.bashrc <<EOF
DEBEMAIL="your.email.address@example.org"
DEBFULLNAME="Firstname Lastname"
export DEBEMAIL DEBFULLNAME
EOF
$ . ~/.bashrc

编译的基础:

由于打包的前提条件是一个编译好的软件,所以不得不提一下编译。通常来说,简单的程序源码会带有 Makefile,我们可以很容易的直接输入 make 来进行编译。略微复杂一点的现代化程序还可能会使用一个叫做 configure 的脚本来生成当前系统所需的 Makefile。类似的还有 cmake 使用的 CMakeLists.txtcmake 会直接使用这个文本文件来生成相应的 Makefile。另外还有比较常见的工具 Autotools ,它使用configure.acMakefile.amMakefile.in等特征文件来识别使用 Autotools 作为编译系统的源代码。在使用 Autotools 生成 configure 后,同样运行configure得到特定的 Makefile。由此可见,在常见的编译系统中,一切的基石就是Makefile。由于这篇文章的主题是打包而非编译,因此并不特别展开说明 Makefile 的写法。


软件包名称和版本:

Debian 的包管理基于软件包和版本号,因此在本地打包的时候仍需要特别注意。

我们直接参考 “Debian 新维护者手册” 中的描述进行总结:

例如 rtorrent-0.9.6.tar.gzrtorrent 就可以作为软件包名称, 0.9.6 就是上游版本号。它们会被debian/changelog 这个文件用到(后面内容将会详细说明)。更为详尽的说明可以参考 Debian 政策。我们可以参照中说明的内容调整命名满足规则。

软件包名 里只能含有 小写字母 (a-z), 数字 (0-9), 加号 (+) 和 减号 (-) , 以及 点号 (.)。 软件包名最短长度两个字符;它必须以字母开头;它不能与仓库软件包名发生冲突(对于需要上传到debian仓库或者launchpad而言)。

upstream version(上游版本号) 只包含字母和数字 (0-9A-Za-z), 以及加号 (+), 波浪号 (~), 还有 点号(.)。它必须以数字开头 (0-9)。

虽然在 Debian Policy 中提到可以使用基于日期的版本号,但是这并不是什么非常明智的方式。由于需要保证版本的唯一性,同时又满足平滑过渡的要求,以数字作为版本号会更加合适。此外还可以用 launchpad 手册中提到的 myapp_1.0-2~ppa1~ubuntu16.04.1的形式,用波浪号 (~)将平台标识起来。

版本字符串可以用 dpkg 来进行比较:

$ dpkg --compare-versions ver1 op ver2 

版本比较规则可总结为以下几点:

  • 字符串要从头到尾进行比较。

  • 字母比数字大。

  • 数字作为整数进行比较。

  • 字母按照 ASCII 编码顺序进行比较。

  • 对于点号 (.),加号 (+),以及波浪号 (~) 则有对应的特殊规则,具体如下:

    0.0 < 0.5 < 0.10 < 0.99 < 1 < 1.0~rc1 < 1.0 < 1.0+b1 < 1.0+nmu1 < 1.1 < 2.0


源码包的工作目标:

在类似 launchpad 的软件源中除去 .deb 二进制包以外还存在以下文件:.orig.tar.xz, .debian.tar.xz.dsc。这些文件是在构建软件包以后最终得到的。

  • .orig.tar.xz 存储的内容是软件包的源码,这一步可以通过初始化外来软件包来得到。例如:

    $ cd ~/gentoo
    $ wget http://example.org/gentoo-0.9.12.tar.gz
    $ tar -xvzf gentoo-0.9.12.tar.gz
    $ cd gentoo-0.9.12
    $ dh_make -f ../gentoo-0.9.12.tar.gz
    

    这个操作实际上是使用 dh_make 按照默认模板生成默认的文件,并且将已有的 tar 包拷贝并且重命名。

    $ cd ~/gentoo ; ls -F
    gentoo-0.9.12/
    gentoo-0.9.12.tar.gz
    gentoo_0.9.12.orig.tar.gz
    
  • .debian.tar.xz 存储的是软件包的信息以及构建规则。在上一步的操作中你可以看到你的源码目录中还成成了一个 debian 文件夹,在这个目录下已经有了很多模板(后文还将详细说明这些文件的用途)。此压缩包就是归档该目录中的文件,在使用 dpkg-buildpackage 或者 debuild 等命令生成源码包的时候就会得到该归档文件。
  • .dsc存储的是从 control 文件生成的源代码概要,是构成软件打包的重要文件。

值得注意的是,这些操作生成的文件都是以 ${softwarename}_${version} 的形式出现,也就是说文件夹中用于分割软件名称的 - 被替换成了 _


debian 目录中的必须内容:

上面的章节已经又介绍过,在初始化一个外来的软件包以后,在源码目录下会生成一个带有默认模板文件的 debian 目录,这个目录中将会存储有软件包的信息(包括维护者,来源,内容等),同时有着构建规则(依赖,编译指令,安装指令,以及打包规则)。下面是自动生成的文件的例子:

./
├── changelog
├── compat
├── control
├── copyright
├── manpage.1.ex
├── manpage.sgml.ex
├── manpage.xml.ex
├── menu.ex
├── postinst.ex
├── postrm.ex
├── preinst.ex
├── prerm.ex
├── README.Debian
├── README.source
├── rtorrent.cron.d.ex
├── rtorrent.doc-base.EX
├── rtorrent-docs.docs
├── rules
├── source
│   └── format
└── watch.ex

在这些文件中有如下必备的文件:controlcopyrightchangelogrules。下面依次说明这几个文件的作用:

control

这个文件包含了很多供 dpkgdselectapt-getapt-cacheaptitude 等包管理工具进行管理时所使用的许多变量。可以查看 Debian Policy Manual, 5 “Control files and their fields” 的定义。

继续以一个文件为例:

Source: rtorrent
Section: unknown
Priority: optional
Maintainer: Amefs <efs@amefs.net>
Build-Depends: debhelper (>= 10), autotools-dev
Standards-Version: 4.1.2
Homepage: <insert the upstream URL, if relevant>
#Vcs-Git: https://anonscm.debian.org/git/collab-maint/rtorrent.git
#Vcs-Browser: https://anonscm.debian.org/cgit/collab-maint/rtorrent.git

Package: rtorrent
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: <insert up to 60 chars description>
 <insert long description, indented with spaces>

可以看到这里的信息被分成两个部分:一个是构建二进制包相关 ,另一个是安装二进制包相关。

简单解读一下这里面的内容:

  • Source: 软件名称。

  • Section: Debian 仓库被分为几个类别:main (自由软件)、non-free (非自由软件)以及 contrib (依赖于非自由软件的自由软件)。也就是平时在 sourcelist 中看到的那些不同的源。在这些大分类下还有根据软件包用途区分的子分类:admin 为供系统管理员使用的程序,devel 为开发工具,doc 为文档,libs 为库,mail 为电子邮件阅读器或邮件系统守护程序,net 为网络应用程序或网络服务守护进程,x11 为不属于其他分类的为 X11 程序,等等。以这里的 rtorrent 软件为例,它是一个 p2p 的客户端因此分类就是 net

  • Priority: 这里虽然还有 requiredimportantstandard 等优先级,但是一般这类常规的软件保持 optional 即可。

  • Maintainer: 软件包的维护者,考虑到可能会收到一些 issue 反馈,因此应该正确填写此信息以获取反馈。

  • Build-Depends: 这里需要添加编译必须的依赖包,有些被 build-essential依赖的软件包,如 gccmake 等,已经会被默认安装而不需再写到此处。但是类似 bcdh-autoreconflibcurl4-openssl-dev 之类的编译工具链以及一些必须的库就应该被添加到此处。多个软件包可以通过半角逗号隔开。注:在这里我们是可以指定软件依赖的版本,通过 =>= 之类的符号来指定特定版本或者最低版本。

  • Standards-Version: 这个是 control 文件的标准版本,一般来说无需修改。

  • Homepage: 上游代码的来源

  • Package: 二进制软件包名称,这个通常来说与源码包一致,但是并非必需,如果你想要分割多个软件包也可以在这里以多个不同名称的 Package 代码块来实现。实际举例:xmlrpc-c 的打包被分为很多个小的软件包,并通过添加 ${Packagename.install} 实现向这些软件包安装不同的文件。

  • Architecture: 描述了可以编译本二进制包的体系结构。一般来说这个值是 any 或者 allany 一般是编译型语言编写的程序生成的二进制,而 all 则是脚本型或者文本,图片等二进制包。

  • Depends: 这个是软件包本身的依赖,一般来说是一些运行库或者依赖的其他软件。当然与 Depends 同级别的还有 RecommendsSuggestsPre-DependsBreaksConflictsProvidesReplaces 等关系。具体可以参考以下说明

  • Description: 软件用途的描述。每行的第一个格应当留空。描述中不应存在空行,如果必须使用空行,则在行中仅放置一个 . (半角句点)来近似。同时,长描述后也不应有超过一行的空白。

到此我们就完成了 control 文件的修改。

Source: rtorrent
Section: net
Priority: optional
Maintainer: Amefs <efs@amefs.net>
Build-Depends:
 bc,
 debhelper (>= 9),
 dh-autoreconf,
 libcppunit-dev,
 libcurl4-openssl-dev,
 libncurses5-dev,
 libncursesw5-dev,
 libtorrent-dev (>= 0.13.4),
 libxmlrpc-core-c3-dev,
 pkg-config
Standards-Version: 3.9.6
Vcs-Git: git://git.debian.org/git/collab-maint/rtorrent.git
Vcs-Browser: http://git.debian.org/?p=collab-maint/rtorrent.git
Homepage: https://rakshasa.github.io/rtorrent/

Package: rtorrent
Architecture: any
Depends:
 ${misc:Depends},
 ${shlibs:Depends}
Suggests:
 screen | dtach
Description: ncurses BitTorrent client based on LibTorrent from rakshasa
 rtorrent is a BitTorrent client based on LibTorrent.  It uses ncurses
 and aims to be a lean, yet powerful BitTorrent client, with features
 similar to the most complex graphical clients.
 .
 Since it is a terminal application, it can be used with the "screen"/"dtach"
 utility so that the user can conveniently logout from the system while keeping
 the file transfers active.
 .
 Some of the features of rtorrent include:
  * Use an URL or file path to add torrents at runtime
  * Stop/delete/resume torrents
  * Optionally loads/saves/deletes torrents automatically in a session
    directory
  * Safe fast resume support
  * Detailed information about peers and the torrent
  * Support for distributed hash tables (DHT)
  * Support for peer-exchange (PEX)
  * Support for initial seeding (Superseeding)

copyright

这个文件包含了上游软件的版权以及许可证信息。Debian Policy Manual, 12.5 “Copyright information” 掌控着它的内容,另外 DEP-5: Machine-parseable debian/copyright 提供了关于其格式的方针。这个虽然是必须的文件,但是在这里就不详细展开了。

changelog

这是一个必须的文件,它的特殊格式在 Debian Policy Manual, 4.4 “debian/changelog” 中有详细的描述。这种格式被 dpkg 和其他程序用以解析版本号信息、适用的发行版和紧急程度。这个文件可以用来记录你对软件包进行的哪些修改,不同的版本之间有什么区别,建议尽可能正确,详细的填写其中的内容。

还是以一个实际文件为例:

libtorrent (0.13.4-1ppa1~18.04) bionic; urgency=medium

  * Initial release

 -- Amefs <efs@amefs.net>  Fri, 14 Jun 2019 08:09:31 +0000

第一行内容是软件名称,版本号,发行版,紧急程度(正常情况下为 medium)

后面几行一般是描述这次更新的具体内容,可多可少。

最后一行则是维护者信息,以及发布这条 changelog 的时间戳。

除了直接编辑这个文件以外,还可以使用 dch -i 这将会自动生成一个新的 changelog 条目并且应用预制的信息生成维护者信息以及新的时间戳。

rules

这是指导 dpkg-buildpackage 构建软件包的 rules 文件,这个文件的本质是一个 Makefile。这与源码内的 Makefile 并不相同,它专门负责指导 dpkg-buildpackage 使用源码包中的各种信息构建二进制文件。

每一个 rules 文件, 就像其他的 Makefile 一样,包含着若干 rules,其中每一个都定义了一个 target 以及其具体 操作。 一个新的 rule 以自己的 target 声明(置于第一列)来起头。 后续的行都以 TAB 字符 (ASCII 9) 来开头,以指示 target 的具体行为。 空行和以井号 # 开头的行会被当作注释而被忽略。 在过去,这些 target 是需要手动编写的,但是现在这些任务一般会交给 debhelper 来完成。

dh_make 给出的默认 rules 文件就是以 debhelper 支持的命令生成的最简规则:

#!/usr/bin/make -f
# See debhelper(7) (uncomment to enable)
# output every command that modifies files on the build system.
#DH_VERBOSE = 1

# see FEATURE AREAS in dpkg-buildflags(1)
#export DEB_BUILD_MAINT_OPTIONS = hardening=+all

# see ENVIRONMENT in dpkg-buildflags(1)
# package maintainers to append CFLAGS
#export DEB_CFLAGS_MAINT_APPEND  = -Wall -pedantic
# package maintainers to append LDFLAGS
#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed


%:
         dh $@ 
 

第 16 和 17 行使用了 pattern rule,以此隐式地完成所有工作。 其中的百分号意味着“任何 targets”, 它会以 target 名称作参数调用单个程序 dhdh 命令是一个包装脚本,它会根据参数执行妥当的 dh_* 程序序列。而我们使用的 dpkg-buildpackage 实际会通过执行 debian/rules build-target 以及 fakeroot debian/rules binary-target 来生成二进制并安装打包到一个 deb 文件中。

  • debian/rules clean 运行了 dh clean,实际执行的命令为:

      dh_testdir
      dh_auto_clean
      dh_clean
    

     

  • debian/rules build 执行了 dh build,实际执行的命令为:

    dh_testdir
    dh_auto_configure
    dh_auto_build
    dh_auto_test
    
  • fakeroot debian/rules binary 执行了 fakeroot dh binary,其实际执行的命令为:

     
    dh_testroot
    dh_prep
    dh_installdirs
    dh_auto_install
    dh_install
    dh_installdocs
    dh_installchangelogs
    dh_installexamples
    dh_installman
    dh_installcatalogs
    dh_installcron
    dh_installdebconf
    dh_installemacsen
    dh_installifupdown
    dh_installinfo
    dh_installinit
    dh_installmenu
    dh_installmime
    dh_installmodules
    dh_installlogcheck
    dh_installlogrotate
    dh_installpam
    dh_installppp
    dh_installudev
    dh_installwm
    dh_installxfonts
    dh_bugfiles
    dh_lintian
    dh_gconf
    dh_icons
    dh_perl
    dh_usrlocal
    dh_link
    dh_compress
    dh_fixperms
    dh_strip
    dh_makeshlibs
    dh_shlibdeps
    dh_installdeb
    dh_gencontrol
    dh_md5sums
    dh_builddeb
    

可以看到这里有非常多 dh_* 命令,可以参考 debhelper 的说明

我们可以通过为 dh $@ 命令添加各种参数来添加 dh_python2dh_quilt_patchdh_dkms 等命令支持。并且我们可以通过类似如下的参数来覆盖默认的配置。例如:

#!/usr/bin/make -f

%:
	dh $@ --with autoreconf --parallel

override_dh_auto_configure:
	dh_auto_configure -- --with-xmlrpc-c --enable-ipv6

这是 rtorrent 的 rules。我们简单解读一下,首先在 dh 命令初始化的是时候,加入 autoreconf 支持,这就会在编译的时候执行 autoreconf,这样就会生成一个适用于这个平台的 configure 文件。parallel 命令将会打开并行编译支持。最后通过 override_dh_auto_configure./configure 命令添加 --with-xmlrpc-c --enable-ipv6 参数。这个过程就与我们在编译安装的时候执行 autogen.sh./configure --with-xmlrpc-c --enable-ipv6 的效果相同。根据这个 rules 就可以完成 Makefile 的生成,并利用生成的文件构建二进制,最后打包。

对于很多软件包来说,我们并不需要这样一步一步手动生成这些文件。常见的软件包往往已经实现了 debian 化,也就是说在一些软件源中可以找到这些东西,我们只需要下载它们的源码,提取出 .debian.tar.xz 中的这些必要文件,就可以按照我们的需求进行定制。

参考文档:

https://www.debian.org/doc/manuals/maint-guide/

https://hosiet.me/blog/2016/09/15/make-debian-package-with-git-the-canonical-way/

https://blog.packagecloud.io/eng/2016/12/15/howto-build-debian-package-containing-simple-shell-scripts/

https://www.mobibrw.com/2016/3138

, , ,
上一篇文章
Markdown 转 PDF 小记
下一篇文章
Debian 软件包制作小记 (2)

发表评论

电子邮件地址不会被公开。 必填项已用*标注

Fill out this field
Fill out this field
请输入正确的电子邮件地址。

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据

菜单