章 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 命令的基礎打包腳本,不過對它的描述已經超出了我們的討論範圍。