makepkg
makepkg 是一个自动化构建软件包的脚本。使用该脚本的要求是一个具备构建能力的 Unix 平台和一个 PKGBUILD。
makepkg 由 pacman 软件包提供。
配置
系统级配置位于 /etc/makepkg.conf,但用户特定的更改可以在 $XDG_CONFIG_HOME/pacman/makepkg.conf 或 ~/.makepkg.conf 中进行。此外,也可以通过 /etc/makepkg.conf.d/makepkg.conf 配置文件进行系统级更改。建议在构建软件包之前检查配置。
更多信息请参见 makepkg.conf(5)。
打包者信息
每个软件包都标记有元数据,其中包含打包者 (packager)身份识别。默认情况下,用户编译的软件包被标记为 Unknown Packager。如果一个系统上有多个用户编译软件包,或者需要将软件包分发给其他用户,提供真实的联系方式会很方便。这可以通过在 makepkg.conf 中设置 PACKAGER 变量来实现。
在已安装的软件包上检查此项:
$ pacman -Qi package
... Packager : John Doe <john@doe.com> ...
要自动生成签名的软件包,还需要在 makepkg.conf 中设置 GPGKEY 变量。
软件包输出
默认情况下,makepkg 在工作目录中创建软件包 tar 包,并将源码直接下载到 src/ 目录。可以配置自定义路径,例如将所有构建好的软件包保留在 ~/build/packages/,所有源码保留在 ~/build/sources/。
如果需要,请配置以下 makepkg.conf 变量:
PKGDEST— 存储结果软件包的目录SRCDEST— 存储源码数据的目录(如果指向其他地方,符号链接将被放置在src/)SRCPKGDEST— 存储结果源码包的目录(使用makepkg -S构建)
你也可以在每个软件包目录内使用相对路径。
签名校验
如果 .sig 或 .asc 形式的签名文件是 PKGBUILD source 数组的一部分,makepkg 会自动尝试验证它。如果用户的密钥环不包含签名验证所需的公钥,makepkg 将中止安装并提示无法验证 PGP 密钥。
如果软件包缺少所需的公钥,PKGBUILD 通常会包含一个带有必需密钥 ID 的 validpgpkeys 条目。手动导入它,或者在密钥服务器上查找并导入。要临时禁用签名校验,请使用 --skippgpcheck 选项运行 makepkg。
用法
在继续之前,请安装 base-devel 元软件包。该软件包的依赖项不需要在 PKGBUILD 文件中列为构建时依赖(makedepends)。
要构建软件包,必须首先创建一个 PKGBUILD(即构建脚本),如创建软件包中所述。现有的脚本可以从 Arch 构建系统 (ABS) 树或 AUR 获取。拥有 PKGBUILD 后,切换到其保存目录并运行以下命令来构建软件包:
$ makepkg
如果缺少必需的依赖项,makepkg 会在失败前发出警告。要构建软件包并安装所需的依赖项,请添加 -s/--syncdeps 标志:
$ makepkg --syncdeps
添加 -r/--rmdeps 标志会使 makepkg 稍后删除不再需要的构建依赖。如果经常构建软件包,请考虑偶尔使用 Pacman/提示与技巧#删除未使用的软件包(孤儿)。
- 这些依赖项必须在配置好的仓库中可用;详见 pacman#仓库与镜像。或者,可以在构建之前手动安装依赖项(
pacman -S --asdeps dep1 dep2)。 - 安装依赖项时仅使用全局值,即在拆分包的 packaging 函数中进行的任何覆盖都不会被使用。
一旦所有依赖项都满足并且软件包构建成功,将在工作目录中创建一个软件包文件(pkgname-pkgver.pkg.tar.zst)。要进行安装,请使用 -i/--install(等同于 pacman -U pkgname-pkgver.pkg.tar.zst):
$ makepkg --install
要清理残留的文件和目录(例如解压到 $srcdir 的文件),请添加选项 -c/--clean。这对于多次构建同一软件包或更新软件包版本非常有用,因为它防止了过时和残留的文件结转到新的构建中:
$ makepkg --clean
更多内容请参见 makepkg(8)。
优化
默认选项与 devtools 为官方仓库构建软件包时使用的选项一致。[4] 因此,最终用户可以通过调整以下选项以匹配其本地环境,从而获得或多或少的显著收益。
构建优化的二进制文件
通过为宿主机启用编译器优化,可以提高打包软件的性能。缺点是为特定处理器架构编译的二进制文件无法在其他机器上正常运行。在 x86_64 机器上,很少有足够显著的实际性能提升值得投入时间去重新构建官方软件包。
然而,使用“非标准”编译器标志很容易降低性能。许多编译器优化仅在某些情况下有用,不应不加区分地应用于每个软件包。除非有基准测试数据证明某些设置更快,否则极有可能并非如此!Gentoo 的 GCC 优化和 Safe CFLAGS wiki 文章提供了关于编译器优化的更深入信息。
C 和 C++
传递给 C/C++ 编译器(如 gcc 或 clang)的选项由 CFLAGS、CXXFLAGS 和 CPPFLAGS 环境变量控制。在 Arch 构建系统中,makepkg 将这些环境变量作为 makepkg.conf 中的配置选项公开。默认值配置为生成可在各种机器上安装的通用二进制文件。
- 请记住,并非所有构建系统都使用
makepkg.conf中配置的变量。例如,cmake 会忽略预处理器选项环境变量CPPFLAGS。因此,许多 PKGBUILD 包含针对打包软件所用构建系统的特定选项的权宜之计。 - 在源码
Makefile中提供的配置或编译命令行中的特定参数具有优先权,并可能覆盖makepkg.conf中的配置。
GCC 可以自动检测并启用安全的特定架构优化。要使用此功能,请先移除任何 -march 和 -mtune 标志,然后添加 -march=native。例如:
/etc/makepkg.conf
CFLAGS="-march=native -O2 -pipe ..."
CXXFLAGS="${CFLAGS} ..."
要查看这启用了哪些标志,请运行:
$ gcc -march=native -v -Q --help=target
-march=native 以外的不同值,然后运行 -Q --help=target 不会如预期般工作。[5] 要了解真正启用了哪些选项,请进行一次完整编译。请参阅 Gentoo:Safe CFLAGS#Manual 获取说明。通过启用在内存使用和编译时间方面被认为代价昂贵的优化,可以进一步优化二进制文件。这可以通过将优化级别标志从 -O2 更改为 -O3 来实现。在大多数情况下,使用此标志会提高二进制文件的性能,尽管这并不能保证,且取决于二进制文件本身。详情请参阅 Gentoo:GCC 优化#-O 和 GCC 优化选项页面。
Rust
从 pacman 版本 5.2.2 开始,makepkg.conf 还包括对 RUSTFLAGS 环境变量的覆盖,用于提供给 Rust 编译器的标志。Rust 编译器也可以通过在给定的 RUSTFLAGS 值中添加 -C target-cpu=native 来检测并启用特定架构的优化:
/etc/makepkg.conf.d/rust.conf
RUSTFLAGS="-C force-frame-pointers=yes -C target-cpu=native"
要查看这将启用哪些 CPU 特性,请运行:
$ rustc -C target-cpu=native --print cfg
在不带 -C target-cpu=native 的情况下运行 --print cfg 将打印默认配置。
可能添加到 RUSTFLAGS 中的其他优化相关选项:
-C opt-level=3:该值可根据需要更改为3、s或z。opt-level=3是发布构建的默认值。-C codegen-units=n:选择小于16的n也会优化二进制文件,但会增加构建时间。-C link-arg=-z -C link-arg=pack-relative-relocs减小二进制文件的大小。
详情请参阅 Rust 编译器文档。
提高构建时间
并行编译
make 构建系统使用 MAKEFLAGS 环境变量来为 make 指定额外选项。该变量也可以在 makepkg.conf 文件中设置。
拥有多核/多处理器系统的用户可以指定同时运行的任务数量。这可以通过使用 nproc(1) 来确定可用处理器的数量,例如 MAKEFLAGS="--jobs=$(nproc)"。
一些 PKGBUILD 会专门用 -j1 覆盖此设置,因为某些版本存在竞态条件,或者根本不支持并行。在确认错误确实是由 MAKEFLAGS 引起后,应在错误跟踪器上(或者对于 AUR 软件包,向包维护者)报告因其导致构建失败的包。
有关可用选项的完整列表,请参见 make(1)。
在内存中构建
由于编译需要大量的 I/O 操作和处理小文件,将工作目录移动到 tmpfs 可能会缩短构建时间。
可以将 BUILDDIR 变量临时导出到 makepkg,以将构建目录设置为现有的 tmpfs。例如:
$ BUILDDIR=/tmp/makepkg makepkg
可以在 makepkg.conf 中通过取消注释 BUILDDIR 选项来进行持久化配置,该选项位于默认 /etc/makepkg.conf 文件 BUILD ENVIRONMENT 部分的末尾。将其值设置为例如 BUILDDIR=/tmp/makepkg 将利用 Arch 默认的 /tmp 临时文件系统。
- 避免在 tmpfs 中编译较大的软件包,以防止内存不足。
- 挂载 tmpfs 目录时不能使用
noexec选项,否则会阻止编译出的二进制文件执行。 - 请记住,在 tmpfs 中编译的软件包在重启后将不会保留。请考虑适当设置 PKGDEST 选项,以便自动将构建好的软件包移动到持久目录。
使用编译缓存
使用 ccache 可以通过缓存编译结果供后续使用来缩短构建时间。
使用 mold 链接器
mold 是 ld/lld 链接器的直接替代品,声称速度显著更快。
要使用 mold,请将 -fuse-ld=mold 添加到 LDFLAGS。例如:
/etc/makepkg.conf
LDFLAGS="... -fuse-ld=mold"
要向 mold 传递额外选项,请同时将它们添加到 LDFLAGS。例如:
/etc/makepkg.conf
LDFLAGS="... -fuse-ld=mold -Wl,--separate-debug-file"
要对 Rust 软件包使用 mold,请将 -C link-arg=-fuse-ld=mold 添加到 RUSTFLAGS。例如:
/etc/makepkg.conf.d/rust.conf
RUSTFLAGS="... -C link-arg=-fuse-ld=mold"
禁用调试包和 LTO
提交 90bf367e(包含在 2024 年 2 月的 pacman 6.0.2-9 中)默认启用了 debug 和 lto 选项。
构建调试包使官方仓库能够为用户提供更多排查问题的工具 (archlinux/packaging/packages/pacman#23#note_173528),但在自行构建软件包时这不是必需的,且会减慢构建过程。见 archlinux/packaging/packages/pacman#23#note_173782。
链接时优化 (LTO) 可以生成更优化的二进制文件,但会大大延长构建过程 (archlinux/packaging/packages/pacman#23#note_173678),这可能不是理想的权衡。
要禁用这些选项,请在 OPTIONS=() 数组中直接在它们前面添加 ! 字符,例如 OPTIONS=(...!debug !lto...)。
压缩
使用其他压缩算法
为了加快打包和安装速度(代价是软件包归档文件变大),请更改 PKGEXT。
例如,以下设置跳过软件包文件的压缩,进而在安装时无需解压:
$ PKGEXT='.pkg.tar' makepkg
又如,以下设置使用专注于速度的 LZ4 算法:
$ PKGEXT='.pkg.tar.lz4' makepkg
要使其中一项设置永久生效,请在 /etc/makepkg.conf 中设置 PKGEXT。
在压缩时利用多核
zstd 通过 -T/--threads 标志支持对称多处理 (SMP) 以加快压缩速度。/etc/makepkg.conf 的 COMPRESSZST 数组中默认包含 -T0 标志,这让 zstd 使用与物理 CPU 核心数一样多的线程来压缩软件包。通过 --auto-threads=logical 标志指示 zstd 基于逻辑 CPU 计数,可以进一步增加使用的线程数:
COMPRESSZST=(zstd -c -T0 --auto-threads=logical -)
lz4 和 xz 默认是多线程的,因此 /etc/makepkg.conf 中无需更改。
pigz 是 gzip 的直接替代、并行实现,默认使用所有可用的 CPU 核心(可以使用 -p/--processes 标志来使用更少的核心):
COMPRESSGZ=(pigz -c -f -n)
pbzip2 是 bzip2 的并行实现,默认也使用所有可用的 CPU 核心。-p# 标志可用于使用更少的核心(注意:-p 与核心数之间没有空格)。
COMPRESSBZ2=(pbzip2 -c -f)
lbzip2 是 bzip2 的另一个并行实现,默认也使用所有可用的 CPU 核心。-n 标志可用于使用更少的核心。
COMPRESSBZ2=(lbzip2 -c -f)
plzipAUR 是 lzip 的多线程实现,默认也使用所有可用的 CPU 核心。-n/--threads 标志可用于使用更少的核心。
COMPRESSLZ=(plzip -c -f)
更改压缩级别
几种压缩算法(包括 zstd 和 xz)支持设置压缩级别,该级别定义了速度、内存和压缩效率之间的权衡。
技巧与提示
减少源码下载和解压时间
定义源码存放位置
利用 SRCDEST,尤其是在构建 VCS 软件包时,可以节省后续重新构建时获取和解压源码的时间。
生成新的校验和
安装 pacman-contrib 并在 PKGBUILD 文件所在的目录中运行以下命令以生成新的校验和:
$ updpkgsums
updpkgsums 使用 makepkg --geninteg 来生成校验和。更多详情请参见此论坛讨论。
校验和也可以通过例如 sha256sum 获取,并手动添加到 sha256sums 数组中。
从本地源文件构建
如果你想修改源码,可以使用 -o, --nobuild 仅下载并解压文件 选项下载源码而不构建软件包。
$ makepkg -o
现在你可以对源码进行修改,然后使用 -e, --noextract 不解压源文件(使用现有的 $srcdir/ 目录) 选项构建软件包。使用 -f 选项覆盖已构建且存在的软件包。
$ makepkg -ef
显示指定打包者的软件包
expac 是一个 pacman 数据库提取工具。此命令显示系统中安装的所有打包者名为 packagername 的软件包:
$ expac "%n %p" | grep "packagername" | column -t
这显示了系统中安装的所有打包者设置为 /etc/makepkg 变量 PACKAGER 的软件包。这仅显示 /etc/pacman.conf 中定义的仓库中的软件包。
$ . /etc/makepkg.conf; grep -xvFf <(pacman -Qqm) <(expac "%n\t%p" | grep "$PACKAGER$" | cut -f1)
在 64 位系统上构建 32 位软件包
参见 32 位软件包指南。
无人值守软件包签名
在 Jenkins 等自动化构建环境中,可能无法人工提供用于签名的 gpg 私钥密码。在没有密码的情况下在系统中存储 gpg 私钥是不明智的。
使用 makepkg 制作的 zst 软件包在创建后仍然可以签名:
$ gpg --detach-sign --pinentry-mode loopback --passphrase --passphrase-fd 0 --output NewlyBuilt.pkg.tar.zst.sig --sign NewlyBuilt.pkg.tar.zst
其中 GPG 密码由你选择的自动化套件安全地提供并掩码。
生成的 zst 和 sig 文件可以被期望有效签名的 pacman 客户端引用,也可以在托管你自己的仓库时被使用 repo-add --sign 创建的仓库引用。
磁力链接 (Magnet URIs)
可以使用 transmission-dlagentAUR 下载代理,在 source 字段中添加对磁力链接资源(带 magnet:// 前缀)的支持。
在 systemd 控制组中运行 makepkg
如果你正在构建的包需要消耗过多资源,而你的默认 make 标志(通常适用于大多数包)设置得当,你可以尝试在它自己的控制组中运行它。makepkg-cgAUR 是 makepkg 的一个封装器,通过 systemd 控制组实现了这一点(见 systemd.resource-control(5))。
使用空闲调度策略运行
软件包构建过程会导致 CPU 利用率过高,特别是在#并行编译的情况下。在 CPU 负载过重的情况下,即使设置了最高的 nice(1) 值,系统也会出现明显的卡顿甚至变得无法使用。用户界面和前台应用程序可能会出现卡顿甚至无响应。
这可以通过在运行 makepkg 之前将调度策略更改为 SCHED_IDLE 来解决。它确保软件包构建过程不会干扰常规任务,并且仅利用剩余的空闲 CPU 时间。
摘自 sched(7) § SCHED_IDLE: 调度极低优先级的任务:
- 此策略旨在以极低的优先级运行作业(甚至低于
SCHED_OTHER或SCHED_BATCH策略中的 +19 nice 值)。
SCHED_IDLE 策略可以通过运行带 -i 标志的 chrt(1) 命令来设置,指定优先级为 0(SCHED_IDLE 唯一有效的选项)并指定当前 shell 的 PID。
对于大多数 shell:
$ chrt -iap 0 $$
makepkg.conf 中,以便将其应用于每次构建。对于未设置 $$ 的 fish shell:
$ chrt -iap 0 %self
软件包目录内的相对路径
除了为软件包输出选项使用绝对路径外,你还可以配置每个软件包目录内的相对路径。
例如,你可以在 makepkg.conf 文件中如下定义目标路径。$startdir 变量是指你构建软件包时 PKGBUILD 所在的目录。
PKGDEST="$startdir/build/packages/" SRCDEST="$startdir/build/sources/" SRCPKGDEST="$startdir/build/srcpackages/" LOGDEST="$startdir/logs/"
这将导致:
- 构建好的软件包将存储在:
"软件包目录"/build/packages/ - 所有下载的源文件将存储在:
"软件包目录"/build/sources/ - 构建出的源码包将存储在:
"软件包目录"/build/srcpackages/ - 所有日志将存储在:
"软件包目录"/logs/
makepkg 仍会照常创建 src/ 和 pkg/ 目录,这是预期行为。
故障排除
为基于 QMAKE 的包指定安装目录
由 qmake 生成的 makefile 使用环境变量 INSTALL_ROOT 来指定程序的安装位置。因此,此 package 函数应该有效:
PKGBUILD
...
package() {
cd "$srcdir/${pkgname%-git}"
make INSTALL_ROOT="$pkgdir" install
}
...
注意,qmake 也必须进行相应配置。例如,将其放入对应的 .pro 文件中:
YourProject.pro
... target.path = /usr/local/bin INSTALLS += target ...
警告:软件包包含对 $srcdir 的引用
变量 $srcdir 或 $pkgdir 中包含的字面字符串最终出现在软件包中安装的某个文件中。[6]
要识别是哪些文件,请在 makepkg 构建目录中运行以下命令:
$ grep -R "$PWD/src" pkg/
一个可能的原因是在 C/C++ 代码中使用了 __FILE__ 宏,并将完整路径传递给了编译器。
Dotnet 二进制文件在默认配置下有时也会包含 .pdb 文件的完整路径。
代理环境下 Makepkg 无法下载依赖
当 makepkg 调用依赖项时,它会调用 pacman 来安装软件包,这需要通过 sudo 获得管理权限。但是,sudo 不会将任何环境变量传递到特权环境中,这些变量包括代理相关的变量 ftp_proxy、http_proxy、https_proxy 和 no_proxy。
为了让 makepkg 在代理后工作,请调用以下方法之一。
通过在 XferCommand 中设置 URL 来启用代理
可以在 /etc/pacman.conf 中设置 XferCommand 以使用所需的代理 URL。在 pacman.conf 中添加或取消注释以下行:
/etc/pacman.conf
... XferCommand = /usr/bin/curl --proxy http://username:password@proxy.proxyhost.com:80 --location --continue-at - --fail --output %o %u ...
通过 sudoer 的 env_keep 启用代理
或者,可能希望使用 sudoer 的 env_keep 选项,该选项允许将给定变量保留在特权环境中。更多详情请参见 Pacman#Pacman 不遵守代理设置。
Makepkg 失败,但 make 成功
如果某物使用 make 可以成功编译,但通过 makepkg 失败,几乎可以肯定是因为 /etc/makepkg.conf 设置了不兼容的编译变量。尝试将这些标志添加到 PKGBUILD 的 options 数组中:
!buildflags,以防止其默认的 CPPFLAGS、CFLAGS、CXXFLAGS 和 LDFLAGS。
!makeflags,以防止其默认的 MAKEFLAGS。
!debug,以防止其默认的 DEBUG_CFLAGS 和 DEBUG_CXXFLAGS(如果 PKGBUILD 是调试构建)。
如果这些中的任何一个解决了问题,在确定了违规标志后,可以向开发者提交错误报告。