From eadcdfacfbb0ea8f96212bfdd23e5a6fb23a9d26 Mon Sep 17 00:00:00 2001 From: Fabio Valentini Date: Nov 27 2025 14:19:30 +0000 Subject: [PATCH 1/3] Rust: move sections for generated BuildRequires and RPM macros --- diff --git a/guidelines/modules/ROOT/pages/Rust.adoc b/guidelines/modules/ROOT/pages/Rust.adoc index fc4f27f..e656a51 100644 --- a/guidelines/modules/ROOT/pages/Rust.adoc +++ b/guidelines/modules/ROOT/pages/Rust.adoc @@ -94,6 +94,45 @@ it will automatically detect usage of these features, and include the necessary `BuildRequires` for the RPM macro package automatically. +=== 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: + +.... +BuildRequires: (crate(serde/default) >= 1.0.100 with crate(serde/default) < 2.0.0~) +.... + +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. + === License tags Similar to other languages that produce statically linked binaries, @@ -433,159 +472,6 @@ 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. - To prevent installation of executables by this macro, - the `+%cargo_install_bin+` macro can be defined to `0`. - To prevent installation of library sources by this macro, - the `+%cargo_install_lib+` macro can be defined to `0`. -* `%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` and `%cargo_vendor_manifest`) -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, -for example when enabling a different backend than the one enabled by default, -and *MUST NOT* be used to avoid missing dependencies -that are part of the `"default"` feature set of a crate. - -When passing any of the `-a` or `-n` flags or an `-f` argument -to a `%cargo_build` and / or `%cargo_install` macro, -the same flags MUST also be passed -to `%cargo_license` and `%cargo_license_summary` (if present). -Otherwise, the list of generated licenses and the generated license summary -will not match what is used when the application or library is compiled. - -It is recommended to set these flags in a `rust2rpm.toml` config file -which causes the flags to be injected into generated spec files automatically, -whereever necessary. - -=== 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: - -.... -BuildRequires: (crate(serde/default) >= 1.0.100 with crate(serde/default) < 2.0.0~) -.... - -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 @@ -922,3 +808,116 @@ The `cargo-c` package includes RPM macros for this functionality (`%cargo_cbuild` and `%cargo_cinstall`), which accept the same arguments as their cargo counterparts. +== 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. + To prevent installation of executables by this macro, + the `+%cargo_install_bin+` macro can be defined to `0`. + To prevent installation of library sources by this macro, + the `+%cargo_install_lib+` macro can be defined to `0`. +* `%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` and `%cargo_vendor_manifest`) +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, +for example when enabling a different backend than the one enabled by default, +and *MUST NOT* be used to avoid missing dependencies +that are part of the `"default"` feature set of a crate. + +When passing any of the `-a` or `-n` flags or an `-f` argument +to a `%cargo_build` and / or `%cargo_install` macro, +the same flags MUST also be passed +to `%cargo_license` and `%cargo_license_summary` (if present). +Otherwise, the list of generated licenses and the generated license summary +will not match what is used when the application or library is compiled. + +It is recommended to set these flags in a `rust2rpm.toml` config file +which causes the flags to be injected into generated spec files automatically, +whereever necessary. From aa3ff2d22742f87cd06de9964b136a2c50b06e86 Mon Sep 17 00:00:00 2001 From: Fabio Valentini Date: Nov 27 2025 14:20:26 +0000 Subject: [PATCH 2/3] Rust: simplify some sections, update for rust2rpm v27 / v28 changes --- diff --git a/guidelines/modules/ROOT/pages/Rust.adoc b/guidelines/modules/ROOT/pages/Rust.adoc index e656a51..142e9d1 100644 --- a/guidelines/modules/ROOT/pages/Rust.adoc +++ b/guidelines/modules/ROOT/pages/Rust.adoc @@ -1,6 +1,6 @@ = Rust Packaging Guidelines -:last-reviewed: 2024-04-03 +:last-reviewed: 2025-11-26 https://www.rust-lang.org[Rust] is a strongly and statically typed, compiled programming language @@ -18,10 +18,9 @@ specific to the different ways in which projects can be set up: 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:#_non_crate_rust_projects[Non-crate Rust projects]: + Rust projects that are not published on crates.io, + or larger projects that are comprised of many crates (cargo workspace) * 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: @@ -29,9 +28,8 @@ specific to the different ways in which projects can be set up: 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. +The https://pagure.io/fedora-rust/rust2rpm[rust2rpm] tool can be used +to generate spec files for Rust crates from cargo / crate metadata. It is designed to produce spec files that are in line with the (Rust) Packaging Guidelines. @@ -39,6 +37,7 @@ There are also guidelines for link:#_building_shared_libraries_with_cargo_c[pack 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. @@ -55,7 +54,7 @@ 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, +For compatibility with older releases or EPEL, this environment variable can be set manually at the start of `%build` and `%check` in package's spec files: @@ -74,26 +73,16 @@ 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`. +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. +or which call any of the `+%cargo_*+` macros, +*MUST* add `+BuildRequires: cargo-rpm-macros+`, +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. - === Dynamically generated `BuildRequires` for crate dependencies With Semantic Versioning (_"SemVer"_) being @@ -107,8 +96,7 @@ 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, +Since dependencies of Rust projects change frequently, packages for projects that build Rust code with cargo *MUST* use dynamically generated `BuildRequires` by calling the `%cargo_generate_buildrequires` macro @@ -128,11 +116,6 @@ BuildRequires: (crate(serde/default) >= 1.0.100 with crate(serde/default) < 2.0 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. - === License tags Similar to other languages that produce statically linked binaries, @@ -144,10 +127,12 @@ 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 https://docs.fedoraproject.org/en-US/legal/license-field/[Fedora Legal docs]. +More information about `License` tags is available from the +https://docs.fedoraproject.org/en-US/legal/license-field/[Fedora Legal docs]. The `cargo-rpm-macros` package provides two RPM macros -that help with filling the `License` tag correctly: +that help with filling the `License` tag correctly +for projects that are built with `cargo`: - `%cargo_license_summary` @@ -170,9 +155,9 @@ 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`), +as 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`. +the same flags *MUST* be passed to them and `%cargo_build`. === Vendored dependencies @@ -189,11 +174,8 @@ 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. +There are also two situations +in which bundling at least _some_ Rust crates is usually unavoidable: ==== Replacing git dependencies @@ -215,6 +197,8 @@ 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. +Adding this subproject as a workspace member ensures +that its dependencies are included during dependency resolution. ==== Replacing patched crate sources @@ -234,7 +218,7 @@ similar to the process described for link:#_replacing_git_dependencies[git-type ==== Using vendor tarballs -Official support for building with vendored dependencies +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, @@ -274,9 +258,24 @@ The necessary spec file adaptations and the generation of the vendor tarball itself happen automatically when running rust2rpm in "vendor" mode. -== rust2rpm -The recommended way to write spec files for Rust projects +== 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), +https://pagure.io/fedora-rust/rust2rpm[rust2rpm] *MUST* be re-run +for every new version of a crate +to ensure that generated feature subpackages +stays in sync with crate metadata. + +=== rust2rpm + +The recommended way to write spec files for Rust crates is to use https://pagure.io/fedora-rust/rust2rpm[rust2rpm], and apply any necessary modifications on top of the generated spec file. @@ -343,81 +342,23 @@ since changes like these affect spec file generation 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), -https://pagure.io/fedora-rust/rust2rpm[rust2rpm] *MUST* be re-run -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]. - -==== Crates with Rust library interface - -Crates that are published on https://crates.io[crates.io] -and that are intended to provide a Rust library interface -*MUST* be packaged with `rust-$crate` as the name of the source package +Packages for Rust crates from https://crates.io[crates.io] +*MUST* use `rust-$crate` as the name of the source package (where `$crate` is the name of the project on https://crates.io[crates.io]). + This ensures that there are no name collisions between Rust crates published on https://crates.io[crates.io] and Rust crates packaged for Fedora. -Projects from sources other than https://crates.io[crates.io] -*MUST NOT* use the `rust-` prefix for source package names, -and *MUST* follow the general Naming Guidelines instead. -In this case, the guidelines for either -link:#_rust_applications_non_crates_io_crates[single-crate Rust applications] or -link:#_rust_applications_cargo_workspace_projects[cargo workspaces] apply. - -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 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) that contain the Rust crate sources -*MUST* be named `rust-$crate-devel` -and `rust-$crate+$feature-devel` for all crate features -and ensure that the virtual `Provides` for the Rust crate are correct. -The `rust-` prefix is not required for the name of the source package. - -==== Crates without Rust library interface - -Crates that do not provide a Rust library interface -(for example, crates that only contain executable targets) -*MAY* drop the `rust-` prefix for the name of the source package -or use the "project name" if it is different from the crate name -_if and only if_ the project will not provide (or need to provide) -a Rust library interface in the future, -since this would require renaming the source package to `rust-$crate`. -In this case, the guidelines for -link:#_rust_applications_non_crates_io_crates[single-crate Rust applications] apply, -and the package is not required to use the sources -that are published on https://crates.io[crates.io]. - -When building a crate with vendored dependencies -the `rust-` prefix of the source package name *MAY* be dropped as well -- -since packages cannot provide a Rust library interface in this case -- -_if and only if_ the project will not need to provide a Rust library interface. - -When generating a package for a Rust crate -that also (or exclusively) contains an application, +When generating a package for a Rust crate that contains executable targets, 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 +to generate a subpackage with a name that matches the crate 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. +If necessary, this subpackage can be renamed, +for example, if the name does not match expectations, or +if it would conflict with another already existing package. === Package versioning @@ -445,6 +386,7 @@ This happens automatically when using rust2rpm version 25 or newer. === Package sources +The canonical source of Rust crates is https://crates.io[crates.io]. 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). @@ -456,6 +398,10 @@ 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. +Alternatively, if the project in question does not provide a Rust library interface, +it can be packaged as a link:#_non_crate_rust_projects[Non-crate Rust project] +using the upstream sources instead. + === Crate license Most tooling support for determining licenses @@ -467,9 +413,8 @@ 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. +*MUST* be patched to set `package.license` in their metadata, +and an accurate SPDX expression *MUST* be provided instead. Patches like this *SHOULD* be submitted upstream. === Subpackages for crate features @@ -585,13 +530,13 @@ 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. +if the `check` bcond is enabled. -Additionally, packages for Rust crates or workspace projects +Additionally, packages for Rust crates 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 +Packages for Rust crates *MUST* set this bcond to avoid unexpected behaviour of the `%cargo_*` macros, by either explicitly _enabling_ or _disabling_ tests. @@ -624,9 +569,9 @@ 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 -- --bin foo`: only run _"unit tests"_ for binary "foo" * `%cargo_test -- --doc`: only run _"doctests"_ -* `%cargo_test -- --tests`: only run _"integration tests"_ +* `%cargo_test -- --test bar`: only run _"integration test"_ "bar" This can be combined with additional flags to skip tests with specific names @@ -647,91 +592,48 @@ the package *SHOULD* include comments that explain why this is the case, and include links to upstream issues, if available. -== 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]. - -https://pagure.io/fedora-rust/rust2rpm[rust2rpm] -has basic support for generating spec files for this type of package -by running `rust2rpm --path path/to/Cargo.toml` in unpacked upstream sources. - -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}+`. - -To prevent the `+%cargo_install+` macro -from installing library sources into `+%{cargo_registry}+`, -the `%cargo_install_lib` macro can be defined to `0`. -When using rust2rpm with a path to a Cargo.toml file -this macro definition is injected automatically. - -=== Package naming - -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. - -=== Package sources - -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. -== Rust applications (cargo workspace projects) +== Non-crate Rust projects -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]. +This section applies to Rust projects +that are not packaged from sources on crates.io. -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]. +The most common cases are -https://pagure.io/fedora-rust/rust2rpm[rust2rpm] -has basic support for generating spec files for this type of package -by running `rust2rpm --path 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). +* applications that do not provide a library interface, +* projects comprised of multiple crates (cargo workspaces) + that may or may not be published (or useful) separately, and +* Python packages implemented in Rust (link:#_python_projects[covered below]). -Packages that fall into this category -*MUST NOT* ship crate sources in `+%{cargo_registry}+`, +Packages like this *MUT 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}+`. +or have subpackages that have virtual provides for `+crate(...) = %{version}+`. === Package naming -Rust projects that are organized as "cargo workspaces" -*MUST* be named according to the generic +Packages for non-crate Rust projects *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. === Package sources The generic guidelines for -https://docs.fedoraproject.org/en-US/packaging-guidelines/SourceURL/[referencing sources] -apply. +https://docs.fedoraproject.org/en-US/packaging-guidelines/SourceURL/[referencing sources] apply. -=== RPM macros +The `+%{crates_source}+` macro *SHOULD NOT* be used for packages like this, +even when packaging a Rust crate that is also published on crates.io. -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]. +=== RPM macros -Note that currently, any `-a` and `-n` flags or `-f` arguments +Any `-a` and `-n` flags or `-f` arguments that are passed to `%cargo_generate_buildrequires` are applied to _all_ workspace members during dependency resolution. +The `+%cargo_install+` macro *SHOULD NOT* be used for packages like this. +Instead, use `install` or `cp` to copy built executables or shared libraries +from `+target/rpm/*+` into the buildroot explicltly, as needed. + + == Python projects Python packages that use https://github.com/PyO3/setuptools-rust[setuptools_rust] @@ -757,7 +659,10 @@ Additionally, `%cargo_license` and / or `%cargo_license_summary` 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. +that the default link:#_compiler_flags[compiler flags] are passed to rustc, +and that debuginfo is not stripped during the build process +due to settings in the `setuptools-rust` or `maturin` configuration. + == Mixed Rust / C/C++ projects @@ -779,16 +684,17 @@ as other projects that build Rust code with cargo. 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`. +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`. === 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. +Recent versions of https://mesonbuild.com/Rust.html[meson] +have limited support for building crate dependencies without cargo. +It is necessary to manually override crate sources +with the local registry to use packaged crate dependencies. + == Building shared libraries with cargo-c @@ -808,9 +714,10 @@ The `cargo-c` package includes RPM macros for this functionality (`%cargo_cbuild` and `%cargo_cinstall`), which accept the same arguments as their cargo counterparts. + == RPM macros -The process of building and installing Rust crates +The process of building and installing Rust projects is almost entirely automated with several RPM macros: * `%cargo_prep`: @@ -859,10 +766,21 @@ is almost entirely automated with several RPM macros: 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]). +* `%cargo_vendor_manifest`: + This macro *MUST* be called in the `%build` scriptlet after `%cargo_build` + when building Rust projects with vendored dependencies. + It writes a machine-readable list of all vendored dependencies + to `cargo-vendor.txt`, which *MUST* be included as a `%license` file + in the package that contains the statically linked executable(s). 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`. +either `%bcond check 1` or `%bcond check 0`. +The value of this macro affects whether `%cargo_generate_buildrequires` +includes dependencies that are required for building and running tests. + +Non-crate packages can either use the `%bcond check` +_or_ pass the `-t` flag to the `%cargo_generate_buildrequires` macro +to include test-only dependencies in `BuildRequires` generation. All `%cargo_*` macros (except `%cargo_prep` and `%cargo_vendor_manifest`) accept a set of optional flags / arguments @@ -918,6 +836,118 @@ to `%cargo_license` and `%cargo_license_summary` (if present). Otherwise, the list of generated licenses and the generated license summary will not match what is used when the application or library is compiled. -It is recommended to set these flags in a `rust2rpm.toml` config file -which causes the flags to be injected into generated spec files automatically, -whereever necessary. + +== Templates + +=== Non-crate Rust project + +[source,rpm] +---- +Name: my-awesome-project +Version: 25.11.26 +Release: %autorelease +Summary: My Awesome Rust Project + +SourceLicense: WTFPL +# FIXME: paste output of %%cargo_license_summary here +License: %{shrink: + WTFPL AND + ... +} +# LICENSE.dependencies contains a full license breakdown + +URL: https://forge.example/me/my-awesome-project +Source: %{url}/archive/v%{version}.tar.gz + +BuildRequires: cargo-rpm-macros + +%description +My Awesome Rust Project. + +%prep +%autosetup -p1 +%cargo_prep + +%generate_buildrequires +%cargo_generate_buildrequires -t + +%build +%cargo_build +%{cargo_license_summary} +%{cargo_license} > LICENSE.dependencies + +%install +install -Dpm 0755 target/rpm/my-awesome-cli -t %{buildroot}%{_bindir} + +%check +%cargo_test + +%files +%license LICENSE +%license LICENSE.dependencies +%doc README.md +%{_bindir}/my-awesome-cli + +%changelog +%autochangelog +---- + +=== Python project + +[source,rpm] +---- +Name: python-rustypackage +Version: 25.11.26 +Release: %autorelease +Summary: Rusty Python Package + +License: WTFPL +URL: https://forge.example/me/python-rustypackage +Source: %{url}/archive/v%{version}.tar.gz + +BuildRequires: python3-devel +BuildRequires: cargo-rpm-macros + +%description +My Rusty Python Package. + +%package -n python3-rustypackage +Summary: %{summary} + +# FIXME: paste output of %%cargo_license_summary here +License: WTFPL AND ... +# LICENSE.dependencies contains a full license breakdown + +%description -n python3-rustypackage +My Rusty Python Package. + +%prep +%autosetup -p1 +%cargo_prep + +%generate_buildrequires +# maturin requires all dependencies to be available, +# even those for tests and features that are not enabled +%cargo_generate_buildrequires -a -t +%pyproject_buildrequires + +%build +%pyproject_wheel +%{cargo_license_summary} +%{cargo_license} > LICENSE.dependencies + +%install +%pyproject_install +%pyproject_save_files rustypackage + +%check +%pyproject_check_import +# %%pytest +# %%cargo_test + +%files -n python3-rustypackage +%doc README.md + +%changelog +%autochangelog +---- From 9e7217c616d9d92b0236fcf52ebcdc887777d56c Mon Sep 17 00:00:00 2001 From: Fabio Valentini Date: Dec 07 2025 17:54:17 +0000 Subject: [PATCH 3/3] Rust: more small improvements and clarifications --- diff --git a/guidelines/modules/ROOT/pages/Rust.adoc b/guidelines/modules/ROOT/pages/Rust.adoc index 142e9d1..0fdfc77 100644 --- a/guidelines/modules/ROOT/pages/Rust.adoc +++ b/guidelines/modules/ROOT/pages/Rust.adoc @@ -342,6 +342,9 @@ since changes like these affect spec file generation which is only correctly taken into account if the patch is created _before_ generation of the spec file. +It is recommended to track non-empty `+rust2rpm.toml+` configuration files +in the package repository alongside the generated `+.spec+` file. + === Package naming Packages for Rust crates from https://crates.io[crates.io] @@ -734,12 +737,14 @@ is almost entirely automated with several RPM macros: 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. + This macro *MUST* only be called in the `%build` scriptlet + unless the build is handled in another way, + i.e. "cargo build" is called internally by build scripts. 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 + This macro *MUST* only 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}+` @@ -749,13 +754,13 @@ is almost entirely automated with several RPM macros: 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. + with manual installation steps (copying from `+target/rpm/*+`). To prevent installation of executables by this macro, the `+%cargo_install_bin+` macro can be defined to `0`. To prevent installation of library sources by this macro, the `+%cargo_install_lib+` macro can be defined to `0`. * `%cargo_test`: - This macro *MUST* be called in the `%check` scriptlet. + This macro *MUST* only 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 @@ -903,26 +908,30 @@ Summary: Rusty Python Package License: WTFPL URL: https://forge.example/me/python-rustypackage -Source: %{url}/archive/v%{version}.tar.gz +Source: %{url}/archive/v%{version}/rustypackage-%{version}.tar.gz BuildRequires: python3-devel BuildRequires: cargo-rpm-macros -%description -My Rusty Python Package. +%global _description %{expand: +My Rusty Python Package.} + +%description %_description %package -n python3-rustypackage Summary: %{summary} # FIXME: paste output of %%cargo_license_summary here -License: WTFPL AND ... +License: %{shrink: + WTFPL AND + ... +} # LICENSE.dependencies contains a full license breakdown -%description -n python3-rustypackage -My Rusty Python Package. +%description -n python3-rustypackage %_description %prep -%autosetup -p1 +%autosetup -n rustypackage-%{version} -p1 %cargo_prep %generate_buildrequires @@ -938,16 +947,20 @@ My Rusty Python Package. %install %pyproject_install -%pyproject_save_files rustypackage +%pyproject_save_files -l rustypackage %check %pyproject_check_import # %%pytest # %%cargo_test -%files -n python3-rustypackage +%files -n python3-rustypackage -f %{pyproject_files} %doc README.md %changelog %autochangelog ---- + +Verify that project license file(s) *and* the `LICENSE.dependencies` file +are included in built packages as expected (`+rpm -qL -p +`) +when using `+%pyproject_save_files -l+`.