附錄 A. 高級打包

內容目錄

A.1. 共享庫
A.2. 管理 debian/package.symbols
A.3. 多體繫結構
A.4. 構建共享庫包
A.5. Debian 本土軟件包

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

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

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

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

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

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

  • 共享庫主要用於在不同的二進制可執行程序之間共享代碼,這背後使用了 ld (譯註:鏈接)機制。

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

  • 共享庫能導出代表着變量,函數和類的 symbols (符號); 並讓鏈接到它的可執行文件訪問。

  • 共享庫 libfoo.so.1 中的 SONAME : objdump -p libfoo.so.1 | grep SONAME [89]

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

  • 鏈接到 /usr/bin/foo 的共享庫的 SONAME : objdump -p /usr/bin/foo | grep NEEDED [90]

  • libfoo1: 共享庫 libfoo.so.1 的庫文件包,其 SONAME ABI 版本爲 1.[91]

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

  • libfoo1-dbg: 包含了調試共享庫包用的調試符號的軟件包 libfoo1.

  • libfoo-dev: 包含了頭文件等內容的開發包。用於 libfoo.so.1.[93]

  • 一般而言,Debian 軟件包不應當包含 *.la Libtool 歸檔文件。 [94]

  • 一般來說,Debian 軟件包不應當使用 RPATH 。[95]

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

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

這是個粗略的例子來演示創建 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-11.2-1[98]

      $ 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
      
  • 嘗試用像 debuildpdebuild 這樣的工具來對源碼樹進行試構建。 (如果這因爲缺失符號之類原因而失敗,那麼這裏就有一些不向後兼容的 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 文件。 [99]

    $ 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
    
  • 使用像 debuildpdebuild 這樣的工具來構建發行軟件包。

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

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

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

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

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

下面是一些典型的多體繫結構軟件包分離情景:

  • 庫源碼 libfoo-1.tar.gz

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

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

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

你可以用 dh(1) 通過以下方法構建一個支持多體繫結構的 Debian 庫軟件包:

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

所有作爲多體繫結構軟件包而同時安裝到同一個文件路徑的所有文件應當具有完全一致的文件內容。你必須小心由 數據字節序 和 壓縮算法 造成的區別。

如果一個軟件包是僅僅爲 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 修訂)



[89] 或者這樣: readelf -d libfoo.so.1 | grep SONAME

[90] 或者這樣: readelf -d libfoo.so.1 | grep NEEDED

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

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

[98] 所有先前的 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

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

[101] 老舊且具有特殊用途的庫路徑,比如 /lib32//lib64/ 不再被使用。

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