Packaging policy
Current revision: 2023.2.11
NOTE: This is a work in progress to move the old policy on the wiki here and tidy it up.
The revision is the last update date of the wiki page; to be decided.
Note on automation tools
Debian Rust Team uses the debcargo
package preparation tool and
the dh-cargo
debhelper buildsystem to automate most requirements
of this policy. Normally it's not necessary to manually implement them. This
policy mainly serves as the specification, with accompanying explanation, and
highlights those parts not automatically handled.
Terminology
- source package
- A Debian source package defined by a
Source
stanza indebian/control
, to-be-packed into a.dsc
file. - binary package
- A Debian binary package defined by a
Package
stanza indebian/control
, to-be-built into a.deb
file. - crate
- Cargo's unit-of-source-distribution.
Under this policy, each Cargo crate corresponds to a single Debian source package. - library crate
- A Cargo crate containing a library-type target. Each crate can have none or one of these. If none then it's not a "library crate" for the purposes of this document.
Under this policy, each library crate is translated to one or more Debian binary packages, a "main library package" and some number of "feature library packages". - application crate
- A Cargo crate containing a binary-type target. Each crate can have any number of these. If none then it's not an "application crate" for the purposes of this document.
Under this policy, all the binary-type targets of a crate are bundled into one Debian binary package.
Package naming
Rust library crates must have one of the following package names:
- Either
Source: rust-$cratename
Package: librust-$cratename-dev
- Or
Source: rust-$cratename-$shortversion
Package: librust-$cratename-$shortversion-dev
, where$shortversion
is explained below.
If the crate name contains underscores (_
), the source and binary package
names must replace them with dashes (-
); in this case, dh-cargo requires the
package's debian/control
file to have an X-Cargo-Crate:
field in the source
section specifying the upstream name of the crate.
$shortversion
is any prefix of $crateversion
needed to support
co-installation, in the case where multiple versions of the same crate are
needed to satisfy the build dependencies of another package. For a crate with a
semantic version (SemVer) major.minor.patch
, it is recommended to
use major
(if major != 0
) or 0.minor
(if major == 0
). This supports the
Cargo and SemVer notion of "compatible versions"; see
http://doc.crates.io/specifying-dependencies.html#caret-requirements. The
latest version of a crate in Debian should use the first naming option above,
without any $shortversion
.
Rust applications may use any binary package name; they do not need to include a version in the package name or allow concurrent installation of multiple versions. A source package that builds no Rust library packages may use any source package name; a source package that builds both Rust library packages and Rust application packages must follow the naming rules for its source package name and its library package names.
This policy does not yet specify any policies for cdylib
crates, though they
will need to follow all applicable policies for shared libraries.
Crate features
A library crate must also generate binary packages for every Cargo "feature" it declares, named:
Package: librust-$cratename+$featurename-dev
, orPackage: librust-$cratename-$shortversion+$featurename-dev
depending on the naming choice made for the source and main library packages earlier.
Cargo crate names do not allow a plus sign (+
) in them, so this package name
will not conflict with the package name for any other Rust library crate or
feature package. If the feature name contains underscores (_
), the
corresponding binary package name must replace them with dashes (-
).
Each feature package must have a versioned dependency on the same version of the main library package. Each feature package must pull in all the dependencies needed to build a package that depends on that feature of the crate, including any dependencies on other features of the same crate. That is, if a feature of the crate depends on another feature of the same crate, the corresponding feature package for the first feature must have a dependency on the same version of the feature package for the second feature.
In order to avoid too many feature packages, it is allowed to "collapse" some
of them together by using Provides
. Specifically, if a feature package pulls
in the same-or-a-subset-of additional dependencies (i.e. from other source
packages) as another feature package, the latter package may Provide:
the
former package at version (= $crateversion)
in which case the former package
may be dropped as a real package. The main library package may participate in
this process as well.
Provides
field
For a crate version X.Y.Z, the main library package must declare Provides
:
librust-$cratename-dev (= $crateversion),
librust-$cratename-X-dev (= $crateversion),
librust-$cratename-X.Y-dev (= $crateversion),
librust-$cratename-X.Y.Z-dev (= $crateversion)
.
where up to one of these may be omitted if it is the same name as the binary package.
A library binary package that includes features 0 .. n-1
with crate version
X.Y.Z must declare Provides
for the following:
librust-$cratename+$feature[i]-dev (= $crateversion)
librust-$cratename-X+$feature[i]-dev (= $crateversion)
librust-$cratename-X.Y+$feature[i]-dev (= $crateversion)
librust-$cratename-X.Y.Z+$feature[i]-dev (= $crateversion)
for all i
in [0 .. n-1]
, where up to one of these may be omitted if it is
the same name as the binary package.
Package dependencies
Library packages must include Depends
corresponding to the crate and feature
dependencies specified in the crate's Cargo.toml
file. These dependencies
must satisfy the crate's [dependencies]
and [build-dependencies]
sections,
including any architecture-specific or target-specific dependencies; this
allows users and packages to cross-compile for other target platforms,
including Windows. However, the dependencies need not satisfy the crate's
[dev-dependencies]
, as those may require experimental or nightly tools for
testing or code analysis.
The version constraints on those dependencies must ensure that the version
satisfies the corresponding version predicates in Cargo.toml
, so that the
package can build successfully. However, in some situations, you may want to
loosen the version predicates in Cargo.toml and the corresponding version
constraints in debian/control. In particular, this can arise if the version
constraints across multiple such packages would otherwise require multiple
versions of a library crate simultaneously (such as two libraries with the same
non-zero major version and different minor versions), while loosening the
version constraints would allow packaging just the newer version of that
library crate. If you think you may need to change a crate's dependencies,
please contact the mailing list first. (These cases most commonly arise because
Cargo cannot currently express constraints on external non-Rust packages,
including the rustc compiler itself; upstream may depend on an older version
because they do not want to require a newer rustc compiler. Debian can use the
newer compatible version for all such packages, once Debian packages the newer
rustc compiler.)
In order for constraints to be preserved correctly, a Cargo dependency on a
given crate must be translated to a single item in the comma-separated
Depends:
AND-list in the metadata of a Debian binary package. This single
item could be a |-separated OR-list, so for example a constraint like foo (>= 6.1, << 9.5)
must be translated to librust-foo-6-dev (>= 6.1) | librust-foo-7-dev | librust-foo-8-dev | librust-foo-9-dev (<< 9.5)
, and
''not'' for example librust-foo-dev (>= 6.1), librust-foo-dev (<< 9.5)
.
debcargo as of version 2.1.0 implements an algorithm to do this. Furthermore,
in the translated Debian dependencies it is recommended to use only >=
and
<<
constraints and append a ~~
onto version names, in order to better allow
for backporting and future patches to the same crate.
Library packages may have Recommends
and Suggests
for their feature
packages.
Library packages may do a test build in dh_auto_test
and in these cases they
must have Build-Depends
on the Rust library and feature packages needed to
run this test build, with <!nocheck>
annotated as appropriate. Otherwise,
they do not need any Build-Depends
since they do not build any Rust code when
constructing their binary -dev
packages (which only contain source code; see
below). Application crates must have Build-Depends
on the Rust library and
feature packages needed to build, with appropriate version constraints
corresponding to the version predicates of that crate's Cargo dependencies.
Library package structure
A library crate's main binary package must ship the source code of the crate in
the /usr/share/cargo/registry/$cratename-$version/
directory.
Motivation: At the time of writing, Rust does not have a stable ABI. So, we can't reasonably ship compiled versions of Rust libraries. Instead, library packages ship source code, and application packages build all the library crates they use from source. We will revisit this point, if the situation changes.
The main binary package must also ship a .cargo-checksum.json
file in that
directory. This file must include a key "package"
, whose value provides the
SHA256 checksum of the corresponding upstream .crate file, as an ASCII hex
lowercase string. This file must also include a key "files", with either the
value {}
, or a value providing checksums of the source files in the package
in the format expected for a Cargo directory registry. dh-cargo expects the
source package to contain this file as debian/cargo-checksum.json
.
This format allows using /usr/share/cargo/registry
as a Cargo directory
registry with directory sources. For more details on how this mechanism works,
see http://doc.crates.io/source-replacement.html#directory-sources.
Feature packages should not contain any files, other than a symlink from
/usr/share/doc/packagename
to the main library package.
In order to support cross-compiling, each library binary package - the main
package plus the feature packages - should use Architecture: any
and
Multi-Arch: same
. (This is not ideal; it is due to a deficiency in the
current Debian cross-compiling infrastructure and the Multi-Arch
specification.) This applies even if the library crate only runs on certain
target architectures or operating systems. Note that if running a test build in
dh_auto_test
, this might need to be disabled based on the target/foreign
architecture (DEB_HOST_ARCH
) and unlikely but possibly also the host/native
architecture (DEB_BUILD_ARCH
).
A higher-level library crate or application crate may use an
architecture-specific library conditionally, using Cargo conditionals to only
require it on the architectures it supports. In such cases the Depends
or
Build-Depends
may be annotated with architecture-specific constraints. If an
application crate depends unconditionally on a non-portable library crate, that
makes the application itself non-portable, and the application can use a
correspondingly narrower Architecture
field.
Package build process
Package builds must not allow Cargo to access the network when building. In particular, they must not download or check out any sources at build time. Instead, builds must use the packaged versions of crate sources, via the corresponding library crate packages, which provide a Cargo directory registry.
Package builds must set $CARGO_HOME
to a directory within the package build
directory, to avoid writing to the building user's home directory outside the
package build directory.
Package builds should run Cargo in --verbose
mode. They may set --cap-lints warn
to avoid newer compiler warnings breaking old crates, which is mostly
relevant for test builds done as part of dh_auto_test
but also relevant when
building old applications.
Package builds should pass on the Rust equivalent of anything set in
dpkg-buildflags
. dh-cargo
should contain code for the latest version of
this; an incomplete list includes -g -O2
and translating LDFLAGS
to -C link-arg=
arguments.
Package builds should support cross-compiling by passing --target ${DEB_HOST_RUST_TYPE}
and -C linker=${DEB_HOST_GNU_TYPE}-gcc
. These
variables are available from /usr/share/{dpkg,rustc}/architecture.mk
. Again,
dh-cargo
should contain code for the latest version of this.
Maintenance
Library crates should generally have
Maintainer: Debian Rust Maintainers <pkg-rust-maintainers@alioth-lists.debian.net>
to keep them grouped together, and to simplify transitions and rebuilds. Applications may list any maintainer, but please coordinate with the Rust Team when packaging new Rust libraries or applications. Application packages may require bin-NMUs to rebuild with newer versions of Rust libraries.
Copyright
Rust packages must have a debian/copyright
file that follows DEP 5,
"Machine-readable debian/copyright".
If the license places the restriction such that derivative works in binary form
can only be distributed together with the source code, and the license is not
one of the reserved short names in the format document just mentioned, then the
copyright file must contain a X-Binary-Requires-Source: yes
line in the
header stanza. This allows dh-cargo to correctly generate
Built-Using fields for packages that are built from this
package.
Upstreaming changes
Packaging a Rust library or application may in some cases require changes to its source code, to integrate better with the Debian ecosystem, comply with Debian policies, or otherwise produce better results with packaging. Whenever possible, prefer to integrate such changes upstream, rather than as patches in Debian (upstream-first approach). Whenever possible, seek to improve the debcargo logic or the upstream crate metadata used by that, rather than patching the resulted generated sources (automation-first approach). This makes the Rust and crates.io ecosystem easier to maintain both upstream and in Debian, and avoids technical debt.
dh-cargo
Rust library and application packages should use dh-cargo, to simplify implementation of the Rust packaging policy, including any future transitions or new policy requirements.
Architectures and Multi-Arch
Rust library packages must use Architecture: any
in debian/control
. The
detailed reasoning is explained below. It should never be changed unless dpkg
changes fundamentally. dpkg maintainers also confirmed this to be best Debian
policy.
The concrete problem is related to cross-compilation. If a rust package were arch:any that build-depended on another arch:all rust lib that in turn (runtime-)depended on an arch:any package, cross-compilation will fail as dpkg will be unable to resolve the correct architecture for the last package.
Why Architecture: any
?
Copied from debcargo comment:
This is the best but not ideal option for us.
Currently Debian Multi-Arch spec has a deficiency where a package X that build-depends on a M-A:foreign + arch:all package that itself depends on an arch:any package Z, will pick up the BUILD_ARCH of package Z instead of the HOST_ARCH. This is because we currently have no way of telling dpkg to use HOST_ARCH when checking that the dependencies of Y are satisfied, which is done at install time without any knowledge that we're about to do a cross-compile. It is also problematic to tell dpkg to "accept any arch" because of the presence of non-M-A:same packages in the archive, that are not co-installable - different arches of Z might be depended-upon by two conflicting chains. (dpkg has so far chosen not to add an exception for the case where package Z is M-A:same co-installable).
The recommended work-around for now from the dpkg developers is to make our packages arch:any M-A:same even though this results in duplicate packages in the Debian archive. For very large crates we will eventually want to make debcargo generate -data packages that are arch:all and have the arch:any -dev packages depend on it.