PKGBUILD (简体中文)
本页面讨论 PKGBUILD 中使用的变量。若要获取关于 PKGBUILD 功能,和创建通用软件包的信息,请参考创建软件包和 PKGBUILD(5).
PKGBUILD
是一个 shell 脚本,包含 Arch Linux 在构建软件包时需要的信息。
Arch Linux 用 makepkg 创建软件包。当 makepkg 运行时,它会在当前目录寻找 PKGBUILD
文件,并依照其中的指令编译或获取所需的依赖文件,并生成 pkgname.pkg.tar.xz
软件包。生成的包内有二进制文件和安装指令,可以使用 pacman 进行安装。
pkgname
,pkgver
,pkgrel
和 arch
是必须包含的变量。license
在构建包时并不强制要求,但若要分享 PKGBUILD 文件,推荐加上该变量,否则 makepkg
会有警告。
一般来说,建议按照下面的顺序在 PKGBUILD 文件中定义这些变量。但这并不是强制性的,只要使用正确的 Bash 语法就行了。
PKGBUILD
文件中的常见打包错误。软件包名称
pkgbase
在建立常规的软件包时,这个变量不应该在 PKGBUILD
文件中显式指出。这个值默认会与 #pkgname 的值相同。
构建拆分包时,这个变量 pkgbase 可以在 makepkg 的输出和纯源代码包中指定软件包组。此变量不允许以下划线开头。若该变量没有明确定义,则会默认对应到 pkgname 序列的第一个元素。
拆分软件包中的所有选项和指令都默认使用全局 PKGBUILD 设置的值。除了makedepends,source variables, integrity variables
, 其他变量都可以在拆分包的 package()
函数中进行额外设置。但是,也可以在拆分包的打包函数中重载这些选项:#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")
- 不常用变量的顺序可以通过 pacman 软件包提供的 vercmp(8) 进行测试。
- 在 PKGBUILD 中定义
pkgver()
,makepkg 就可以自动更新此变量。详情参阅 pkgver() 函数。
pkgrel
软件的发布号。这通常是一个正整数,用来区分同一版本软件的多次构建。当软件包的补丁和附加功能被添加进入 PKGBUILD
,从而导致生成的软件包发生变化时,pkgrel
应该增加 1。而当这个软件包发布一个新版本时,发布号重置为 1。在个别情况下,也会有其他的发布号形式。比如 主版本号.次要版本号。
epoch
epoch
变量。用于强制升级软件包。在判定逻辑中,不论版本号如何,只要 epoch
值较大,就会被视为更新的软件包。这个值应为非负整数,且默认值为0。通常当一个软件的版本编号方式改变(或者使用某些字母-数字混编的版本符号),导致正常的版本比较逻辑无法进行时,会使用这个变量来控制升级。比如:
pkgver=5.13 pkgrel=2 epoch=1
1:5.13-2
更多关于版本比较的信息,参见 pacman(8)。
一般变量
pkgdesc
软件包描述。建议最多 80 个字符,并不要自己引用自己的名字,除非软件包名和程序名不相同。比如:pkgdesc="Nedit is a text editor for X11"
应该被写成 pkgdesc="Text editor for X11"
。
合理地在描述中使用关键字,这样可以使软件包在相关的搜索中的展示次数增加。
arch
一个描述软件包所有能够生成并使用的架构的序列。Arch 官方仅支持 x86_64
, 但是其它项目提供了其它架构支持。比如说,Arch Linux 32 提供了 i686
支持,Arch Linux ARM 项目提供 arm
(armv5),armv6h
(带有硬件浮点运算模块的 armv6),armv7h
(带有硬件浮点运算模块的 armv7),和 aarch64
(64 位 armv8) 的支持。
这个序列可以以两种类型呈现:
-
arch=('any')
表明软件包可以在任意架构上生成,并且编译后的形式与架构无关(例如 shell 脚本、字体、主题、各种扩展等)。
-
arch=('x86_64')
和其他的一些架构,表明这个软件包可以在上述的任意架构上生成,但生成产物只能在特定的架构上运行。对于这些软件包,您必须在PKGBUILD
文件中指定所有官方支持的架构。对于官方软件源和 AUR 软件包,这指的是 x86_64。可选的,AUR 软件包可以选择添加一些已知的可运行架构的支持。
在运行过程中可以通过 $CARCH
变量来获知目标架构。
url
待打包软件官方站点的网址。
license
软件发布所规定的许可。软件包 licenses(这个包在 base 包组中)中包含了许多通用的许可证协议,您可以在 /usr/share/licenses/common
中找到。如果软件包是发布在这些许可证中的任何一个,这个值应该被设定成许可证的目录名,比如 license=('GPL')
。如果软件包适用的许可证不在上述文件夹中,您需要完成以下几个步骤:
- 将
custom
添加到license
序列中。可选的,您也可以用custom:许可证名称
来替代custom
。当一个许可证被两个以上的官方软件源的软件包(包括 community 仓库)使用,它就会被添加到 licenses 软件包中。 - 将许可证安装到
/usr/share/licenses/pkgname/
目录下,例如/usr/share/licenses/foobar/LICENSE
。您可以使用下面命令install -Dm644 LICENSE "$pkgdir"/usr/share/licenses/$pkgname/LICENSE
来实现。 - 如果许可证的内容仅保存在网站上,那么你需要单独保存一个版本。
-
BSD、ISC、MIT、zlib/png、Python 和 OFL 是几个例外的情况,它们不能加入 licenses 包。但在
license
序列中,您可以按照通常的许可证来添加他们 (就是说,按照license=('BSD')
,license=('ISC')
,license=('MIT')
,license=('ZLIB')
,license=('Python')
和license=('OFL')
来添加),但是技术上来讲,这些都是定制的许可证,因为这些许可证中都包含各自特定的版权声明。任何以这五个许可协议分发的软件包都应该在/usr/share/licenses/pkgname
下存放它们独有的许可证文件。 - 一些软件包可能不止一个许可证,这种情况下,在
license
列表中置入多个值,比如说,license=('GPL' 'custom:name of license')
。 - (L)GPL 许可证有很多版本和版本的变种。对于一个 (L)GPL 软件,约定如下:
- (L)GPL - 指代 (L)GPLv2 或之后的任意版本
- (L)GPL2 - 仅 (L)GPL2
- (L)GPL3 - 指代 (L)GPL3 或之后的任意版本
- 如果最终无法决定使用哪种许可证,PKGBUILD.proto 建议使用
unknown
。但是这种情况下,应该联系上游的软件发布者,来确定这个软件在哪些情况下是可以使用的。
ReadMe.txt
中声明。可以在 build()
时将这些声明提取为单独文件,比如通过这条命令:sed -n '/This software/,/ thereof./p' ReadMe.txt > LICENSE
。更多信息,请参考非自由软件打包指引。
关于自由/开源软件协议的更多信息请参考:
- w:Free software licence
- w:Comparison of free and open-source software licenses
- A Legal Issues Primer for Open Source and Free Software Projects
- GNU Project - Various Licenses and Comments about Them
- Debian - License information
- Open Source Initiative - Licenses by Name
groups
软件包所在的包组。例如:当你安装 base,它会安装 base 组里的所有包。
依赖关系
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 软件包。同时,当 pacman 清除不再被依赖的孤儿软件包时,由于 baz 软件包被依赖而没有被安装,foo 可能崩溃或者发生运行错误。
但在一些情况下,上面所述的某些依赖是没有必要,或者不应该被列出的。比如说每个操作系统都或多或少需要 C 运行库,因此 glibc 是不能够被卸载的。或者说,当这个软件包已经依赖于一个以 python- 开头的模块,就不需要包括 python 软件包。因为这样的模块必须绝对地依赖于 python 软件包,而不允许从依赖中删除。
通常的依赖应该包括生成所有可选功能所需的依赖。否则,任何依赖于不包含在其中的软件的功能需要通过一个配置的功能显式地排除在外。如果不这样做,会给软件包添加了所谓"自动魔法依赖":一些在生成时可选的功能因为生成软件包的机器上安装的一些传递依赖或者不相关的软件被启用了,但是没有表现在包的依赖中。
如果依赖的名称写成了库的名称(例如 depends=('libfoobar.so')
),makepkg 会在编译完成的包中添加依赖这个库的二进制文件所依赖的版本,或者你可以自己加上版本号来停用自动检测。比如说 depends=('libfoobar.so=2')
。
makedepends
仅在软件生成时需要的软件包列表。可以像depends
序列里提到的一样指定依赖的版本限制。depends
序列里面的软件包默认也是生成时需要的,此处不应该重复。
$ LC_ALL=C pacman -Si $(pactree -rl ''package'') 2>/dev/null | grep -q "^Groups *:.*base-devel"
makedepends
列表中。checkdepends
运行测试组件时需要,而运行时不需要的包列表。该列表中的包遵循和 depends
相同的格式。这些依赖只在 check() 函数存在,且被 makepkg 执行时会被处理。
checkdepends
列表中。optdepends
可选软件包序列。这些可选软件包不影响软件主要功能,但能提供额外特性。这通常暗示除非安装了对应的可选软件包软件包的个别程序可能无法正常使用[1]。如果软件有一些替代依赖,您可以将其在此处,而不是 depends
序列中,全部列出。
应该简要说明每个包所能提供的额外功能,例如:
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 包其版本号为 3.3.8,命名为 qt-foobar,那么 provides
应该写成 provides=('qt=3.3.8')
。如果忽略了版本号,会导致所有依赖于 qt 的某个特定版本的包编译失败。不要把 pkgname
加入 provides
序列。这个操作会自动进行。conflicts
与当前软件包发生冲突的包与功能的列表。安装此软件时,所有这个列表中的软件包,和提供这个功能的软件包都会被删除。可以像 depends
那样指定冲突包的版本号。
如果你在制作已有的软件包(例如官方源或 AUR)的替代时,你需要在 conflicts
中指定它们与你的包冲突。
有时候,指定所有软件包的冲突很难实现。比如说很多人都创建了提供这个功能的软件包,要让每个这些包的维护者都在他们的 conflicts
列表中全部加入您的包,是一个很麻烦的事情。
所以如果软件包的 provides
和其它软件包的 provides
提供了相同的功能,就不需要将冲突的包添加进 conflicts
列表中。
例如:
-
netbeans 显式地提供
netbeans
(因为pkgname
就是如此)。 -
netbeans-javaseAUR[损坏的链接:package not found] 提供
netbeans
功能,并和netbeans
冲突。 -
netbeans-phpAUR 提供
netbeans
功能,而且和netbeans
冲突。显然这与同时提供netbeans
功能的 netbeans-javaseAUR[损坏的链接:package not found] 包冲突。但是您不需要在conflicts
中说明。Pacman 可以发现它们相互冲突,因为他们提供了相同的功能,而且他们提供的这项功能与其他提供者都冲突。
- 反过来说,netbeans-javaseAUR[损坏的链接:package not found] 因为和 netbeans-phpAUR 提供相同的功能,于是不必显式声明相互冲突。
replaces
会因安装当前包而取代的过时的包的列表。比如:wireshark-qt 中的 replaces=('wireshark')
。在同步软件数据库后,pacman 会立刻用软件库中的另一个包替换掉 replaces
中已安装的包。如果你只是提供已存在包的一个替代品,或者上传到 AUR, 请不要使用 replace
,而是使用 conflicts
和 provides
两个变量——它们仅在安装冲突软件包时被检查。
其它
backup
当包被升级或卸载时,应当备份的文件(的路径)序列。这些文件一般是用户会更改的文件,如主要放置在 /etc
中的配置文件。
列表中的文件应该使用没有绝对路径标识(/
)的相对路径(如 etc/pacman.conf
),而不是绝对路径(如 /etc/pacman.conf
)。
在升级时,新版本会被命名为 file.pacnew
以避免覆盖旧有且被用户修改过的文件。类似地,当卸载包时,用户修改过的文件会以 file.pacsave
为名而保留下来——除非用 pacman -Rn
命令卸载。
参见 pacnew 和 pacsave 文件获取更多信息。
options
这个序列允许你重载 makepkg 的部分定义在 /etc/makepkg.conf
中的默认行为。要设置一个选项,请在序列中指定选项名。要禁用一个默认行为,则还需要在前面加上!
。
参见 PKGBUILD(5) 以获取所有可用选项。
install
.install 脚本的名称。这个值应该和 pkgname
相同。pacman 可以在安装、卸载或升级一个软件包时存储及执行一些特定的脚本。在不同的情况下,脚本包含了下面几个函数,并且在特定时刻执行它们:
-
pre_install
- 安装前运行的脚本。可以传递一个参数:版本号。 -
post_install
- 安装后运行的脚本。可以传递一个参数:版本号。 -
pre_upgrade
- 升级前运行的脚本。可以按以下顺序传递两个参数:新版本号,旧版本号。 -
post_upgrade
- 升级后运行的脚本。可以按以下顺序传递两个参数:新版本号,旧版本号。 -
pre_remove
- 卸载前运行的脚本,可以传递一个参数:版本号。 -
post_remove
- 卸载后运行的脚本,可以传递一个参数:版本号。
每一个函数都是在 pacman 安装目录下通过 chroot 运行。参见这个帖子.
- 一个
.install
文件的模板(原型)可以在/usr/share/pacman/proto.install 这里找到。 - Pacman 钩子也提供相似的功能。
exit
结束,否则包含的函数无法被执行。changelog
软件包的更新日志的文件名。要查看安装软件的更新日志(如果有):
$ pacman -Qc pkgname
源码
source
构建软件包时需要的文件列表。它必须包含软件源的位置,大多数情况下是一个完整的 HTTP 或 FTP 地址。您可以在此处调用前面提到的变量 pkgname
和 pkgver
,来实现高效的命名。(如 source=("https://example.com/$pkgname-$pkgver.tar.gz")
)。
文件也可以放到与 PKGBUILD
文件相同目录,并将文件名添加到这个列表。在实际的编译过程开始之前,所有该列表中引用的文件都会被下载或检查是否存在,如果有文件丢失 makepkg
就不会继续。
.install 文件会被 makepkg 自动识别,而不应该被包含在这个列表中。makepkg 会自动把 .sig, .sign 或 .asc 结尾的文件当成 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 规避。
noextract
一个在 source
中列出,但不应该在运行 makepkg
时被解包的文件列表。这通常包括那些压缩文件不能被 /usr/bin/bsdtar
处理,或者本来就不需要解压、按照原样提供的文件。对于前者,需要将额外的解包工具(如unzip
、p7zip
,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[@]%%::*}")
validpgpkeys
PGP 指纹列表。如果使用,makepkg 仅接受这里定义的签名,并且忽略密钥环中的值。如果源代码用子密钥签名,makepkg 仍然会使用主密钥进行比较。
此处仅接受完整的指纹。它们必须是大写字母而且不能有空白字符。
gpg --list-keys --fingerprint <KEYID>
查找 key 的合适指纹。请参阅 Makepkg 验证签名了解签名验证过程的详细信息。
完整性检验
下面描述的这些序列中的变量是 source 序列中对应文件的校验和。可以插入 SKIP
跳过某个不需要检验的文件。
校验和的数值和版本应该始终使用上游,比如在新版本公告中,提供的数值。当存在多种版本的时候,最好选用最强的校验版本,也就是说,按照如下从强到弱的顺序:sha256
> sha1
> md5
。这样可以最大限度地保证从上游的公告到软件包的生成整个流程中下载文件的完整性。
makepkg 的 -g
/--geninteg
选项可以自动生成校验值,通常可以通过 makepkg -g >> PKGBUILD
命令写入。updpkgsums
也可以自动更新 PKGBUILD 中的数值。两个工具都会自动检测 PKGBUILD 中的算法, 如果两个都没找到就回滚到 md5sums
。
sha256sums_x86_64=()
。md5sums
source
列表文件中的 128 位 MD5 校验和。
sha1sums
source
列表文件中 SHA-1 160 位校验和。
sha256sums
256 位 SHA-2 校验和。
sha224sums, sha384sums, sha512sums
SHA-2 校验和列表,分别对应224,384 和 512 位。这些是较为不常见的 sha256sums
替代品。
b2sums
512 位 BLAKE2 校验和。