章 10. 高階打包

# 章 10. 高階打包

Let’s describe advanced topics on Debian packaging.

## 10.1. Historical perspective

Let me oversimplify historical perspective of Debian packaging practices focused on the non-native packaging.

Debian was started in 1990s when upstream packages were available from public FTP sites such as Sunsite. In those early days, Debian packaging used Debian source format currently known as the Debian source format “1.0”:

- The Debian source package ships a set of files for the Debian source package.

  - package_version.orig.tar.xz : symlink to or copy of the upstream released file.
  - package_version-revision.diff.gz : “One big patch” for Debian modifications.
  - package_version-revision.dsc : package description.
- Several workaround approaches such as dpatch, dbs, or cdbs were deployed to manage multiple topic patches.

The modern Debian source format “3.0 (quilt)” was invented around 2008 (see “ProjectsDebSrc3.0”):

- The Debian source package ships a set of files for the Debian source package.

  - package_version.orig.tar.?z : symlink to or copy of the upstream released file.

  - package_version-revision.debian.tar.?z : tarball of debian/ for Debian modifications.

    - The debian/source/format file contains “3.0 (quilt)”.
    - Optional multiple topic patches are stored in the debian/patches/ directory.
  - package_version-revision.dsc : package description.
- The standardized approach to manage multiple topic patches using quilt(1) is deployed for the Debian source format “3.0 (quilt)”.

Most Debian packages adopted the Debian source formats “3.0 (quilt)” and “3.0 (native)”.

Now, the git(1) is popular with upstream and Debian developers. The git and its associated tools are important part of the modern Debian packaging workflow. This modern workflow involving git will be mentioned later in “章 11, Packaging with git”.

## 10.2. Current trends

Current Debian packaging practices and their trends are moving target. See:

- “Debian Trends”— Hints for “De facto standard” of Debian practices

  - Build systems: dh
  - Debian source format: “3.0 (quilt)”
  - VCS: git
  - VCS Hosting: salsa
  - Rules-Requires-Root: adopted, fakeroot
  - Copyright format: DEP-5
- “debhelper-compat-upgrade-checklist(7) manpage”— Upgrade checklist for debhelper
- “DEP - Debian Enhancement Proposals”— Formal proposals to enhance Debian

You can also search entire Debian source code data by yourself, too.

- “Debian Sources”— code search tool

  - “Debian Code Search”— wiki page describing its usage
- “Debian Code Search”— another code search tool

## 10.3. Note on build system

Auto-generated files of the build system may be found in the released upstream tarball. These should be regenerated when Debian package is build. E.g.:

- “dh $@ --with autoreconf” should be used in the debian/rules if Autotools (autoconf + automake) are used.

Some modern build system may be able to download required source codes and binary files from arbitrary remote hosts to satisfy build requirements. Don’t use this download feature. The official Debian package is required to be build only with packages listed in Build-Depends: of the debian/control file.

## 10.4. 持續整合

The dh_auto_test(1) command is a debhelper command that tries to automatically run the test suite provided by the upstream developer during the Debian package building process.

The autopkgtest(1) command can be used after the Debian package building process. It tests generated Debian binary packages in the virtual environment using the debian/tests/control RFC822-style metadata file as continuous integration (CI). See:

- Documents in the /usr/share/doc/autopkgtest/ directory
- “4. autopkgtest: Automatic testing for packages” of the “Ubuntu Packaging Guide”

您可以在 Debian 系統上探索使用不同的持續整合系統。

- The Salsa offers “節 11.3, “Salsa CI service””.
- debci 套件：建立在 autopkgtest 之上的持續整合平臺
- jenkins 套件：通用持續整合平臺

## 10.5. 自主生成 (Bootstrapping)

Debian cares about supporting new ports or flavours. The new ports or flavours require bootstrapping operation for the cross-build of the initial minimal native-building system. In order to avoid build-dependency loops during bootstrapping, the build-dependency needs to be reduced using the DEB_BUILD_PROFILES environment variable.

See Debian wiki: “BuildProfileSpec”.

| 提示
If a core package foo build depends on a package bar with deep build dependency chains but bar is only used in the test target in foo, you can safely mark the bar with <!nocheck> in the Build-depends of foo to avoid build loops.

## 10.6. 編譯強化

The compiler hardening support spreading for Debian jessie (8.0) demands that we pay extra attention to the packaging.

您應當詳細閱讀下列參考內容。

- Debian wiki: “Hardening”
- Debian wiki: “Hardening Walkthrough”

debmake 命令會對 debian/rules 檔案中按需新增 DEB_BUILD_MAINT_OPTIONS、DEB_CFLAGS_MAINT_APPEND 和 DEB_LDFLAGS_MAINT_APPEND 的專案（參見 “章 5, Simple packaging” 和 dpkg-buildflags(1)）。

## 10.7. 可重現的構建

為了做到套件可重現的構建，這裡給出一些相關的建議。

- 不要嵌入基於系統時間的時間戳。
- Don’t embed the file path of the build environment.
- Use “dh $@” in the debian/rules to access the latest debhelper features.
- Export the build environment as “LC_ALL=C.UTF-8” (see “節 12.1, “在 UTF-8 環境下構建””).
- 對上游原始碼中使用的時間戳，使用 debhelper 提供的環境變數 $SOURCE_DATE_EPOCH 的值。

- 閱讀“可重現構建”瞭解更多資訊。

  - “可重現構建操作方法”。
  - “可重現構建時間戳處理提議”。

Reproducible builds are important for security and quality assurance. They allow independent verification that no vulnerabilities or backdoors have been introduced during the build process.

由 dpkg-genbuildinfo(1) 生成的控制檔案 source-name_source-version_arch.buildinfo 記錄了構建環境資訊。參見 deb-buildinfo(5)

## 10.8. Substvar

debian/control 也定義了套件的依賴關系，其中“變數替換機制”（substvar）的功能可以用來將套件維護者從追蹤（大多數簡單的）套件依賴的重複勞動中解放出來。請參見 deb-substvars(5)。

debmake 命令支援下列變數替換指令：

- ${misc:Depends}，可用於所有二進位制套件
- ${misc:Pre-Depends}，可用於所有 multiarch 套件
- ${shlibs:Depends}，可用於所有含有二進位制可執行檔案或程式庫的套件
- ${python:Depends}，可用於所有 Python 套件
- ${python3:Depends}，可用於所有 Python3 套件
- ${perl:Depends}，用於所有 Perl 套件
- ${ruby:Depends}，用於所有 Ruby 套件

For the shared library, required libraries found simply by “objdump -p /path/to/program | grep NEEDED” are covered by the shlib substvar.

For Python and other interpreters, required modules found simply looking for lines with “import”, “use”, “require”, etc., are covered by the corresponding substvars.

對其它沒有部署屬於自己範疇內的變數替換機制的情況，misc 變數替換佔位符通常用來覆蓋對應的依賴替換需求。

對 POSIX shell 程式來說，並沒有簡單的辦法來驗證其依賴關係，substvar 的變數替換也無法自動得出它們的依賴。

對使用動態載入機制，包括 “GObject introspection” 機制的程式庫和模組來說，現在沒有簡單的方法可以檢查依賴關係，變數替換機制也無法自動推匯出所需的依賴。

## 10.9. 程式庫套件

打包軟體程式庫需要您投入更多的工作。下面有一些打包軟體程式庫的提醒和建議：

- 程式庫二進位制套件必須根據 “節 10.17, “Library package name”” 進行命名。
- Debian 按照 /usr/lib/<triplet>/lib foo-0.1.so.1.0.0 這樣的路徑提供共享連結程式庫（參見 “節 10.10, “多體系架構””）。
- Debian 鼓勵在共享程式庫中使用帶版本的符號（見 “節 10.16, “程式庫符號””）。
- Debian 不提供 *.la libtool 程式庫歸檔檔案。
- Debian 不推薦使用、提供 *.a 靜態程式庫檔案。

在打包共享程式庫軟體之前，請查閱：

- “Chapter 8 - Shared libraries” of the “Debian Policy Manual”
- “10.2 Libraries” of the “Debian Policy Manual”
- “6.7.2. Libraries” of the “Debian Developer’s Reference”

如需研究其歷史背景，請參見：

- “逃離依賴地獄” [18]

  - 該文件鼓勵在共享程式庫中使用帶版本的符號。

- “Debian Library Packaging guide” [19]

  - Please read the discussion thread following its announcement, too.

## 10.10. 多體系架構

Debian wheezy（7.0，2013年5月）在 dpkg 和 apt 中引入了對跨架構二進位制套件安裝的多架構支援（特別是 i386 架構和 amd64 架構，但也支援其它的組合），這部分內容值得我們額外關注。

您應當詳細閱讀下列參考內容。

- Ubuntu 維基（上游）

  - “多架構規範（MultiarchSpec）”

- Debian 維基（Debian 的現狀）

  - “Debian 多架構支援”
  - “多架構支援/實現（Multiarch/Implementation）”

多架構支援使用三元組（<triplet>）的值，例如 i386-linux-gnu 和 x86_64-linux-gnu；它們出現在共享連結程式庫的安裝路徑中，例如 /usr/lib/<triplet>/，等等。

- 三元組 <triplet> 的值由 debhelper 指令碼隱性提前設定好，套件維護者無需擔心。

- 不過，在 debian/rules 檔案中用於 override_dh_* 目標的三元組 <triplet> 值需要由維護者手動進行顯性設定。三元組 <triplet> 的值可由 $(DEB_HOST_MULTIARCH) 變數在 debian/rules 檔案中取得到，具體方法如下：

      DEB_HOST_MULTIARCH = $(shell dpkg-architecture -qDEB_HOST_MULTIARCH)
      ...
      override_dh_install:
      	mkdir -p package1/lib/$(DEB_HOST_MULTIARCH)
      	cp -dR tmp/lib/. package1/lib/$(DEB_HOST_MULTIARCH)

參見：

- “節 9.3, “Variables for debian/rules””
- “節 16.2, “debmake -b””
- “節 10.12, “拆包的場景和例子””
- “dpkg-architecture(1) manpage”

## 10.11. Debian 二進位制套件的拆分

對行為良好的構建系統來說，對 Debian 二進位制包的拆分可以由如下方式實現。

- 為所有二進位制套件在 debian/control 檔案中建立對應的二進位制套件條目。
- 在對應的 debian/二進位制套件名.install 檔案中列出所有檔案的路徑（相對於 debian/tmp 目錄）。

請檢視本指南中相關的例子：

- “節 14.11, “Autotools（多個二進位制套件）””（基於 Autotools）
- “節 14.12, “CMake（多個二進位制套件）””（基於 CMake）

An intuitive and flexible method to create the initial template debian/control file defining the split of the Debian binary packages is accommodated with the -b option. See “節 16.2, “debmake -b””.

## 10.12. 拆包的場景和例子

對於下面這樣的上游原始碼範例，我們在這裡給出使用 debmake 處理時一些典型的 multiarch 套件拆分的場景和做法：

- a library source libfoo-1.0.tar.xz
- a tool source bar-1.0.tar.xz written in a compiled language
- a tool source baz-1.0.tar.xz written in an interpreted language

二進位制套件 | 型別 | Architecture: | Multi-Arch: | 套件內容
--- | --- | --- | --- | ---
lib foo1 | lib* | any | same | 共享程式庫，可共同安裝
lib foo-dev | dev* | any | same | 共享程式庫標頭檔案及相關開發檔案，可共同安裝
lib foo-tools | bin* | any | foreign | 執行時支援程式，不可共同安裝
lib foo-doc | doc* | all | foreign | 共享程式庫文件
bar | bin* | any | foreign | 編譯好的程式檔案，不可共同安裝
bar-doc | doc* | all | foreign | 程式的配套文件檔案
baz | script | all | foreign | 解釋型程式檔案

## 10.13. Multiarch library path

Debian policy requires to comply with the “Filesystem Hierarchy Standard (FHS), version 3.0”, with the exceptions noted in “File System Structure”.

The most notable exception is the use of /usr/lib/<triplet>/ instead of /usr/lib<qual>/ (e.g., /lib32/ and /lib64/) to support a multiarch library.

表格 10.1. 多架構程式庫路徑選項

經典路徑 | i386 多體系架構路徑 | amd64 多體系架構路徑
--- | --- | ---
/lib/ | /lib/i386-linux-gnu/ | /lib/x86_64-linux-gnu/
/usr/lib/ | /usr/lib/i386-linux-gnu/ | /usr/lib/x86_64-linux-gnu/

對基於 Autotools 且由 debhelper （compat>=9）管理的套件來說，這些路徑設定已由 dh_auto_configure 命令自動處理。

對於其它使用不支援的構建系統的套件，您需要按照下面的方式手動調整安裝路徑。

- If “./configure” is used in the override_dh_auto_configure target in debian/rules, make sure to replace it with “dh_auto_configure --” while re-targeting the install path from /usr/lib/ to /usr/lib/$(DEB_HOST_MULTIARCH)/.
- 請在 debian/foo.install 檔案中將所有出現的 /usr/lib/ 字串替換為 /usr/lib/*/。

所有啟用多架構的套件安裝至相同路徑的檔案必須內容完全相同。您必須小心處理，避免資料位元組序或者壓縮演算法等等問題帶來的檔案內容差異。

位於預設路徑 /usr/lib/ 和 /usr/lib/<triplet>/ 的共享程式庫可被自動載入。

對位於其它路徑的共享程式庫，必須使用 pkg-config 命令設定 GCC 選項 -l 以正確進行載入。

## 10.14. Multiarch header file path

在支援多架構的 Debian 系統上，GCC 預設會同時包含、使用 /usr/include/ 和 /usr/include/<triplet>/ 下的標頭檔案。

如果標頭檔案不在這些路徑中，必須使用 pkg-config 命令設定 GCC 的 -I 引數以使得“#include <foo.h>”正常工作。

表格 10.2. 多架構標頭檔案路徑選項

經典路徑 | i386 多體系架構路徑 | amd64 多體系架構路徑
--- | --- | ---
/usr/include/ | /usr/include/i386-linux-gnu/ | /usr/include/x86_64-linux-gnu/
/usr/include/套件名/ | /usr/include/i386-linux-gnu/套件名/ | /usr/include/x86_64-linux-gnu/套件名/
| /usr/lib/i386-linux-gnu/套件名/ | /usr/lib/x86_64-linux-gnu/軟體包名/

為程式庫檔案使用 /usr/lib/<triplet>/套件名/ 路徑可幫助上游維護者對使用 /usr/lib/<triplet> 的多架構系統和使用 /usr/lib<qual>/ 的雙架構系統使用相同的安裝指令碼。[20]

使用包含 packagename 的檔案路徑也使得在同一系統上同時安裝多個架構的開發程式庫成為可能。

## 10.15. Multiarch *.pc file path

packagename 用來取得系統上已安裝程式庫的資訊。它在 *.pc 檔案中儲存配置引數，用來設定 GCC 的 -I 和 -l 選項。

表格 10.3. *.pc 檔案路徑選項

經典路徑 | i386 多體系架構路徑 | amd64 多體系架構路徑
--- | --- | ---
/usr/lib/pkgconfig/ | /usr/lib/pkgconfig/ | /usr/lib/x86_64-linux-gnu/pkgconfig/

## 10.16. 程式庫符號

Debian lenny（5.0，2009年5月）中引入的 dpkg 符號支援可以幫助我們管理同一共享鏈接程式庫套件的向後 ABI 相容性（backward ABI compatibility）。二進位制套件中的 DEBIAN/symbols 檔案提供了每個符號及其對應的最小版本號。

一個極其簡化的軟體程式庫打包流程大概如下所示。

- Extract the old DEBIAN/symbols file of the immediate previous binary package with the “dpkg-deb -e” command.

  - 或者，mc 命令也可以用來解壓得到 DEBIAN/symbols 檔案。

- 將其複製為 debian/binarypackage.symbols 檔案。

  - 如果這是第一次打包的話，可以只建立一個空檔案。

- 構建二進位制套件。

  - 如果 dpkg-gensymbols 命令警告添加了新的符號的話：

    - Extract the updated DEBIAN/symbols file with the “dpkg-deb -e” command.
    - 將其中的 Debian 修訂版本號，例如 -1，從檔案中去除。
    - 將其複製為 debian/binarypackage.symbols 檔案。
    - 重新構建二進位制套件。

  - 如果 dpkg-gensymbols 命令不報和新連結符號有關的警告：

    - 您已完成了共享程式庫的打包工作。

如需瞭解詳細資訊，您應當閱讀下列第一手參考資料。

- “8.6.3 The symbols system” of the “Debian Policy Manual”
- “dh_makeshlibs(1) manapage”
- “dpkg-gensymbols(1) manapage”
- “dpkg-shlibdeps(1) manapage”
- “deb-symbols(5) manapage”

您也應當檢視：

- Debian wiki: “UsingSymbolsFiles”
- Debian wiki: “Projects/ImprovedDpkgShlibdeps”
- Debian kde team: “Working with symbols files”
- “節 14.11, “Autotools（多個二進位制套件）””
- “節 14.12, “CMake（多個二進位制套件）””

| 提示
For C++ libraries and other cases where the tracking of symbols is problematic, follow “8.6.4 The shlibs system” of the “Debian Policy Manual”, instead. Please make sure to erase the empty debian/binarypackage.symbols file generated by the debmake command. For this case, the DEBIAN/shlibs file is used.

## 10.17. Library package name

Let’s consider that the upstream source tarball of the lib foo library is updated from lib foo-7.0.tar.xz to lib foo-8.0.tar.xz with a new SONAME major version which affects other packages.

The binary library package must be renamed from lib foo7 to lib foo8 to keep the unstable suite system working for all dependent packages after the upload of the package based on the lib foo-8.0.tar.xz.

| 警告
如果這個二進位制程式庫套件沒有得到更名，許多使用 unstable 套件的系統上的各個依賴該程式庫的套件會在新的程式庫包上傳後立刻破損，即便立刻請求進行 binNMU 上傳也無法避免這個問題。由於種種原因，binNMU 不可能在上傳後立刻開始進行，故無法緩解問題。

-dev 套件必須遵循以下命名規則：

- 使用不帶版本號的 -dev 套件名稱：lib foo-dev

  - 該情況通常適用於依賴關係處於葉節點的程式庫套件。

  - 倉庫內只允許存在一個版本的程式庫原始碼套件。

    - The associated library package needs to be renamed from lib foo7 to lib foo8 to prevent dependency breakage in the unstable suite during the library transition.
  - This approach should be used if the simple binNMU resolves the library dependency quickly for all affected packages. (ABI change by dropping the deprecated API while keeping the active API unchanged.)
  - This approach may still be a good idea if manual code updates, etc. can be coordinated and manageable within limited packages. (API change)

- 使用帶版本的 -dev 套件名稱：lib foo7-dev 和 lib foo8-dev

  - 該情況通常適用於各類重要程式庫套件。

  - 兩個版本的程式庫原始碼套件可同時出現在發行版倉庫中。

    - 令所有依賴該程式庫的套件依賴 lib foo-dev。
    - 令 lib foo7-dev 和 lib foo8-dev 兩者都提供 lib foo-dev。
    - The source package needs to be renamed as lib foo7-7.0.tar.xz and lib foo8-8.0.tar.xz respectively from lib foo-?.0.tar.xz.
    - 需要仔細選擇 lib foo7 和 lib foo8 套件檔案安裝時的路徑，如標頭檔案等等，以保證它們可以同時安裝。
  - 可能的話，不要使用這個重量級的、需要大量人為干預的方法。
  - This approach should be used if the update of multiple dependent packages require manual code updates, etc. (API change) Otherwise, the affected packages become RC buggy with FTBFS (Fails To Build From Source).

| 提示
如果包內資料檔案編碼方案有所變化（如，從 latin1 變為 utf-8），該場景應比照 API 變化做類似的考慮與處理。

參見 “節 10.9, “程式庫套件””。

## 10.18. 程式庫變遷

When you package a new library package version which affects other packages, you must file a transition bug report against the release.debian.org pseudo package using the reportbug command with the ben file and wait for the approval for its upload from the Release Team.

發行團隊提供了“變遷追蹤系統”。參見 “變遷（Transition）”。

| 注意
請確保您按照 “節 10.17, “Library package name”” 的描述正確地對二進位制套件進行了重命名。

## 10.19. binNMU 安全

A “binNMU” is a binary-only non-maintainer upload performed for library transitions etc. In a binNMU upload, only the “Architecture: any” packages are rebuilt with a suffixed version number (e.g. version 2.3.4-3 will become 2.3.4-3+b1). The “Architecture: all” packages are not built.

The dependency defined in the debian/control file among binary packages from the same source package should be safe for the binNMU. This needs attention if there are both “Architecture: any” and “Architecture: all” packages involved in it.

- “Architecture: any” package: depends on “Architecture: any” foo package

  - Depends: foo (= ${binary:Version})

- “Architecture: any” package: depends on “Architecture: all” bar package

  - Depends: bar (= ${source:Version})

- “Architecture: all” package: depends on “Architecture: any” baz package

  - Depends: baz (>= ${source:Version}), baz (<< ${source:Version}.0~)

## 10.20. 除錯資訊

The Debian package is built with the debugging information but packaged into the binary package after stripping the debugging information as required by “Chapter 10 - Files” of the “Debian Policy Manual”.

參見

- “6.7.9. Best practices for debug packages” of the “Debian Developer’s Reference”.
- “18.2 Debugging Information in Separate Files” of the “Debugging with gdb”
- “dh_strip(1) manapage”
- “strip(1) manapage”
- “readelf(1) manapage”
- “objcopy(1) manapage”
- Debian wiki: “DebugPackage”
- Debian wiki: “AutomaticDebugPackages”
- Debian debian-devel 列表釋出的郵件：“自動除錯套件狀態” (2015-08-15)

## 10.21. -dbgsym package

除錯資訊由 dh_strip 命令的預設行為自動打包並進行移除。所分離得到的除錯套件名具有 -dbgsym 的字尾。

- debian/rules 檔案不應顯性包括 dh_strip 。
- Set the Build-Depends to debhelper-compat (>=14) while removing Build-Depends to debhelper in debian/control.

## 10.22. debconf

debconf 套件可以幫助我們在下列兩種情況下配置套件：

- 在 debian-installer（Debian 安裝程式）預安裝時進行非互動式配置。

- interactively from the menu interface (dialog, gnome, kde, …​)

  - 套件安裝時：由 dpkg 命令呼叫
  - 對已安裝套件：由 dpkg-reconfigure 命令呼叫

套件安裝時的所有使用者互動都必須由這裡的 debconf 系統進行處理，下列配置檔案對這個過程進行控制。

- debian/binarypackage.config

  - 這是 debconf config 指令碼，用於對使用者詢問對於配置套件必需的問題。

- debian/binarypackage.template

  - 這是 debconf templates（模板）檔案，用於對使用者詢問對於配置套件必需的問題。

These debconf files are called by package configuration scripts in the binary Debian package

- DEBIAN/binarypackage.preinst
- DEBIAN/binarypackage.prerm
- DEBIAN/binarypackage.postinst
- DEBIAN/binarypackage.postrm

See dh_installdebconf(1), debconf(7), debconf-devel(7) and “3.9.1 Prompting in maintainer scripts” in the “Debian Policy Manual”.

[18] 該文件是在 symbols 檔案被引入之前寫成的。

[19] 在“第六章 - 開發（-DEV）套件”中，存在強烈的使用含有 SONAME 版本號的 -dev 套件名而非僅使用 -dev 作為名稱的偏好，但前 ftp-master 成員（Steve Langasek）對此有不同意見。請注意該文件在 multiarch 系統和 symbols 引入之前寫成，可能有一定程度的過時。

[20] This path is compliant with the FHS. “Filesystem Hierarchy Standard: /usr/lib : Libraries for programming and packages” states “Applications may use a single subdirectory under /usr/lib. If an application uses a subdirectory, all architecture-dependent data exclusively used by the application must be placed within that subdirectory.”

章 9. More on packaging |  | 章 11. Packaging with git