Getting started
Welcome to Rust, and packaging all things Rust in Debian! Like for other languages, you should first get an idea of what Rust is if you haven't.
Rust is famous for its comprehensive and well written documentation; check out the official learning materials, including The Rust Programming Language. There's also a crash course on the language grammar at Learn X in Y minutes, though it doesn't cover many aspects integral to the Rust ecosystem.
Rust has an extremely streamlined and easy to use build system called
cargo. Most of the time, just run cargo build
to build a program. There
is also cargo check
, to check if the code is correct without fully building
it; cargo run
to run a built program; and cargo test
to run tests.
Dependencies are managed consistently. There's no need to fight with the build
system like CMake.
Like other languages that are normally compiled, Rust crates are thus split
into two categories: binary (programs only, as of now) and library. Binary
crates are more or less simple in terms of packaging: they aren't used by other
crates. Point cargo to Debian's crate registry (using source
replacement), run cargo build --release
, install the
resulting executable, and you are done. (Well, there are also accessories like
man pages and shell completions, but you get the idea.) Libraries, on the other
hand, are more involved.
What is a crate? TRPL covers it in detail, but in the context of Debian Rust packaging, we often use package to refer to a Debian package, and crate to refer to a Rust package.
Things to know
Before diving into packaging the new shiny thing, some things ought to be known:
First steps for newcomers and packaging new binary crates
This section is for newcomers from the Rust community who want to package binary crates in Debian. First-of-all you should first read the Debian Packaging Guide. Even though these packaging guide is not Rust specific, it is a good introduction to the Debian packaging process in general.
As newcomer, new binary crates is maybe what you intent to package. In consequence, you should first is to generate a ITP (Intent To Package) bug report, so that the Rust and Debian community knows about it. This bug report allows you to get first feedback blockers from the community. For example, if the binary name conflicts with an existing package, or if there is already an ITP for the binary.
Now that you have an ITP, you need to know what dependencies your crate needs.
Indeed, all Debian Rust packages must be packaged with all their dependencies
packaged resursively too. to know the state of your crate you can use
cargo debstatus
command inside your crate directory. This command will show
you the packaging status of your crate and its dependencies.
However, this command is not always accurate, and you may need to manually
check the dependencies of your crate. You can use apt-cache search rust-<dep>*
to look for if the dependency is already packaged or not.
At this point, you may have a list of dependencies that are not packaged yet or requires updating. We recommend you to start creating an Tracking issue in the Debian Rust Team issue tracker to track the progress of your packaging. A simple command could generate the markdown checklist for the issue:
cargo debstatus | grep -E "🔴|⌛" | sed -e 's/[├─└│]*//g' | tr -s " " | sed -E 's/ *(🔴|⌛)/- [ ]/g' | sort | uniq | sort -t'(' -k2
Now you are ready to start packaging everything! You can follow the rest of this book to learn how to package Rust crates in Debian.
Static linking
In contrast to the C/C++ world, which is the source of a norm in Linux/*nix that is dynamic linking, Rust, like Go, is statically linked. Advantages and disadvantages aside (both linking strategies have both), this means Rust libraries are not packaged like C/C++ libraries, which carry foo.so objects; but rather, they are simply a pack of source files, installed to a specific location. (Rust could build as dynamic libraries, "dylibs" by its jargon, but the support is not as mature as static linking, and has yet to see an example in Debian.)
Features, SemVer, and encoding in virtual packages
Below crates, they have another level of optional compilation and dependency,
called features. With presence and absence of the #[cfg(feature = "feather")]
macro, certain parts of the source code could be included or
excluded in compilation, or conditional compilation. They can
also enable or disable optional dependencies.
To make features work in Debian, the policy states they are
encoded as virtual packages: librust-foo-dev Provides
librust-foo+feather-dev, encoding its "feather" feature. A package who
Depend
s on it semantically "depends" on that feature. Sometimes, though
rarely, there is a feature dependency loop, then we have to split feature
packages into real ones (i.e. Package: librust-foo+feather-dev
).
Another thing encoded in virtual packages is semantic versioning:
librust-foo-1.2.3-dev Provides
version 1.2.3 of the "foo" crate. This is
due to the semantic differences between (cargo's flavor of) SemVer and Debian
version specifiers. A package who Depends
on librust-foo-1.2-dev semantically
"depends" on at least version 1.2 of the "foo" crate.
Combined, librust-foo-1.2.3+feature-dev
Provides the "feather" feature of
version 1.2.3 of the "foo" crate.
This sure leads to exponential bloat of the Provides
field. Do you have a
better idea? Don't hesitate to talk to us ;)
Patching, a lot of patching, and integrity
Due to various reasons, we can't always just use the original sources, and need to patch them. The most common ones are 1. dependency version mismatch, 2. removing things Debian don't have or need, 3. adapting to Debian specific things, 4. devendoring and excluding non-packageable content.
This has a minor consequence: cargo projects have a Cargo.lock file, "locking" the versions and checksums of dependencies, ensuring their integrity. However, we often don't have the pristine sources in Debian. Thus, we remove the lock file when building, instead rely on Debian package checksums.
Tests
Debian has two types of tests:
- Pre-install tests run in
debian/rules
, targetdh_auto_test
- Post-install tests defined in
debian/tests/control
, run by/with autopkgtest
(1) determines if packages build and enter unstable; (2) determines if packages could migrate to testing.
For Debian Rust packages, in (1) the crate's test suite is run with default
features but only if there are no dev-dependencies, and in (2) the whole test
suite is run once for each feature with only it enabled, once with
--no-default-features
, and once with --all-features
.
The autopkgtests are named with Features: test-name=name
. The convention is:
--all-features
is namedlibrust-foo-dev:@
--no-default-features
is namedlibrust-foo-dev:
(nothing after:
)--no-default-features --features bar
is namedlibrust-foo-dev:bar
, includingdefault