Rust 包指南
本文档涵盖了为用 Rust 编写的软件编写 PKGBUILD 的标准和指南。
包命名
在打包 Rust 项目时,包名几乎总是应该与生成的二进制文件名相同。请注意,打包库 crate 没有意义,因此只打包带有 bin 的 crate。对于生成多个二进制文件的 crate,上游 crate 名称通常是合适的。无论如何,包名应该全部小写。
Source(生效配置)
大多数 Rust 项目可以从 tarball、源档案(例如 GitHub releases 上的源链接)或任何其他已发布的源进行构建。
当其他源不可用时,大多数 Rust 项目发布在 crates.io 上,它提供了一个稳定的下载 URL 方案供 cargo 使用。这种源的缺点是它通常不包含所有测试文件、许可证文件或其他通常存在于其他源中的资源。如果需要,PKGBUILD#source 可以使用以下模板
source=("$pkgname-$pkgver.tar.gz::https://static.crates.io/crates/$pkgname/$pkgname-$pkgver.crate")
依赖
虽然一些 Rust 项目有外部依赖,但大多数项目只使用 Rust 生态系统库,这些库是静态嵌入在最终二进制文件中的。因此,大多数项目不需要指定很多 depends。大多数 Rust 二进制文件通常会链接 glibc 库,因此 gcc-libs 和 glibc 通常是大多数 Rust 包的依赖项。如果构建过程查找并链接任何系统库,可能会有更多依赖项。
对于 makedepends,绝大多数 Rust 项目都被设计为使用 cargo 依赖管理器进行构建,该管理器协调下载库以满足构建时依赖项,并进行所有必要的 rustc(实际的 Rust 编译器)调用。目前 cargo 和 rustc 都由 rust 包提供,但也有其他方法可以同时或分别获取这两个包,包括 rustup 包。因此,大多数 PKGBUILD 将调用的工具是 cargo,你应该直接依赖它。
makedepends=(cargo)
如果项目需要使用 Rust 工具链的 nightly 版本,请使用
makedepends=(cargo-nightly)
准备
Rust 依赖管理器 cargo 能够提前下载构建项目所需的所有库。在 prepare() 阶段运行此获取操作,可以使后续的 build() 和其他阶段完全离线运行。
prepare() {
export RUSTUP_TOOLCHAIN=stable
cargo fetch --locked --target $(rustc --print host-tuple)
}
其中
RUSTUP_TOOLCHAIN=stable确保默认工具链设置为 stable,以防用户更改了他们的默认设置。当然,如果上游项目需要,应该将其设置为 nightly。这可以避免在非 chroot 环境中构建时用户偏好设置带来的常见副作用。另请注意,如果上游项目在其源中有一个rust-toolchain文件或rust-toolchain.toml文件来实现此目的,则 不需要 设置此环境变量。--locked告诉 cargo 严格遵守Cargo.lock文件中指定的版本,并阻止其更新依赖项。这对于 可复现构建 很重要。--target $(rustc --print host-tuple)告诉 cargo 只获取构建指定目标平台所需的依赖项,从而减少下载量(请参阅 PKGBUILD#arch 和 Rust 平台支持)。
Cargo.lock 文件与 Cargo.toml 同步,请在运行 cargo fetch 之前添加 cargo update。构建的其他方面应按此处记录的方式工作,尽管结果将不是完全可复现的构建,因为依赖项将在构建时解析。构建
构建 Rust 包。
build() {
export RUSTUP_TOOLCHAIN=stable
export CARGO_TARGET_DIR=target
cargo build --frozen --release --all-features
}
其中
--release告诉 cargo 编译一个发布版本(默认是调试版本)--frozen告诉 cargo 保持离线状态,并且只使用Cargo.lock文件中指定的版本以及在prepare()阶段的 fetch 操作缓存的版本。这在功能上等同于--locked --offline,也可以使用。这对于 可复现构建 很重要。--all-features告诉 cargo 启用包的所有功能进行编译。或者,如果你只想启用选定的功能,可以使用--features FEATURE1,FEATURE2。CARGO_TARGET_DIR=target告诉 cargo 将输出放在当前目录的相对路径 target 中。这可以避免在非 chroot 环境中构建时用户偏好设置带来的常见副作用。
检查
大多数 Rust 项目提供了一种简单的方式来运行测试套件。
check() {
export RUSTUP_TOOLCHAIN=stable
cargo test --frozen --all-features
}
你也应该检查仓库是否是 cargo workspace。只需打开 /Cargo.toml 文件,看看它是否包含 [workspace] 部分。如果是,你应该在 cargo test 中添加 --workspace 标志,以确保也运行了 workspace 成员的所有测试。
--release 标志运行测试,因为它会启用编译器优化并禁用某些功能,如整数溢出检查和 debug_assert!() 宏,所以理论上你 可能 会捕获到更少的问题。由于正在测试的最终二进制工件与我们打包的工件不同,并且我们没有测试构建工具链,因此对于大多数 Rust 项目,在发布模式下测试没有什么好处。软件包 (Package)
Rust 在 target/release 中构建二进制文件,并可以简单地安装到 /usr/bin。
package() {
install -Dm0755 -t "$pkgdir/usr/bin/" "target/release/$pkgname"
}
如果一个包在 /usr/bin 中有多个可执行文件,你可以使用 find 命令
package() {
find target/release \
-maxdepth 1 \
-executable \
-type f \
-exec install -Dm0755 -t "$pkgdir/usr/bin/" {} +
}
关于使用 cargo install 的注意事项
一些包应该安装更多文件,例如 man 页或其他资源。如果这样的包没有其他安装这些文件的方式,可以使用 cargo install。在这种情况下,build() 是不必要的,因为 cargo install 会强制重新构建,即使包已经被 cargo build 构建过。prepare() 阶段仍然可以用来提前获取源文件。
package() {
export RUSTUP_TOOLCHAIN=stable
cargo install --no-track --frozen --all-features --root "$pkgdir/usr/" --path .
}
--no-track 参数应该始终使用,否则 cargo install 将创建不需要的文件,例如 /usr/.crates.toml 或 /usr/.crates2.json。
完整的 PKGBUILD 模板
# Maintainer: Firstname Lastname <email@example.org>
pkgname=
pkgver=
pkgrel=1
pkgdesc=''
url=''
license=()
makedepends=('cargo')
depends=()
arch=('i686' 'x86_64' 'armv6h' 'armv7h')
source=()
b2sums=()
prepare() {
export RUSTUP_TOOLCHAIN=stable
cargo fetch --locked --target "$(rustc --print host-tuple)"
}
build() {
export RUSTUP_TOOLCHAIN=stable
export CARGO_TARGET_DIR=target
cargo build --frozen --release --all-features
}
check() {
export RUSTUP_TOOLCHAIN=stable
cargo test --frozen --all-features
}
package() {
install -Dm0755 -t "$pkgdir/usr/bin/" "target/release/$pkgname"
# for custom license, e.g. MIT
# install -Dm644 LICENSE "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
}
示例包
在包页面上点击 Package Actions > Source Files 查看其示例 PKGBUILD。