第 2 章 第一步

目录

2.1. Debian 软件包构建流程
2.2. 选择你的程序
2.3. 获取程序并试用
2.4. 简易构建系统
2.5. 常见的可移植的构建系统
2.6. 软件包名称和版本
2.7. 设置 dh_make
2.8. 初始化外来 Debian 软件包

让我们尝试创建一个自己的软件包(或者,“收养”一个已存在的软件包则更好)。

如果你要基于某个上游程序构建软件包,那么典型的Debian软件包构建流程就会包含生成几个特定的文件,如下:

请注意,在 Debian 软件包文件名中,分隔 packageversion 的字符从 tarball 名称的 - (连字符)换成了 _ (下划线)。

在上述的文件名中,将 package 部分替换为 package name, 将 version 部分替换为 upstream version, 将revision 部分替换为 Debian revision,以及将 arch 部分替换为 package architecture,根据 Debian 方针手册。 [5]

本大纲中的每一步都会在后续的章节中辅以详细的例子进行解释。

可能你已经选好了要制作的软件包。第一件要做的事是检查它是否已经存在于发行版仓库中了,参考方法如下:

如果软件包已经存在,直接安装就好了!:-) 如果它是被 抛弃(orphaned) 的——也就是说它的维护者被设置为 Debian QA Group,那么你可以尝试接手维护它。你也可以“收养”维护者发出“Request for Adoption”(RFA)请求的软件包。[6]

软件包归属状态有这几种:

作为旁注必须指出,Debian 已经拥有了绝大多数类型软件的软件包,仓库中软件包的数量也远远超过了有上传权限的贡献者的数量。因此,为已经在仓库中的软件包贡献力量是非常受其他开发者欢迎的(且更容易获得 sponsorship)[7]。你可以通过非常多的方式来实现这一目的:

  • 接手被抛弃而仍然被很多人使用的软件包。

  • 加入 打包小组

  • 为某些常用的软件包分类 Bug。

  • 在需要时准备 QA 或 NMU 上传

如果你有能力“收养”那个软件包,下载(使用 apt-get source packagename 或其他类似的工具)并分析它的代码。这篇文档没有包含有关如何接手软件包的详细信息,但幸运的是由于接手软件包时起始的工作已经有人完成,接手的工作应比从头开始轻松得多。尽管如此,请继续阅读,下面给出的建议会对你很有帮助。

如果你要制作的软件包是全新的,并且希望它出现在 Debian 中,请按照以下的步骤进行:

  • 首先,你必须知道这个软件的可用性,并且需要试用一段时间。

  • 你必须在 Work-Needing and Prospective Packages 上查看以确定没有其他人已经开始了工作。如果没有,则提交一份 ITP (Intent To Package) Bug 报告到 wnpp 伪软件包(可以使用 reportbug)。如果已经有人在处理,则在需要的情况下联系他或他们。如果不需要你的帮助,就寻找其他你感兴趣且没有人维护的软件包吧。

  • 该软件 必须有一个许可证

    • 对于 main 类的软件, Debian 方针要求它 完全兼容 Debian Free Software Guidelines (Debian 自由软件准则) (DFSG) 并且 不要求用 main 类以外的软件来编译或执行。这是最理想的状况。

    • 对于 contrib 类的软件,其许可证必须满足 DFSG 的全部条件,但可以依赖于 main 之外的软件包以完成编译或运行。

    • 对于 non-free 类的软件,其许可证可以不满足 DFSG 中的一些条件,但至少 必须是可分发的

    • 如果你不清楚你的软件应该分入哪一类,则把许可证文本发送到 debian-legal@lists.debian.org 以寻求意见。

  • 程序 不应该 给 Debian 系统带来安全和维护上的问题。

    • 程序应当有良好的文档,最好源代码也容易理解(比如,不混乱)。

    • 你应该与程序的作者取得联系问一下他是否认为程序应当被打包,以及他是否对 Debian 友好。能够询问作者关于程序的任何问题是非常重要的,所以不要尝试打包一个无人维护的软件。

    • 程序一定 不应该 setuid 到 root 。更好的情况是它不 setuid 或 setgid 到任何用户或组。

    • 程序不应该是守护进程,也不应该进入 */sbin 目录或者以 root 打开任何端口。

当然,这些问题都只是为了安全,并试图让你不至于在比如 setuid 守护进程等问题上犯错误而激怒了用户... 当你在打包方面有了更多经验时,就可以处理这样的软件包了。

我们鼓励你,作为一个新维护人员,选择易于打包和维护的软件包,而不鼓励选择复杂的软件包。

  • 简单软件包

    • 单二进制软件包,arch = all (比如像壁纸那样的资料集)

    • 单二进制软件包,arch = all (用解释型语言编写的可执行脚本文件,比如 POSIX shell)

  • 中等复杂软件包

    • 单二进制软件包,arch = any (用C/C++等语言编写的 ELF 二进制可执行文件)

    • 复合二进制软件包,arch = any + all (包含ELF二进制可执行程序+文档的软件包)

    • 既不是 tar.gz 也不是 tar.bz2 格式的上有源代码

    • 源代码中包含不可分发的内容。

  • 高复杂软件包

    • 被其他软件包使用的解释器模块包

    • 被其他软件包使用的一般ELF库文件包

    • 复合二进制的软件包,包含ELF库文件包

    • 多上游的源码包

    • 内核模块软件包

    • 内核补丁软件包

    • 包含冷门维护者脚本的软件包

打包高复杂软件包并非难如登天,但需要更多知识。你应该针对每一个复杂特性来搜寻针对性的指南。比如,一些语言有它们自己的子策略文档:

还有一句拉丁谚语:fabricando fit faber (熟能生巧)。我们 强烈 建议你在阅读这篇教程的时候,用一个简单软件包来实践和实验所有的 Debian 打包步骤。下边的步骤创建了一个微不足道的软件包 hello-sh-1.0.tar.gz ,它可以提供一个良好的起点:[8]

$ mkdir -p hello-sh/hello-sh-1.0; cd hello-sh/hello-sh-1.0
$ cat > hello <<EOF
#!/bin/sh
# (C) 2011 Foo Bar, GPL2+
echo "Hello!"
EOF
$ chmod 755 hello
$ cd ..
$ tar -cvzf hello-sh-1.0.tar.gz hello-sh-1.0

第一件要做的事就是找到并下载原始的源代码。我们假定你已经从作者的主页上找到了它的源代码。Unix 下的自由软件源代码通常是以 tar+gzip 格式(扩展名为 .tar.gz)或 tar+bzip2 格式(扩展名为 .tar.bz2)或tar+xz (扩展名为.tar.xz)的形式提供的。通常归档文件中包含了一个名为 package-version 的子目录,里面包含了全部的源代码。

如果最新你版本的源代码可通过像 Git,Subversion,CVS 这样的版本控制系统获得,你可以用 git clonesvn co, 或 cvs co 来下载它,并自行将它重新打包为 tar+gzip, 别忘了 --exclude-vcs 选项。

如果你的程序源代码是以其他形式提供的(比如文件名以 .Z.zip 结尾[9]),则使用合适的工具将其解包,再重新打包。

如果你的程序源代码中包含一些不符合DFSG的内容,你应当解包后移除它们,再以添加了 dfsg 的上游版本号重新打包。

作为示例,我将使用一个名为 gentoo 的程序,它是一个 GTK+ 文件管理器。[10]

在你的用户主目录下创建一个子目录,命名为 debiandeb 或其他你喜欢且合适的名字(本例中使用 ~/gentoo)。把下载好的归档文件放在其中并解包(使用 tar xzf gentoo-0.9.12.tar.gz 命令)。要确定解包过程中没有发生错误,即便是有一点 不恰当 也不行,因为在别人的系统上解包这些文件时,可能他们的工具并不忽略这些反常的现象,于是就会出现问题。在你的终端屏幕上,应该看到如下的情形。

$ mkdir ~/gentoo ; cd ~/gentoo
$ wget http://www.example.org/gentoo-0.9.12.tar.gz
$ tar xvzf gentoo-0.9.12.tar.gz
$ ls -F
gentoo-0.9.12/
gentoo-0.9.12.tar.gz

现在又有了一个新的子目录,名为 gentoo-0.9.12。进入该目录并 彻底 读完其中的文档。通常情况下这些文档被命名为 README*INSTALL**.lsm*.html。你必须找到关于如何正确编译和安装程序的指导(最可能的是他们会默认你希望把程序安装到 /usr/local/bin 目录;但事实上你不能那样做,详细的内容稍后将在 第 3.3 节 “把文件安装到目的位置” 中说明)。

开始打包时源代码目录应当是绝对干净(原始)的,或者直接使用刚刚解包得到的源代码。

带有 Makefile 文件的简单程序可以很容易地使用 make 来编译。[11] 其中的一些还支持 make check,这可以完成一系列自检。编译好后可以使用 make install 来将程序安装到目标目录。

现在尝试编译和运行你的程序,确保它工作正常,且在安装和运行时不会导致其他问题。

你还可以运行 make clean (或更好的 make distclean)来清理编译目录。有时还会带有 make uninstall 用以卸载已经安装了的文件。

非常多的自由软件是使用 CC++ 语言编写的。其中的有很多使用 Autotools 或 CMake 来使其可以在不同平台上移植。这些工具首先用于生成 Makefile 和其他必须的源文件,然后这些程序可以使用正常的 make; make install 来编译和安装。

Autotools 是 GNU 编译系统工具,包括 AutoconfAutomakeLibtoolgettext。你可以通过 configure.acMakefile.amMakefile.in 等文件来识别这种类型的源代码。[12]

使用 Autotools 的第一步是在上游作者在代码中运行 autoreconf -i -f ,然后把生成的文件同源代码一起分发。

configure.ac-----+-> autoreconf -+-> configure
Makefile.am -----+        |      +-> Makefile.in
src/Makefile.am -+        |      +-> src/Makefile.in
                          |      +-> config.h.in
                      automake
                      aclocal
                      aclocal.m4
                      autoheader

编辑 configure.acMakefile.am 等文件需要一些关于 autoconfautomake 的知识。参看 info autoconfinfo automake

使用 Autotools 的第二步是用户获得分发的源代码后在源码目录下运行 ./configure && make 来将其编译成为 binary (二进制可执行程序)。

Makefile.in -----+                +-> Makefile -----+-> make -> binary
src/Makefile.in -+-> ./configure -+-> src/Makefile -+
config.h.in -----+                +-> config.h -----+
                 |
  config.status -+
  config.guess --+

你可以改变 Makefile 文件中的许多设置,比如修改默认的文件安装位置(使用 ./configure --prefix=/usr)。

尽管不是必须的:你还可以使用 autoreconf -i -f 来更新 configure 和其他相关文件,这样做有可能提高源代码的兼容性。 [13]

CMake 是另一个可选的构建系统,你可以通过 CMakeLists.txt 文件来识别使用它的源代码。

如果上游源代码以像 gentoo-0.9.12.tar.gz 的形式分发,你可以用 gentoo 作为(源代码) 软件包名 ,并用 0.9.12 作为 上游版本。 它们会被 debian/changelog 这个文件用到;该文件一会会在 第 4.3 节 “changelog 部分详细描述。

虽然这个简单的方法在大部分情况下能够显灵,但你仍需要能根据 Debian 方针和惯例来调整 软件包名上游版本

你必须让 软件包名 里只留下 小写字母 (a-z), 数字 (0-9), 加号 (+) 和 减号 (-) , 以及 点号 (.)。 软件包名最短长度两个字符;必须以字母开头;不能与库存软件包名冲突。相信我,把软件包名的长度控制在 30 字符以内是明智之举。 [14]

如果上游在它的名称中使用了一些通用术语比如 test-suite, 那么将其重命名,以显式指明其内容并避免命名空间污染。 [15]

你可以让 upstream version 中只包含字母和数字 (0-9A-Za-z), 加号 (+), 波浪号 (~), 以及 点号(.)。它必须以数字开头 (0-9)。 [16] 如果可能的话,最好把它的长度控制在8字符以内。 [17]

如果上游不使用像 2.30.32 这样的常规版本格式,而是用类似 11Apr29 这样的日期作为版本,亦即随机的代号字符串,或者以VCS的哈希值作为版本号的一部分,那么请确认将其从 upstream version 中移除。 这样的信息可以记录在 debian/changelog 文件中。 如果你需要发明一个版本字符串,请使用 YYYYMMDD 这个格式作为上游版本,比如 20110429 。这可以确保 dpkg 在升级软件包时正确解读新版本。 如果需要确保未来能够平滑过渡到类似 0.1 这样的版本号的话,请使用 0~YYMMDD 格式作为上游版本,例如 0~110429

版本字符串 [18] 可以用 dpkg(1) 来进行比较:

$ 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

有一种比较棘手的情况,当上游释出 gentoo-0.9.12-ReleaseCandidate-99.tar.gz 作为 gentoo-0.9.12.tar.gz 的预发布版本时,就需要确保升级工作妥当进行:重命名该上游源代码为 gentoo-0.9.12~rc99.tar.gz.

首先我们设置两个 环境变量,$DEBEMAIL$DEBFULLNAME,这样能使使大多数 Debian 维护工具能够正确识别你用于维护软件包的姓名和电子邮件地址。[19]

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

一般的 Debian 软件包是由上游程序产生的 外来 Debian 软件包。 若你想要用上游源代码 gentoo-0.9.12.tar.gz 创建一个外来 Debian 软件包,你可以为它创建一个初始的外来 Debian 软件包,通过如下方法调用 dh_make 命令:

$ 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

当然,请用你原始源码归档的名字来替换 filename (文件名)。 [20] 详情请参见 dh_make(8)

你应该会看到一些输出,它们询问你想要创建什么类型的软件包。这里的 Gentoo 是一个单一二进制包 -- 它仅仅创建一个二进制包, 亦即, 一个 .deb 文件 -- 于是我们就选择第一项 (用 s 键), 检查屏幕上的信息, 并按 ENTER 键来确认。 [21]

执行 dh_make 后,上一级目录中自动创建了一份上游 tarball 的副本,名为 gentoo_0.9.12.orig.tar.gz,这个文件和稍后介绍的 debian.tar.gz 在一起满足了 Debian 非本地源代码包的要求。

$ cd ~/gentoo ; ls -F
gentoo-0.9.12/
gentoo-0.9.12.tar.gz
gentoo_0.9.12.orig.tar.gz

请注意 gentoo_0.9.12.orig.tar.gz 这个文件名的两个关键特点:

  • 软件包名称和版本是以字符 _ (下划线)分隔的。

  • .tar.gz 扩展名前插有 .orig

你应该可以注意到 debian 目录下有了许多模板文件。这些文件将在 第 4 章 debian 目录中的必须内容第 5 章 debian 目录下的其他文件 中一一解释。你还应该明白,打包没办法变成全自动的过程。你还需要按照 第 3 章 修改源代码 中的方法来为 Debian 修改软件包。此后,你还要按照 第 6 章 构建软件包 中叙述的合适的方法来构建 Debian 软件包,并按照 第 7 章 检查软件包中的错误 中的方法进行测试,最终依照 第 9 章 上传软件包 的介绍将其上传。本教程将对所有的这些步骤进行解释。

如果你在修改过程中不小心删除或弄坏了某些模板文件,你可以使用 dh_make 加上 --addmissing 参数来将其还原。

更新一个已存在的软件包可能比较复杂,因为它可能使用了旧技术。在学习基本功的阶段,请只创建全新的软件包;稍后的 第 8 章 更新软件包中会有更细致的讲解。

请注意,源代码文件不必包含任何在第 2.4 节 “简易构建系统”第 2.5 节 “常见的可移植的构建系统” 中谈论到的构建系统。 就算仅仅是图像数据集合之类的也可以。文件的安装可以使用 debhelper 的配置文件来摆平,比如 debian/install (参见 第 5.11 节 “install)。



[4] 对于旧式的 1.0 格式非本地 Debian 源码包,应当使用 package_version-revision.diff.gz

[5] 参见 5.6.1 "Source", 5.6.7 "Package", 以及 5.6.12 "Version"package architecture 遵循 Debian Policy Manual, 5.6.8 "Architecture" 并且会在软件包构建的过程中被自动分配。

[7] 当然了,总有值得打包的新软件。

[8] 不用担心失踪的 Makefile。你可以参照 第 5.11 节 “install ,简单地通过 debhelper 来安装 hello 程序,或者修改上游源代码来添加带有install目标的新Makefile,参照第 3 章 修改源代码

[9] 当文件扩展名不足以判断文件类型时,可以使用 file 命令来判断。

[10] 需要注意的是,这个程序已经被打包好了。当前的版本 使用 Autotools 作为其构造(build structure),并且已经和下边的例子大不相同,下边的例子基于版本 0.9.12 。

[11] 许多现代程序都配有一个叫做 configure 的脚本,它被执行的时候会生成一个为你机器定制的 Makefile

[12] 关于 Autotools 这个庞然大物的讨论已经超出本篇小教程的讨论范围。而本节旨在提供关键字和参考。如果你需要使用它,请认真研读 Autotools Tutorial 以及 /usr/share/doc/autotools-dev/README.Debian.gz 的本地副本。

[13] 你可以用 dh-autoreconf 软件包来将这个过程自动化。参见 第 4.4.3 节 “定制 rules 文件”.

[14] aptitude 工具中,软件包名字段的默认最大长度为 30。 而对于 90% 以上的软件包来说,软件包名都少于 24 个字符。

[15] 如果你遵循 Debian Developer's Reference 5.1. "New packages", 那么在 ITP 过程中总是会遇到这样的问题。

[16] 这一条更严格的规则能帮助你避免混淆文件名。

[17] aptitude 命令的版本字段默认长度为10。其中通常 Debian 修订号和前置的连字符会消耗2个位置。对于 80% 以上的软件包来说,上游版本小于8字符,Debian 修订号小于2字符。对于 90% 以上的软件包来说,上游版本小于10字符,Debian 修订号小于3字符。

[18] 版本字符串可以是 upstream version (version), Debian revision (revision), 或者 version (version-revision)。 关于 Debian 修订号如何增长的信息,请参见 第 8.1 节 “新的 Debian 版本”Debian revision

[19] 以下默认你以 Bash 作为登陆 shell。如果你使用其他的 shell,例如 Z shell,那就使用它们的配置文件代替这里提到的 ~/.bashrc

[20] 如果上游源代码已经提供了 debian 目录 以及其内容,那么带上参数 --addmissing 来执行dh_make 命令。 新的源码包格式 3.0 (quilt) 的鲁棒性(Robust)已经足够优秀,以不至于损坏,即使对于这类软件包。 另外,你可能需要更新上游提供的内容,以满足你的 Debian 软件包之需。

[21] 此处有这几种选择: s 代表单一二进制包, i 代表独立于体系结构的软件包, m 代表复合二进制包, l 代表库文件包,k 代表内核模块包, n 代表内核补丁包, b 代表 cdbs 软件包。本教程专注于使用 dh 命令 (来自 debhelper 软件包) 来创建单一二进制包,但 也会涉及到如何用它来创建 独立于体系结构 或 复合二进制软件包。软件包 cdbs 提供了 另一套可以代替 dh 命令的基础打包脚本,不过对它的描述已经超出了我们的讨论范围。