Documents
packaging-rust
packaging-rust
Type
External
Status
Published
Created
Jun 13, 2026
Updated
Jun 13, 2026
Source
View

Packaging Rust Projects#

Load when packaging a Rust project with Cargo for dakota/Bluefin BuildStream.

When NOT to Use#

  • Go project → packaging-go.md
  • Zig project → packaging-zig.md
  • Pre-built binary → packaging-binaries.md

Overview#

Rust elements use kind: make with a cargo2 source block that vendors all crate dependencies offline. BST builds are network-isolated — cargo fetch is not available at build time.

Scaffolding (Fastest Path)#

There is no scaffold command. Copy an existing Rust element as a starting point:

cp elements/bluefin/sudo-rs.bst elements/bluefin/<name>.bst
# Edit name, URL, version, binary name, and regenerate cargo2 sources

Then regenerate cargo2 sources (see below).

cargo2 Sources Are Generated — Never Hand-Written#

# Generate cargo2 source block from Cargo.lock
python3 files/scripts/generate_cargo_sources.py path/to/Cargo.lock

After bumping a git ref with just bst source track bluefin/<name>.bst:

  1. Enter the build sandbox to get the new Cargo.lock: just bst shell --build bluefin/<name>.bst
  2. Run generate_cargo_sources.py on the new Cargo.lock
  3. Replace the generated block in the element

The cargo2 source block (starting at the first - kind: cargo2 line) is generated output. Do not edit it manually.

Element Structure#

kind: make

build-depends:
- freedesktop-sdk.bst:components/rust.bst
- freedesktop-sdk.bst:bootstrap-import.bst

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

variables:
  version: '1.2.3'
  cargo-home: '%{build-root}/.cargo'
  cargo-opts: '--release --locked'

config:
  build-commands:
  - |
    export CARGO_HOME="%{cargo-home}"
    cargo build %{cargo-opts} --bin project

  install-commands:
  - install -Dm755 "target/release/project" "%{install-root}%{bindir}/project"
  - '%{install-extra}'

sources:
# ── hand-authored section above; generated cargo2 block below ──
- kind: git_repo
  url: github:owner/project.git
  track: main
  ref: abc123...
# ── GENERATED: do not edit below this line ──
- kind: cargo2
  url: "https://static.crates.io/crates/some-crate/some-crate-1.0.0.crate"
  ref: sha256hex...
# ... (many more cargo2 entries)

Cargo Flags#

FlagPurpose
--releaseOptimized build
--lockedUse exact Cargo.lock versions — required for reproducibility
--offlinePrevent any network access during build

Use --locked --offline together:

variables:
  cargo-opts: '--release --locked --offline'

Tracking Group#

Rust elements require human review when bumping refs because cargo2 regeneration touches many lines. Add the element to the manual-merge matrix in .github/workflows/track-bst-sources.yml (not auto-merge).

systemd Service (if needed)#

install-commands:
  # ... install binary ...
  - |
    install -Dm644 /dev/stdin "%{install-root}%{indep-libdir}/systemd/system/project.service" <<'SERVICE'
    [Unit]
    Description=Project
    After=network.target

    [Service]
    ExecStart=/usr/bin/project
    Restart=on-failure

    [Install]
    WantedBy=multi-user.target
    SERVICE
  - |
    install -Dm644 /dev/stdin "%{install-root}%{indep-libdir}/systemd/system-preset/80-project.preset" <<'PRESET'
    enable project.service
    PRESET

Checklist#

  • cargo2 sources generated by script (not hand-written)
  • --locked --offline in cargo-opts
  • strip-binaries: "" NOT needed (Rust produces ELF)
  • Tracking group set to manual-merge in tracking workflow
  • Element added to elements/bluefin/deps.bst
  • just validate passes
  • just bst build bluefin/<name>.bst passes

Lessons Learned#

Wrong template: copy tailscale.bst (pre-built binary), not for Rust-from-source (2026-06-07)#

The "Scaffolding" section above said to copy tailscale.bst as a starting point for Rust
elements. That's wrong — tailscale.bst is a pre-built binary element (kind: manual without
cargo). The correct Rust-from-source template is sudo-rs.bst:

cp elements/bluefin/sudo-rs.bst elements/bluefin/<name>.bst

sudo-rs.bst uses kind: make + cargo build --release and has the correct cargo2 source
structure.

overlap-whitelist required when binary conflicts with another element (2026-06-07)#

sudo-rs replaces the system sudo, so it needs an explicit whitelist for the paths it shares
with the upstream sudo element from fdsdk. Without it, the OCI image build fails with an
overlap error:

public:
  bst:
    overlap-whitelist:
    - /usr/bin/sudo
    - /usr/bin/sudoedit
    - /usr/lib/debug/usr/bin/sudo.debug

Any Rust element replacing a binary that already exists in fdsdk or gnome-build-meta will need
this. Use just bst show --format '%{name}: %{overlap-whitelist}' oci/bluefin.bst to audit.

cargo build --release alone (no --locked --offline) works because cargo2 provides deps (2026-06-07)#

The cargo2 source block vendors all crates offline. BST's hermetic sandbox means no network
access is possible, so --offline is redundant (it will succeed either way). --locked is
still recommended as best practice but sudo-rs.bst omits it without issue. The build will
still fail with a clear error if cargo2 sources are missing or mismatched — treat that as a
signal to regenerate cargo2 sources, not a reason to add --offline.

Fat LTO + codegen-units=1 causes SIGABRT in BST btrfs overlay on ghost (2026-06-07)#

Upstream Rust crates that set lto = true + codegen-units = 1 in their [profile.release]
emit -C linker-plugin-lto -C codegen-units=1 to each rustc invocation. Inside BST's
btrfs overlay sandbox on ghost (Podman container + btrfs), LLVM's fat LTO codegen pass
corrupts its own allocator:

realloc(): invalid next size
rustc ... -C linker-plugin-lto -C codegen-units=1 (signal: 6, SIGABRT)

This is a ghost-environment issue — CI's remote execution server handles fat LTO fine.

Do NOT fix in the element. Put the override in ghost's userconfig to avoid
invalidating the remote CAS artifact (see ci.mdGhost-specific build fixes):

# ~/.config/buildstream/userconfig.yaml on ghost
projects:
  dakota:
    elements:
      bluefin/uutils-coreutils.bst:
        environment:
          CARGO_PROFILE_RELEASE_LTO: "thin"

Thin LTO provides most cross-crate optimization benefits with far less memory per
codegen unit and does not trigger the allocator corruption.

packaging-rust | Dosu