Rust 程序如何从 Cargo 项目创建 Debian 包??
cargo-deb
是一个 Cargo 辅助命令,它能够自动从 Cargo 项目创建二进制 Debian 包(.deb
格式)。
❝注意
cargo-deb
使用xz2
crate,该 crate 包含了 liblzma 5.2 的一个旧版安全版本,由原始维护者提供,以及一个简单的基于 Cargo 的构建脚本。 它不受CVE-2024-3094 的影响。
安装
rustup update # Debian中的Rust版本可能过时,请使用rustup.rs
cargo install cargo-deb
需要 Rust 1.63 或以上版本,可选需要dpkg
、dpkg-dev
和liblzma-dev
。与 Ubuntu 兼容。如果 LZMA 依赖导致问题,尝试cargo install cargo-deb --no-default-features
。
如果编译出错,请运行rustup update
!如果运行rustup update
出错,请卸载 rust/cargo 包,然后安装官方 Rust。
使用方法
cargo deb
在 Rust 项目的根目录下运行cargo deb
命令后,Debian 包将被创建在target/debian/<project_name>_<version>-1_<arch>.deb
(或者你可以使用--output
选项更改位置)。这个包可以使用dpkg -i target/debian/*.deb
安装。
默认情况下,主二进制文件的调试符号会被剥离,除非你在Cargo.toml
中设置[profile.release] debug = true
。如果运行cargo deb --separate-debug-symbols
,调试符号将作为单独的文件打包,并安装在/usr/lib/debug/<path-to-binary>.debug
。这也可以配置在[package.metadata.deb]
节中的separate-debug-symbols键。如果在那里启用了,可以使用参数cargo deb --no-separate-debug-symbols
来抑制调试符号的包含。
cargo deb --install
构建并在整个系统中安装项目。
配置
不需要额外配置即可从具有二进制的 Cargo 项目创建基本包。此命令从Cargo.toml
文件中获取所需的基本信息。它使用 Cargo 字段:name
、version
、license
、license-file
、description
、readme
、homepage
和repository
。
为了创建一个更完整的 Debian 包,您还可以定义一个新表[package.metadata.deb]
,其中包含maintainer
、copyright
、license-file
、changelog
、depends
、conflicts
、breaks
、replaces
、provides
、extended-description
/extended-description-file
、section
、priority
和assets
。
如果 Debian 包包括一个或多个 systemd 单元文件,您可能还想定义一个新的(内联)表[package.metadata.deb.systemd-units]
,以便单元文件自动添加为资产,并且单元被正确安装。Systemd 集成
[package.metadata.deb]
选项
所有选项都是可选的:
name: Debian 包的名称。如果未提供,则使用 crate 的名称。 maintainer: 维护 Debian 打包的人。如果未提供,则使用第一个作者。 copyright: 软件版权授予谁以及何时授予。如果未提供,则使用作者列表。 license-file: 包含许可证文件位置和顶部要跳过的行数的 2 元素数组。如果未提供,则使用包级别的 license-file
。depends: 项目的运行时依赖。默认情况下自动生成,或者如果列表中包含 $auto
关键字。pre-depends: 项目的先决依赖。默认为空。 recommends: 项目的推荐依赖。默认为空。 suggests: 项目的建议依赖。默认为空。 enhances: 此包可以增强的包列表。默认为空。 conflicts、breaks、replaces、provides — 包过渡控制。 extended-description: 项目的扩展描述 —— 越详细越好。如果没有提供,则使用extended-description-file(见下文)或包的 readme
文件。extended-description-file: 包含项目扩展描述的文件。如果指定,当extended-description未提供时使用。 revision: Debian 包的附加版本(当包比项目更频繁地更新时)。默认为"1",但可以设置为空字符串以省略修订版。 section: 软件所属的应用类别。 priority: 定义包是 required
还是optional
。assets: 要包含在包中的文件及其要分配的权限。如果未指定资产,则从 [[bin]]
中列出的二进制文件(复制到/usr/bin/
)和包readme
(复制到usr/share/doc/…
)中获取默认值。
每个资产的第一个参数是该资产在 Rust 项目中的位置。允许使用通配符模式。即使 Cargo 配置为交叉编译或使用自定义的 CARGO_TARGET_DIR
,也可以在资产路径中使用target/release/
。目标目录路径将自动校正。第二个参数是文件将被复制到的位置。
如果参数以 /
结尾,将推断目标是文件将被复制到的目录。否则,将推断在复制时源参数将被重命名。
merge-assets: 见"高级使用"下的"合并资产"部分 maintainer-scripts: 包含 templates
、preinst
、postinst
、prerm
或postrm
脚本的目录。conf-files: 配置文件列表,包管理系统在包升级时不会覆盖这些文件。 triggers-file: 用于 dpkg 触发设施的触发控制文件的路径。 changelog: Debian 格式的变更日志文件的路径。 features: 构建包时使用的 Cargo 特性列表。 default-features: 是否在 features
列表之外使用默认的 crate 特性(默认为true
)。separate-debug-symbols: 是否保留调试符号,但从可执行文件中剥离它们并将它们保存在单独的文件中(默认为 false
)。preserve-symlinks: 是否保留资产文件中的符号链接(默认为 false
)。systemd-units: 自动安装 systemd 单元的可选配置设置。
自定义Cargo.toml
示例
[package.metadata.deb]
maintainer = "Michael Aaron Murphy <mmstickman@gmail.com>"
copyright = "2017, Michael Aaron Murphy <mmstickman@gmail.com>"
license-file = ["LICENSE", "4"]
extended-description = """
A simple subcommand for the Cargo package manager for \
building Debian packages from Rust projects.
"""
depends = "$auto"
section = "utility"
priority = "optional"
assets = [
["target/release/cargo-deb", "usr/bin/", "755"],
["README.md", "usr/share/doc/cargo-deb/README", "644"],
]
高级使用
Debian 包可以使用不同的压缩格式,但目标系统可能只支持其中一些。
默认格式目前是 xz,但随时可能更改以支持新格式。
可以使用--compress-type
命令行选项明确指定格式。支持的格式有"gzip"和"xz"。
--fast
标志使用较轻的压缩。适用于非常大的包或快速部署。
--compress-system
强制使用系统命令行工具进行数据压缩。
[package.metadata.deb.variants.$name]
一个Cargo.toml
文件中可以有多个元数据变体。--variant=name
选择要使用的变体。在变体中设置的选项会覆盖[package.metadata.deb]
中的选项。
合并资产
在定义变体时,定义资产合并策略也可能很有用。
如果使用merge-assets
选项,cargo-deb
将提供的资产列表与父资产列表合并。有三种合并策略:append
、by.dest
和by.src
。
merge-assets.append: 将此资产列表追加到父资产列表。 merge-assets.by.dest: 将此资产列表与父资产列表合并,按目标路径连接。将替换源路径和权限。 merge-assets.by.src: 将此资产列表与父资产列表合并,按源路径连接。将替换目标路径和权限。
注意:使用append
和by.*
选项都是允许的,前者在后者之前应用。
merge-assets
示例
# 示例父资产列表
[package.metadata.deb]
assets = [
# 二进制文件
["target/release/example", "usr/bin/", "755"],
# 资产
["assets/*", "var/lib/example", "644"],
["target/release/assets/*", "var/lib/example", "644"],
["3.txt", "var/lib/example/3.txt", "644"],
["3.txt", "var/lib/example/merged.txt", "644"],
]
# 示例追加资产列表
[package.metadata.deb.variants.mergeappend]
merge-assets.append = [
["4.txt", "var/lib/example/appended/4.txt", "644"]
]
# 示例按`dest`路径合并
[package.metadata.deb.variants.mergedest]
merge-assets.by.dest = [
["4.txt", "var/lib/example/merged.txt", "644"]
]
# 示例按`src`路径合并
[package.metadata.deb.variants.mergesrc]
merge-assets.by.src = [
["3.txt", "var/lib/example/merged-2.txt", "644"]
]
# 示例追加和按`src`路径合并
[package.metadata.deb.variants.mergeappendandsrc]
merge-assets.append = [
["4.txt", "var/lib/example/appended/4.txt", "644"]
]
merge-assets.by.src = [
["3.txt", "var/lib/example/merged-2.txt", "644"]
]
[package.metadata.deb.systemd-units]
见 Systemd 集成。
交叉编译
cargo deb
支持交叉编译。它可以在任何类 Unix 主机上运行,包括 macOS,只要为交叉编译设置好了构建环境:
交叉编译目标必须通过 rustup 安装(例如
rustup target add i686-unknown-linux-gnu
),并且必须为宿主系统安装(例如apt-get install libc6-dev-i386
)。注意 Rust 和 Debian 的架构名称不同。查看rustc --print target-list
获取--target
参数支持的值列表。必须安装并使 Linux 兼容的链接器和系统库(例如 glibc 或 musl)对 Rust/Cargo 可用,
dpkg --add-architecture <debian architecture name>
apt-get install pkg-config build-essential crossbuild-essential-<debian architecture name>
Cargo 必须配置为使用交叉链接器。
使用 C 库的 Cargo 依赖可能不会工作,除非你为 pkg-config 安装了目标的 sysroot。设置
PKG_CONFIG_ALLOW_CROSS=1
不会有任何帮助,只会让事情变得更糟。apt-get install libssl-dev:<debian architecture name>
构建 C 代码的 Cargo 依赖可能不会工作,除非你为目标系统安装了 C 编译器,并配置了适当的
CC_<target>
变量。export HOST_CC=gcc
export CC_x86_64_unknown_linux_gnu=/usr/bin/x86_64-linux-gnu-gcc
(为你的操作系统更正目标和路径)剥离可能不会工作,除非你安装了与目标兼容的版本,并在
.cargo/config
中配置了它们的路径,通过添加[target.<target triple>] strip = { path = "..." } objcopy = { path = "..." }
。或者,使用--no-strip
。
是的,这些要求很繁重。你也可以尝试使用cross
或cargo zigbuild
,因为 Zig 在交叉编译方面要好得多,然后运行cargo deb --target=… --no-build
。
cargo deb --target=i686-unknown-linux-gnu
交叉编译的存档保存在target/<target triple>/debian/*.deb
。实际存档路径在成功时打印。
注意,你不能使用交叉编译为旧版本的 Debian 构建。如果你需要支持比宿主更旧的 Debian 发行版,请考虑使用容器或 VM,或者为 MUSL 制作一个完全静态的二进制文件。
独立调试信息
要获取调试符号,请在Cargo.toml
中设置[profile.release] debug = true
。
cargo deb --separate-debug-symbols
从可执行文件中移除调试符号,并将它们作为独立文件放置在/usr/lib/debug
中。需要 GNU objcopy
工具。
自定义构建标志
如果你想自己处理构建过程,可以使用cargo deb --no-build
,这样cargo-deb
命令就不会尝试重新构建你的项目。
cargo deb -- <cargo build flags>
--
后的标记传递给cargo build
,因此你可以使用-Z
、--frozen
和--locked
等选项。请仅在cargo-deb
不支持的功能上使用它。
工作区
Cargo-deb 理解工作区,并且如果需要,可以构建工作区中的所有 crates。但是,你必须选择一个 crate 作为包元数据的来源。你可以使用-p crate_name
或--manifest-path=<path/to/Cargo.toml>
来选择要构建的 crate。
自定义版本字符串
cargo deb --deb-version my-custom-version
覆盖从 Cargo 清单生成的版本字符串。它还抑制了revision
选项。
故障排除
为了获得最大日志记录,请使用:
RUST_LOG=debug cargo deb --verbose
lzma_stream_encoder_mt
未定义引用错误
当系统提供的 LZMA 库太旧时会发生这种情况。尝试使用捆绑版本:
cargo install cargo-deb --features=static-lzma
或者通过设置--compress-system
标志使用 xz 命令行工具。
附录
https://lib.rs/crates/cargo-deb