CtrlK
BlogDocsLog inGet started
Tessl Logo

packaging-rust-cargo-projects

Use when packaging a Rust project with Cargo.toml, when an element needs cargo2 sources for offline builds, or when generating cargo dependency lists for BuildStream

85

Quality

82%

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Advisory

Suggest reviewing before use

SKILL.md
Quality
Evals
Security

Packaging Rust/Cargo Projects

Overview

Rust projects in this BuildStream repo use kind: make elements with cargo2 sources for offline dependency vendoring. The element overrides build-commands to call cargo build --release directly. Do NOT use kind: cargo elements or kind: cargo sources -- they don't exist in this project's plugin set.

When to Use

  • Project has a Cargo.toml and builds with cargo build
  • You need to package crate dependencies for offline BuildStream builds
  • You're replacing an upstream binary with a Rust alternative (overlap-whitelist)
  • You're generating cargo2 source lists from a Cargo.lock file

Element Kind and Build Dependencies

Always use kind: make with these build-depends:

kind: make

build-depends:
  - freedesktop-sdk.bst:components/rust.bst
  - freedesktop-sdk.bst:public-stacks/buildsystem-make.bst

Both are required. rust.bst provides the Rust toolchain. buildsystem-make.bst provides the make build system that kind: make expects.

Source Structure

A Rust element has two sources:

sources:
  # 1. Project source (git_repo for tracked projects, tar for releases)
  - kind: git_repo
    url: github:<org>/<repo>.git
    track: <ref-or-tag-pattern>
    ref: <commit-or-tag-ref>

  # 2. Cargo dependencies (offline vendored crates)
  - kind: cargo2
    ref:
      # Registry crates (from crates.io)
      - kind: registry
        name: <crate-name>
        version: <version>
        sha: <sha256>

      # Git-hosted crates (from GitHub/GitLab repos, rare)
      - kind: git
        commit: <git-commit>
        repo: github:<org>/<repo>
        query:
          rev: <git-commit>
        name: <crate-name>
        version: <version>

Critical: The source kind is cargo2, NOT cargo. Each crate is listed under ref: with kind: registry (for crates.io) or kind: git (for git-hosted crates).

Projects without Cargo.lock in repo

Some upstream projects don't commit their Cargo.lock. For these, add a gen_cargo_lock source between the git_repo and cargo2 sources:

sources:
  - kind: git_repo
    url: github:<org>/<repo>.git
    track: <ref>
    ref: <ref>
  - kind: gen_cargo_lock
    ref: <base64-encoded-Cargo.lock-content>
  - kind: cargo2
    cargo-lock: Cargo.lock
    ref:
      - kind: registry
        ...

The gen_cargo_lock source generates the Cargo.lock file at source time. The cargo2 source then references it with cargo-lock: Cargo.lock.

Build and Install Commands

Override build-commands with a direct cargo invocation:

config:
  build-commands:
    - cargo build --release

  install-commands:
    - install -Dm755 target/release/<binary> "%{install-root}/usr/bin/<binary>"

Add feature flags as needed:

build-commands:
    - cargo build --release --features feat_os_unix --no-default-features

Install Patterns

Simple binary:

install-commands:
    - install -Dm755 target/release/<binary> "%{install-root}/usr/bin/<binary>"

Setuid binary (e.g., sudo-rs replacing sudo):

install-commands:
    - install -Dm4755 target/release/sudo "%{install-root}/usr/bin/sudo"
    - ln -sr "%{install-root}/usr/bin/sudo" "%{install-root}/usr/bin/sudoedit"

-Dm4755 sets the setuid bit. Use this when the binary needs elevated privileges at runtime.

Multi-call binary with symlinks (e.g., uutils-coreutils):

install-commands:
    - install -Dm755 target/release/coreutils "%{install-root}/usr/bin/uutils-coreutils"
    - |
      for prog in $(target/release/coreutils --help | grep -E '^  [a-z]+' | awk '{print $1}'); do
        ln -sr "%{install-root}/usr/bin/uutils-coreutils" "%{install-root}/usr/bin/uutils-${prog}"
      done

Custom make targets (e.g., upstream bootc with man pages, systemd units):

variables:
  make-install-args: >-
    PREFIX="%{prefix}" LIBDIR="%{lib}" DESTDIR="%{install-root}" install-all

This uses kind: make's built-in install mechanism instead of custom install-commands.

Replacing Upstream Binaries (overlap-whitelist)

When your Rust binary intentionally replaces a file from an upstream dependency (e.g., sudo-rs replacing GNU sudo), declare the overlap:

public:
  bst:
    overlap-whitelist:
      - /usr/bin/sudo
      - /usr/bin/sudoedit

Without this, BuildStream will error on overlapping files during layer composition.

Generating cargo2 Source Lists

The helper script files/scripts/generate_cargo_sources.py reads a Cargo.lock and outputs the cargo2 source YAML:

# Clone the project, then:
python3 files/scripts/generate_cargo_sources.py path/to/Cargo.lock

This outputs registry crate entries. Note: The script does NOT handle git-hosted crates -- add kind: git entries manually by inspecting the Cargo.lock for source = "git+https://..." entries.

For git-hosted crates, the format is:

- kind: git
  commit: <the-git-commit-from-Cargo.lock>
  repo: github:<org>/<repo>
  query:
    rev: <same-git-commit>
  name: <crate-name>
  version: <version-from-Cargo.lock>

Element Template

kind: make

build-depends:
  - freedesktop-sdk.bst:components/rust.bst
  - freedesktop-sdk.bst:public-stacks/buildsystem-make.bst
  # Add other build-time deps (e.g., linux-pam, systemd, openssl)

depends:
  - freedesktop-sdk.bst:public-stacks/runtime-minimal.bst
  # Add runtime deps

config:
  build-commands:
    - cargo build --release

  install-commands:
    - install -Dm755 target/release/<binary> "%{install-root}/usr/bin/<binary>"

sources:
  - kind: git_repo
    url: github:<org>/<repo>.git
    track: <commit-or-tag>
    ref: <commit-or-tag>
  - kind: cargo2
    ref:
      - kind: registry
        name: <crate>
        version: <version>
        sha: <sha256>
      # ... more crates

Dependency Tracking

ElementLocationTracked ByGroup
sudo-rselements/bluefin/sudo-rs.bstNot tracked--
uutils-coreutilselements/bluefin/uutils-coreutils.bstNot tracked--

Elements in the bst source track workflow get automatic PRs when upstream refs change. Elements not tracked require manual version bumps. Note: bootc is an upstream gnome-build-meta element (gnomeos-deps/bootc.bst) and is tracked by GNOME upstream, not by this project.

Common Mistakes

MistakeSymptomFix
Using kind: cargo elementElement kind not found / wrong build behaviorUse kind: make and override build-commands
Using kind: cargo sourceSource kind not foundUse kind: cargo2
Manual cargo vendor approachDoesn't work in BuildStream's offline sandboxUse cargo2 source kind -- it handles vendoring
Missing buildsystem-make.bst build-depMake-related errors during buildAdd freedesktop-sdk.bst:public-stacks/buildsystem-make.bst
Missing rust.bst build-depcargo command not foundAdd freedesktop-sdk.bst:components/rust.bst
Wrong install permissions for setuidBinary doesn't have elevated privilegesUse -Dm4755 not -Dm755 for setuid binaries
Missing overlap-whitelistBuild fails with overlap error during layer compositionAdd public.bst.overlap-whitelist listing conflicting paths
Forgetting git crates in cargo2Build fails with unresolved dependencyCheck Cargo.lock for git+https:// sources, add kind: git entries

Real Examples

  • Simple binary with setuid: elements/bluefin/sudo-rs.bst (41 lines)
  • Complex with system integration: gnome-build-meta.bst:gnomeos-deps/bootc.bst (upstream GNOME element, ~1358 lines, includes kind: git cargo2 entries for composefs-rs, systemd units, man pages)
  • gen_cargo_lock pattern: gnomeos-deps/zram-generator.bst (in gnome-build-meta junction)
Repository
projectbluefin/dakota
Last updated
Created

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.