本文旨在帮助用户利用类似于 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
建议参考创建一个干净的 chroot 来保证你的系统中的其它软件包和配置不会影响 PKGBUILD
。这个方法容错性更强,是一个更加恰当的构建软件包的方法,通常能够发现那些你未曾意识到需要的依赖项,因为它们已经在你的系统中存在。
创建 PKGBUILD
当你运行 makepkg
时,它会在当前工作目录寻找一个 PKGBUILD
文件。如果找到 PKGBUILD
文件,它会下载该软件的源代码,根据 PKGBUILD
文件中的指令编译它。PKGBUILD 中的指令必须能完全被 Bash 解释。
编译成功后,最后的二进制文件和包的元信息(即包的版本、依赖)被一起打包在 pkgname.pkg.tar.zst
软件包文件中,这个文件包可以通过 makepkg --install
调用 pacman 或直接使用 pacman -U <package file>
来安装。
要开始制作一个包,你应该先创建一个空工作目录,进入该目录,创建一个 PKGBUILD
文件。你可以复制 PKGBUILD
模板(位于 /usr/share/pacman/)到工作目录,或者复制一个类似包的 PKGBUILD
也可以。如果你只想在别人的基础上更改一些选项的话,后一种方法比较方便。
PKGBUILD 变量
makepkg 预定义了以下变量,打包者需使用它们指代构建过程的临时路径:
srcdir
- makepkg将会把源文件解压到此文件夹,或在此文件夹中生成指向 source 数组中文件的软连接。
pkgdir
- makepkg会把该文件夹当成系统根目录,并将软件安装在此文件夹下。
这些变量都是绝对路径,即意味着只要你合适地使用这些变量,就不用担心当前工作目录的影响.
build()
和 package()
函数在运行过程中都应当是非交互的。在这些函数中调用交互工具或脚本可能会中断 makepkg 的运行,特别是启用了构建日志的时候(--log
)。具体信息请参考 FS#13214。PKGBUILD 函数
在构建软件包时,makepkg
会在 PKGBUILD
中有定义的情况下调用以下五个函数。PKGBUILD
中必须包含 package()
函数并总会被调用。对于其它函数,如果其未被定义,那么 makepkg
会将其略过。
在构建时,函数将按下文列出的顺序执行。
另请参考 PKGBUILD(5) § PACKAGING FUNCTIONS。
prepare()
此函数会执行用于预处理源文件以进行构建的命令,例如 patching。此函数执行在 pkgver() 之前,软件包解压之后。如果解压过程被跳过(makepkg --noextract
),那么 prepare()
函数就不会被执行。
bash -e
模式下,意味着任何以非零状态退出的命令都会造成该函数中止.如果不清楚应该将操作放到 prepare()
还是 build()
,只需要记得 prepare()
中的步骤应在解压后仅执行一次,而 build()
中的步骤应在解压后文件被修改时都执行一次。
pkgver()
pkgver()
会在抓取并解压源文件,执行 prepare() 后执行此函数,可用于在 makepkg 阶段中更新 pkgver 变量。
为使用 git/svn/hg 等工具的项目打包时,由于它们的构建过程相同,但源文件可能每天甚至每小时更新一次,这一特性将会十分有用。过去的方法是把日期写入到 pkgver 变量中,但这样一来 makepkg 会在即使软件没有更新的情况下依然重新构建软件包,因为它会认为软件包的版本改变了。其他与此有关的命令有 git describe
,hg identify -ni
等等。请在提交 PKGBUILD 前做好测试,因为如果 pkgver()
执行失败,整个构建过程都会终止。
-
),通常可以用 sed 来进行修正。build()
现在你需要编写 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()
函数依然是必须的。check()
用于执行 make check
和其它一些例行测试。强烈建议使用 check()
确保软件正常构建,并能搭配依赖正常使用。
如果不需要或是维护者无法使软件包通过测试,可以通过在 PKGBUILD
/makepkg.conf(5) 中的 options
数组添加 !check
,或是给 makepkg
传入参数 --nocheck
来禁用它。
如果要测试的是 GUI 图形化应用,可以在虚拟 xserver 中运行测试。
package()
最后一步是将编译好的文件放到一个目录下,让 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
选项。测试 PKGBUILD 文件和软件包
在编写 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 检查包的习惯,以避免提交包后再做修复的麻烦。
Using pkgctl to build in a clean chroot environment
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
请参考 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 生成器
某些软件包的 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
New upstream releases
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.