Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Packaging workspaces, crates not published on crates.io, and projects being partly written in Rust

Certain projects pack tens of internal crates in a workspace. Some of them still publish on crates.io, but it's quite a hassle to package them one by one with the single crate process, not to mention maintaining them in sync later; some don't, often seen in projects that build programs - these internal crates see no use outside.

Thus, there is a desire to package such projects as a single source package, and either each library crate as a binary package, or only one or few binary package(s) packing the built program(s).

We are still exploring ways to build them. Recorded here are some existing practices, not team recommendations.

For programs, it doesn't matter if the crates are not published on crates.io. For libraries not on crates.io, however, their package name scheme has yet to be decided, since the librust-*-dev namespace is reserved for libraries from crates.io. Luckily, there is currently no need to.

General setup

This kind of packaging falls back to "traditional" ways in Debian. This assumes you are already familiar with general Debian packaging. Experience with the single crate process isn't strictly needed but is helpful.

  1. Import upstream source by either cloning the upstream repository, gbp import-orig, or ways you prefer.
  2. Prepare the common things in debian/ as usual.
  3. Check packaging status of dependencies. There is currently no ready-made tool for this, but you can gather all [dependencies], [dev-dependencies], and [build-dependencies] in Cargo.toml of these internal crates into an empty cargo project, and run cargo-debstatus there.
  4. Add them as Build-Depends along with rustc and cargo in debian/control (you might use debcargo deb-dependencies Cargo.toml as a starting point)
  5. Choose a build approach: there is dh-rust which seemingly supports workspaces, there is dh-cargo which doesn't yet but works fine for single crates (example), and there is a semi-manual approach for other programs or projects not written in pure Rust (example #1, example #2).
  6. Patch the source as needed.

No need to publish internal crates

This is the easy part. After adding needed dependencies as B-D, patching them to use versions in Debian, patching out unavailable dependencies and feature depending on them, use one of the debhelper buildsystems described above.

Publish internal crates

Consider these situations:

  1. Crates in workspace are released in lockstep, sharing the same version for each release.
  2. They only reside in a workspace, but are released in their own pace.

There's not much to say about (1). For (2), though, there is a problem: the source package, or the workspace, has a version. Each binary package, or crate, also has their own version. The only known way to achieve this is to dpkg-gencontrol/dh_gencontrol for each binary package; see rust-curve25519-dalek for an example.

Currently, use dh-rust. dh-cargo has no workspace support yet.

Add a Package stanza for each of them in debian/control, which Provides their versioned feature virtual packages. Policy states which are needed.

Then it's the tiresome work of keeping B-Ds, Packages, and Depends in sync. There hasn't been strong urge to write a tool for this.

Manual wrapper approach

In case you are packaging a complex application or shared library written in Rust, or a project combining Rust with other languages, and do not need to package the contained Rust source code as librust-xxx-dev package, consider using the cargo wrapper in /usr/share/cargo/bin/cargo together with some setup in debian/rules.

You can setup the environment variables like this:


include /usr/share/dpkg/default.mk
include /usr/share/rustc/architecture.mk

export DEB_HOST_RUST_TYPE
export DEB_HOST_GNU_TYPE
export PATH := /usr/share/cargo/bin:$(PATH)
export CARGO=/usr/share/cargo/bin/cargo
export CARGO_HOME=$(CURDIR)/debian/cargo_home
export DEB_CARGO_CRATE=$(DEB_SOURCE)_$(DEB_VERSION_UPSTREAM)

and prepare the cargo wrapper for packaging usage like this:

execute_before_dh_auto_configure:
	/usr/share/cargo/bin/cargo prepare-debian debian/cargo_registry --link-from-system

This invocation will prepare a cargo config file for the package build, pointing at a local registry directory instead of crates.io. It will also ensure that debug symbols are included in built binaries, optimization and LTO flags set by the builder are honored and that the correct target is selected even when cross-building.

Depending on the build system used, you might need to add additional compat symlinks or instructions to ensure every tool involved in the build picks up the correct target directory and calls the cargo wrapper instead of the "real" cargo.

Finally, do not forget to call dh-cargo-built-using to emit Built-Using and Static-Built-Using substvars at the end of the build:

execute_after_dh_auto_install:
	/usr/share/cargo/bin/dh-cargo-built-using <MAIN PACKAGE NAME>

and include them in the debian/control file.