Rust Packaging Guide
This document describes the Rust packaging guidelines for openRuyi. For the related build systems, see Rust.
Rust packages in openRuyi usually fall into the following categories:
- Rust crate provider packages.
- Rust applications or command-line tools.
- Packages in other languages that contain Rust extensions, such as Python/Rust extension packages.
Use different build systems and dependency declarations for different package types. Rust packaging work usually uses TakoPack to generate crate providers, analyze dependencies, and assist package review. Project address: https://github.com/TakoPack/TakoPack
Naming
Rust crate provider packages must use the rust- prefix.
A crate provider package name usually comes from the crate compatibility name. For example:
%global crate_name serde
%global full_version 1.0.228
%global pkgname serde-1
Name: rust-serde-1
Version: 1.0.228
Where:
crate_nameis the original crate name from upstreamCargo.toml.full_versionis the full upstream crate version.pkgnameis the compatibility name that openRuyi uses for package names, directory names, andcrate(...)capabilities.
RPM package names and crate(...) capabilities usually normalize underscores in crate names to hyphens. For example, when the upstream crate name is os_str_bytes, the provider package name usually uses os-str-bytes.
Keep the crate provider directory name, spec file name, and Name consistent. For example:
SPECS/rust-serde-1/rust-serde-1.spec
corresponds to:
%global pkgname serde-1
Name: rust-serde-1
If you must change pkgname, also update the directory name, spec file name, and related crate(...) dependency declarations. Explain the reason in the commit and PR.
Compatibility Names
Rust crate providers use compatibility names so that different incompatible versions can coexist in the repository.
Use the following rules in most cases:
- Group versions
1.x.yand later by major version. - Group versions
0.y.zwherey > 0by minor version. - Group versions
0.0.zby the full patch version.
For example:
serde 1.0.228 -> serde-1
tokenizers 0.22.2 -> tokenizers-0.22
foo 0.0.7 -> foo-0.0.7
Do not introduce unnecessarily fine-grained compatibility names, such as serde-1.0.15, unless the crate has a clear compatibility issue.
Build Systems
Use the following build system for Rust crate provider packages:
BuildSystem: rustcrates
Use the following build system for Rust applications, command-line tools, or other packages that need to run Cargo build/test workflows:
BuildSystem: rust
Python packages with Rust extensions should usually continue to use a Python-related build system, for example:
BuildSystem: pyproject
Dependencies
Declare Rust crate dependencies through crate(...) capabilities. For example:
BuildRequires: crate(serde-1/default) >= 1.0.0
BuildRequires: crate(clap-4/derive) >= 4.5.0
If the package needs a feature, explicitly declare the corresponding feature capability.
Do not use ordinary RPM package names instead of crate(...) capabilities to declare Rust crate dependencies. crate(...) capabilities are the main interface for dependency resolution between Rust ecosystem packages.
When you modify a crate or package, confirm the following:
- Newly added or modified crate providers follow openRuyi naming, source, patch, and dependency rules.
- The change does not break the build of existing Rust packages in the repository.
- The crate providers already available in the repository can support dependency analysis based on the
Cargo.tomlof software that builds with crate packages. - You have checked the impact of newly added providers, upgraded providers, and existing providers.
Rust Crate Provider Packages
Rust crate provider packages install crate source code into the system and provide crate(...) capabilities for other Rust packages.
Generate crate provider packages with TakoPack, and submit the generated result as the initial packaging content for review. Maintainers need to check that the generated result follows openRuyi naming, source, dependency, and patch rules.
A typical crate provider package looks like this:
%global crate_name anes
%global full_version 0.1.6
%global pkgname anes-0.1
Name: rust-anes-0.1
Version: 0.1.6
Release: %autorelease
Summary: Benchmarking helper library
License: MIT OR Apache-2.0
URL: https://crates.io/crates/%{crate_name}
#!RemoteAsset: sha256:...
Source: https://static.crates.io/crates/%{crate_name}/%{full_version}/download#/%{name}-%{version}.tar.gz
BuildArch: noarch
BuildSystem: rustcrates
BuildRequires: rust-rpm-macros
Source-related fields such as Source, #!RemoteAsset, and Patch should follow the actual generated result and repository rules. Do not break the consistency between source files, checksums, and spec declarations when you make manual changes.
Crate provider packages usually do not need hand-written %build, %install, or %check sections. The rustcrates build system handles these behaviors.
Cargo.toml in the Provider Directory
When TakoPack generates a crate provider package, it keeps a Cargo.toml file in the provider directory. For example:
SPECS/rust-serde-1/Cargo.toml
SPECS/rust-serde-1/rust-serde-1.spec
This Cargo.toml is a copy of the crate metadata used for packaging. It records the crate name, version, dependencies, and feature information. It also provides important input for generating BuildRequires: crate(...), feature subpackages, and related dependency relationships.
For providers generated from crates.io, this file usually comes from the upstream crate archive. For providers generated from a local Cargo.toml, it comes from the local file specified in the command.
If you need to patch upstream Cargo.toml during packaging, keep the following content consistent:
Cargo.tomlin the provider directory.Cargo.tomlin the source package after patches are applied.Patchdeclarations in the spec file.RequiresandProvidesgenerated from crate metadata.
Do not only modify generated dependency declarations in the spec file without updating the corresponding Cargo.toml.
Generating a Provider from crates.io
For crates published on crates.io, use TakoPack to generate the initial provider package. For example:
takopack cargo pkg cbindgen 0.29.2
You can also omit the version, in which case the tool uses the currently resolved version:
takopack cargo pkg pem
The generated result usually appears in the current directory, for example:
rust-cbindgen-0.29/
rust-pem-3/
After generation, check that the directory name, spec file name, pkgname, source URL, checksum, and dependency declarations follow the packaging rules.
Generating a Provider from a Local Cargo.toml
If the crate's Cargo.toml has already been patched, or if you need to generate the provider from local source metadata, use the local Cargo.toml as input. For example:
takopack cargo localpkg value-bag-1.12.0/Cargo.toml
You can also specify an output directory:
takopack cargo localpkg value-bag-1.12.0/Cargo.toml -o outdir
This workflow is commonly used when:
- You have relaxed or corrected dependency constraints.
- You have changed git/path dependencies into a form suitable for openRuyi packaging.
After generating a provider from a local Cargo.toml, make sure the Cargo.toml in the provider directory stays in sync with the patched Cargo.toml in the packaged source.
Rust Application Packages
Rust application packages usually use:
BuildSystem: rust
Rust application packages also need to declare the Rust build environment and the crate dependencies required by the actual build. For example:
BuildRequires: rust
BuildRequires: rust-rpm-macros
BuildRequires: crate(clap-4/default) >= 4.5.0
BuildRequires: crate(clap-4/derive) >= 4.5.0
If the application needs to install binaries, explicitly install the build artifacts in %install. For example:
%install
install -Dm0755 target/release/example-cli %{buildroot}%{_bindir}/example-cli
For Rust applications that use git dependencies or in-tree path dependencies, patch Cargo.toml so that dependencies point to source locations that actually exist during packaging, or to crate providers already available in the system. After modification, make sure the Cargo.toml shipped with the spec, the Cargo.toml actually used in the source tree, and the patch content are consistent.
If the Rust application itself is also published on crates.io, you can use TakoPack to generate an initial provider directory first, and then adjust the spec file according to the needs of the application package. In this case, copy the generated Cargo.toml into the actual package directory and make sure it stays consistent with the Cargo.toml used by the final packaged source.
If the Rust application does not come from crates.io, copy the main Cargo.toml from the unpacked upstream source into the package directory as the basis for dependency analysis and review.
Python/Rust Extension Packages
If a Python package contains a Rust extension, keep using a Python-related build system in most cases, for example:
BuildSystem: pyproject
Declare both Python and Rust build dependencies. For example:
BuildRequires: rust
BuildRequires: rust-rpm-macros
BuildRequires: python3dist(maturin)
BuildRequires: crate(pyo3-0.27/default) >= 0.27.0
BuildRequires: crate(pyo3-0.27/extension-module) >= 0.27.0
For this kind of package, %prep usually needs to configure the Rust system registry after the Python build system's default preparation logic, and remove upstream Cargo.lock when appropriate. For example:
%prep -a
%rust_setup_registry
rm -f Cargo.lock
If the Rust source is located in a subdirectory, handle the corresponding Cargo.lock in the correct directory. For example:
%prep -a
%rust_setup_registry
rm -f rust/Cargo.lock
Python/Rust extension packages usually need to copy the main Cargo.toml from the unpacked upstream source into the package directory as the basis for dependency analysis and review.
CI Checks
When packaging or updating a Rust crate provider, handle issues reported by CI in the spec or related packaging content as needed. CI may expose issues such as source file permissions, interpreter paths, or missing dependency closures for certain features.
openRuyi does not necessarily use all features of a crate. If CI reports dependency closure issues for features that the package does not actually use, and you confirm that you do not need to package additional crate providers for those features at the moment, explain the reason in the PR comments.
After upgrading a crate provider, even if an application package's dependencies still satisfy the build requirements, check whether the actual BuildRequires: crate(...) entries in the spec file need to be updated.
Patches
Some crate dependency constraints cannot be directly mapped to openRuyi crate provider rules. Common cases include:
- Dependency ranges crossing multiple crate compatibility keys.
- Dependencies on prerelease versions.
- Overly strict version pinning.
- Dependencies on git repositories.
- Dependencies on internal workspace path crates.
In these cases, prefer patching upstream Cargo.toml, and then regenerate or update the provider based on the patched Cargo.toml.
Do not only modify generated Requires or Provides to work around the problem. Regenerate Requires and Provides from the corrected crate metadata, otherwise the spec file may become inconsistent with the actual source.
Patches should follow the repository's general patching rules. Make sure Patch, source content, Cargo.toml in the provider directory, and the generated provider directory remain consistent.
Practical Recommendations
When packaging Rust software, follow these principles:
- Use
BuildSystem: rustcratesfor crate providers. - Use
BuildSystem: rustfor Rust applications. - Usually keep
BuildSystem: pyprojectfor Python/Rust extensions. - Generate Rust crate providers with TakoPack.
- Express Rust crate dependencies with
BuildRequires: crate(...). - Manually review generated providers.
- When dependencies are unreasonable, patch
Cargo.tomlfirst and regenerate the provider. - Do not directly modify generated
RequiresorProvidesas a substitute for metadata fixes. - When modifying a crate or package, confirm that the change does not break the build of existing Rust packages in the repository.
- Check the impact of newly added providers, upgraded providers, and existing packages.
Credits
The automatic generation tool for Rust crate providers is adapted from debcargo by the Debian Rust Packaging Team to meet openRuyi packaging needs. We thank them for their work.