創建軟體包
本文旨在幫助用戶利用類似於 ports 的 Arch 構建系統來創建自己的軟體包,以及如何提交到 Arch 用戶軟體倉庫。本文講述了如何創建 PKGBUILD(5),這是一個描述如何構建軟體包的文件,makepkg 會使用它來從原始碼創建二進制軟體包。
關於構建規則和提高軟體包質量的方法,請參考 Arch 打包準則。
Arch Linux 中的軟體包是通過 makepkg 工具以及存儲在 PKGBUILD 文件中的信息編譯的。運行 makepkg 時,它會自動在當前目錄下搜索 PKGBUILD 文件,然後根據其中的信息下載文件,按需把軟體源碼重新編譯打包為一個 pkgname.pkg.tar.zst 軟體包文件。軟體包文件中包含了 pacman 安裝需要的二進制文件和安裝指令。
一個 Arch 軟體包就是一個使用 zstd(1) 壓縮的 tar(1) 壓縮包,或者叫 tarball。它包含了以下由 makepkg 生成的文件:
- 要安裝的二進制文件;
-
.PKGINFO: 包含所有 pacman 處理軟體包所需的元數據、依賴等等;
-
.BUILDINFO: 包含可復現編譯需要的信息,參閱 BUILDINFO(5) ;
-
.MTREE: 包含了文件的哈希值與時間戳,pacman 能夠根據這些儲存在本地資料庫的信息校驗軟體包的完整性;
-
.INSTALL: 可選的文件,可以用來在安裝/升級/刪除操作之後運行命令。(本文件只有在PKGBUILD中制定才會存在);
-
.Changelog: 一個可選的文件,保存了包管理員描述軟體變更的日誌。
首先,確定你已安裝必須的工具包。安裝 base-devel包 元軟體包應當足夠;它會引入 make(1) 和其它一些編譯源碼時需要的工具。
創建包的一個關鍵工具是 makepkg(由 pacman包 提供),它的功能請參閱 Arch 打包準則#Makepkg 的任務。
下載你想打包的軟體的原始碼壓縮包,解壓,按照作者所說的步驟安裝它。記錄下在編譯和安裝軟體過程中需要的所有命令或步驟。你將要在 PKGBUILD 文件中重複這些命令和步驟。
大多數軟體作者遵循三步走的安裝慣例:
$ ./configure $ make # make install
建議在這時測試軟體的功能是否正常。
建議參考創建一個乾淨的 chroot 來保證你的系統中的其它軟體包和配置不會影響 PKGBUILD。這個方法容錯性更強,是一個更加恰當的構建軟體包的方法,通常能夠發現那些你未曾意識到需要的依賴項,因為它們已經在你的系統中存在。
當你運行 makepkg 時,它會在當前工作目錄尋找一個 PKGBUILD 文件。如果找到 PKGBUILD 文件,它會下載該軟體的原始碼,根據 PKGBUILD 文件中的指令編譯它。PKGBUILD 中的指令必須能完全被 Bash 解釋。
編譯成功後,最後的二進制文件和包的元信息(即包的版本、依賴)被一起打包在 pkgname.pkg.tar.zst 軟體包文件中,這個文件包可以通過 makepkg --install 調用 pacman 或直接使用 pacman -U <package file> 來安裝。
要開始製作一個包,你應該先創建一個空工作目錄,進入該目錄,創建一個 PKGBUILD 文件。你可以複製 PKGBUILD 模板(位於 /usr/share/pacman/)到工作目錄,或者複製一個類似包的 PKGBUILD 也可以。如果你只想在別人的基礎上更改一些選項的話,後一種方法比較方便。
makepkg 預定義了以下變量,打包者需使用它們指代構建過程的臨時路徑:
srcdir- makepkg將會把源文件解壓到此文件夾,或在此文件夾中生成指向 source 數組中文件的軟連接。
pkgdir- makepkg會把該文件夾當成系統根目錄,並將軟體安裝在此文件夾下。
這些變量都是絕對路徑,即意味著只要你合適地使用這些變量,就不用擔心當前工作目錄的影響.
build() 和 package() 函數在運行過程中都應當是非交互的。在這些函數中調用交互工具或腳本可能會中斷 makepkg 的運行,特別是啟用了構建日誌的時候(--log)。具體信息請參考 FS#13214。在構建軟體包時,makepkg 會在 PKGBUILD 中有定義的情況下調用以下五個函數。PKGBUILD 中必須包含 package() 函數並總會被調用。對於其它函數,如果其未被定義,那麼 makepkg 會將其略過。
在構建時,函數將按下文列出的順序執行。
另請參考 PKGBUILD(5) § PACKAGING FUNCTIONS。
此函數會執行用於預處理源文件以進行構建的命令,例如 patching。此函數執行在 pkgver() 之前,軟體包解壓之後。如果解壓過程被跳過(makepkg --noextract),那麼 prepare() 函數就不會被執行。
bash -e 模式下,意味著任何以非零狀態退出的命令都會造成該函數中止.如果不清楚應該將操作放到 prepare() 還是 build(),只需要記得 prepare() 中的步驟應在解壓後僅執行一次,而 build() 中的步驟應在解壓後文件被修改時都執行一次。
pkgver() 會在抓取並解壓源文件,執行 prepare() 後執行此函數,可用於在 makepkg 階段中更新 pkgver 變量。
為使用 git/svn/hg 等工具的項目打包時,由於它們的構建過程相同,但源文件可能每天甚至每小時更新一次,這一特性將會十分有用。過去的方法是把日期寫入到 pkgver 變量中,但這樣一來 makepkg 會在即使軟體沒有更新的情況下依然重新構建軟體包,因為它會認為軟體包的版本改變了。其他與此有關的命令有 git describe,hg identify -ni 等等。請在提交 PKGBUILD 前做好測試,因為如果 pkgver() 執行失敗,整個構建過程都會終止。
-),通常可以用 sed 來進行修正。現在你需要編寫 PKGBUILD 文件中的 build() 函數。這個函數使用 Bash 語法的通用 shell 命令來自動編譯軟體,並創建名為 pkg 的軟體安裝目錄。這允許 makepkg 無需詳查你的文件系統就可以打包你的軟體。
在 build() 函數中第一步就是進入由解壓源碼包所生成的目錄。makepkg 會在執行 build() 函數之前將當前活動目錄設為為 $srcdir;因此,大多數情況下第一條命令是這樣的:(參考示例文件 /usr/share/pacman/PKGBUILD.proto):
cd "$pkgname-$pkgver"
現在,你需要把你當時手動編譯軟體時用到的命令一一列上。build() 基本上會自動運行你當時手動輸入的命令,並在偽 root 環境下編譯該軟體。如果要打包的軟體使用了一個配置腳本,最好在配置中加上 --prefix=/usr。許多軟體都將自己安裝到 /usr/local 下,但你只應在手動從源碼安裝時這麼做。所有的 Arch Linux 軟體包都應當使用 /usr 目錄。文件中接下來的兩行應當如下(參考 /usr/share/pacman/PKGBUILD.proto):
./configure --prefix=/usr make
build() 函數,但 package() 函數依然是必須的。用於執行 make check 和其它一些例行測試。強烈建議使用 check() 確保軟體正常構建,並能搭配依賴正常使用。
如果不需要或是維護者無法使軟體包通過測試,可以通過在 PKGBUILD/makepkg.conf(5) 中的 options 數組添加 !check,或是給 makepkg 傳入參數 --nocheck 來禁用它。
如果要測試的是 GUI 圖形化應用,可以在虛擬 xserver 中運行測試。
最後一步是將編譯好的文件放到一個目錄下,讓 makepkg 可以獲取並打包為軟體包。默認情況下該目錄為 pkg —— 一個簡單的偽 root 環境。pkg 目錄複製了根目錄下軟體安裝路徑的繼承關係,如果你需要手動把文件放到根目錄下,那麼需要把文件放在 pkg 下相同的文件層級結構中。比如,你想把一個文件安裝到 /usr/bin,那麼在偽 root 環境中對應的路徑為 $pkgdir/usr/bin。安裝步驟通常不需要用戶手動複製大量文件到某個地方,而只需調用 make install 即可。為了將軟體正確地安裝到 pkg 路徑下,最後一行一般應該這樣寫:
make DESTDIR="$pkgdir" install
Makefile 裡沒有使用DESTDIR;你可能需要使用 prefix 來替代。如果軟體包是用 autoconf/automake 來構建的,那就需根據文檔使用 DESTDIR;如果 DESTDIR 不起作用,可以試試 make prefix="$pkgdir/usr" install。如果這還不起作用的話,你就需要深入檢查 make <...> install 具體執行的安裝命令了。makepkg --repackage 命令只運行 package() 函數,它只是將文件打包成軟體包,並不運行編譯過程。如果你只是更改了 depends 依賴變量,用這個命令來打包可以節省很多時間。
mv 的命令從 $srcdir 移動由 build() 生成的文件到 $pkgdir 會破壞 makepkg 的 --repackage 選項。在編寫 build() 函數時,需要頻繁測試更改以確保沒有 bug。你可以在包含 PKGBUILD 的目錄下運行 makepkg 命令來確保沒有問題。如果 PKGBUILD 沒有錯誤,將會生成一個軟體包;但是如果 PKGBUILD 有問題或未完成,它將拋出一個錯誤。
如果運行 makepkg 成功,在你的工作目錄下將會生成一個名為 pkgname-pkgver.pkg.tar.zst 的新文件,可以使用 pacman -U 進行安裝。注意,成功生成了軟體包不代表軟體能正常運行,它有可能僅包含有空目錄,也可能包含如不正確的前綴等問題。你可以使用 pacman 的 pacman -Qlp pkgname 查詢軟體包的文件清單,以及通過 pacman -Qip pkgname 檢查依賴項是否正確。
如果包看起來是正確的,那你的工作就完成了。但是如果你打算發布這個 PKGBUILD 文件,你就需要確認確認再確認 depends 數組中的依賴是否正確。
另外也要確保包二進制文件完全正常地運行!如果發布出去的軟體包已涵蓋所有必要文件,但僅因某些選項與其他系統不兼容而崩潰會非常令人討厭。如果你只是為自己的系統編譯這個軟體,就不必做這個質量保證了,因為只有你一個人需要忍受這些錯誤。
確定包可以正常使用後,再使用namcap來檢查錯誤:
$ namcap PKGBUILD $ namcap pkgname.pkg.tar.zst
Namcap 將會做以下工作:
- 檢查
PKGBUILD裡的一些常見錯誤,以及包文件結構是否有不必要或錯位的文件 - 用
ldd掃描包中所有的 ELF 文件,自動報告depends中缺失或可去除的依賴 - 啟發式搜尋缺失或冗餘的依賴
等等。
要養成用 namcap 檢查包的習慣,以避免提交包後再做修復的麻煩。
You can use pkgctl from devtools包 to check if the package can be built where no other packages are already installed. While in the PKGBUILD directory:
$ pkgctl build
And check the output for potential errors or warnings. If the package depends on other AUR packages, those packages must be built and brought into chroot jail:
$ pkgctl build -I path/to/somepkg.tar.gz -I ...
Refer to pkgctl-build(1) for more options.
請參考 AUR 提交準則,裡面詳細介紹了提交流程。
- 下載你希望打包的軟體原始碼
- 試著編譯安裝包到任意目錄
- 複製文件模板
/usr/share/pacman/PKGBUILD.proto到一個臨時目錄,並重命名為PKGBUILD - 根據需要修改
PKGBUILD文件 - 運行
makepkg看看輸出的打包結果是否正確 - 如果不正確,重複前兩個步驟
- 除非你「很清楚」你正在做什麼,否則在開始自動打包之前,需確保你至少已成功手動打包一次。不幸的是,雖然大多數軟體作者遵循了三步走的安裝慣例:
./configure;make;make install,但事情並不都是這樣的,有時候你不得不自己打補丁才能安裝成功。經驗是:如果你手動無法編譯成功或者無法將軟體安裝到指定子目錄下,那你就不必費心打包了。makepkg沒有任何魔力能消除原始碼的問題讓你編譯成功。
- 在少數情況下,軟體包不提供源碼,而只能使用類似
sh installer.run的命令進行安裝,這時就需要你自己做很多工作了(比如查看 README,安裝指導,手冊,或者 Gentoo 或其它軟體包安裝器的 ebuild 等等)。在一些很變態的情況下,你需要自己編輯源碼才能正常安裝。但是,makepkg需要完全自主運行,不能有用戶的干預。因此,如果你想修改 makefiles,需要隨PKGBUILD附上一個定製的補丁,然後在prepare()函數裡安裝這個補丁;或者你可以在prepare()函數裡通過sed來修改。
可以使用 updpkgsums 工具簡化為新版本軟體更新校驗和的工作,具體信息請參考 Makepkg#生成新校驗和。
某些軟體包的 PKGBUILD 可以通過工具自動生成。
- Haskell: cblrepo,arch-hs包
- Node.js: nodejs-npm2archAUR
- Perl: perl-cpanplus-dist-arch包
- Python: pipman-gitAUR,pip2arch-gitAUR,python-pypi2pkgbuildAUR
- Ruby: gem2archAUR,pacgemAUR
- Rust: cargo-pkgbuildAUR,cargo-aur-binAUR
pkgctl (from the devtools包 package) supports nvchecker integration in the form of a .nvchecker.toml configuration file (which should be placed in the same directory as the PKGBUILD). See the pacman package's .nvchecker.toml configuration file for an example.
pkgctl version setup will attempt to create the .nvchecker.toml configuration file automatically by analyzing the source array specified in the PKGBUILD.You can then use pkgctl version check to check if a new upstream version has been released (compared to the one specified as pkgver in the PKGBUILD) and pkgctl version upgrade to update the PKGBUILD accordingly. See pkgctl-version(1) for more details.