附錄 A. 高級打包

# 附錄 A. 高級打包

本教程文件已被重寫為另外的 Debian 維護者指導 文件，其中包含了更新的內容與更多實際例子。請使用新的教程作為主要的教程文件。

這裡有一些關於你可能遇到的高階打包問題的提示。如果有需要的話，本教程強烈建議閱讀這裡引用和建議的文件。

你可能需要手工編輯由 dh_make 命令生成的打包模板文件，以此來解決本章中所討論的問題。 新的 debmake 命令應該能更好地解決這些問題。

## A.1. 共享庫

在打包 共享庫 之前，你 應該閱讀以下的主要參考資料：

- Debian Policy Manual, 8 "Shared libraries"

- Debian Policy Manual, 9.1.1 "File System Structure"

- Debian Policy Manual, 10.2 "Libraries"

以下是幫助你開始的極簡解釋：

- 共享庫均爲 elf 對象文件，其包含編譯好的機器碼。

- 共享庫以 `*.so` 文件的形式發放。(既非 `*.a` 文件也非 `*.la` 文件)

- 共享庫主要用於在不同的二進制可執行程序之間共享代碼，這背後使用了 ld （譯註：鏈接）機制。

- 共享庫有時會爲一個可執行程序提供多個插件，這背後使用了 dlopen 機制。

- 共享庫能匯出代表著變數，函式和類的 symbols（符號）；並允許連結到它的可執行檔案訪問之。

- 共享庫 `lib`foo`.so`.`1` 中的 SONAME : `objdump -p lib`foo`.so.`1` | grep SONAME` [87]

- 共享庫的 SONAME 常常與庫文件自身文件名一致(不過有特例)。

- 鏈接到 ``/usr/bin/foo`` 的共享庫的 SONAME ： `objdump -p `/usr/bin/foo` | grep NEEDED` [88]

- `lib`foo``1``: 共享庫 `lib`foo`.so.`1`` 的庫文件包，其 SONAME ABI 版本爲 `1`.[89]

- 在某些情況下，庫軟件包的 maintainer scripts 必須調用 ldconfig 來爲 SONAME 創建 必要的符號鏈接。[90]

- `lib`foo``1`-dbg`: 包含了除錯共享庫包用的除錯符號的軟體包 `lib`foo``1``.

- `lib`foo`-dev`: 包含了標頭檔案等內容的開發包。用於 `lib`foo`.so.`1``.[91]

- 一般而言，Debian 軟體包不應當包含 `*.la` Libtool 歸檔檔案。 [92]

- 一般來說，Debian 軟體包不應當使用 RPATH。[93]

- 雖然這有點過時，而且是第二參考， Debian Library Packaging Guide 可能仍然對你有用。

## A.2. 管理 `debian/`package`.symbols`

當你給共享庫打包時，你應當建立 `debian/`package`.symbols` 檔案來管理在共享庫名稱不變，在同一個 SONAME 下又要提供 ABI 向後相容性的情況下每個符號關聯到的最小版本號。 [94] 你可以閱讀下邊的主要參考以獲知細節：

- Debian Policy Manual, 8.6.3 "The symbols system"[95]

- dh_makeshlibs(1)

- dpkg-gensymbols(1)

- dpkg-shlibdeps(1)

- deb-symbols(5)

這是個粗略的例子，用來來演示建立 `libfoo1` 軟體包的方法，此時使用上游 `1.3` 版本，有著妥當的`debian/libfoo1.symbols` 檔案：

- 使用上游提供的 `libfoo-1.3.tar.gz` 文件來準備 Debian化 的源碼骨架。

  - 如果這是庫軟件包 `libfoo1` 的第一次打包，那麼以空內容創建 `debian/libfoo1.symbols` 文件。

  - 如果之前的上游版本 `1.2` 已經被 `libfoo1` 軟件包打包了，並且其源碼包中有妥當的 `debian/libfoo1.symbols`， 再用它一次。

  - 如果前一個上游 `1.2` 版本打包時沒有 `debian/libfoo1.symbols`，那就從具有相同庫 SONAME 的同一個共享庫包的所有可用的二進位制軟體包中建立它並命名為 `symbols` 檔案。比如 `1.1-1` 和 `1.2-1`。 [96]

        $ dpkg-deb -x libfoo1_1.1-1.deb libfoo1_1.1-1
        $ dpkg-deb -x libfoo1_1.2-1.deb libfoo1_1.2-1
        $ : > symbols
        $ dpkg-gensymbols -v1.1 -plibfoo1 -Plibfoo1_1.1-1 -Osymbols
        $ dpkg-gensymbols -v1.2 -plibfoo1 -Plibfoo1_1.2-1 -Osymbols

- 嘗試用像 debuild 和 pdebuild 這樣的工具來對原始碼樹進行試構建。 (如果這因為缺失符號之類原因而失敗，那麼這裡就有一些不向後相容的 ABI 改變，這就需要你轉移(bump)共享庫的名稱到諸如 `libfoo1a` ，並重新開始一次。）

      $ cd libfoo-1.3
      $ debuild
      ...
      dpkg-gensymbols: warning: some new symbols appeared in the symbols file: ...
       see diff output below
      --- debian/libfoo1.symbols (libfoo1_1.3-1_amd64)
      +++ dpkg-gensymbolsFE5gzx        2012-11-11 02:24:53.609667389 +0900
      @@ -127,6 +127,7 @@
        foo_get_name@Base 1.1
        foo_get_longname@Base 1.2
        foo_get_type@Base 1.1
      + foo_get_longtype@Base 1.3-1
        foo_get_symbol@Base 1.1
        foo_get_rank@Base 1.1
        foo_new@Base 1.1
      ...

- 如果你如上述看見由 dpkg-gensymbols 命令打印出來的差異， 那就從生成的二進位制庫包中抽取妥當更新的 `symbols` 檔案。 [97]

      $ cd ..
      $ dpkg-deb -R  libfoo1_1.3_amd64.deb libfoo1-tmp
      $ sed -e 's/1\.3-1/1\.3/' libfoo1-tmp/DEBIAN/symbols \
              >libfoo-1.3/debian/libfoo1.symbols

- 使用像 debuild 和 pdebuild 這樣的工具來構建發行軟件包。

      $ cd libfoo-1.3
      $ debuild -- clean
      $ debuild
      ...

對上邊這個例子補充一點，我們需要進一步檢查 ABI (應用程序二進制接口) 兼容性 並在需要的時候手動更新一些符號的版本。 [98]

雖然這只是第二參考， Debian wiki UsingSymbolsFiles 和它指向的頁面可能會有所幫助。

## A.3. 多體繫結構

Debian wheezy 引入的多體繫結構特性，集成了對 二進制包跨體繫結構安裝的支持 (尤其是 `i386`<->`amd64`，其他的組合也有) 於 `dpkg` 和 `apt` 中。你可以閱讀下邊的參考：

- Ubuntu wiki MultiarchSpec (上游)

- Debian wiki Multiarch/Implementation (Debian 的局勢)

它爲每個共享庫的安裝路徑使用了類似 `i386-linux-gnu` 和 `x86_64-linux-gnu` 這樣的三元名字。實際上每個二進制軟件包構建的三元路徑是被動態設置到 `$(DEB_HOST_MULTIARCH)` 變量中的，經由 dpkg-architecture(1) 命令。舉個例子， 安裝多體繫結構庫文件的路徑被按照下表進行了修改：[99]

舊路徑 | 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/`

下面是一些典型的多體系結構軟體包分離情景：

- 庫源碼 `lib`foo`-1.tar.gz`

- 一個用編譯型語言編寫的工具的源碼 ``bar`-1.tar.gz`

- 一個用解釋型語言編寫的工具的源碼 ``bar`-1.tar.gz`

軟件包 | 體繫結構： | 多體繫結構： | 軟件包內容
--- | --- | --- | ---
`lib`foo``1`` | 任何 | 相同 | 共享庫，可共同安裝
`lib`foo``1`-dbg` | 任何 | 相同 | 共享庫調試符號，可共同安裝
`lib`foo`-dev` | 任何 | 相同 | 共享庫頭文件之類，可共同安裝
`lib`foo`-tools` | 任何 | 外來 | 運行時支持程序，不可共同安裝
`lib`foo`-doc` | 全部 | 外來 | 共享庫文檔
``bar`` | 任何 | 外來 | 編譯好的程序文件，不可共同安裝
``bar`-doc` | 全部 | 外來 | 程序的配套文檔文件
``baz`` | 全部 | 外來 | 解釋型程序文件

請注意，開發軟件包應該包含一個指向共享庫的符號鏈接並且 不帶有版本號。 比如： `/usr/lib/x86_64-linux-gnu/libfoo.so` -> `libfoo.so.1`

## A.4. 構建共享庫包

你可以用 dh(1) 透過以下方法構建一個支援多體系結構的 Debian 庫軟體包：

- 更新 `debian/control`。

  - 為原始碼包部分新增 `Build-Depends: debhelper (>=10)` 部分。

  - 爲每個二進制庫軟件包添加 `Pre-Depends: ${misc:Pre-Depends}` 。

  - 在每個二進制包的段中添加 `Multi-Arch:` 小節。

- 設定 `debian/compat` 為"10"。

- 將所有打包腳本中的路徑從普通的 `/usr/lib/` 調整到 多體繫結構的 `/usr/lib/$(DEB_HOST_MULTIARCH)/`。

  - 首先，在 `debian/rules` 中呼叫 `DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH)` 以設定 `DEB_HOST_MULTIARCH` 變數。

  - 在 `debian/rules` 中用 `/usr/lib/$(DEB_HOST_MULTIARCH)/` 替換 `/usr/lib/`。

  - 如果 `debian/rules` 檔案中的 `override_dh_auto_configure` 目標使用了 `./configure` 檔案，那麼請確認用 `dh_auto_configure --` 來替換它。 [100]

  - 在`debian/`foo`.install` 文件中將所有 `/usr/lib/` 的事件替換爲 `/usr/lib/*/` 。

  - 如需從 `debian/`foo`.links.in` 動態地生成像 `debian/`foo`.links` 這樣的檔案，可以新增一個指令碼到 `debian/rules` 檔案的 `override_dh_auto_configure` 目標中。

        override_dh_auto_configure:
                dh_auto_configure
                sed 's/@DEB_HOST_MULTIARCH@/$(DEB_HOST_MULTIARCH)/g' \
                        debian/foo.links.in > debian/foo.links

請確認該共享庫軟件包僅僅包含預期中的文件，並且你的 -dev 軟件包還奏效。

所有作為多體系結構軟體包而同時安裝到同一個檔案路徑的所有檔案應當具有完全一致的檔案內容。你必須小心由資料位元組序和壓縮演算法造成的區別。

## A.5. Debian 本土軟件包

如果一個軟件包是僅僅爲 Debian 維護的，或者是可能的本地使用，那麼它的源碼可以容納所有的 `debian/*` 於其中。這裏有它的兩種打包方式。

你可以將除 `debian/*` 檔案之外的部分製作成上游 tarball，然後將其作為非本土 Debian 軟體包來打包，正如 節 2.1, “Debian 軟件包構建流程” 所述。 這是一些人鼓勵使用的普通方法。

另一種方法就是本土 Debian 軟件包的打包工作流。

- 用包含所有文件的，單一壓縮過的 tar 文件，以 `3.0 (native)` 格式來創建本土 Debian 源碼包。

  - ``package`_`version`.tar.gz`
  - ``package`_`version`.dsc`

- 用 Debian 本土源碼包構建二進制包

  - ``package`_`version`_`arch`.deb`

比如說，如果你的原始碼檔案都存放在 `~/mypackage-1.0` 中，而且沒有 `debian/*` 這些檔案，那麼你可以用它建立 一個本土 Debian 軟體包，只要按照下邊的方法使用 dh_make 命令：

    $ cd ~/mypackage-1.0
    $ dh_make --native

接下來 `debian` 目錄和它的內容都會被建立， 正如 節 2.8, “初始化外來 Debian 軟件包” 中那樣。 這不會建立一個 tarball，因為這是個本土 Debian 軟體包。不過這也是唯一的區別。 剩下的打包操作就是完全一致的了。

在執行了 dpkg-buildpackage 命令後，你將會在上一級目錄中看到這些文件：

- `mypackage_1.0.tar.gz`

  這是 dpkg-source 命令用 `mypackage-1.0` 目錄創建出來的源代碼 tarball 。 (它的 文件名後綴不是 `orig.tar.gz`)

- `mypackage_1.0.dsc`

  這是對原始碼內容的簡述，正如在非本土 Debian 軟體包中那樣。（沒有 Debian 修訂號。）

- `mypackage_1.0_i386.deb`

  這是完成的二進位制包，正如在非本土 Debian 軟體包中那樣。（沒有 Debian 修訂號。）

- `mypackage_1.0_i386.changes`

  這個文件描述了這個軟件作爲外來 Debian 包，在當前版本所作出的所有更改。（沒有 Debian 修訂）

[87] 或者這樣： `readelf -d lib`foo`.so.`1` | grep SONAME`

[88] 或者這樣： `readelf -d lib`foo`.so.`1` | grep NEEDED`

[89] 參見 Debian Policy Manual, 8.1 "Run-time shared libraries".

[90] 參見 Debian Policy Manual, 8.1.1 "ldconfig".

[91] 參見 Debian Policy Manual, 8.3 "Static libraries" and Debian Policy Manual, 8.4 "Development files".

[92] 參見 Debian wiki ReleaseGoals/LAFileRemoval.

[93] 參見 Debian wiki RpathIssue.

[94] 向後不兼容的 ABI 變更常常需要你更新共享庫的 SONAME，並把共享庫名稱換成新的。

[95] 對於 C++ 庫和其他追蹤單個符號過於困難的情況下，請遵循 Debian Policy Manual, 8.6.4 "The shlibs system"。

[96] 所有先前的 Debian 軟件包版本都能在 http://snapshot.debian.org/ 找到。不過 Debian 修訂號 被去掉了，以使軟件包的 backport 更爲容易： `1.1` << `1.1-1~bpo70+1` << `1.1-1` and `1.2` << `1.2-1~bpo70+1` << `1.2-1`

[97] Debian 修訂號已被從版本中去掉，這能讓軟件包的 backport 更爲容易： `1.3` << `1.3-1~bpo70+1` << `1.3-1`

[98] 參見 Debian Policy Manual, 8.6.2 "Shared library ABI changes".

[99] 老舊且具有特殊用途的庫路徑，比如 `/lib32/` 和 `/lib64/` 已不再使用。

[100] 作爲替代，你可以添加 `--libdir=\$${prefix}/lib/$(DEB_HOST_MULTIARCH)` 和 `--libexecdir=\$${prefix}/lib/$(DEB_HOST_MULTIARCH)` 參數到 `./configure` 後頭。 請注意 `--libexecdir` 指定了 安裝可執行程序(它們被其他程序使用，而更少是用戶)的默認路徑。它的 Autotools 默認設置爲 `/usr/libexec/` 但是 Debian 的設置爲 `/usr/lib/`。

章 9. 上傳套件 |  |