makepkg

出自 ArchWiki

makepkg 是一个用于自动化构建软件包的脚本。使用该脚本的要求是具备构建能力的 Unix 平台和一个 PKGBUILD 文件。

makepkgpacman 软件包提供。

配置

系统配置位于 /etc/makepkg.conf,但用户特定的更改可以在 $XDG_CONFIG_HOME/pacman/makepkg.conf~/.makepkg.conf 中进行。此外,系统范围的更改可以通过 drop-in 文件 /etc/makepkg.conf.d/makepkg.conf 进行。建议在构建软件包之前查看配置。

提示: 用于在干净的 chroot 环境中构建软件包devtools 辅助脚本使用 /usr/share/devtools/makepkg.conf.d/arch.conf 配置文件。

有关更多信息,请参阅 makepkg.conf(5)

打包者信息

每个软件包都标记有元数据,用于标识包括打包者在内的信息。默认情况下,用户编译的软件包标记为 Unknown Packager。如果多个用户将在系统上编译软件包,或者某人正在向其他用户分发软件包,则提供真实的联系方式会很方便。这可以通过在 makepkg.conf 中设置 PACKAGER 变量来完成。

要在已安装的软件包上检查此信息

$ pacman -Qi package
...
Packager       : John Doe <john@doe.com>
...

要自动生成签名软件包,还请在 makepkg.conf 中设置 GPGKEY 变量。

软件包输出

默认情况下,makepkg 在工作目录中创建软件包 tarball,并将源数据直接下载到 src/ 目录。可以配置自定义路径,例如将所有构建的软件包保存在 ~/build/packages/ 中,将所有源文件保存在 ~/build/sources/ 中。

如果需要,配置以下 makepkg.conf 变量

  • PKGDEST — 用于存储生成软件包的目录
  • SRCDEST — 用于存储 数据的目录(如果指向其他位置,则将符号链接放置到 src/
  • SRCPKGDEST — 用于存储生成的源软件包的目录(使用 makepkg -S 构建)
提示: 可以使用 paccache -c ~/build/packages/ 清理 PKGDEST 目录,如 pacman#清理软件包缓存 中所述。

您还可以使用每个软件包目录内的相对路径

签名检查

注意: makepkg 中实现的签名检查不使用 pacman 的密钥环,而是依赖于用户的密钥环。[1]

如果 .sig.asc 形式的签名文件是 PKGBUILD 源数组的一部分,则 makepkg 会自动尝试 验证 它。如果用户的密钥环不包含签名验证所需的公钥,则 makepkg 将中止安装,并显示 PGP 密钥无法验证的消息。

如果软件包缺少所需的公钥,则 PKGBUILD 很可能包含带有所需密钥 ID 的 validpgpkeys 条目。导入 它,或者在 密钥服务器上 找到它并从那里导入。要临时禁用签名检查,请使用 --skippgpcheck 选项运行 makepkg

用法

在继续之前,安装 base-devel 元软件包。此软件包的依赖项不需要PKGBUILD 文件中列为构建时依赖项 (makedepends)。

注意
  • 确保为传递给 pacman 的命令正确配置了 sudo。或者,可以使用 makepkg.conf(5) 配置文件中的 PACMAN_AUTH 指定不同的授权命令。
  • 禁止以 root 身份运行 makepkg[已禁止][2] 除了 PKGBUILD 可能包含任意命令之外,以 root 身份构建通常被认为是不安全的。[3] 没有常规用户帐户访问权限的用户应以 nobody 用户身份运行 makepkg,例如使用命令 runuser -u nobody makepkg

要构建软件包,必须首先创建一个 PKGBUILD,或构建脚本,如创建软件包中所述。可以从 Arch 构建系统 (ABS) 树或 AUR 获取现有脚本。获得 PKGBUILD 后,切换到保存它的目录并运行以下命令来构建软件包

$ makepkg

如果缺少必需的依赖项,makepkg 将在失败之前发出警告。要构建软件包并安装所需的依赖项,请添加标志 -s/--syncdeps

$ makepkg --syncdeps

添加 -r/--rmdeps 标志会导致 makepkg 稍后删除不再需要的构建依赖项。如果经常构建软件包,请考虑不时使用 Pacman/技巧与窍门#删除未使用的软件包(孤立包)

注意
  • 这些依赖项必须在配置的软件仓库中可用;有关详细信息,请参阅 pacman#软件仓库和镜像。或者,可以在构建之前手动安装依赖项 (pacman -S --asdeps dep1 dep2)。
  • 安装依赖项时仅使用全局值,即拆分软件包的打包函数中所做的任何覆盖都将不会使用。

一旦满足所有依赖项并且软件包成功构建,将在工作目录中创建一个软件包文件 (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++ 编译器(例如 gccclang)的选项由 CFLAGSCXXFLAGSCPPFLAGS 环境变量 控制。为了在 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

pacman 5.2.2 版本开始,makepkg.conf 也包含了对 RUSTFLAGS 环境变量的覆盖,用于传递给 Rust 编译器的标志。Rust 编译器还可以通过在给定的 RUSTFLAGS 值中添加 -C target-cpu=native 来检测并启用特定于架构的优化。

/etc/makepkg.conf.d/rust.conf
RUSTFLAGS="-C opt-level=2 -C target-cpu=native"

要查看这将启用哪些 CPU 功能,请运行

$ rustc -C target-cpu=native --print cfg

在不使用 -C target-cpu=native 的情况下运行 --print cfg 将打印默认配置。opt-level 参数可以根据需要更改为 3sz。有关详细信息,请参阅Rust 编译器文档

改进构建时间

并行编译

make 构建系统使用 MAKEFLAGS 环境变量来指定 make 的附加选项。该变量也可以在 makepkg.conf 文件中设置。

拥有多核/多处理器系统的用户可以指定同时运行的作业数。这可以通过使用 nproc(1) 来确定可用处理器数量来实现,例如 MAKEFLAGS="--jobs=$(nproc)"

一些 PKGBUILD 会专门使用 -j1 覆盖此设置,因为某些版本中存在竞争条件,或者仅仅是因为它一开始就不受支持。因 此而构建失败的软件包应在错误跟踪器上报告(或者对于 AUR 软件包,则向软件包维护者报告),在确保错误确实是由 MAKEFLAGS 引起的之后。

有关可用选项的完整列表,请参阅 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 链接器

moldld/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"

要将 mold 用于 Rust 软件包,请将 -C link-arg=-fuse-ld=mold 附加到 RUSTFLAGS。例如

/etc/makepkg.conf.d/rust.conf
RUSTFLAGS="... -C link-arg=-fuse-ld=mold"

禁用调试软件包和 LTO

Commit 90bf367e 包含在 2024 年 2 月 pacman 6.0.2-9 中,默认启用了 debuglto 选项。

构建调试软件包使官方仓库能够为用户提供更多工具来解决问题(archlinux/packaging/packages/pacman#23#note_173528),但这在您自己构建软件包时不是必需的,并且会减慢构建过程。请参阅 archlinux/packaging/packages/pacman#23#note_173782

链接时优化 可以生成更优化的二进制文件,但会大大延长构建过程(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),以加快压缩速度。-T0 标志默认包含在 /etc/makepkg.conf 中的 COMPRESSZST 数组中,这使 zstd 可以使用与物理 CPU 核心数量相同的线程数来压缩软件包。可以使用 --auto-threads=logical 标志指示 zstd 基于逻辑 CPU 计数,从而进一步增加使用的线程数

COMPRESSZST=(zstd -c -T0 --auto-threads=logical -)

lz4xz 默认是多线程的,因此无需在 /etc/makepkg.conf 中进行任何更改。

pigzgzip 的直接替代的并行实现,默认情况下使用所有可用的 CPU 核心(-p/--processes 标志可用于使用较少的内核)

COMPRESSGZ=(pigz -c -f -n)

pbzip2bzip2 的直接替代的并行实现,默认情况下也使用所有可用的 CPU 核心。-p# 标志可用于使用较少的内核(注意:-p 和核心数之间没有空格)。

COMPRESSBZ2=(pbzip2 -c -f)

lbzip2bzip2 的另一个直接替代的并行实现,默认情况下也使用所有可用的 CPU 核心。-n 标志可用于使用较少的内核。

COMPRESSBZ2=(lbzip2 -c -f)

plzipAURlzip 的多线程实现,默认情况下也使用所有可用的 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 位软件包指南

无人值守的软件包签名

本文或本节是与GnuPG#无人值守的密码短语合并的候选对象。

注意: 这并非 makepkg 特有。(在Talk:Makepkg中讨论)

本文或本节需要扩充。

原因: 另一种选择是 gnupg-set-passphrase(1)[死链接 2022-06-25] (在Talk:Makepkg中讨论)

在诸如 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 密码短语由您选择的自动化套件安全地提供和隐藏。

生成的 zstsig 文件可以被期望有效签名的 pacman 客户端以及使用 repo-add --sign 创建的仓库引用,当您托管自己的仓库时。

Magnet URI

可以使用 transmission-dlagentAUR 下载代理来添加对 source 字段中 magnet URI 资源(带有 magnet:// 前缀)的支持。

在 systemd 控制组中运行 makepkg

如果您正在构建的软件包占用过多资源,无法使用您为大多数软件包正确设置的默认 make 标志构建,您可以尝试在其自己的控制组中运行它。makepkg-cgAURmakepkg 的包装器,它通过 systemd 控制组实现了这一点(请参阅 systemd.resource-control(5))。

以空闲调度策略运行

软件包构建过程可能导致 CPU 使用率过高,尤其是在#并行编译的情况下。在高 CPU 负载下,系统可能会发出明显的减速,甚至变得无法使用,即使使用最高的 nice(1) 值也是如此。用户界面和前台应用程序可能会卡顿甚至变得无响应。

可以通过在运行 makepkg 之前将调度策略更改为 SCHED_IDLE 来解决此问题。它可以确保软件包构建过程不会干扰常规任务,并且仅利用剩余的未使用 CPU 时间。

来自 sched(7) § SCHED_IDLE:调度极低优先级的作业

此策略旨在以极低的优先级(甚至低于使用 SCHED_OTHERSCHED_BATCH 策略的 +19 nice 值)运行作业。

可以通过运行 chrt(1) 命令并使用 -i 标志来设置 SCHED_IDLE 策略,指定优先级为 0(SCHED_IDLE 唯一有效的选项),并指定当前 shell 的 PID。

对于大多数 shell

$ chrt -iap 0 $$
提示: 您可以将此命令放入 makepkg.conf 中,以便应用于每次构建。

对于 fish shell,其中未设置 $$

$ chrt -iap 0 %self

每个软件包目录内的相对路径

除了为软件包输出选项使用绝对路径外,您还可以在每个软件包目录内配置相对路径。

注意: 以下选项可能会导致某些 AUR 助手出现问题,因为它们可能会在 $startdir 未定义的情况下使用 makepkg.conf。因此请小心。

例如,您可以在 makepkg.conf 文件中按如下方式定义目标路径。 $startdir 变量指的是您构建软件包时 PKGBUILD 所在的目录。

PKGDEST="$startdir/build/packages/"
SRCDEST="$startdir/build/sources/"
SRCPKGDEST="$startdir/build/srcpackages/"
LOGDEST="$startdir/logs/"

这将导致

  • 构建的软件包将存储在:"package directory"/build/packages/
  • 所有下载的源文件将存储在:"package directory"/build/sources/
  • 构建的源码包将存储在:"package directory"/build/srcpackages/
  • 所有日志将存储在:"package directory"/logs/

makepkg 仍然会像往常一样创建 src/pkg/ 目录,这是预期行为。

故障排除

为基于 QMAKE 的软件包指定安装目录

qmake 生成的 makefile 使用环境变量 INSTALL_ROOT 来指定程序的安装位置。因此,此软件包函数应该有效

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__ 宏,并将完整路径传递给编译器。

Makepkg 在代理后面下载依赖项失败

makepkg 调用依赖项时,它会调用 pacman 来安装软件包,这需要通过 sudo 获取管理员权限。但是,sudo 不会将任何环境变量传递给特权环境,包括代理相关的变量 ftp_proxyhttp_proxyhttps_proxyno_proxy

为了使 makepkg 在代理后面工作,请调用以下方法之一。

通过在 XferCommand 中设置其 URL 来启用代理

可以设置 XferCommand 以在 /etc/pacman.conf 中使用所需的代理 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,以阻止其默认的 CPPFLAGSCFLAGSCXXFLAGSLDFLAGS

!makeflags,以阻止其默认的 MAKEFLAGS

!debug,以阻止其默认的 DEBUG_CFLAGSDEBUG_CXXFLAGS,以防 PKGBUILD 是调试构建。

如果这些中的任何一个解决了问题,则可能需要提交上游错误报告,假设已识别出有问题的标志。

参见