PKGBUILD
本文讨论维护者可以在 PKGBUILD 中定义的变量。有关 PKGBUILD 函数及通用软件包创建的信息,请参考 创建软件包。另请阅读 PKGBUILD(5)。
PKGBUILD 是一个 Bash 脚本,包含 Arch Linux 软件包所需的构建信息。
Arch Linux 中的软件包是使用 makepkg 工具构建的。运行 makepkg 时,它会在当前目录中搜索 PKGBUILD 文件,并按照其中的说明编译或以其他方式获取文件,以构建软件包归档文件——pkgname.pkg.tar.zst。生成的软件包包含二进制文件和安装说明,可直接使用 pacman 安装。
强制性变量包括 pkgname、pkgver、pkgrel 和 arch。虽然构建软件包不严格要求 license,但对于任何与他人共享的 PKGBUILD,建议定义该变量,因为如果缺失,makepkg 会产生警告。
在 PKGBUILD 中按此处给出的顺序定义变量是常见做法,但这并非强制要求。
- 使用 namcap 检查
PKGBUILD是否存在常见的打包错误。 - 使用 shellcheck(1) 检查
PKGBUILD是否存在常见的脚本错误。另见 SC2034、SC2154 和 SC2164。
shellcheck --shell=bash --exclude=SC2034,SC2154,SC2164 PKGBUILD
- termux-language-serverAUR 为
PKGBUILD、makepkg.conf等提供 语言服务器 支持。
请参阅 /usr/share/pacman/ 目录中的 .proto 文件 作为示例。
软件包名称
pkgbase
构建普通软件包时,不应在 PKGBUILD 中显式声明此变量:其值默认为 #pkgname 的值。
构建 拆分软件包 时,可以使用此变量显式指定在 makepkg 输出和源码包命名中引用该软件包组的名称。该值不允许以连字符开头。如果未指定,该值将默认为 pkgname 数组中的第一个元素。
拆分软件包的所有选项和指令默认为 PKGBUILD 中给出的全局值。尽管如此,以下内容可以在每个拆分软件包的打包函数中被覆盖:#pkgdesc、#arch、#url、#license、#groups、#depends、#optdepends、#provides、#conflicts、#replaces、#backup、#options、#install 以及 #changelog。
pkgname
既可以是软件包名称(例如 pkgname=foo),也可以是针对拆分软件包的名称数组(例如 pkgname=(foo bar))。软件包名称应仅由小写字母、数字和以下字符组成:@._+-(at 符号、点、下划线、加号、连字符)。名称不允许以连字符或点开头。为了保持一致性,pkgname 应与软件源码包的名称匹配:例如,如果软件位于 foobar-2.5.tar.gz 中,请使用 pkgname=foobar。
版本
pkgver
软件包的版本。这应与上游软件作者发布的版本相同。它可以包含字母、数字、句点和下划线,但不能包含连字符 (-)。如果软件作者使用了连字符,请将其替换为下划线 (_)。如果在 PKGBUILD 后续使用了 pkgver 变量,则可以轻松地将下划线替换回连字符,例如 source=("${pkgname}-${pkgver//_/-}.tar.gz")。
- 不常见数值的排序可以使用 vercmp(8) 进行测试,该工具由 pacman 软件包提供。
- makepkg 可以通过在
PKGBUILD中定义pkgver()函数来自动 更新 此变量。有关详细信息,请参阅 VCS 软件包指南#pkgver() 函数。
pkgrel
发布编号。这通常是一个正整数,用于区分同一版本软件包的连续构建。随着 PKGBUILD 的修复和新增功能的增加,且影响到生成的软件包,pkgrel 应递增 1。当发布新版本的软件时,此值必须重置为 1。在特殊情况下,也可以看到使用其他格式,如 major.minor。
epoch
epoch。用于强制软件包被视为比具有较低 epoch 的任何先前版本都要新。此值必须为非负整数;默认为 0。当软件包的版本命名方案发生变化(或是字母数字格式),破坏了正常的版本比较逻辑时,会使用此变量。例如:
pkgver=5.13 pkgrel=2 epoch=1
1:5.13-2
有关版本比较的更多信息,请参阅 pacman(8)。
通用
pkgdesc
软件包的描述。建议不超过 80 个字符,且不应以自我引用的方式包含软件包名称,除非应用程序名称与软件包名称不同。例如,使用 pkgdesc='Text editor for X11' 而不是 pkgdesc='Nedit is a text editor for X11'。
此外,明智地使用关键词对于增加在相关搜索查询中出现的机会非常重要。
arch
PKGBUILD 旨在构建和运行的一组架构。Arch 官方仅支持 x86_64,但其他项目可能支持其他架构。例如,Arch Linux 32 提供对 i686 和 pentium4 的支持,而 Arch Linux ARM 提供对 armv7h (armv7 hardfloat) 和 aarch64 (armv8 64-bit) 的支持。
该数组可以使用两种类型的值:
arch=(any)表示该软件包可以在任何架构上构建,并且一旦构建,其编译状态是与架构无关的(通常是 shell 脚本、字体、主题、多种类型的扩展、Java 程序等)。- 包含一个或多个架构(但不是
any)的arch=(...)表示该软件包可以针对任何指定的架构进行编译,但一旦编译即为架构特定的。对于这些软件包,请指定PKGBUILD官方支持的所有架构。对于官方仓库和 AUR 软件包,这意味着arch=('x86_64')。AUR 软件包也可以选择额外支持其他已知可用的架构。
在构建过程中,可以通过变量 CARCH 访问目标架构。
url
被打包软件官方网站的 URL。
license
软件分发所采用的许可证。Arch Linux 使用 SPDX 许可证标识符。每个许可证必须在 /usr/share/licenses/ 中有对应的条目。
对于常见许可证(如 GPL-3.0-or-later),软件包 licenses 提供了所有相应的文件。该软件包由于是 base 元软件包 的依赖而默认安装,文件可以在 /usr/share/licenses/spdx/ 中找到。只需从 SPDX 标识符列表 中引用相应的 SPDX 许可证标识符即可。
严格来说,像 BSD 或 MIT 这样的许可证系列并不是单一的许可证,每个实例都需要一个单独的许可证文件。在 license 变量中,使用通用的 SPDX 标识符(例如 BSD-3-Clause 或 MIT)引用它们,但随后需像提供自定义许可证一样提供对应的文件。
对于自定义许可证,如果它们不属于上述常见系列,标识符应为 LicenseRef-license-name 或 custom:license-name。对应的许可证文本必须放在 /usr/share/licenses/pkgname 目录中。为了安装该文件,可以在 package() 章节中使用如下代码片段:
install -Dm644 LICENSE "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
pkgdir 变量由 makepkg 定义,有关更多信息请参阅 PKGBUILD(5) § 打包函数。组合多个许可证或添加例外应遵循 SPDX 语法。例如,一个在 GNU/GPL 2.0 或 GNU/LGPL 2.1 下发布的软件包可以使用 'GPL-2.0-or-later OR LGPL-2.1-or-later';一个在带有 LLVM 例外的 Apache 2.0 下发布的软件包应使用 'Apache-2.0 WITH LLVM-exception';而一个部分属于 BSD 3 clause、部分属于 GNU/LGPL 2.1 且部分属于 GNU/GPL 2.0 的软件包则使用 'BSD-3-Clause AND LGPL-2.1-or-later AND GPL-2.0-or-later' [2]。注意这必须是单个字符串,因此整个表达式必须括在引号内。截至 2023 年 11 月,SPDX 例外列表 仍然有限,因此通常必须使用自定义许可证路径。
如果在过渡期间遇到 SPDX 标识符问题,使用旧的标识符(/usr/share/licenses/common 中的目录名称)是可以接受的。
另见 非自由应用程序软件包指南。
关于自由和开源软件许可证的其他信息和观点可以在以下页面找到:
- 维基百科:自由软件许可证
- 维基百科:自由及开放源代码软件许可证比较
- 开源和自由软件项目法律问题初级读本
- GNU 项目 - 各种许可证及其评论
- Debian - 许可证信息
- 开源倡议 (OSI) - 按名称排列的许可证
groups
软件包所属的 包组。例如,安装 plasma 时,它会安装属于该组的所有软件包。
依赖
optdepends_x86_64=()。depends
软件构建及运行所必需的软件包数组。在 package() 函数内部定义的依赖项仅在运行软件时需要。
可以使用比较运算符指定版本限制,例如 depends=('foobar>=1.8.0');如果需要多个限制,可以为每个限制重复该依赖项,例如 depends=('foobar>=1.8.0' 'foobar<2.0.0')。
depends 数组应列出所有直接的第一级依赖项,即使其中一些已经通过传递性声明。例如,如果软件包 foo 同时依赖于 bar 和 baz,而 bar 软件包反过来也依赖于 baz,如果 bar 停止拉取 baz,最终会导致非预期的行为。Pacman 将不会在重新安装 foo 或清理了孤立软件包的系统上强制安装 baz,从而导致 foo 在运行时崩溃或出现其他异常。
在某些情况下,这不是必需的,可以列出也可以不列出,例如 glibc 是不能卸载的,因为每个系统都需要某个 C 库;或者对于已经依赖于另一个 python- 模块的软件包,不需要列出 python,因为根据定义,第二个模块必须依赖于 python,且永远不会停止将其作为依赖项拉取。
依赖项通常应包括构建软件包所有可选功能的要求。或者,任何其依赖项未包含的功能都应通过配置选项显式禁用。如果不这样做,可能会导致软件包出现“自动魔法依赖”——即由于传递依赖或构建机器上安装了无关软件而不可预测地启用的编译时可选功能,但这些功能并未反映在软件包依赖关系中。
如果依赖项名称看起来像是一个库,例如 depends=(libfoobar.so),makepkg 将尝试在构建好的软件包中查找依赖于该库的二进制文件,并附加该二进制文件所需的 soname 版本。手动附加版本会禁用自动检测,例如 depends=('libfoobar.so=2')。
makedepends
仅在构建软件包时需要的软件包数组。可以按照 depends 数组中相同的格式指定最低依赖版本。depends 数组中的软件包是构建软件包所隐式需要的,不应在此处重复。
- 使用 makepkg 构建时,假设已经安装了 base-devel 软件包。该软件包的依赖项不应包含在
makedepends数组中。 - 如果使用 VCS 源,请不要忘记包含适当的 VCS 工具(git、subversion、cvs 等)。
checkdepends
软件运行其测试套件所依赖、但运行时不需要的软件包数组。此列表中的软件包遵循与 depends 相同的格式。仅当存在 check() 函数且将由 makepkg 运行时,才会考虑这些依赖项。
checkdepends 数组中。optdepends
软件运行非必需、但能提供额外功能的软件包数组。这可能意味着如果没有相应的 optdepends,软件包提供的某些可执行文件将无法运行。[3] 如果软件可以在多个备选依赖项上工作,可以将所有备选项列在此处,而不是 depends 数组中。
还应简要说明每个 optdepend 提供的额外功能:
optdepends=('cups: printing support'
'sane: scanners support'
'libgphoto2: digital cameras support'
'alsa-lib: sound support'
'giflib: GIF images support'
'libjpeg: JPEG images support'
'libpng: PNG images support')
软件包关系
conflicts_x86_64=()。provides
该软件提供的附加功能的软件包数组,包括 虚拟软件包(如 cron 或 sh)以及 所有外部共享库。提供相同项目的软件包可以并存,除非其中至少一个使用了 conflicts 数组。
- 应提到软件包提供的版本(
pkgver以及可能的pkgrel),以防引用该软件的软件包有版本要求。例如,一个名为 qt-foobar 的修改版 qt 软件包版本为 3.3.8,应使用provides=('qt=3.3.8');省略版本号会导致需要特定 qt 版本的依赖失败。 - 不要将
pkgname添加到provides数组中,因为这是自动完成的。
conflicts
与该软件包冲突或在安装时会导致问题的软件包数组。所有这些软件包以及提供这些项目的软件包都必须被移除。冲突软件包的版本属性也可以按照与 depends 数组相同的格式指定。
请注意,冲突检查既针对 pkgname,也针对 provides 数组中指定的名称。因此,如果你的软件包 provides 了 foo 功能,在 conflicts 数组中指定 foo 将导致你的软件包与 provides 数组中包含 foo 的所有其他软件包发生冲突(即无需在 conflicts 数组中列出所有这些冲突软件包的名称)。让我们看一个具体的例子:
- netbeans 隐式地将
netbeans作为pkgname本身提供。 - 一个假设的 netbeans-cpp 软件包将提供
netbeans并与netbeans冲突。 - 一个假设的 netbeans-php 软件包将提供
netbeans并与netbeans冲突,但不需要显式地与 netbeans-cpp 冲突,因为提供相同功能的软件包会隐式地发生冲突。
当软件包通过 provides 数组提供相同功能时,显式添加备选软件包到 conflicts 数组与不添加是有区别的。如果显式声明了 conflicts 数组,提供相同功能的两个软件包将被视为互斥备选;如果缺失 conflicts 数组,提供相同功能的两个软件包将被视为可能共存。打包者在决定是否声明 conflicts 变量时,应始终忽略 provides 变量的内容。
replaces
由该软件包替换的过时软件包数组,例如 wireshark-qt 使用了 replaces=('wireshark')。同步时,pacman 在仓库中遇到另一个具有匹配 replaces 的软件包时,会立即替换已安装的软件包。如果是提供现有软件包的备选版本或上传到 AUR,请使用 conflicts 和 provides 数组,这些数组仅在实际安装冲突软件包时才会求值。
其他
backup
在升级或移除软件包时应保留的用户更改文件数组,主要用于 /etc 中的配置文件。如果这些文件与软件包附带的文件相比没有变化,它们将在升级或移除过程中像普通文件一样被移除或替换。
此数组中的文件应使用相对路径,不带前导斜杠 (/)(例如 etc/pacman.conf,而不是 /etc/pacman.conf)。backup 数组不支持空目录或通配符如 "*"。
更新时,新版本可能会保存为 file.pacnew,以避免覆盖用户已修改的现有文件。同样,当移除软件包时,用户修改过的文件将保留为 file.pacsave,除非使用 pacman -Rn 命令移除软件包。
options
此数组允许覆盖 /etc/makepkg.conf 中定义的 makepkg 的某些默认行为。要启用选项,请将名称包含在数组中。要禁用选项,请在名称前放置一个 !。
可用选项的完整列表可以在 PKGBUILD(5) § 选项与指令 中找到。
install
要包含在软件包中的 .install 脚本的名称。
pacman 能够在安装、移除或升级软件包时执行特定于该包的脚本。该脚本包含在不同时间运行的以下函数:
pre_install— 在解压文件之前运行。传递一个参数:新软件包版本。post_install— 在解压文件之后运行。安装后应打印的任何额外提示都应放在此处。传递一个参数:新软件包版本。pre_upgrade— 在解压文件之前运行。按以下顺序传递两个参数:新软件包版本、旧软件包版本。post_upgrade— 在解压文件之后运行。按以下顺序传递两个参数:新软件包版本、旧软件包版本。pre_remove— 在移除文件之前运行。传递一个参数:旧软件包版本。post_remove— 在移除文件之后运行。传递一个参数:旧软件包版本。
每个函数都在 pacman 安装目录中 chroot 运行。参见 此贴。
- .install 的原型位于 /usr/share/pacman/proto.install。
- pacman#钩子 (Hooks) 提供类似的功能。
exit 结束脚本。这会阻止包含的函数执行。changelog
软件包更新日志的名称。要查看已安装软件包(带有此文件)的更新日志:
$ pacman -Qc pkgname
源
source
构建软件包所需文件的数组。它必须包含软件源码的位置,在大多数情况下是一个完整的 HTTP 或 FTP URL。先前设置的变量 pkgname 和 pkgver 可以在此处有效地使用;例如 source=("https://example.com/${pkgname}-${pkgver}.tar.gz")。
也可以在 PKGBUILD 所在的同一目录中提供文件,并将其名称添加到此数组。在实际构建过程开始之前,将下载或检查此数组中引用的所有文件的存在性,如果缺少任何文件,makepkg 将不会继续。
.install 文件由 makepkg 自动识别,不应包含在 source 数组中。source 数组中扩展名为 .sig、.sign 或 .asc 的文件被 makepkg 识别为 PGP 签名,并将自动用于验证相应源文件的完整性。
source=('unique_package_name::file_uri') 提供替代的唯一文件名;例如 source=("${pkgname}-${pkgver}.tar.gz::https://github.com/coder/program/archive/v${pkgver}.tar.gz")。- 可以通过附加下划线和架构名称来添加特定于架构的数组,例如
source_x86_64=()。必须有相应的带有校验和的完整性数组,例如sha256sums_x86_64=()。 - 某些服务器通过过滤客户端的 User-Agent 字符串或其他类型的限制来限制下载,这可以通过 DLAGENTS 绕过。
- 使用
file://URL 指向计算机文件系统中的目录或文件。例如,本地 Git 仓库可以指定为"${pkgname}::git+file:///path/to/repository"。 - 磁力链接支持可以使用 transmission-dlagentAUR 作为
DLAGENT并使用magnet://URI 前缀代替规范的magnet:?来添加。 - 有关 VCS 特定选项(如针对特定 Git 分支或提交)的详细信息,请参阅 PKGBUILD(5) § 使用 VCS 源 和 VCS 软件包指南#VCS 源。
noextract
source 中列出的不应由 makepkg 从归档格式中解压的文件数组。这可用于 /usr/bin/bsdtar 无法处理的归档文件,或者那些需要按原样安装的文件。如果使用了备选的解压工具(例如 lrzip),则应将其添加到 makedepends 数组中,并且 prepare() 函数的第一行应手动解压源归档文件;例如:
prepare() {
lrzip -d source.tar.lrz
}
请注意,虽然 source 数组接受 URL,但 noextract 仅包含文件名部分:
source=("http://foo.org/bar/foobar.tar.xz")
noextract=('foobar.tar.xz')
如果要不解压任何内容,请考虑以下方案:
- 如果
source仅包含普通 URL 且没有自定义文件名,请在最后一个斜杠前剥离source数组:
noextract=("${source[@]##*/}")
- 如果
source仅包含具有自定义文件名的条目,请在::分隔符后剥离source数组(摘自先前版本的 firefox-i18n 的 PKGBUILD):
noextract=("${source[@]%%::*}")
noextract 并手动将其解压到子目录中。validpgpkeys
PGP 指纹数组。如果使用,makepkg 将仅接受此处列出的密钥的签名,并忽略密钥环中的信任值。如果源文件是使用子密钥签名的,makepkg 仍将使用主密钥进行比较。
仅接受完整的指纹。它们必须是大写且不得包含空白字符。
gpg --list-keys --fingerprint KEYID 来查找相应密钥的指纹。请阅读 makepkg#签名检查 以获取更多信息。
完整性
这些变量是数组,其条目是校验和字符串,将用于验证 source 数组中相应文件的完整性。为特定文件插入 SKIP,将不会测试其校验和。
校验和类型和值应始终由上游提供(例如在发布公告中)。当有多种类型可用时,应优先选择最强的校验和(按首选程度排序):b2、sha512、sha384、sha256、sha224、sha1、md5、ck。这能最好地确保下载文件从上游发布到软件包构建的完整性。
这些变量的值可以通过 makepkg 的 -g/--geninteg 选项自动生成,然后通常使用 makepkg -g >> PKGBUILD 附加。来自 pacman-contrib 的 updpkgsums(8) 命令能够更新 PKGBUILD 中任何位置的这些变量。这两个工具都将使用 PKGBUILD 中已经设置的变量,如果没有设置,则回退到 sha256sums。
要使用的文件完整性检查可以在 /etc/makepkg.conf 中的 INTEGRITY_CHECK 选项中设置。参见 makepkg.conf(5)。
sha256sums_x86_64=()。b2sums
摘要大小为 512 位的 BLAKE2b 校验和数组。
sha512sums, sha384sums, sha256sums, sha224sums
摘要大小分别为 512、384、256 和 224 位的 SHA-2 校验和数组。sha256sums 是其中最常用的。
sha1sums
source 数组中所列文件的 160 位 SHA-1 校验和数组。
md5sums
source 数组中所列文件的 128 位 MD5 校验和数组。