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 in debian/control, to-be-packed into a .dsc file.
binary package
A Debian binary package defined by a Package stanza in debian/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, or
  • Package: 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.

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.