| |
@@ -1,65 +1,133 @@
|
| |
= Rust Packaging Guidelines
|
| |
- :last-reviewed: 2019-03-28
|
| |
+ :last-reviewed: 2021-01-24
|
| |
|
| |
- 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.
|
| |
+ CAUTION: The workflow for Rust packaging on Fedora will be different starting with Fedora 34.
|
| |
+ Older releases (up until Fedora 33) do not ship source-only packages for Rust crates,
|
| |
+ but only application packages created via a special build process.
|
| |
+ In general, the Rust Packaging Guidelines apply to all current Fedora releases,
|
| |
+ only the build process differs depending on the targeted Fedora release.
|
| |
|
| |
- CAUTION: This document is applicable only for Fedora Rawhide.
|
| |
- Stable releases do not contain any crates.
|
| |
- However, this document can be used to build modules
|
| |
- with Rust applications
|
| |
- (with crates filtered out).
|
| |
+ == rust2rpm
|
| |
|
| |
- == Naming
|
| |
+ Note that the https://pagure.io/fedora-rust/rust2rpm[*rust2rpm*] tool
|
| |
+ (packaged as `python3-rust2rpm` for Fedora)
|
| |
+ automates most of the steps that are necessary for creating .spec files for Rust crates.
|
| |
|
| |
- Rust crates MUST be named `rust-$crate`.
|
| |
- The crates are expected to be from https://crates.io[crates.io].
|
| |
- Rust applications that aren't from crates.io MUST follow the main guidelines for package names.
|
| |
+ It is advisable to try `+rust2rpm $crate+` first,
|
| |
+ and to only modify its output for your needs,
|
| |
+ before attempting to write a .spec file by hand.
|
| |
|
| |
- At this time,
|
| |
- Rust libraries MUST be from crates.io,
|
| |
- as this enforces a certain standard in how they are packaged and built.
|
| |
+ Most of the time, rust2rpm output can be used almost without modifications,
|
| |
+ except for adding `LICENSE` files with `+%license+` to the appropriate `+%files+` listings,
|
| |
+ or sometimes trimming the `Summary` if the upstream description text is too long.
|
| |
|
| |
- == Dependencies
|
| |
+ == Package Naming
|
| |
|
| |
- Packages MUST have `+BuildRequires: rust-packaging+`.
|
| |
+ Packages for Rust crates MUST be named `rust-$crate`.
|
| |
+
|
| |
+ Any library crates that get packaged MUST be published on crates.io,
|
| |
+ since this enforces a certain standard in how crates are packaged and built,
|
| |
+ and also ensures that all crate dependencies are available from crates.io as well.
|
| |
+
|
| |
+ Crates that only contain an application -- but no library -- may be packaged
|
| |
+ even if they are not published on crates.io,
|
| |
+ because those packages are leaves and no other Rust packages can depend on them.
|
| |
+
|
| |
+ Rust applications that are not published to crates.io MUST follow the default xref:Naming.adoc[Naming Guidelines].
|
| |
+
|
| |
+ == Package sources
|
| |
+
|
| |
+ The primary Source for library crates -- that MUST be published on crates.io --
|
| |
+ MUST be specified as `+%{crates_source}+`,
|
| |
+ which uses the `+%{crate}+` and `+%{version}+` macros to calculate the download URL.
|
| |
+
|
| |
+ Application crates do not have to be published on crates.io,
|
| |
+ so only the general Guidelines for Sources apply.
|
| |
+
|
| |
+ In the case that crates published to crates.io are missing files
|
| |
+ (`.desktop` files for GUI applications, manual pages, default configuration files, etc.),
|
| |
+ they MAY be included from the upstream sources,
|
| |
+ but the packager SHOULD query upstream to include those missing files in their published crates.
|
| |
+
|
| |
+ == Package Dependencies
|
| |
+
|
| |
+ All Rust packages MUST have `+BuildRequires: rust-packaging+`.
|
| |
+
|
| |
+ === Bundled Dependencies
|
| |
+
|
| |
+ NOTE: As stated in the general xref:index.adoc#bundling[Packaging Guidelines],
|
| |
+ packages MUST be built against system libraries, if that is possible.
|
| |
+ For Rust, this means that packages MUST NOT use dependencies from a "vendor tarball"
|
| |
+ (e.g. crated by running `+cargo vendor+`),
|
| |
+ but package all library dependencies separately.
|
| |
+
|
| |
+ However, two common reasons that can make building a package
|
| |
+ against system libraries (crates) basically impossible are
|
| |
+ if a crate applies downstream patches on top of its dependencies,
|
| |
+ or if it depends on "internal" crates that are not published on crates.io.
|
| |
+ In these circumstances, packagers MAY use bundled dependencies,
|
| |
+ but - if possible - SHOULD work with upstream to, for example,
|
| |
+ make their downstream patches unnecessary, or publish internal crates.
|
| |
+
|
| |
+ If bundled dependencies are used to build a binary package,
|
| |
+ the subpackage that will contain the compiled binary
|
| |
+ MUST have `+Provides: bundled(crate(foo)) = version+` for each bundled crate
|
| |
+ and the packager MUST keep this list of crates and their versions up-to-date
|
| |
+ every time the package or its bundled dependencies are updated.
|
| |
|
| |
=== Automatic Dependency Generation
|
| |
|
| |
- `rust-packaging` automatically creates Requires/Provides
|
| |
- based on `+%{cargo_registry}/*/Cargo.toml+` files.
|
| |
+ `rust-packaging` automatically creates Requires and Provides
|
| |
+ based on cargo metadata in `+%{cargo_registry}/*/Cargo.toml+` files.
|
| |
|
| |
The Provides generator creates:
|
| |
|
| |
* `crate($name) = $version` for base package (`rust-$name-devel`)
|
| |
* `crate($name/$feature) = $version` for feature subpackages (`rust-$name+$feature-devel`)
|
| |
|
| |
- The automatic requirement generator takes this into account
|
| |
- and creates the appropriate rich dependencies
|
| |
- to ensure that the code works.
|
| |
+ The Provides / Requires generator relies on empty subpackages with special names
|
| |
+ for encoding the dependency information of optional features.
|
| |
+ These subpackages MUST be named `+rust-%{crate}+$FEATURE-devel+`
|
| |
+ for all features that are present in `Cargo.toml` after patching this file in `+%prep+`.
|
| |
|
| |
- For example:
|
| |
+ NOTE: These feature subpackage definitions are automatically correctly generated
|
| |
+ when using rust2rpm (if necessary, with the `-p` flag to apply any initial changes).
|
| |
+
|
| |
+ The dependency generator then creates `Requires` for all features that a crate depends on.
|
| |
+ For example, a dependency on `syn` with the `visit` and `extra-traits` features
|
| |
|
| |
....
|
| |
syn = { version = "0.15", features = ["visit", "extra-traits"] }
|
| |
....
|
| |
|
| |
- becomes
|
| |
+ will be encoded as
|
| |
|
| |
....
|
| |
- (crate(syn/default) >= 0.15.0 with crate(syn/default) < 0.16.0)
|
| |
- (crate(syn/extra-traits) >= 0.15.0 with crate(syn/extra-traits) < 0.16.0)
|
| |
- (crate(syn/visit) >= 0.15.0 with crate(syn/visit) < 0.16.0)
|
| |
+ Requires: (crate(syn/default) >= 0.15.0 with crate(syn/default) < 0.16.0)
|
| |
+ Requires: (crate(syn/extra-traits) >= 0.15.0 with crate(syn/extra-traits) < 0.16.0)
|
| |
+ Requires: (crate(syn/visit) >= 0.15.0 with crate(syn/visit) < 0.16.0)
|
| |
....
|
| |
|
| |
+ where the `default` feature of `syn` is also included,
|
| |
+ because the `default-features=false` option was not specified.
|
| |
+
|
| |
=== BuildRequires
|
| |
|
| |
- Packagers MUST specify all BuildRequires according to the definition in Cargo.toml,
|
| |
- for example:
|
| |
+ Rust packages SHOULD use automatic generation of `BuildRequires`
|
| |
+ by including this scriptlet between `+%prep+` and `+%build+`:
|
| |
+
|
| |
+ ....
|
| |
+ %generate_buildrequires
|
| |
+ %cargo_generate_buildrequires
|
| |
+ ....
|
| |
+
|
| |
+ This will automatically generate the necessary `BuildRequires` based on the `Cargo.toml` file.
|
| |
+ This step runs *after* `+%prep+`, so any modification of `Cargo.toml`
|
| |
+ (for example, after applying patches to remove dependencies or to modify versions of dependencies)
|
| |
+ will be taken into account.
|
| |
+
|
| |
+ If using `+%cargo_generate_buildrequires+` is not possible,
|
| |
+ `BuildRequires` MUST be specified manually and kept up-to-date with each package update:
|
| |
|
| |
....
|
| |
[dependencies]
|
| |
@@ -75,27 +143,68 @@
|
| |
BuildRequires: (crate(clap/default) >= 2.24.1 with crate(clap/default) < 3.0.0)
|
| |
....
|
| |
|
| |
- === Versions
|
| |
+ == Crate Versions
|
| |
+
|
| |
+ * Packagers SHOULD package the latest version of a crate.
|
| |
+ * Packagers SHOULD patch crates to use the latest version of their dependencies,
|
| |
+ to reduce the downstream maintenance burden and the need for compat packages.
|
| |
+ * When introducing patches to bump the version of dependencies,
|
| |
+ packagers SHOULD forward these patches to the upstream project
|
| |
+ to keep the divergence between downstream and upstream small over time.
|
| |
+
|
| |
+ === Compatibility packages for older crate versions
|
| |
+
|
| |
+ If it is not possible to port a crate to the version of a dependency that is available in Rawhide,
|
| |
+ a compatibility ("compat") package for the *older* version of a crate can be introduced.
|
| |
+ No package reviews for such compatibility packages are necessary,
|
| |
+ but they must follow the xref:Naming.adoc#multiple[Naming Guidelines] for compatibility packages.
|
| |
+
|
| |
+ For example, the latest `rust-nix` package might ship the latest version of `nix`,
|
| |
+ but some packages still require the older `0.14.1` version of the crate --
|
| |
+ in this case, the compatibility package would be called `rust-nix0.14`.
|
| |
+
|
| |
+ NOTE: Compatibility packages for older versions of library crates
|
| |
+ are always parallel-installable with each other,
|
| |
+ since all files in them are namespaced by both the crate's name *and* version.
|
| |
+
|
| |
+ However, compatibility packages for older crate versions MUST NOT ship application binaries.
|
| |
+ Neither the names of the subpackages that contain those binaries,
|
| |
+ nor the binaries themselves, are namespaced by the crate version.
|
| |
+ The package for the _older_ version MUST be adapted to remove its binary subpackage,
|
| |
+ so it only contains `-devel` subpackage(s) but no `+%{crate}+` subpackage,
|
| |
+ which would conflict with the corresponding package from the newer version of the crate.
|
| |
|
| |
- * 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.
|
| |
+ NOTE: When introducing a compatibility package, the packager SHOULD check
|
| |
+ if keeping the test suite enabled causes additional unwanted dependencies,
|
| |
+ for example, on other compatibility packages, or on old versions of other packages.
|
| |
+ If that is the case, the test suite SHOULD be disabled to lower the overall maintenance burden.
|
| |
|
| |
== ExclusiveArch
|
| |
|
| |
All rust packages MUST have `+ExclusiveArch: %{rust_arches}+`.
|
| |
|
| |
- == Others
|
| |
+ == License for binary packages
|
| |
|
| |
- Packagers MUST run `+%cargo_prep+` to prepare configuration for further cargo invocations
|
| |
- (sets up RUSTFLAGS and all other stuff).
|
| |
+ Since Rust applications are statically linked and contain code from all their dependencies,
|
| |
+ the effective license for the subpackage containing the built binary
|
| |
+ must be calculated from the individual licenses of all dependencies.
|
| |
|
| |
- === Exclude unnecessary files
|
| |
+ The `+rust2rpm+` project provides a https://pagure.io/fedora-rust/rust2rpm/blob/master/f/tools/fedora-helper.py[helper script]
|
| |
+ that can be used to obtain a list of Licenses of all real crate dependencies,
|
| |
+ excluding dependencies that are only required at build-time or for running tests.
|
| |
+
|
| |
+ Both the list of individual licenses *and* the `License` tag with the effective license
|
| |
+ MUST be included in the subpackage that contains the statically linked binary.
|
| |
+
|
| |
+ == Miscellaneous
|
| |
+
|
| |
+ Packagers MUST run `+%cargo_prep+` after unpacking the crate's sources in `+%prep+`,
|
| |
+ which sets up configuration for cargo (compilation flags, location of system crates, etc.).
|
| |
+
|
| |
+ === Excluding unnecessary files
|
| |
|
| |
* Packagers SHOULD exclude files which are not used by anything (things like `appveyor.yml` and CI scripts).
|
| |
- * Packagers SHOULD use https://doc.rust-lang.org/cargo/reference/manifest.html#the-exclude-and-include-fields-optional[exclude field in Cargo.toml] instead of using `+%exclude+`
|
| |
+ * Packagers SHOULD use the https://doc.rust-lang.org/cargo/reference/manifest.html#the-exclude-and-include-fields-optional[exclude field in Cargo.toml] instead of using `+%exclude+` in `+%files+`
|
| |
* Packagers SHOULD forward such patches to upstream
|
| |
|
| |
Example:
|
| |
@@ -114,17 +223,15 @@
|
| |
|
| |
----
|
| |
|
| |
- === Nightly, Other Platforms, etc. crates
|
| |
+ === Nightly features and dependencies for other platforms
|
| |
|
| |
- 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.
|
| |
+ Packagers MUST NOT package crates which do not work on Fedora, this includes:
|
| |
|
| |
- If the crate can be made usable,
|
| |
- packagers MUST patch packages which use such dependencies,
|
| |
- for example:
|
| |
+ * crates depending on nightly-only features of the Rust compiler
|
| |
+ * crates with (non-Linux) platform-specific dependencies
|
| |
+
|
| |
+ If such features and/or platform-specific dependencies are optional and can be removed,
|
| |
+ the `Cargo.toml` file MUST be patched to remove them, for example:
|
| |
|
| |
[source,diff]
|
| |
----
|
| |
@@ -142,26 +249,53 @@
|
| |
|
| |
----
|
| |
|
| |
- Such patches SHOULD be forwarded upstream.
|
| |
+ == Notes on the build process
|
| |
+
|
| |
+ CAUTION: The build process for Rust packages has evolved over time,
|
| |
+ especially for binary packages.
|
| |
+ Packages for source-only library crates were historically only built on Rawhide,
|
| |
+ but will also start being available on stable branches starting with Fedora 34.
|
| |
+
|
| |
+ * Originally, binary packages for stable releases were built using Modularity,
|
| |
+ leveraging its buildroot-only packages filtering capabilities,
|
| |
+ but this approach was dropped in the Fedora 31 timeframe.
|
| |
+ * From Fedora 31 to Fedora 33, binary packages are built with a special process
|
| |
+ (leveraging koji side tags), using base packages from the relevant stable Fedora release,
|
| |
+ except for Rust crates, which are included from Rawhide.
|
| |
+ * Starting with Fedora 34, the workflow for building Rust packages will no longer be special --
|
| |
+ packages for both library crates *and* applications will be available and built normally,
|
| |
+ without having to rely on a special procedure for release branches.
|
| |
+
|
| |
+ NOTE: Source-only Rust packages were granted a general exception to the
|
| |
+ https://docs.fedoraproject.org/en-US/fesco/Updates_Policy/[Updates policy],
|
| |
+ so they can be freely updated in release branches in addition to rawhide
|
| |
+ (announced in the corresponding FESCo ticket:
|
| |
+ https://pagure.io/fesco/issue/2474[F34 System-Wide Change: Rust Crate Packages For Release Branches]).
|
| |
|
| |
== Examples
|
| |
|
| |
- === Library
|
| |
+ === Library crate
|
| |
|
| |
- Rust library packages are packaged as source-only packages,
|
| |
- as we do not build dynamic link libraries at this time
|
| |
+ Rust library crates are packaged as source-only packages
|
| |
+ because Rust does not (yet) support shared libraries
|
| |
due to the lack of a stabilized ABI for Rust.
|
| |
|
| |
+ The source code is shipped in a `-devel` subpackage,
|
| |
+ with separate subpackages for all features specified in `Cargo.toml`,
|
| |
+ which encode the dependency information for all features and dependencies.
|
| |
+
|
| |
.rust-serde.spec
|
| |
[source]
|
| |
----
|
| |
include::{examplesdir}/rust-serde.spec[]
|
| |
----
|
| |
|
| |
- === Binary
|
| |
+ === Binary crate
|
| |
|
| |
- Application packages are compiled into binaries,
|
| |
- with the application name used as the package name for the output binary package.
|
| |
+ Rust applications are compiled to statically linked binaries,
|
| |
+ which are put into a subpackage that matches the name of the crate (without `rust-` prefix),
|
| |
+ e.g. the `rust-ripgrep` source package produces a `ripgrep` binary package,
|
| |
+ which contains the `ripgrep` binary.
|
| |
|
| |
.rust-ripgrep.spec
|
| |
[source]
|
| |
@@ -171,6 +305,14 @@
|
| |
|
| |
=== Library + Binary
|
| |
|
| |
+ Some crates ship both a compiled binary and a reusable library component,
|
| |
+ in this case,
|
| |
+ both the `-devel` subpackage(s) *and* the subpackage containing the binary are built.
|
| |
+
|
| |
+ When building a package like this for a stable Fedora release (up to and including Fedora 33),
|
| |
+ the .spec file SHOULD be modified to remove all `-devel` subpackages,
|
| |
+ so no dangling broken dependencies on other (unavailable) library crates are introduced.
|
| |
+
|
| |
.rust-yubibomb.spec
|
| |
[source]
|
| |
----
|
| |
spec files
specified manually
statically linked Rust binaries
about the build process for Rust packages
rawhide (see the F34 System-Wide Change page for details:
https://fedoraproject.org/wiki/Changes/Rust_Crate_Packages_For_Release_Branches )