From f05cf4d31c88bf48869b190dc570e86f31883030 Mon Sep 17 00:00:00 2001
From: Fabio Valentini <decathorpe@gmail.com>
Date: Oct 11 2023 23:22:48 +0000
Subject: Rewrite Rust Packaging Guidelines (and drop outdated example specs)


---

diff --git a/guidelines/modules/ROOT/examples/rust-ripgrep.spec b/guidelines/modules/ROOT/examples/rust-ripgrep.spec
deleted file mode 100644
index ab20cff..0000000
--- a/guidelines/modules/ROOT/examples/rust-ripgrep.spec
+++ /dev/null
@@ -1,82 +0,0 @@
-# Generated by rust2rpm 15
-%bcond_without check
-%global __cargo_skip_build 0
-
-%global crate ripgrep
-
-Name:           rust-%{crate}
-Version:        12.1.1
-Release:        1%{?dist}
-Summary:        Line oriented search tool using Rust's regex library
-
-License:        Unlicense OR MIT
-URL:            https://crates.io/crates/ripgrep
-Source:         %{crates_source}
-# Initial patched metadata
-# * No simd
-# * No jemalloc
-Patch:          ripgrep-fix-metadata.diff
-
-BuildRequires:  rust-packaging
-
-%global _description %{expand:
-Line-oriented search tool that recursively searches your current directory for
-a regex pattern while respecting your gitignore rules. ripgrep has first class
-support on Windows, macOS and Linux.}
-
-%description %{_description}
-
-%package     -n %{crate}
-Summary:        %{summary}
-# * Apache-2.0-or-later or BSL-1.0
-# * Apache-2.0-or-later or MIT
-# * MIT
-# * MIT or Apache-2.0-or-later
-# * Unlicense or MIT
-License:        MIT and (BSL-1.0 OR Apache-2.0-or-later)
-
-%description -n %{crate} %{_description}
-
-%files       -n %{crate}
-%license LICENSE-MIT UNLICENSE COPYING
-%doc README.md CHANGELOG.md
-%{_bindir}/rg
-%{_mandir}/man1/rg.1*
-%dir %{_datadir}/bash-completion
-%dir %{_datadir}/bash-completion/completions
-%{_datadir}/bash-completion/completions/rg.bash
-%dir %{_datadir}/fish
-%dir %{_datadir}/fish/vendor_completions.d
-%{_datadir}/fish/vendor_completions.d/rg.fish
-%dir %{_datadir}/zsh
-%dir %{_datadir}/zsh/site-functions
-%{_datadir}/zsh/site-functions/_rg
-
-%prep
-%autosetup -n %{crate}-%{version_no_tilde} -p1
-%cargo_prep
-
-%generate_buildrequires
-%cargo_generate_buildrequires -a
-echo '/usr/bin/asciidoctor'
-
-%build
-%cargo_build -a
-
-%install
-%cargo_install -a
-install -Dpm0644 -t %{buildroot}%{_mandir}/man1 \
-  target/release/build/%{crate}-*/out/rg.1
-install -Dpm0644 -t %{buildroot}%{_datadir}/bash-completion/completions \
-  target/release/build/%{crate}-*/out/rg.bash
-install -Dpm0644 -t %{buildroot}%{_datadir}/fish/vendor_completions.d \
-  target/release/build/%{crate}-*/out/rg.fish
-install -Dpm0644 -t %{buildroot}%{_datadir}/zsh/site-functions \
-  complete/_rg
-
-%if %{with check}
-%check
-%cargo_test -a
-%endif
-
-%changelog
diff --git a/guidelines/modules/ROOT/examples/rust-serde.spec b/guidelines/modules/ROOT/examples/rust-serde.spec
deleted file mode 100644
index 9f7e445..0000000
--- a/guidelines/modules/ROOT/examples/rust-serde.spec
+++ /dev/null
@@ -1,145 +0,0 @@
-# Generated by rust2rpm 15
-# * RUSTC_BOOTSTRAP breaks tests
-%bcond_with check
-%global debug_package %{nil}
-
-%global crate serde
-
-Name:           rust-%{crate}
-Version:        1.0.116
-Release:        1%{?dist}
-Summary:        Generic serialization/deserialization framework
-
-# Upstream license specification: MIT OR Apache-2.0
-License:        MIT OR Apache-2.0-or-later
-URL:            https://crates.io/crates/serde
-Source:         %{crates_source}
-
-%if %{__cargo_skip_build}
-BuildArch:      noarch
-%endif
-
-BuildRequires:  rust-packaging
-
-%global _description %{expand:
-Generic serialization/deserialization framework.}
-
-%description %{_description}
-
-%package        devel
-Summary:        %{summary}
-BuildArch:      noarch
-
-%description    devel %{_description}
-
-This package contains library source intended for building other packages
-which use "%{crate}" crate.
-
-%files          devel
-%license LICENSE-MIT LICENSE-APACHE
-%doc README.md crates-io.md
-%{cargo_registry}/%{crate}-%{version_no_tilde}/
-
-%package     -n %{name}+default-devel
-Summary:        %{summary}
-BuildArch:      noarch
-
-%description -n %{name}+default-devel %{_description}
-
-This package contains library source intended for building other packages
-which use "default" feature of "%{crate}" crate.
-
-%files       -n %{name}+default-devel
-%ghost %{cargo_registry}/%{crate}-%{version_no_tilde}/Cargo.toml
-
-%package     -n %{name}+alloc-devel
-Summary:        %{summary}
-BuildArch:      noarch
-
-%description -n %{name}+alloc-devel %{_description}
-
-This package contains library source intended for building other packages
-which use "alloc" feature of "%{crate}" crate.
-
-%files       -n %{name}+alloc-devel
-%ghost %{cargo_registry}/%{crate}-%{version_no_tilde}/Cargo.toml
-
-%package     -n %{name}+derive-devel
-Summary:        %{summary}
-BuildArch:      noarch
-
-%description -n %{name}+derive-devel %{_description}
-
-This package contains library source intended for building other packages
-which use "derive" feature of "%{crate}" crate.
-
-%files       -n %{name}+derive-devel
-%ghost %{cargo_registry}/%{crate}-%{version_no_tilde}/Cargo.toml
-
-%package     -n %{name}+rc-devel
-Summary:        %{summary}
-BuildArch:      noarch
-
-%description -n %{name}+rc-devel %{_description}
-
-This package contains library source intended for building other packages
-which use "rc" feature of "%{crate}" crate.
-
-%files       -n %{name}+rc-devel
-%ghost %{cargo_registry}/%{crate}-%{version_no_tilde}/Cargo.toml
-
-%package     -n %{name}+serde_derive-devel
-Summary:        %{summary}
-BuildArch:      noarch
-
-%description -n %{name}+serde_derive-devel %{_description}
-
-This package contains library source intended for building other packages
-which use "serde_derive" feature of "%{crate}" crate.
-
-%files       -n %{name}+serde_derive-devel
-%ghost %{cargo_registry}/%{crate}-%{version_no_tilde}/Cargo.toml
-
-%package     -n %{name}+std-devel
-Summary:        %{summary}
-BuildArch:      noarch
-
-%description -n %{name}+std-devel %{_description}
-
-This package contains library source intended for building other packages
-which use "std" feature of "%{crate}" crate.
-
-%files       -n %{name}+std-devel
-%ghost %{cargo_registry}/%{crate}-%{version_no_tilde}/Cargo.toml
-
-%package     -n %{name}+unstable-devel
-Summary:        %{summary}
-BuildArch:      noarch
-
-%description -n %{name}+unstable-devel %{_description}
-
-This package contains library source intended for building other packages
-which use "unstable" feature of "%{crate}" crate.
-
-%files       -n %{name}+unstable-devel
-%ghost %{cargo_registry}/%{crate}-%{version_no_tilde}/Cargo.toml
-
-%prep
-%autosetup -n %{crate}-%{version_no_tilde} -p1
-%cargo_prep
-
-%generate_buildrequires
-%cargo_generate_buildrequires
-
-%build
-%cargo_build
-
-%install
-%cargo_install
-
-%if %{with check}
-%check
-%cargo_test
-%endif
-
-%changelog
diff --git a/guidelines/modules/ROOT/examples/rust-yubibomb.spec b/guidelines/modules/ROOT/examples/rust-yubibomb.spec
deleted file mode 100644
index cdd5476..0000000
--- a/guidelines/modules/ROOT/examples/rust-yubibomb.spec
+++ /dev/null
@@ -1,85 +0,0 @@
-# Generated by rust2rpm 13
-%bcond_without check
-
-%global crate yubibomb
-
-Name:           rust-%{crate}
-Version:        0.2.1
-Release:        1%{?dist}
-Summary:        Rust command line tool that prints out a 6-digit random number
-
-License:        GPL-3.0-only
-URL:            https://crates.io/crates/yubibomb
-Source:         %{crates_source}
-
-%if %{__cargo_skip_build}
-BuildArch:      noarch
-%endif
-
-BuildRequires:  rust-packaging
-
-%global _description %{expand:
-Don't you love when you accidentally tap your Yubikey when you have your IRC
-client in focus and you send 987947 into Freenode? Want to be able to have that
-experience without having to reach all the way over to your laptop's USB port?
-Now you can!.}
-
-%description %{_description}
-
-%if ! %{__cargo_skip_build}
-%package     -n %{crate}
-Summary:        %{summary}
-
-%description -n %{crate} %{_description}
-
-%files       -n %{crate}
-%license LICENSE
-%doc README.md
-%{_bindir}/yubibomb
-%endif
-
-%package        devel
-Summary:        %{summary}
-BuildArch:      noarch
-
-%description    devel %{_description}
-
-This package contains library source intended for building other packages
-which use "%{crate}" crate.
-
-%files          devel
-%license LICENSE
-%doc README.md
-%{cargo_registry}/%{crate}-%{version_no_tilde}/
-
-%package     -n %{name}+default-devel
-Summary:        %{summary}
-BuildArch:      noarch
-
-%description -n %{name}+default-devel %{_description}
-
-This package contains library source intended for building other packages
-which use "default" feature of "%{crate}" crate.
-
-%files       -n %{name}+default-devel
-%ghost %{cargo_registry}/%{crate}-%{version_no_tilde}/Cargo.toml
-
-%prep
-%autosetup -n %{crate}-%{version_no_tilde} -p1
-%cargo_prep
-
-%generate_buildrequires
-%cargo_generate_buildrequires
-
-%build
-%cargo_build
-
-%install
-%cargo_install
-
-%if %{with check}
-%check
-%cargo_test
-%endif
-
-%changelog
diff --git a/guidelines/modules/ROOT/pages/Rust.adoc b/guidelines/modules/ROOT/pages/Rust.adoc
index db66eff..09ee8a0 100644
--- a/guidelines/modules/ROOT/pages/Rust.adoc
+++ b/guidelines/modules/ROOT/pages/Rust.adoc
@@ -1,374 +1,870 @@
 = Rust Packaging Guidelines
-:last-reviewed: 2022-08-23
 
-== rust2rpm
-
-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.
-
-It is advisable to try `+rust2rpm -s $crate+` first,
-and to only modify its output for your needs,
-before attempting to write a `+.spec+` file by hand.
-Most of the time, `+rust2rpm+` output can be used almost without modifications.
-
-However, there are a few common situations
-in which manual edits of the generated `+.spec+` file
-or patches for upstream `+Cargo.toml+` files (with `+rust2rpm -p+`)
-are required, for example:
-
-* trimming a generated `+Summary+` tag that is too long
-* dropping non-linux platform-specific dependencies (automated by rust2rpm v22+)
-* dropping features that are only supported by nightly builds of the Rust compiler
-* dropping unstable features that require manually overriding compiler flags
-* dropping *unused* optional features with missing dependencies
-  (can be automated by using a `+rust2rpm.conf+` configuration file)
-
-== Package Sources
-
-Only library crates that are published on crates.io MAY be packaged for Fedora,
-since this enforces a certain standard of how crates are packaged and built,
-ensures that all the crate's dependencies are available from crates.io as well,
-and that there are no package name collisions
-due to the unique crate names in the crates.io namespace.
-
-Crates that contain only an application but no library interface
-MAY be packaged even if they are not published on crates.io,
-because those packages are leaves
-and cannot be depended on by other Rust crates.
-
-Complex projects with multiple components (i.e. bindings for other languages),
-which cannot be packaged from sources that are published on crates.io alone,
-MAY include subpackages for their Rust library interface
-(`+rust-$crate-devel+`, `+rust-$crate+feature-devel+`, etc.)
-ONLY if this crate is also published on crates.io under the same name.
-
-== Package Naming
-
-=== Library crates
-
-Source packages for Rust crates which contain a library with a public API
-MUST be named `rust-$crate`.
-
-=== Mixed library / application crates
-
-Source packages for Rust crates
-which contain both a library with a public API *and* an application
-MUST be named `rust-$crate`
-(i.e. follow the rules for library crates).
-
-NOTE:   The convention set by `rust2rpm` for the name of the subpackage
-        that contains the application binary (or binaries)
-        is to use the name of the crate itself.
-        However, in some cases,
-        the crate name does not match the name of the built application(s),
-        and in this case the subpackage name SHOULD be adjusted
-        to match expectations.
-
-=== Application-only crates
-
-Packages for application-only crates that are published on crates.io
-MUST NOT drop the `+rust-+` prefix from their source package names,
-because a crate can start to include a public library API at any time,
-in which case the "Mixed library / application crates" rules would apply,
-and the package would need to be renamed to include the `+rust-+` prefix.
-
-Application-only Rust projects which are not packaged from crates.io
-must follow the general xref:Naming.adoc[Naming Guidelines]
-(i.e. drop the `+rust-+` prefix from the source package name)
-so that they cannot introduce potential source package name collisions
-with crates from the crates.io namespace.
-
-NOTE:   `rust2rpm` does not support generating `.spec` files
-        for packages without a `+rust-+` name prefix yet.
-        In this case, `.spec` files written by `rust2rpm`
-        will need to be manually edited after each update.
-
-== 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 include `+BuildRequires: rust-packaging >= 21+`.
-
-However, if the package is for a Rust crate
-whose `+Cargo.toml+` metadata uses syntax for feature dependencies
-which was added with Rust 1.60,
-i.e. *namespaced dependencies* (`+"dep:foo"+`)
-or *weak dependency features* (`+"foo/bar"+`),
-it MUST include `+BuildRequires: cargo-rpm-macros >= 24+` instead,
-since support for this syntax was only added
-as part of a complete rewrite of `+rust-packaging+`
-that was shipped as `+cargo-rpm-macros+` version 24.
-
-=== 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. created 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
-
-The RPM packaging machinery for Rust crates
-automatically generates 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 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+`.
-
-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"] }
-....
-
-will be encoded as
-
-....
-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
+:last-reviewed: 2023-10-12
+
+https://www.rust-lang.org[Rust] is a strongly and statically typed,
+compiled programming language
+that supports concepts from both imperative and functional programming.
+
+Because there is not yet a stable Rust ABI,
+and because conditional compilation is a widely used feature in the Rust ecosystem,
+Rust libraries ("crates") can not be distributed in compiled form,
+and are instead distributed as source code.
+
+This document covers how to handle Rust code in packages,
+specific to the different ways in which projects can be set up:
+
+* link:#_rust_crates[Rust "crates"]:
+  packages that are individually published on https://crates.io[crates.io],
+  the official package registry for Rust
+  (primarily libraries, but also "single-crate" applications)
+* Rust link:#_rust_applications_non_crates_io_crates[applications that are single crates but not published on crates.io]
+* Rust projects that are organized as link:#_rust_applications_cargo_workspace_projects[cargo "workspaces"]:
+  usually larger projects with "internal" crates,
+  which are often not separately published on https://crates.io[crates.io]
+* link:#_python_projects[Python projects] with a "native" component implemented in Rust:
+  usually built with https://github.com/PyO3/setuptools-rust[setuptools_rust] or https://github.com/PyO3/maturin[maturin]
+* link:_mixed_rust_cc_projects[mixed Rust / C/C++ projects] where parts of the project are implemented in Rust:
+  either built by wrapping cargo,
+  or by utilizing https://mesonbuild.com/Rust.html[meson]’s limited support
+  for directly building Rust code
+
+For the first three of these cases,
+the https://pagure.io/fedora-rust/rust2rpm[rust2rpm] tool can be used
+to generate spec files from cargo / crate metadata.
+It is designed to produce spec files
+that are in line with the (Rust) Packaging Guidelines.
+
+There are also guidelines for link:#_building_shared_libraries_with_cargo_c[packaging shared libraries]
+that are implemented in Rust
+(usually built and installed with https://crates.io/crates/cargo-c[cargo-c]).
+
+== Generic rules
+
+This section covers rules that apply to _all_ packages that ship Rust code.
+
+=== Compiler flags
+
+Similarly to other language ecosystems in Fedora,
+there is a standardized set of compiler flags
+that *MUST* be passed to the Rust compiler.
+
+The defaults for Rust are defined in the `%build_rustflags` macro
+from the `rust-srpm-macros` package.
+It is part of the default buildroot on Fedora 39+,
+where the `%set_build_flags` macro automatically sets the `$RUSTFLAGS` environment variable
+based on this macro.
+
+For compatibility with older releases,
+this environment variable can be set manually at the start of `%build` and `%check`
+in package’s spec files:
+
+[source,shell]
+----
+export RUSTFLAGS="%build_rustflags"
+----
 
-Rust packages SHOULD use automatic generation of `BuildRequires`
-by including this scriptlet between `+%prep+` and `+%build+`:
+This is not necessary for packages that use the `%cargo_prep`, `%cargo_build`, and `%cargo_test` macros,
+which configure `cargo` to use the default `%build_rustflags` directly.
+
+=== Mandatory `BuildRequires`
+
+The RPM macros that provide basic functionality
+for building Rust code are included in `rust-rpm-macros`,
+which is part of the default buildroot in Fedora,
+as it is a dependency of `redhat-rpm-config`.
+When building for ELN or EPEL8, this is not the case,
+and packages need to use `BuildRequires: rust-toolset`.
+
+Packages that build Rust code with cargo – directly or indirectly –
+or which call any of the `%cargo_*` macros,
+*MUST* add `BuildRequires:  cargo-rpm-macros >= 24`,
+which provides the implementations of all `%cargo_*` macros.
+This package is not part of the default buildroot,
+since it pulls in additional dependencies
+(i.e. a Python interpreter).
+
+For backwards compatibility,
+packages *MAY* instead depend on earlier versions of the `%cargo_*` macros,
+which were provided in the `rust-packaging` package,
+if they do not depend on any of the features or macros
+that are only available with newer versions.
+When generating a spec file for a crate with https://pagure.io/fedora-rust/rust2rpm[rust2rpm],
+it will automatically detect usage of these features,
+and include the necessary `BuildRequires`
+for the RPM macro package automatically.
+
+=== License tags
+
+Similar to other languages that produce statically linked binaries,
+Rust executables (and shared libraries) contain code
+that originates in other packages
+(i.e. packages for other Rust crates),
+which in turn are covered by different license terms.
+
+This needs to be taken into account
+by maintaining a separate `License` tag
+for the subpackage that contains these binaries.
+More information about `License` tags is available from the link:[Fedora Legal docs].
+
+The `cargo-rpm-macros` package provides two RPM macros
+that help with filling the `License` tag correctly:
+
+- `%cargo_license_summary`
+
+This macro determines and prints a summary
+of all the licenses of the Rust crates
+that end up statically linked into final binaries
+(properly excluding build-only or test-only dependencies).
+This summary can then be copied from the build log
+into the spec file as a comment.
+The actual contents of the `License` tag can then be obtained
+by constructing a conjunction of these individial licenses (with SPDX `AND` operators).
+
+- `%cargo_license`
+
+This macro determines and prints a complete breakdown of all Rust crates
+that end up statically linked into final binaries
+(according to the same logic as in the `%cargo_license_summary` macro),
+their versions, _and_ their individual license expressions.
+Generating this list dynamically at build-time
+ensures that its contents always match the actual dependencies.
+
+Both macros accept the same arguments
+as all other `%cargo_*` macros (`-a`, `-n`, `-f`),
+and for their output to match the actual binaries,
+the same flags need to be passed to them and `%cargo_build`.
+
+=== Vendored dependencies
+
+https://docs.fedoraproject.org/en-US/packaging-guidelines/#bundling[In general],
+packages *SHOULD NOT* use bundled crate dependencies,
+whenever possible.
+
+Whenever vendored / bundled crate dependencies _are_ used
+(no matter which mechanism is used for the purpose),
+all bundled crate dependencies *MUST* be declared
+with virtual `Provides` in the format `Provides: bundled(crate($crate)) = $version`
+in the subpackage that contains the Rust component.
+For example, these virtual `Provides` are used to determine
+the impact of security vulnerabilities on packages
+that use vendored Rust dependencies.
+
+Building exclusively from vendored dependencies
+by using a tarball that was generated by running `cargo vendor`
+*SHOULD* only be a last resort.
+However, there are also two _rare_ situations
+in which bundling at least _some_ Rust crates is likely unavoidable.
+
+==== Replacing git dependencies
+
+One of the types of dependencies cargo supports are git snapshots,
+which are usually used to reference either a specific commit,
+or a reference to a downstream fork of a crate.
+
+The project *SHOULD* be patched to use a version of this crate
+that is available on https://crates.io[crates.io] instead,
+if that is possible.
+If it turns out that depending on a git snapshot is no longer necessary,
+this patch *SHOULD* be submitted to the upstream project.
+
+If the dependency is not published on https://crates.io[crates.io],
+or if the versions published there are not a suitable replacement,
+a git snapshot of the crate can be bundled.
+This can be achieved by creating and supplying a tarball
+with the git snapshot as a separate source,
+unpacking the tarball in `%prep`,
+and patching `Cargo.toml` to replace the git-based dependency
+with a path-based dependency.
+
+==== Replacing patched crate sources
+
+Another way in which cargo supports specifying modified dependencies is
+by "patching" a crate source,
+specifying an alternative source for specific crates –
+which will likely be either git references or path-based dependencies
+that are present to override a crate
+that is published on https://crates.io[crates.io] with a (modified) local copy,
+or a git repository that points to a (modified) fork of the crate.
+
+These replacements *SHOULD* be dropped
+in favor of using only published versions of crates.
+If that is not possible,
+they must be replaced by path-based dependencies,
+similar to the process described for link:#_replacing_git_dependencies[git-type dependencies].
+
+==== Using vendor tarballs
+
+Official support for building with vendored dependencies
+was added in version 25 of cargo-rpm-macros and rust2rpm.
+
+* The `%cargo_prep` macro accepts a `-v $VENDOR` argument,
+  where `$VENDOR` is the path to the directory
+  that contains the vendored crate dependencies.
+  When this argument is passed,
+  the generated cargo configuration is set up
+  for building against dependencies in this directory
+  instead of dependencies from the system-wide registry.
+* The `%cargo_generate_buildrequires` macro *MUST NOT* be called
+  when using vendored dependencies.
+* The `%cargo_vendor_manifest` macro generates a manifest (`cargo-vendor.txt`)
+  that lists the names and versions of all crates in the vendor tarball.
+  This macro *MUST* be called (for example, in the `%build` scriptlet),
+  and the generated file *MUST* be added as a `%license` file
+  in the appropriate package’s list of `%files`.
+  An RPM generator parses this file
+  and generates appropriate virtual `Provides` for all bundled crates,
+  as is required for any bundled dependencies.
+
+Typically, the `%prep` scriptlet will look like this
+when using vendored dependencies
+(assuming `Source1` is the vendor tarball,
+and it contains a top-level `vendor` directory):
+
+[source,rpm]
+----
+%prep
+%autosetup -%{crate}-%{version} -p1 -a1
+%cargo_prep -v vendor
+----
 
-....
-%generate_buildrequires
-%cargo_generate_buildrequires
-....
+The necessary spec file adaptations
+and the generation of the vendor tarball itself
+happen automatically when running rust2rpm in "vendor" mode.
 
-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.
+== rust2rpm
 
-If using `+%cargo_generate_buildrequires+` is not possible,
-`BuildRequires` MUST be specified manually and kept up-to-date with each package update:
+The recommended way to write spec files for Rust projects
+is to use https://pagure.io/fedora-rust/rust2rpm[rust2rpm],
+and apply any necessary modifications on top of the generated spec file.
+
+There are a few common situations
+in which automatically generated spec files
+need manual changes:
+
+* invalid `Summary` / `%description`:
+  The heuristics for generating the `Summary` or `%description` for the package
+  from the crate metadata can fail to produce valid values
+  (i.e. `Summary` tag that is too long).
+  In this case, the `Summary` needs to be shortened manually.
+  This can also be overridden
+  in the package-specific rust2rpm configuration file.
+* unwanted dependencies / subpackages:
+  Some crates provide non-default / optional features
+  that are either unnecessary
+  (i.e. only applicable to non-Linux systems),
+  or have additional dependencies
+  that are not packaged for Fedora.
+  These features and unavailable optional dependencies *MUST*
+  be removed from crate metadata – otherwise,
+  the package will either fail to build,
+  or produce subpackages with broken dependencies.
+* nightly-only / unstable features:
+  Some crates provide features
+  that are only available on a nightly version of the Rust compiler,
+  or features that are unstable and require an opt-in
+  by passing environment variables.
+  Features like these *SHOULD* be removed from crate metadata,
+  since they either cannot work
+  (Fedora ships only the stable Rust toolchain)
+  or are not feasible to support.
+* unwanted / unnecessary files ("bloat"):
+  Some projects include files
+  that are not required for the crate to function properly
+  (files for CI settings, development / helper scripts, etc.).
+  Files like these *SHOULD* be prevented from being installed
+  (by adding / modifying the `package.include`
+  or `package.exclude` settings in the crate metadata).
+  It is recommended to submit changes like this to the upstream project.
+* incompatible compiler flags:
+  Some crates include custom settings for the `release` profile
+  that are incompatible with RPM packaging.
+  These settings *MUST* be removed from the `release` profile
+  by patching `Cargo.toml`.
+
+Crates that provide Rust bindings for C libraries
+usually require some additional changes (if possible):
+
+* linking against system libraries:
+  This often requires making some dependencies non-optional and / or
+  modifying `build.rs` scripts to unconditionally link against system libraries
+  instead of building and statically linking a bundled copy of the library.
+* regenerating Rust bindings (and tests for them) at build-time:
+  This too often requires making the `bindgen` dependency non-optional and / or
+  modifying `build.rs` to cause regeneration of Rust bindings at build-time.
+
+Note that patching `Cargo.toml` files
+(especially changing the set of optional dependencies and features)
+*MUST* be done by running `rust2rpm -p`,
+since changes like these affect spec file generation
+(i.e. the list of generated subpackages),
+which is only correctly taken into account
+if the patch is created _before_ generation of the spec file.
+
+== Rust crates
+
+A large part of the process of packaging Rust crates
+can (and _should_) be automated by using https://pagure.io/fedora-rust/rust2rpm[rust2rpm].
+It is designed to generate spec files that are compliant
+with both the general and the Rust Packaging Guidelines.
+
+Additionally, due to some properties of packages for Rust crates
+(i.e. subpackages that correspond to crate features / optional dependencies),
+it is necessary to re-run https://pagure.io/fedora-rust/rust2rpm[rust2rpm]
+for every new version of a crate
+to ensure that generated feature subpackages
+stays in sync with crate metadata.
+
+=== Package naming
+
+The canonical source of Rust crates is https://crates.io[crates.io].
+
+Rust crates that are published on https://crates.io[crates.io]
+*MUST* be packaged with `rust-$crate` as the name of the source package
+(where `$crate` is the name of the project on https://crates.io[crates.io]).
+Packages for Rust crates that are generated with https://pagure.io/fedora-rust/rust2rpm[rust2rpm]
+follow to this pattern.
+
+On the other hand,
+projects from other sources *MUST NOT* use the `rust-` prefix for source package names,
+and *MUST* follow the general Naming Guidelines instead.
+They also *MUST NOT* have any virtual `Provides` for Rust crates
+(i.e. `Provides: crate(%{crate}) = %{version}`).
+
+When generating a package for a Rust crate
+that also (or exclusively) contains an application,
+the convention followed by https://pagure.io/fedora-rust/rust2rpm[rust2rpm] is
+to generate a subpackage with a name that matches the crate’s name
+(i.e. the `rust-$crate` source package will have a `$crate` subpackage).
+If this name does not match expectations,
+it is recommended to either change the name of this subpackage,
+or to add virtual `Provides` for the expected name.
+
+=== Package versioning
+
+Projects that are built with cargo and / or published on https://crates.io[crates.io]
+follow Semantic Versioning (with small cargo-specific tweaks).
+Since SemVer strings can contain characters that are invalid in RPM version strings,
+they *MUST* be translated to be RPM-compatible.
+
+For example, pre-releases are denoted by a `-<pre>` suffix in SemVer,
+but the `-` character is invalid in RPM Versions.
+This can be solved by replacing `-` with the `~` character,
+which denotes pre-releases in RPM version strings.
+This translation happens automatically for the `Version` tag
+when generating a spec file with https://pagure.io/fedora-rust/rust2rpm[rust2rpm],
+and the "upstream" version is stored in a separate macro
+that can be used to refer to the "original" version string.
+
+Additionally, some Rust crates carry extra "build" metadata in their versions
+(a `+<build>` suffix).
+This format is primarily used to carry information
+about the version of a bundled C library.
+This `+<build>` suffix *MUST* be removed from crate metadata with a patch,
+since it can interfere with RPM dependency / version resolution.
+This happens automatically when using rust2rpm version 25 or newer.
+
+=== Package sources
+
+Projects from https://crates.io[crates.io] *MUST* be packaged from the sources
+that are published there (i.e. by using the `%{crates_source}` macro).
+
+If the sources published on https://crates.io[crates.io]
+do not contain all files that are necessary for creating the package
+(for example, missing `.desktop` file or man pages),
+the upstream sources can be used as an _additional_ source,
+but they *MUST NOT* be used for building the crate itself.
+It is recommended to file an issue with the upstream project
+about including these additional files in published crates.
+
+If a crate is also part of a larger project
+and it is not feasible to package the Rust crate separately,
+the Rust crate *MAY* be packaged as `rust-$crate` from different sources
+(i.e. an upstream tarball) _if and only if_ the crate is also published on https://crates.io[crates.io]
+under the same name and with matching versions.
+In this case, the subpackage(s) corresponding to Rust crates
+*MUST* have correct virtual `Provides` for the Rust crate.
+
+=== Crate license
+
+Most tooling support for determining licenses
+requires accurate metadata about licenses in crate metadata,
+including the `%cargo_license*` macros,
+and other third-party tools like `cargo-license` and `cargo-deny`.
+
+For this reason, the license metadata for all Rust crates packaged for Fedora
+*MUST* match the license tag of the Fedora package itself.
+Any crates that set `package.license-file` in their metadata
+(which is reserved for non-standard / non-SPDX licenses)
+*MUST* be patched to set `package.license` in their metadata instead
+in cases where this is _not_ appropriate
+and an accurate SPDX expression can be provided.
+Patches like this *SHOULD* be submitted upstream.
+
+=== RPM macros
+
+The process of building and installing Rust crates
+is almost entirely automated with several RPM macros:
+
+* `%cargo_prep`:
+  This macro *MUST* be called in the `%prep` scriptlet
+  after sources have been unpacked.
+  It sets up the build environment for cargo
+  and injects a cargo configuration file,
+  which sets the default compiler flags
+  and configures the local crate registry
+  as a replacement for https://crates.io[crates.io].
+* `%cargo_generate_buildrequires`:
+  This macro *MUST* be called in the `%generate_buildrequires` scriptlet,
+  except when building with vendored dependencies.
+  This is the mechanism that automatically generates depepdencies on other Rust crates
+  based on the metadata in `Cargo.toml`.
+* `%cargo_build`:
+  This macro *MUST* be called in the `%build` scriptlet.
+  It runs `cargo build` with the appropriate command line arguments.
+  Calling this macro *MAY* be skipped
+  if the crate is not supported on the current CPU architecture.
+* `%cargo_install`:
+  This macro *MUST* be called in the `%install` scriptlet
+  for crates that provide a library interface.
+  It runs `cargo package` and installs the resulting directory tree
+  into `%{buildroot}/%{crate_instdir}`
+  (i.e. `%{buildroot}/%{cargo_registry}/%{crate}-%{version}/`).
+  For crates that provide `bin` targets,
+  it installs all built executables into `%{buildroot}/%{_bindir}`.
+  If any built executables need to be installed in a different location,
+  they can be moved after calling `%cargo_install`,
+  or `%cargo_install` can be replaced
+  with manual installation steps.
+* `%cargo_test`:
+  This macro *MUST* be called in the `%check` scriptlet.
+  It runs `cargo test` with the appropriate command line arguments.
+  Calling this macro *MAY* be skipped
+  if the crate is not supported on the current CPU architecture
+  or if tests are disabled in general.
+* `%cargo_license` / `%cargo_license_summary`:
+  These macros *MUST* be called in the `%build` scriptlet after `%cargo_build`
+  when building crates that include binary targets.
+  They can be used to print the list of the licenses
+  of the crates that are statically linked into any built executable
+  or shared library (see link:#_license_tags[License tags]).
+
+All packages for Rust crates *MUST* set
+either `%bcond_without check` or `%bcond_with check`.
+The value of this macro affects the behaviour of `%cargo_generate_buildrequires`.
+
+All `%cargo_*` macros (except `%cargo_prep`)
+accept a set of optional flags / arguments
+that can be used to control the feature flags that are passed to cargo
+(usually to enable optional / non-default features):
+
+* `-a`:
+  Causes the `--all-features` flag to be passed to cargo,
+  and the `%cargo_generate_buildrequires` macro
+  to resolve dependencies with all optional features enabled.
+* `-n`:
+  Causes the `--no-default-features` flags to be passed to cargo,
+  and the `%cargo_generate_buildrequires` macro
+  to resolve dependencies with all default and optional features disabled.
+* `-f foo,bar`:
+  Causes the `--features foo,bar` argument to be passed to cargo,
+  and the `%cargo_generate_buildrequires` macro
+  to resolve dependencies with the additional features `foo` and `bar` enabled.
+  This argument accepts a comma-separated list of feature names
+  (or names of optional dependencies).
+
+The `-a` and `-n` flags are mutually exclusive
+and cannot be passed together.
+The `-a` flag and `-f` arguments are also incompatible,
+since passing `-a` already enables all features.
+However, using the `-n` flag and specifically enabling _some_ features
+with the `-f` argument is valid.
+
+There are some common situations
+in which passing these flags or arguments is necessary:
+
+* It can be necessary to enable additional features and / or optional dependencies
+  to build and run the test suite of a crate.
+  In this case, the required features *MUST* be enabled
+  by passing the corresponding flags to all `%cargo_*` macros,
+  unless the required optional dependencies are not packaged yet.
+* Some applications support additional / non-default features
+  by passing feature flags.
+  If it is desirable to build applications with these features enabled,
+  the required features need to be enabled
+  by passing the corresponding flags to all `%cargo_*` macros
+  (including `%cargo_license` and `%cargo_license_summary`).
+
+Note that the `-n` flag should only be used in exceptional circumstances,
+and *MUST NOT* be used to avoid missing dependencies
+that are part of the `"default"` feature set of a crate.
+
+=== Dynamically generated `BuildRequires` for crate dependencies
+
+With Semantic Versioning (_"SemVer"_) being
+the only supported versioning scheme for Rust crates,
+dependencies on Rust libraries are almost exclusively specified as
+_"this version or any newer version that is API-compatible with it"_,
+i.e. a range of supported versions.
+
+These ranges of supported versions
+need to be correctly translated into RPM dependencies,
+otherwise a wrong version of a dependency might get pulled in for builds,
+causing unhelpful error messages about missing dependencies.
+
+Since dependencies of Rust projects often change with every new release,
+and keeping a list of `BuildRequires` up-to-date manually is tedious and error-prone,
+packages for projects that build Rust code with cargo
+*MUST* use dynamically generated `BuildRequires`
+by calling the `%cargo_generate_buildrequires` macro
+in the `%generate_buildrequires` scriptlet.
+
+For example, a dependency on `serde = "1.0.100"`
+specified in a project's `Cargo.toml` metadata
+(a dependency on the crate named "serde",
+with version "1.0.100" or any version API-compatible with "1.0.100",
+with default features enabled)
+would cause a dependency like this to be generated for RPM:
 
 ....
-[dependencies]
-atty = "0.2.2"
-[build-dependencies]
-clap = "2.24.1"
+BuildRequires:  (crate(serde/default) >= 1.0.100 with crate(serde/default) < 2.0.0~)
 ....
 
-should become
+Refer to the section about link:#_rpm_macros[RPM macros]
+for how to pass feature flags to this macro.
+
+Issues with the `%cargo_generate_buildrequires` macro
+that prevent it from being used for a package
+should be reported against https://pagure.io/fedora-rust/cargo2rpm[cargo2rpm],
+the tool that provides the functionality of this macro.
+
+=== Subpackages for crate features
+
+Optional features / dependencies of Rust crates are translated into RPM subpackages
+to support resolving dependencies for features and optional dependencies of crates.
+The list of crate "features"
+(including any implicitly defined features for optional dependencies)
+*MUST* be kept in sync with the list of subpackages,
+i.e. for every feature `$foo` of the crate `$crate`,
+there must be a subpackage with name `rust-$crate+$foo-devel`,
+and vice-versa.
+This is required for RPM generators for `Provides` and `Requires`
+for these optional features / dependencies to work correctly.
+
+If optional features that are _not_ part of the default feature set
+are unused and would pull in additional (possibly unavailable) dependencies,
+the package *MAY* omit subpackages for these specific feature names.
+However, care needs to be taken
+that the features corresponding to the ommitted subpackages
+are not "reachable" via subpackages that have _not_ been omitted,
+since this would result in packages with unsatisfiable dependencies.
+Disabling optional features sometimes cannot be handled correctly
+simply by omitting subpackages for specific features.
+In these cases, the crate metadata in `Cargo.toml`
+needs to be patched accordinly instead.
+
+Beware that the "default" feature is always implicitly defined by cargo,
+even if the crate metadata does not contain a `[features]` table
+or an explicitly defined "default" feature,
+so the subpackage for the "default" feature
+will be present in all packages for Rust crates with a library interface.
+
+=== RPM generators for `Provides` and `Requires`
+
+The cargo-rpm-macros package includes RPM generators
+for automatically generating `Provides` and `Requires`
+for Rust crates that comply with the Packaging Guidelines
+(i.e. install their files into the correct location, `%{crate_instdir}`).
+
+It is recommended to verify
+that the generated `Provides` and `Requires` are sane – for example,
+the following `Provides` and `Requires`
+must be present to ensure correct inter-subpackage dependencies:
+
+* the main `rust-$crate-devel` subpackage
+  *MUST* provide `crate($crate) = %{version}`
+* the `rust-$crate+$feature-devel` subpackages
+  *MUST* provide `crate($crate/$feature) = %{version}`
+  and require `crate($crate) = %{version}` (i.e. `rust-$crate-devel`)
+
+Additionally, dependencies on external Rust crates must be as expected:
+
+* the main `rust-$crate-devel` subpackage
+  *MUST* require the virtual `Provides` for all non-optional crate dependencies
+* the `rust-$crate+$feature-devel` subpackages
+  *MUST* require the virtual `Provides` for the optional crate dependencies
+  and features that are listed as the feature’s dependencies in crate metadata
+
+=== Packaging multiple versions
+
+In most circumstances,
+the latest version of a crate *SHOULD* be packaged,
+and – if possible – packagers *SHOULD* port crates
+to use the latest available version of their dependencies,
+and submit these patches to upstream
+to limit divergence between the upstream project and the Fedora package.
+
+However, there are two common scenarios
+in which it is often necessary to provide packages
+for multiple versions of a library crate simultaneously:
+
+* It is not feasible to port a crate
+  to the version of a crate dependency in Fedora
+  due to large API changes between the required and the packaged version.
+* The number of packages affected
+  by a required SemVer-incompatible library update is very large.
+
+In these cases, a "compat package" can be created for the older version
+(i.e. usually the current version),
+and the suffix-less package can be updated to the newer version.
+https://pagure.io/fedora-rust/rust2rpm[rust2rpm]
+supports automatically creating "compat packages"
+with names that are compliant with the
+https://docs.fedoraproject.org/en-US/packaging-guidelines/Naming/#multiple[Naming Guidelines for this case]
+_and_ compatible with the restrictions of Semantic Versioning
+by using the `rust2rpm --compat` flag.
+
+All "compat packages" for Rust crates
+*MUST* follow the guidelines for Rust crates,
+and two additional rules apply when creating them:
+
+* For crates that also includes an executable,
+  only the package for the _latest_ version can include this executable,
+  and it *MUST NOT* be built and included in any older versions,
+  to prevent both the name of the executable under `/usr/bin`
+  and the name of the subpackage would conflict
+  between the old and the new version of the package.
+* The packager *SHOULD* check
+  whether running tests in the old version of the crate
+  would cause additional, potentially undesirable dependencies,
+  for example, older versions of other dependencies
+  that would require creating additional "compat packages" –
+  in this case, tests *SHOULD* be disabled
+  (i.e. by flipping the `check` bcond).
+
+=== The `check` bcond
+
+The behaviour of some RPM macros
+depends on the presence and value of the `_with_check` macro,
+i.e. if `%bcond_without check` or `%bcond_with check`
+are used in the spec file – notably,
+the `%cargo_generate_buildrequires` macro only includes `dev-dependencies`
+(i.e. dependencies that are only used
+for compiling and / or running a project’s test suite with cargo)
+if the `check` bconf is enabled.
+
+Additionally, packages for Rust crates or workspace projects
+that are generated by https://pagure.io/fedora-rust/rust2rpm[rust2rpm]
+use the value of this macro to determine if the `%check` scriptlet is run.
+
+Packages *MUST* set this bcond
+to avoid unexpected behaviour of the `%cargo_*` macros,
+by either explicitly _enabling_ or _disabling_ tests.
+
+=== Running tests
+
+Rust crates can have three different kinds of tests in their test suites:
+
+* _"unit tests"_:
+  These tests are included
+  alongside library / application source code in the `src/` directory,
+  and can reference private APIs
+  (similar to "glass-box" tests).
+* _"integration tests"_:
+  These tests are usually separate files under the `tests/` directory,
+  and they can only rely on public API of the tested crate
+  (similar to "black-box" tests).
+* _"doctests"_:
+  These tests are automatically extracted
+  from code blocks in Markdown documentation,
+  which is often used as a mechanism
+  to ensure that code snippets in documentation for public methods
+  are correct and continue to compile.
+
+By default, running `cargo test` (i.e. by calling the `%cargo_test` macro),
+all three kinds of tests are run.
+They can also be invoked separately
+(for example, because parts of the test suite or large data files are
+not included in published sources)
+by passing through filtering arguments
+to the underlying `cargo test` command:
+
+* `%cargo_test -- --lib`: only run _"unit tests"_ for the library interface
+* `%cargo_test -- --bin`: only run _"unit tests"_ for binaries
+* `%cargo_test -- --doc`: only run _"doctests"_
+* `%cargo_test -- --tests`: only run _"integration tests"_
+
+This can be combined with additional flags
+to skip tests with specific names
+(or that contain a specific string in their name)
+by passing the `--skip` argument through to the test harness
+(can be specified multiple times):
 
 ....
-BuildRequires:  (crate(atty/default) >= 0.2.2 with crate(atty/default) < 0.3.0)
-BuildRequires:  (crate(clap/default) >= 2.24.1 with crate(clap/default) < 3.0.0)
+%cargo_test -- --lib -- --skip foo::bar::tests::test1
 ....
 
-== 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.
-
-CAUTION:    Some crates are published with additional information
-            encoded into their version string.
-            For example, Rust bindings for native libraries
-            might add a `+x.y.z`-style suffix to indicate
-            which version of the native library is required or supported.
-            Suffixes like these MUST be removed from the crate's `Cargo.toml` file
-            and dropped from the package Version,
-            because they might introduce dependency resolution problems
-            when this suffix leaks into RPM `Provides` metadata.
-
-=== 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.
+By default, cargo uses substring matching
+to match `--skip` arguments and actual names of tests,
+which can be turned off by using the `--exact` flag.
 
-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.
+If any tests are skipped or disabled,
+the package *SHOULD* include comments
+that explain why this is the case,
+and include links to upstream issues, if available.
 
-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.
+== Rust applications (non-crates.io crates)
+
+Most rules that apply to Rust crates
+that are published in https://crates.io[crates.io]
+also apply to Rust projects that _are_ built with cargo
+but _not_ published on https://crates.io[crates.io].
 
-== License for binary packages
+https://pagure.io/fedora-rust/rust2rpm[rust2rpm]
+has basic support for generating spec files for this type of package
+by running `rust2rpm path/to/Cargo.toml` in unpacked upstream sources.
 
-See xref:legal::license-field.adoc#_rust_packages[License: field in Spec file] guidance specific to Rust.
- 
+Packages that fall into this category *MUST NOT* ship crate sources in `%{cargo_registry}`,
+i.e. they cannot ship `-devel` subpackages
+that contain crate sources or have subpackages
+that have virtual provides for `crate(...) = %{version}`.
 
-== Miscellaneous
+=== Package naming
 
-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.).
+Rust applications that are "crates"
+but which are not published on https://crates.io[crates.io]
+*MUST* be named according to the generic
+https://docs.fedoraproject.org/en-US/packaging-guidelines/Naming/[Naming Guidelines],
+i.e. they *MUST NOT* use a `rust-` prefix for the source package name.
 
-=== Excluding unnecessary files
+=== Package sources
 
-* Packagers SHOULD exclude files which are not used by anything (things like `appveyor.yml` and CI scripts).
-* 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
+The generic guidelines for
+https://docs.fedoraproject.org/en-US/packaging-guidelines/SourceURL/[referencing sources] apply.
+Notably, the `%{crates_source}` macro cannot be used for packages like this.
 
-Example:
+== Rust applications (cargo workspace projects)
 
-[source,diff]
-----
---- 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
- 
-----
+Some Rust projects are organized as "cargo workspaces",
+which are collections of Rust crates
+that are often considered "internal implementation details".
+In most cases, these "internal" crates
+are not published individually on https://crates.io[crates.io].
 
-=== Nightly features and dependencies for other platforms
+Projects that use this setup
+can be handled similarly to Rust projects
+that are organized as a single crate
+but not published on https://crates.io[crates.io].
 
-Packagers MUST NOT package crates which do not work on Fedora, this includes:
+https://pagure.io/fedora-rust/rust2rpm[rust2rpm]
+has basic support for generating spec files for this type of package
+by running `rust2rpm path/to/Cargo.toml` in unpacked upstream sources,
+(where `./Cargo.toml` must be the path to the "workspace root",
+i.e. the `Cargo.toml` file that contains the `[workspace]` table).
 
-* crates depending on nightly-only features of the Rust compiler
-* crates with (non-Linux) platform-specific dependencies
+Packages that fall into this category
+*MUST NOT* ship crate sources in `%{cargo_registry}`,
+i.e. they cannot ship `-devel` subpackages that contain crate sources
+or have any subpackages that have virtual provides for `crate(...) = %{version}`.
+
+=== Package naming
 
-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:
+Rust projects that are organized as "cargo workspaces"
+*MUST* be named according to the generic
+https://docs.fedoraproject.org/en-US/packaging-guidelines/Naming/[Naming Guidelines],
+i.e. they *MUST NOT* use a `rust-` prefix for the source package name.
 
-[source,diff]
-----
---- memmap-0.7.0/Cargo.toml	1970-01-01T00:00:00+00:00
-+++ memmap-0.7.0/Cargo.toml	2019-03-18T19:59:43.683403+00:00
-@@ -23,9 +23,6 @@
- version = "0.3"
- [target."cfg(unix)".dependencies.libc]
- version = "0.2"
--[target."cfg(windows)".dependencies.winapi]
--version = "0.3"
--features = ["basetsd", "handleapi", "memoryapi", "minwindef", "std", "sysinfoapi"]
- [badges.appveyor]
- repository = "danburkert/mmap"
- 
-----
+=== Package sources
 
-NOTE: The patch to remove platform-specific dependencies for non-linux platforms
-      is automatically generated by rust2rpm versions 22 and later.
-      Additionally, a configuration file for rust2rpm (`+rust2rpm.conf+`)
-      can be added to the package's dist-git repository
-      to disable generation of subpackages for specific features,
-      (for example, nightly-only features).
+The generic guidelines for
+https://docs.fedoraproject.org/en-US/packaging-guidelines/SourceURL/[referencing sources]
+apply.
 
-== Update process
+=== RPM macros
 
-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]).
+All `%cargo_*` macros have support for cargo workspaces
+as of `cargo-rpm-macros >= 24`.
+Any unexpected results that occur when using these macros for projects
+that are set up as a cargo workspace
+should be reported against https://pagure.io/fedora-rust/cargo2rpm[cargo2rpm].
 
-If possible, packages for Rust library crates SHOULD be updated
-to the same version across all currently supported branches of Fedora.
-Exceptions for this rule might be:
+Note that currently, any `-a` and `-n` flags or `-f` arguments
+that are passed to `%cargo_generate_buildrequires`
+are applied to _all_ workspace members during dependency resolution.
 
-* system dependencies are too old on older branches (for example, `+libgit2+`)
-* pushing API-incompatible versions would result in broken packages on stable
-  branches (for example, packages for the `+gtk-rs+` project crates)
+== Python projects
 
-== Examples
+Python packages that use https://github.com/PyO3/setuptools-rust[setuptools_rust]
+or https://github.com/PyO3/maturin[maturin]
+to build a "native" Python extension
+also need to apply the link:#_generic_rules[generic rules] for  Rust packages
+in addition to following the
+https://docs.fedoraproject.org/en-US/packaging-guidelines/Python/[Python Packaging Guidelines].
 
-=== Library crate
+Both https://github.com/PyO3/setuptools-rust[setuptools_rust]
+and https://github.com/PyO3/maturin[maturin]
+build the native Python extension by calling cargo internally,
+so the basic setup for projects that build with cargo
+is required for packages like this as well.
 
-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.
+This includes calling `%cargo_prep` in `%prep`
+to set up the build environment for cargo,
+and using `%cargo_generate_buildrequires` to dynamically generate
+the appropriate `BuildRequires` for Rust crate dependencies.
 
-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.
+Additionally, `%cargo_license` and / or `%cargo_license_summary`
+*MUST* be used to determine the licenses
+that apply to the statically linked Python extension.
+
+The packager also *MUST* ensure
+that the default link:#_compiler_flags[compiler flags] are passed to rustc.
 
-.rust-serde.spec
-[source]
-----
-include::{examplesdir}/rust-serde.spec[]
-----
+== Mixed Rust / C/C++ projects
+
+Handling of projects that include both C/C++ and Rust code
+depends on how building the Rust code is integrated
+into the project’s build system.
+
+Independent of the specific setup,
+the correct link:#_compiler_flags[compiler flags] *MUST* be passed to rustc,
+and the License tag of the package that contains the Rust component
+*MUST* take the licenses of statically linked crates into account.
 
-=== Binary crate
+=== Building with cargo internally
+
+Projects with build systems that call cargo internally to build Rust components
+*MUST* follow the same guidelines
+as other projects that build Rust code with cargo.
 
-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.
+Packages *MUST* ensure that the cargo calls
+that are internal to the project’s build system
+do not pass flags or arguments that are incompatible
+with either the default link:#_compiler_flags[compiler flags] or cargo options
+that are set in the `%cargo_build` macro or configured by `%cargo_prep`.
 
-.rust-ripgrep.spec
-[source]
-----
-include::{examplesdir}/rust-ripgrep.spec[]
-----
+=== Building with meson directly
+
+Upcoming versions of https://mesonbuild.com/Rust.html[meson]
+will have support for building crate dependencies
+by reading `Cargo.toml` files directly,
+with meson supporting a similar mechanism
+for overriding https://crates.io[crates.io] sources with a local registry.
 
-=== Library + Binary
+== Building shared libraries with cargo-c
+
+While it is not currently possible
+to build Rust crates as shared libraries,
+Rust projects can define a C-compatible public API
+so that they can be built as standard shared libraries with a C ABI.
+
+In most cases, libraries like this are built with
+https://crates.io/crates/cargo-c[cargo-c],
+which provides convenient wrappers (`cargo-cbuild` and `cargo-cinstall`)
+for both building and installing shared libraries
+(including support for generating and installing header files
+and and pkg-config files).
 
-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.
+The `cargo-c` package includes RPM macros for this functionality
+(`%cargo_cbuild` and `%cargo_cinstall`),
+which accept the same arguments as their cargo counterparts.
 
-.rust-yubibomb.spec
-[source]
-----
-include::{examplesdir}/rust-yubibomb.spec[]
-----

