跳转到内容

本站近期可能因网络攻击出现服务故障,导致无法联网阅读。建议用户安装 arch-wiki-docs-zh-cnCNRepo 或者 arch-wiki-docs-zh-twCNRepo 离线文档包备用,或者使用经由 Cloudflare CDN 的替代版本aw.lilydjwg.me

来自 Arch Linux 中文维基

本文旨在帮助用户利用类似于 portsArch 构建系统来创建自己的软件包,以及如何提交到 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 变量

注意:主页面为 PKGBUILD,另外也请参考 PKGBUILD(5) § OPTIONS AND DIRECTIVES

makepkg 预定义了以下变量,打包者需使用它们指代构建过程的临时路径:

srcdir
makepkg将会把源文件解压到此文件夹,或在此文件夹中生成指向 source 数组中文件的软连接。
pkgdir
makepkg会把该文件夹当成系统根目录,并将软件安装在此文件夹下。

这些变量都是绝对路径,即意味着只要你合适地使用这些变量,就不用担心当前工作目录的影响.

注意:makepkg 及延伸到 build()package() 函数在运行过程中都应当是非交互的。在这些函数中调用交互工具或脚本可能会中断 makepkg 的运行,特别是启用了构建日志的时候(--log)。具体信息请参考 FS#13214

PKGBUILD 函数

在构建软件包时,makepkg 会在 PKGBUILD 中有定义的情况下调用以下五个函数。PKGBUILD 中必须包含 package() 函数并总会被调用。对于其它函数,如果其未被定义,那么 makepkg 会将其略过。

在构建时,函数将按下文列出的顺序执行。

另请参考 PKGBUILD(5) § PACKAGING FUNCTIONS

prepare()

此函数会执行用于预处理源文件以进行构建的命令,例如 patching。此函数执行在 pkgver() 之前,软件包解压之后。如果解压过程被跳过(makepkg --noextract),那么 prepare() 函数就不会被执行。

注意: (从 PKGBUILD(5)) 中可以知道,该函数运行在 bash -e 模式下,意味着任何以非零状态退出的命令都会造成该函数中止.

如果不清楚应该将操作放到 prepare() 还是 build(),只需要记得 prepare() 中的步骤应在解压后仅执行一次,而 build() 中的步骤应在解压后文件被修改时都执行一次。

pkgver()

pkgver() 会在抓取并解压源文件,执行 prepare() 后执行此函数,可用于在 makepkg 阶段中更新 pkgver 变量。

为使用 git/svn/hg 等工具的项目打包时,由于它们的构建过程相同,但源文件可能每天甚至每小时更新一次,这一特性将会十分有用。过去的方法是把日期写入到 pkgver 变量中,但这样一来 makepkg 会在即使软件没有更新的情况下依然重新构建软件包,因为它会认为软件包的版本改变了。其他与此有关的命令有 git describehg identify -ni 等等。请在提交 PKGBUILD 前做好测试,因为如果 pkgver() 执行失败,整个构建过程都会终止。

注意: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

本文或本章节可能需要合并到Arch build system#Build package

附注: This page is not about the build system. There is also #Set up clean chroot and DeveloperWiki:Building in a clean chroot.(在 Talk:创建软件包 中讨论)

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 ...
注意: If the package is installed using an AUR helper, there is a good chance its tarball can be found in its cache directory.

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 可以通过工具自动生成。

注意:用户需要在提交文件到 AUR 前确保软件包满足高质量标准。

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.

参考