Rust Packaging Guidelines

This document details best practices for packaging Rust crates. Note that the rust2rpm tool, available as a Fedora package or at https://pagure.io/fedora-rust/rust2rpm, automates many of these steps. It is advisable to try rust2rpm $crate first before attempting to write a specfile by hand.

Naming

Rust crates MUST be named rust-$crate. The crates are expected to be from crates.io. Rust applications that aren’t from crates.io MUST follow the main guidelines for package names.

At this time, Rust libraries MUST be from crates.io, as this enforces a certain standard in how they are packaged and built.

Dependencies

Packages MUST have BuildRequires: rust-packaging.

Automatic Dependency Generation

rust-packaging automatically creates Requires/Provides based on %{cargo_registry}/*/Cargo.toml files.

The Provides generator creates crate($name) = $version and crate($name/$feature) = $version for each "feature" the crate provides.

The automatic requirement generator takes this into account and creates the appropriate rich dependency to ensure that the code works.

For example, syn = { version = "0.11", features = ["visit"] } becomes Requires: ((crate(syn) >= 0.11.0 with crate(syn) < 0.12.0) with crate(syn/visit)).

BuildRequires

Packagers MUST specify all BuildRequires according to the definition in Cargo.toml, for example:

[dependencies]
atty = "0.2.2"
[build-dependencies]
clap = "2.24.1"

should become

# [dependencies]
BuildRequires:  (crate(atty) >= 0.2.2 with crate(atty) < 0.3.0)
# [build-dependencies]
BuildRequires:  (crate(clap) >= 2.24.1 with crate(clap) < 3.0.0)

Versions

Packagers SHOULD use latest version of dependent crates.

Packagers SHOULD patch crates to use the latest version of dependent crates to reduce maintenance burden.

When doing so, packagers SHOULD forward these upstream so that the upstream software is fixed to support the latest versions of their dependencies.

ExclusiveArch

All rust packages MUST have ExclusiveArch: %{rust_arches}.

Others

Packagers MUST run %cargo_prep to prepare configuration for further cargo invocations (sets up RUSTFLAGS and all other stuff).

Exclude unnecessary files

  • Packagers SHOULD exclude files which are not used by anything (things like appveyor.yml and CI scripts).

  • Packagers SHOULD use exclude field in Cargo.toml instead of using %exclude

  • Packagers SHOULD forward such patches to upstream

Example:

--- csv-1.0.1/Cargo.toml	1970-01-01T01:00:00+01:00
+++ csv-1.0.1/Cargo.toml	2018-09-25T07:14:47.639840+02:00
@@ -22,6 +22,7 @@
 categories = ["encoding", "parser-implementations"]
 license = "Unlicense/MIT"
 repository = "https://github.com/BurntSushi/rust-csv"
+exclude = ["/.travis.yml", "/appveyor.yml", "/ci/*", "/scripts/*"]
 [profile.bench]
 debug = true

Nightly, Other Platforms, etc. crates

Packagers MUST NOT package crates which do not work with the distribution. That is, if the crate depends on nightly-only features or works only for non-Linux platforms, the crate is not suitable for inclusion in Fedora.

If the crate can be made usable, packagers MUST patch packages which use such dependencies, for example:

--- cryptovec-0.3.4/Cargo.toml  2017-05-20T16:48:34+02:00
+++ cryptovec-0.3.4/Cargo.toml  2017-06-20T16:02:18.204182+02:00
@@ -10,5 +10,3 @@

 [dependencies]
 libc = "0.2"
-winapi = "0.2"
-kernel32-sys = "0.2"
--- cryptovec-0.3.4/src/lib.rs  2017-05-20 16:48:10.000000000 +0200
+++ cryptovec-0.3.4/src/lib.rs  2017-06-20 16:04:31.530991420 +0200
@@ -13,7 +13,9 @@
 // limitations under the License.
 //
 extern crate libc;
+#[cfg(windows)]
 extern crate winapi;
+#[cfg(windows)]
 extern crate kernel32;
 use libc::{malloc, free, c_void};
 #[cfg(not(windows))]

Such patches SHOULD be forwarded upstream.

Examples

Library

Rust library packages are packaged as source-only packages, as we do not build dynamic link libraries at this time due to the lack of a stabilized ABI for Rust.

rust-clap.spec
# Generated by rust2rpm
%bcond_without check
%global debug_package %{nil}

%global crate clap

Name:           rust-%{crate}
Version:        2.25.0
Release:        1%{?dist}
Summary:        Simple to use, efficient, and full featured  Command Line Argument Parser

License:        MIT
URL:            https://crates.io/crates/clap
Source0:        https://crates.io/api/v1/crates/%{crate}/%{version}/download#/%{crate}-%{version}.crate
# Initial patched metadata
# * clippy is nightly
# * Loose unicode-segmentation requirements, it was done to support old Rust
Patch0:         clap-2.25.0-fix-metadata.diff

ExclusiveArch:  %{rust_arches}

BuildRequires:  rust-packaging
# [dependencies]
BuildRequires:  (crate(ansi_term) >= 0.9.0 with crate(ansi_term) < 0.10.0)
BuildRequires:  (crate(atty) >= 0.2.2 with crate(atty) < 0.3.0)
BuildRequires:  (crate(bitflags) >= 0.9.0 with crate(bitflags) < 0.10.0)
BuildRequires:  (crate(strsim) >= 0.6.0 with crate(strsim) < 0.7.0)
BuildRequires:  (crate(term_size) >= 0.3.0 with crate(term_size) < 0.4.0)
BuildRequires:  (crate(textwrap) >= 0.6.0 with crate(textwrap) < 0.7.0)
BuildRequires:  (crate(unicode-segmentation) >= 1.1.0 with crate(unicode-segmentation) < 2.0.0)
BuildRequires:  (crate(unicode-width) >= 0.1.4 with crate(unicode-width) < 0.2.0)
BuildRequires:  (crate(vec_map) >= 0.8.0 with crate(vec_map) < 0.9.0)
BuildRequires:  (crate(yaml-rust) >= 0.3.5 with crate(yaml-rust) < 0.4.0)
%if %{with check}
# [dev-dependencies]
BuildRequires:  (crate(lazy_static) >= 0.2.0 with crate(lazy_static) < 0.3.0)
BuildRequires:  (crate(regex) >= 0.2.0 with crate(regex) < 0.3.0)
%endif

%description
%{summary}.

%package        devel
Summary:        %{summary}
BuildArch:      noarch

%description    devel
A simple to use, efficient, and full featured  Command Line Argument Parser.

This package contains library source intended for building other packages
which use %{crate} from crates.io.

%prep
%autosetup -n %{crate}-%{version} -p1
%cargo_prep

%build
%cargo_build

%install
%cargo_install

%if %{with check}
%check
%cargo_test
%endif

%files          devel
%license LICENSE-MIT
%doc README.md CHANGELOG.md CONTRIBUTORS.md
%{cargo_registry}/%{crate}-%{version}/

Binary

Application packages are compiled into binaries, with the application name used as the package name for the output binary package.

rust-ripgrep.spec
# Generated by rust2rpm
%bcond_without check

%global crate ripgrep

Name:           rust-%{crate}
Version:        0.5.2
Release:        3%{?dist}
Summary:        Line oriented search tool using Rust's regex library

License:        Unlicense or MIT
URL:            https://crates.io/crates/ripgrep
Source0:        https://crates.io/api/v1/crates/%{crate}/%{version}/download#/%{crate}-%{version}.crate
# Initial patched metadata
# * No simd
# * No paths
# * Bump encoding_rs to 0.6, https://github.com/BurntSushi/ripgrep/pull/518
Patch0:         ripgrep-0.5.2-fix-metadata.diff

ExclusiveArch:  %{rust_arches}

BuildRequires:  rust-packaging
# [dependencies]
BuildRequires:  (crate(atty) >= 0.2.2 with crate(atty) < 0.3.0)
BuildRequires:  (crate(bytecount) >= 0.1.4 with crate(bytecount) < 0.2.0)
BuildRequires:  (crate(clap) >= 2.24.1 with crate(clap) < 3.0.0)
BuildRequires:  (crate(encoding_rs) >= 0.6.0 with crate(encoding_rs) < 0.7.0)
BuildRequires:  (crate(env_logger) >= 0.4.0 with crate(env_logger) < 0.5.0)
BuildRequires:  (crate(grep) >= 0.1.5 with crate(grep) < 0.2.0)
BuildRequires:  (crate(ignore) >= 0.2.0 with crate(ignore) < 0.3.0)
BuildRequires:  (crate(lazy_static) >= 0.2.0 with crate(lazy_static) < 0.3.0)
BuildRequires:  (crate(libc) >= 0.2.0 with crate(libc) < 0.3.0)
BuildRequires:  (crate(log) >= 0.3.0 with crate(log) < 0.4.0)
BuildRequires:  (crate(memchr) >= 1.0.0 with crate(memchr) < 2.0.0)
BuildRequires:  (crate(memmap) >= 0.5.0 with crate(memmap) < 0.6.0)
BuildRequires:  (crate(num_cpus) >= 1.0.0 with crate(num_cpus) < 2.0.0)
BuildRequires:  (crate(regex) >= 0.2.1 with crate(regex) < 0.3.0)
BuildRequires:  (crate(same-file) >= 0.1.1 with crate(same-file) < 0.2.0)
BuildRequires:  (crate(termcolor) >= 0.3.0 with crate(termcolor) < 0.4.0)
# [build-dependencies]
BuildRequires:  (crate(clap) >= 2.24.1 with crate(clap) < 3.0.0)
BuildRequires:  (crate(lazy_static) >= 0.2.0 with crate(lazy_static) < 0.3.0)

%description
%{summary}.

%package     -n %{crate}
Summary:        %{summary}

%description -n %{crate}
Line oriented search tool using Rust's regex library. Combines
the raw performance of grep with the usability of the silver searcher.

%prep
%autosetup -n %{crate}-%{version} -p1
%cargo_prep

%build
%cargo_build

%install
%cargo_install
install -D -p -m0644 doc/rg.1 %{buildroot}%{_mandir}/man1/rg.1

%if %{with check}
%check
%cargo_test
%endif

%files       -n %{crate}
%license LICENSE-MIT UNLICENSE COPYING
%doc README.md CHANGELOG.md
%{_bindir}/rg
%{_mandir}/man1/rg.1*

Library + Binary

rust-cbindgen.spec
# Generated by rust2rpm
%bcond_without check

%global crate cbindgen

Name:           rust-%{crate}
Version:        0.6.3
Release:        2%{?dist}
Summary:        Tool for generating C bindings to Rust code

# Upstream license specification: MPL-2.0
License:        MPLv2.0
URL:            https://crates.io/crates/cbindgen
Source0:        https://crates.io/api/v1/crates/%{crate}/%{version}/download#/%{crate}-%{version}.crate
# Initial patched metadata
# * Bump pinned serde, https://github.com/eqrion/cbindgen/pull/207
Patch0:         cbindgen-fix-metadata.diff

ExclusiveArch:  %{rust_arches}

BuildRequires:  rust-packaging
# [dependencies]
BuildRequires:  (crate(clap) >= 2.0.0 with crate(clap) < 3.0.0)
BuildRequires:  (crate(log) >= 0.4.0 with crate(log) < 0.5.0)
BuildRequires:  ((crate(serde) >= 1.0.0 with crate(serde) < 2.0.0) with crate(serde/derive))
BuildRequires:  crate(serde_derive) = 1.0.58
BuildRequires:  (crate(serde_json) >= 1.0.0 with crate(serde_json) < 2.0.0)
BuildRequires:  ((crate(syn) >= 0.14.0 with crate(syn) < 0.15.0) with crate(syn/clone-impls) with crate(syn/derive) with crate(syn/extra-traits) with crate(syn/full) with crate(syn/parsing) with crate(syn/printing))
BuildRequires:  (crate(tempfile) >= 3.0.0 with crate(tempfile) < 4.0.0)
BuildRequires:  (crate(toml) >= 0.4.0 with crate(toml) < 0.5.0)

%description
%{summary}.

%package     -n %{crate}
Summary:        %{summary}

%description -n %{crate}
%{summary}.

%package        devel
Summary:        %{summary}
BuildArch:      noarch

%description    devel
A tool for generating C bindings to Rust code.

This package contains library source intended for building other packages
which use %{crate} from crates.io.

%prep
%autosetup -n %{crate}-%{version} -p1
%cargo_prep

%build
%cargo_build

%install
%cargo_install

%if %{with check}
%check
%cargo_test
%endif

%files       -n %{crate}
%license LICENSE
%doc README.md
%{_bindir}/cbindgen

%files          devel
%license LICENSE
%doc ARCHITECTURE.md CONTRIBUTING.md README.md
%{cargo_registry}/%{crate}-%{version}/