Rust 项目自动化构建 Debian 包工具:cargo-deb 指南

文摘   2024-06-23 01:44   江苏  

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 或以上版本,可选需要dpkgdpkg-devliblzma-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 字段:nameversionlicenselicense-filedescriptionreadmehomepagerepository

为了创建一个更完整的 Debian 包,您还可以定义一个新表[package.metadata.deb],其中包含maintainercopyrightlicense-filechangelogdependsconflictsbreaksreplacesprovidesextended-description/extended-description-filesectionpriorityassets

如果 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: 此包可以增强的包列表。默认为空。
  • conflictsbreaksreplacesprovides — 包过渡控制。
  • 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/…)中获取默认值。
  1. 每个资产的第一个参数是该资产在 Rust 项目中的位置。允许使用通配符模式。即使 Cargo 配置为交叉编译或使用自定义的CARGO_TARGET_DIR,也可以在资产路径中使用target/release/。目标目录路径将自动校正。
  2. 第二个参数是文件将被复制到的位置。
  • 如果参数以/结尾,将推断目标是文件将被复制到的目录。
  • 否则,将推断在复制时源参数将被重命名。
  • 第三个参数是分配给该文件的权限(八进制字符串)。
    • merge-assets: 见"高级使用"下的"合并资产"部分
    • maintainer-scripts: 包含templatespreinstpostinstprermpostrm脚本的目录。
    • 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将提供的资产列表与父资产列表合并。有三种合并策略:appendby.destby.src

    • merge-assets.append: 将此资产列表追加到父资产列表。
    • merge-assets.by.dest: 将此资产列表与父资产列表合并,按目标路径连接。将替换源路径和权限。
    • merge-assets.by.src: 将此资产列表与父资产列表合并,按源路径连接。将替换目标路径和权限。

    注意:使用appendby.*选项都是允许的,前者在后者之前应用。

    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

    是的,这些要求很繁重。你也可以尝试使用crosscargo 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


    编程悟道
    自制软件研发、软件商店,全栈,ARTS 、架构,模型,原生系统,后端(Node、React)以及跨平台技术(Flutter、RN).vue.js react.js next.js express koa hapi uniapp Astro
     最新文章