Paketbaurichtlinien für Rust

Rust ist eine stark und statisch typisierte, kompilierte Programmiersprache, die Konzepte sowohl der imperativen als auch der funktionalen Programmierung unterstützt.

Da es noch keine stabile Rust-ABI gibt und die bedingte Kompilierung ein weit verbreitetes Feature im Rust-Ökosystem ist, können Rust-Bibliotheken („crates“) nicht in kompilierter Form verteilt werden, sondern werden stattdessen als Quellcode verteilt.

Dieses Dokument beschreibt, wie Rust-Code in Paketen gehandhabt wird, insbesondere im Hinblick auf die verschiedenen Möglichkeiten, Projekte einzurichten:

  • Rust „crates“: Pakete, die einzeln auf crates.io, dem offiziellen Paketregister für Rust, veröffentlicht werden (hauptsächlich Bibliotheken, aber auch „Single-Crate“-Anwendungen)

  • Nicht-Crate-Rust-Projekte: Rust-Projekte, die nicht auf crates.io veröffentlicht sind, oder größere Projekte, die aus vielen Crates bestehen (Cargo-Workspace)

  • Python-Projekte mit einer in Rust implementierten „nativen“ Komponente: üblicherweise erstellt mit setuptools_rust oder maturin

  • link:#_mixed_rust_cc_projects [gemischte Rust-/C/C++-Projekte], bei denen Teile des Projekts in Rust implementiert sind: entweder durch Einbinden von Cargo oder durch Nutzung der eingeschränkten Unterstützung von https://mesonbuild.com/Rust.html [Meson] für das direkte Kompilieren von Rust-Code

Das Werkzeug rust2rpm generiert Spec-Dateien für Rust-Crates aus Cargo-/Crate-Metadaten. Es erzeugt Spec-Dateien, die den Paketbaurichtlinien für Rust genügen.

Es gibt auch Richtlinien für die Paketierung gemeinsam genutzter Bibliotheken, die in Rust implementiert sind (normalerweise erstellt und installiert mit cargo-c).

Allgemeine Regeln

Die Regeln in diesem Abschnitt beziehen sich auf alle Pakete, die Rust-Code ausliefern.

Compiler-Flags

Ähnlich wie bei anderen Sprachökosystemen in Fedora gibt es einen standardisierten Satz von Compiler-Flags, die unbedingt an den Rust-Compiler übergeben werden MÜSSEN.

Die Standardeinstellungen für Rust sind im Makro %build_rustflags des Pakets rust-srpm-macros definiert. Es ist Teil des Standard-Buildroots unter Fedora 39 und neuer, wo das Makro %set_build_flags die Umgebungsvariable $RUSTFLAGS automatisch anhand dieses Makros setzt.

Für die Kompatibilität zu älteren Versionen oder EPEL kann diese Umgebungsvariable manuell am Anfang von %build und %check in den Spec-Dateien des Pakets gesetzt werden:

export RUSTFLAGS="%build_rustflags"

Dies ist nicht notwendig für Pakete, die die Makros %cargo_prep, %cargo_build und %cargo_test verwenden, welche cargo so konfigurieren, dass die Standard-%build_rustflags direkt verwendet werden.

Obligatorische BuildRequires

Die RPM-Makros, die grundlegende Funktionen zum Bauen aus Rust-Code bereitstellen, sind in rust-rpm-macros enthalten, das Teil des Standard-Buildroots in Fedora ist, da es eine Abhängigkeit von redhat-rpm-config darstellt. Beim Kompilieren für ELN oder EPEL8 ist dies nicht der Fall; hier müssen Pakete BuildRequires: rust-toolset verwenden.

Pakete, die Rust-Code mit Cargo erstellen – direkt oder indirekt – oder die eines der %cargo_*-Makros aufrufen, MÜSSEN BuildRequires: cargo-rpm-macros hinzufügen. Dieses Paket stellt die Implementierungen aller %cargo_*-Makros bereit. Es ist nicht Teil des Standard-Buildroots, da es zusätzliche Abhängigkeiten (z.B. einen Python-Interpreter) benötigt.

Dynamisch erzeugte BuildRequires für Crate-Abhängigkeiten

Da semantische Versionierung („SemVer“) das einzige unterstützte Versionierungsschema für Rust-Crates ist, werden Abhängigkeiten von Rust-Bibliotheken fast ausschließlich als „diese Version oder eine neuere Version, die dazu API-kompatibel ist“ angegeben, d.h. als ein Bereich unterstützter Versionen.

Diese unterstützten Versionsbereiche müssen korrekt in RPM-Abhängigkeiten übersetzt werden, da sonst möglicherweise eine falsche Version einer Abhängigkeit für die Bauvorgänge verwendet wird, was zu nicht hilfreichen Fehlermeldungen über fehlende Abhängigkeiten führt.

Da sich die Abhängigkeiten von Rust-Projekten häufig ändern, MÜSSEN Pakete für Projekte, die Rust-Code mit Cargo erstellen, dynamisch generierte BuildRequires verwenden, indem sie das Makro %cargo_generate_buildrequires im Scriptlet %generate_buildrequires aufrufen.

Eine Abhängigkeit von serde = "1.0.100", die in den Cargo.toml-Metadaten eines Projekts angegeben ist (eine Abhängigkeit von der Crate mit dem Namen "serde" in der Version "1.0.100" oder einer beliebigen API-kompatiblen Version mit "1.0.100" und aktivierten Standardfunktionen), würde beispielsweise dazu führen, dass für RPM eine Abhängigkeit wie diese generiert wird:

BuildRequires:  (crate(serde/default) >= 1.0.100 with crate(serde/default) < 2.0.0~)

Informationen zum Übergeben von Feature-Flags an dieses Makro finden Sie im Abschnitt RPM-Makros.

License-Tags

Ähnlich wie bei anderen Sprachen, die statisch verknüpfte Binärdateien erzeugen, enthalten Rust-Executables (und Shared Libraries) Code, der aus anderen Paketen (d.h. Paketen für andere Rust-Crates) stammt, die wiederum anderen Lizenzbedingungen unterliegen.

Dies muss berücksichtigt werden, indem für das Teilpaket, das diese Binärdateien enthält, ein separates License-Tag gepflegt wird. Weitere Informationen zu License-Tags finden Sie in der Fedora Legal-Dokumentation.

Das Paket cargo-rpm-macros stellt zwei RPM-Makros bereit, die dabei helfen, das License-Tag für Projekte, die mit cargo erstellt wurden, korrekt auszufüllen:

  • %cargo_license_summary

Dieses Makro ermittelt alle Lizenzen der Rust-Crates und gibt eine Zusammenfassung aus, die statisch in die finalen Binärdateien eingebunden werden (wobei Bau- oder Testabhängigkeiten korrekt ausgeschlossen werden). Diese Zusammenfassung kann anschließend als Kommentar aus dem Bauprotokoll in die Spec-Datei kopiert werden. Der tatsächliche Inhalt des License-Tags lässt sich dann durch Verknüpfung dieser einzelnen Lizenzen (mithilfe des SPDX-AND-Operators) ermitteln.

  • %cargo_license

Dieses Makro ermittelt und gibt eine vollständige Aufschlüsselung aller Rust-Crates aus, die statisch in die finalen Binärdateien eingebunden werden (nach der gleichen Logik wie im Makro %cargo_license_summary), sowie deren Versionen und Lizenzausdrücke. Die dynamische Generierung dieser Liste zur Bauzeit stellt sicher, dass ihr Inhalt stets den tatsächlichen Abhängigkeiten entspricht.

Beide Makros akzeptieren die gleichen Argumente wie andere %cargo_*-Makros (-a, -n, -f), und damit ihre Ausgabe mit den tatsächlichen Binärdateien übereinstimmt, MÜSSEN die gleichen Flags an sie und %cargo_build übergeben werden.

Vendored-Abhängigkeiten

Im Allgemeinen SOLLTEN Pakete nach Möglichkeit KEINE gebündelten Crate-Abhängigkeiten verwenden.

Wenn Vendored- oder gebündelte Abhängigkeiten von Crates verwendet werden (unabhängig vom verwendeten Mechanismus), müssen alle gebündelten Crate-Abhängigkeiten mit virtuellen Provides im Format Provides: bundled(crate($crate)) = $version im Teilpaket, das die Rust-Komponente enthält, deklariert werden. Diese virtuellen Provides werden beispielsweise verwendet, um die Auswirkungen von Sicherheitslücken auf Pakete zu ermitteln, die Vendored-Rust-Abhängigkeiten verwenden.

Es gibt außerdem zwei Situationen, in denen das Bündeln von zumindest einigen Rust-Crates in der Regel unvermeidbar ist:

Ersetzen der Git-Abhängigkeiten

Eine der Arten von Abhängigkeiten, die Cargo unterstützt, sind Git-Snapshots, die üblicherweise verwendet werden, um entweder auf einen bestimmten Commit oder auf einen Downstream-Fork einer Crate zu verweisen.

Das Projekt SOLLTE so angepasst werden, dass es eine Version dieser Crate verwendet, die auf crates.io verfügbar ist, sofern dies möglich ist. Falls sich herausstellt, dass die Abhängigkeit von einem Git-Snapshot nicht mehr notwendig ist, SOLLTE dieser Patch an das Upstream-Projekt übermittelt werden.

Falls die Abhängigkeit nicht auf crates.io veröffentlicht ist oder die dort veröffentlichten Versionen keinen geeigneten Ersatz darstellen, kann ein Git-Snapshot des Crates eingebunden werden. Dies geschieht durch Erstellen und Bereitstellen eines Tarballs mit dem Git-Snapshot als separate Quelle, Entpacken des Tarballs mit %prep und Patchen der Cargo.toml, um die Git-basierte Abhängigkeit durch eine pfadbasierte Abhängigkeit zu ersetzen. Durch Hinzufügen dieses Unterprojekts als Workspace-Element wird sichergestellt, dass seine Abhängigkeiten bei der Auflösung berücksichtigt werden.

Ersetzen gepatchter Crate-Quellen

Eine weitere Möglichkeit, wie Cargo die Angabe geänderter Abhängigkeiten unterstützt, besteht darin, eine Crate-Quelle zu „patchen“, indem eine alternative Quelle für bestimmte Crates angegeben wird - dies sind wahrscheinlich entweder Git-Referenzen oder pfadbasierte Abhängigkeiten, die vorhanden sind, um eine auf crates.io veröffentlichte Crate mit einer (modifizierten) lokalen Kopie oder einem Git-Repository, das auf einen (modifizierten) Fork der Crate verweist, zu überschreiben.

Diese Ersetzungen SOLLTEN zugunsten der ausschließlichen Verwendung veröffentlichter Versionen von Crates aufgegeben werden. Falls dies nicht möglich ist, müssen sie durch pfadbasierte Abhängigkeiten ersetzt werden, ähnlich dem für Git-Abhängigkeiten beschriebenen Prozess.

Vendor-Tarballs verwenden

Die Unterstützung für Vendored-Abhängigkeiten wurde in Version 25 von cargo-rpm-macros und rust2rpm hinzugefügt.

  • Das Makro %cargo_prep akzeptiert das Argument -v $VENDOR, wobei $VENDOR der Pfad zum Verzeichnis mit den Abhängigkeiten der Vendored-Crates ist. Wird dieses Argument übergeben, wird die generierte Cargo-Konfiguration so eingerichtet, dass sie die Abhängigkeiten in diesem Verzeichnis verwendet, anstatt die Abhängigkeiten aus der systemweiten Registry.

  • Das Makro %cargo_generate_buildrequires DARF NICHT aufgerufen werden, wenn Vendored-Abhängigkeiten verwendet werden.

  • Das Makro %cargo_vendor_manifest erzeugt ein Manifest (cargo-vendor.txt), das die Namen und Versionen aller Crates im Vendor-Tarball auflistet. Dieses Makro MUSS aufgerufen werden (z.B. im %build-Scriptlet), und die erzeugte Datei MUSS als %license-Datei in die Liste der %files des entsprechenden Pakets aufgenommen werden. Ein RPM-Generator analysiert diese Datei und generiert die entsprechenden virtuellen Provides-Einträge für alle enthaltenen Crates, wie es für alle enthaltenen Abhängigkeiten erforderlich ist.

  • Pakete, die mit Vendored-Abhängigkeiten gebaut werden Es DARF KEINE Rust-Bibliotheksschnittstelle bereitgestellt werden (z.B. in -devel-Teilpaketen), da die resultierenden Pakete zu fehlerhaften Abhängigkeiten führen würden.

Typischerweise sieht das %prep-Scriptlet bei Verwendung von Vendored-Abhängigkeiten folgendermaßen aus (vorausgesetzt, Source1 ist der Vendor-Tarball und enthält ein vendor-Verzeichnis in der obersten Ebene):

%prep
%autosetup -%{crate}-%{version} -p1 -a1
%cargo_prep -v vendor

Die notwendigen Anpassungen der Spec-Datei und die Generierung des Vendor-Tarballs erfolgen automatisch, wenn rust2rpm im „Vendor“-Modus ausgeführt wird.

Rust-Crates

Ein Großteil des Prozesses zum Verpacken von Rust-Crates kann (und sollte) mithilfe von rust2rpm automatisiert werden. Es generiert Spec-Dateien, die sowohl den allgemeinen Richtlinien als auch den Rust-Paketbaurichtlinien entsprechen.

Aufgrund bestimmter Eigenschaften von Paketen für Rust-Crates (z.B. Teilpakete, die Crate-Features/optionalen Abhängigkeiten entsprechen) MUSS rust2rpm für jede neue Version einer Crate erneut ausgeführt werden, um sicherzustellen, dass die generierten Feature-Teilpakete mit den Crate-Metadaten synchron bleiben.

rust2rpm

Die empfohlene Methode zum Schreiben von Spec-Dateien für Rust-Crates ist die Verwendung von rust2rpm und die Anwendung aller notwendigen Änderungen auf die generierte Spec-Datei.

Es gibt einige häufige Situationen, in denen automatisch generierte Spec-Dateien manuell angepasst werden müssen:

  • Ungültige Summary / %description: Die Heuristiken zur Generierung von Summary oder %description für das Paket aus den Metadaten des Crates liefern unter Umständen keine gültigen Werte (z.B. ein zu langes Summary-Tag). In diesem Fall muss Summary manuell gekürzt werden. Dies kann auch in der paketspezifischen rust2rpm-Konfigurationsdatei überschrieben werden.

  • Unerwünschte Abhängigkeiten/Teilpakete: Einige Crates bieten optionale Funktionen, die entweder unnötig sind (d.h. nur für Nicht-Linux-Systeme relevant) oder zusätzliche Abhängigkeiten haben, die für Fedora nicht paketiert sind. Diese Funktionen und nicht verfügbaren optionalen Abhängigkeiten MÜSSEN aus den Metadaten der Crate entfernt werden – andernfalls schlägt der Bau des Pakets fehl oder es entstehen Teilpakete mit fehlerhaften Abhängigkeiten.

  • Nur in der Nightly-Version verfügbare oder instabile Funktionen: Einige Crates bieten Funktionen, die nur mit einer Nightly-Version des Rust-Compilers verfügbar sind, oder Funktionen, die instabil sind und eine Aktivierung durch Umgebungsvariablen erfordern. Solche Funktionen SOLLTEN aus den Metadaten der Crates entfernt werden, da sie entweder nicht funktionieren (Fedora liefert nur die stabile Rust-Toolchain aus) oder deren Unterstützung nicht praktikabel ist.

  • Unerwünschte/unnötige Dateien („Bloatback“): Manche Projekte enthalten Dateien, die für die korrekte Funktion der Crate nicht erforderlich sind (z.B. Dateien für CI-Einstellungen, Entwicklungs-/Hilfsskripte usw.). Die Installation solcher Dateien SOLLTE verhindert werden (durch Hinzufügen/Ändern der package.include- oder package.exclude-Einstellungen in den Crate-Metadaten). Es wird empfohlen, solche Änderungen an das Upstream-Projekt zu übermitteln.

  • Inkompatible Compiler-Flags: Einige Crates enthalten benutzerdefinierte Einstellungen für das release-Profil, die zur RPM-Paketierung nicht kompatibel sind. Diese Einstellungen MÜSSEN durch Patchen der Cargo.toml aus dem release-Profil entfernt werden.

Crates, die Rust-Bindings für C-Bibliotheken bereitstellen, erfordern in der Regel einige zusätzliche Änderungen (sofern möglich):

  • Linken gegen Systembibliotheken: Dies erfordert oft, dass einige Abhängigkeiten als nicht-optional definiert werden und/oder die build.rs-Skripte so geändert werden, dass sie bedingungslos gegen Systembibliotheken linken, anstatt eine gebündelte Kopie der Bibliothek zu erstellen und statisch zu linken.

  • Die Neugenerierung von Rust-Bindings (und Tests dafür) zur Bauzeit: Dies erfordert allzu oft, dass die bindgen-Abhängigkeit nicht optional gemacht wird und/oder die build.rs so geändert wird, dass die Neugenerierung von Rust-Bindings zur Bauzeit erfolgt.

Beachten Sie, dass das Patchen von Cargo.toml-Dateien (insbesondere das Ändern der optionalen Abhängigkeiten und Funktionen) unbedingt mit dem Befehl rust2rpm -p erfolgen MUSS, da solche Änderungen die Generierung der Spec-Datei (d.h. die Liste der generierten Teilpakete) beeinflussen. Das würde aber nur dann korrekt berücksichtigt werden, wenn der Patch vor der Generierung der Spec-Datei erstellt wird.

Es wird empfohlen, nicht-leere rust2rpm.toml-Konfigurationsdateien im Paket-Repository zusammen mit der generierten .spec-Datei zu verwalten.

Benennung von Paketen

Pakete für Rust-Crates von crates.io MÜSSEN rust-$crate als Namen des Quellpakets verwenden (wobei $crate der Name des Projekts auf crates.io ist).

Dadurch wird sichergestellt, dass es keine Namenskonflikte zwischen Rust-Crates gibt, die auf crates.io veröffentlicht werden, und Rust-Crates, die für Fedora paketiert wurden.

Beim Generieren eines Pakets für eine Rust-Crate mit ausführbaren Zielen wird gemäß der Konvention von rust2rpm ein Teilpaket mit dem Namen der Crate erstellt (d.h. das Quellpaket rust-$crate enthält ein Teilpaket $crate). Dieses Teilpaket kann bei Bedarf umbenannt werden, beispielsweise wenn der Name nicht den Erwartungen entspricht oder mit einem bereits existierenden Paket in Konflikt gerät.

Paketversionierung

Projekte, die mit Cargo erstellt und/oder auf crates.io veröffentlicht werden, verwenden semantische Versionierung (mit kleinen Cargo-spezifischen Anpassungen). Da SemVer-Strings Zeichen enthalten können, die in RPM-Versionszeichenketten unzulässig sind, MÜSSEN sie in RPM-kompatible Zeichen übersetzt werden.

Beispielsweise werden Vorabversionen in SemVer durch das Suffix -<pre> gekennzeichnet, das Zeichen - ist jedoch in RPM-Versionen unzulässig. Dieses Problem lässt sich beheben, indem man - durch das Zeichen ~ ersetzt, welches in RPM-Versionszeichenketten Vorabversionen kennzeichnet. Diese Übersetzung erfolgt automatisch für das Version-Tag beim Generieren einer Spec-Datei mit rust2rpm. Die „Upstream“-Version wird in einem separaten Makro gespeichert, mit dem auf die „ursprüngliche“ Versionszeichenkette verwiesen werden kann.

Zusätzlich enthalten manche Rust-Crates in ihren Versionsnummern weitere „Build“-Metadaten (ein Suffix <build>`). Dieses Format dient hauptsächlich dazu, Informationen über die Version einer mitgelieferten C-Bibliothek zu übermitteln. Das Suffix `<build> MUSS per Patch aus den Crate-Metadaten entfernt werden, da es die Auflösung von RPM-Abhängigkeiten bzw. -Versionen beeinträchtigen kann. Dies geschieht automatisch bei Verwendung von rust2rpm Version 25 oder neuer.

Paketquellen

Die offizielle Quelle für Rust-Crates ist crates.io. Projekte von crates.io MÜSSEN aus den dort veröffentlichten Quellen paketiert werden (d.h. mithilfe des Makros %{crates_source}).

Falls die auf crates.io veröffentlichten Quellen nicht alle für die Paketerstellung notwendigen Dateien enthalten (z.B. die fehlende .desktop-Datei oder fehlende Handbuchseiten), können die Upstream-Quellen als zusätzliche Quelle verwendet werden. Sie DÜRFEN jedoch NICHT zum Erstellen des Crates selbst verwendet werden. Es wird empfohlen, beim Upstream-Projekt ein Problem zu melden, um die Aufnahme dieser zusätzlichen Dateien in die veröffentlichten Crates zu beantragen.

Alternativ kann, falls das betreffende Projekt keine Rust-Bibliotheksschnittstelle bereitstellt, es als Nicht-Crate-Rust-Projekt unter Verwendung der Upstream-Quellen paketiert werden.

Crate-Lizenz

Die meisten Werkzeuge zur Lizenzbestimmung benötigen exakte Metadaten über Lizenzen in den Crate-Metadaten, einschließlich der %cargo_license*-Makros, sowie Werkzeuge von Drittanbietern wie cargo-license und cargo-deny.

Aus diesem Grund MÜSSEN die Lizenzmetadaten aller für Fedora paketierten Rust-Crates mit dem Lizenz-Tag des Fedora-Pakets selbst übereinstimmen. Alle Crates, die package.license-file in ihren Metadaten verwenden (was für nicht standardmäßige/nicht SPDX-Lizenzen reserviert ist), MÜSSEN so angepasst werden, dass sie package.license in ihren Metadaten verwenden und stattdessen einen korrekten SPDX-Ausdruck angeben. Solche Patches SOLLTEN an die Upstream-Entwickler übermittelt werden.

Teilpakete für Crate-Funktionen

Optionale Funktionsmerkmale und Abhängigkeiten von Rust-Crates werden in RPM-Teilpakete ausgelagert, um die Auflösung von Abhängigkeiten für Funktionsmerkmale und optionale Abhängigkeiten von Crates zu unterstützen. Die Liste der Crate-Funktionsmerkmale (einschließlich aller implizit definierten Funktionsmerkmale für optionale Abhängigkeiten) MUSS mit der Liste der Teilpakete synchronisiert sein. Das heißt, für jedes Funktionsmerkmal $foo der Crate $crate muss es ein Teilpaket mit dem Namen rust-$crate+$foo-devel geben und umgekehrt. Dies ist erforderlich, damit die RPM-Generatoren für Provides und Requires diese optionalen Funktionsmerkmale und Abhängigkeiten korrekt bereitstellen können.

Wenn optionale Funktionsmerkmale, die nicht zum Standardfunktionsumfang gehören, nicht verwendet werden und zusätzliche (möglicherweise nicht verfügbare) Abhängigkeiten nach sich ziehen würden, KANN das Paket Teilpakete für diese spezifischen Funktionsmerkmalnamen weglassen. Es muss jedoch sichergestellt werden, dass die Funktionsmerkmale, die den weggelassenen Teilpaketen entsprechen, nicht über Teilpakete „erreichbar“ sind, die nicht weggelassen wurden, da dies zu Paketen mit nicht erfüllbaren Abhängigkeiten führen würde. Das Deaktivieren optionaler Funktionsmerkmale kann manchmal nicht einfach durch Weglassen von Teilpaketen für bestimmte Funktionsmerkmale korrekt erfolgen. In diesen Fällen müssen die Crate-Metadaten in Cargo.toml entsprechend angepasst werden.

Beachten Sie, dass das „default“-Funktionsmerkmal immer implizit von Cargo definiert wird, selbst wenn die Metadaten der Crate keine [features]-Tabelle oder ein explizit definiertes „default“-Funktionsmerkmal enthalten. Daher ist das Teilpaket für das „default“-Funktionsmerkmal in allen Paketen für Rust-Crates mit einer Bibliotheksschnittstelle vorhanden.

RPM-Generatoren für Provides und Requires

Das Paket cargo-rpm-macros enthält RPM-Generatoren zum automatischen Generieren von Provides und Requires für Rust-Crates, die den Paktebaurichtlinien entsprechen (d.h. ihre Dateien am richtigen Ort, %{crate_instdir}, installieren).

Es wird empfohlen, zu überprüfen, ob die generierten Provides und Requires sinnvoll sind – beispielsweise müssen die folgenden Provides und Requires vorhanden sein, um korrekte Abhängigkeiten zwischen den Teilpaketen sicherzustellen:

  • das Haupt-Teilpaket rust-$crate-devel MUSS crate($crate) = %{version} bereitstellen

  • die rust-$crate+$feature-devel-Teilpakete MÜSSEN crate($crate/$feature) = %{version} bereitstellen und crate($crate) = %{version} (d.h. rust-$crate-devel) einbinden

Darüber hinaus werden Abhängigkeiten von externen Rust-Crates wie folgt erwartet:

  • das Haupt-Teilpaket rust-$crate-devel MUSS das virtuelle Provides für alle nicht-optionalen Crate-Abhängigkeiten einbinden

  • die Teilpakete rust-$crate+$feature-devel MÜSSEN das virtuelle Provides für die optionalen Crate-Abhängigkeiten und Funktionsmerkmale einbinden, die in den Crate-Metadaten als Funktionsmerkmal-Abhängigkeiten aufgeführt sind

Paketieren mehrerer Versionen

In den meisten Fällen SOLLTE die neueste Version einer Crate paketiert werden, und - wenn möglich - SOLLTEN Paketierer Crates so portieren, dass sie die neueste verfügbare Version ihrer Abhängigkeiten verwenden, und diese Patches an das Upstream-Projekt übermitteln, um Abweichungen zwischen dem Upstream-Projekt und dem Fedora-Paket zu begrenzen.

Es gibt jedoch zwei häufige Szenarien, in denen es oft notwendig ist, Pakete für mehrere Versionen einer Bibliotheks-Crate gleichzeitig bereitzustellen:

  • Aufgrund der großen API-Änderungen zwischen der benötigten und der paketierten Version ist es nicht möglich, eine Crate auf die Version einer Crate-Abhängigkeit in Fedora zu portieren.

  • Die Anzahl der Pakete, die von einer erforderlichen, SemVer-inkompatiblen Bibliotheksaktualisierung betroffen sind, ist sehr groß.

In diesen Fällen kann ein „Kompatibilitätspaket“ für die ältere Version (in der Regel die aktuelle Version) erstellt und das Paket ohne Suffix auf die neuere Version aktualisiert werden. rust2rpm unterstützt die automatische Erstellung von „Kompatibilitätspaketen“ mit Namen, die den Benennungsrichtlinien für diesen Fall entsprechen und zu den Einschränkungen der semantischen Versionierung kompatibel sind. Dies wird durch die Verwendung des Flags rust2rpm --compat ermöglicht.

Alle „Kompatibilitätspakete“ für Rust-Crates MÜSSEN den Richtlinien für Rust-Crates entsprechen, und bei ihrer Erstellung gelten zwei zusätzliche Regeln:

  • Bei Paketen, die auch eine ausführbare Datei enthalten, darf diese ausführbare Datei nur im Paket für die neueste Version enthalten sein und DARF NICHT in älteren Versionen erstellt und eingebunden werden. Das soll verhindern, dass sowohl der Name der ausführbaren Datei unter /usr/bin als auch der Name des Teilpakets zwischen der alten und der neuen Version des Pakets in Konflikt geraten.

  • Der Paketierer SOLLTE prüfen, ob das Ausführen von Tests in der alten Version der Crate zusätzliche, möglicherweise unerwünschte Abhängigkeiten verursachen würde, zum Beispiel ältere Versionen anderer Abhängigkeiten, die die Erstellung zusätzlicher „Kompatibilitätspakete“ erfordern würden - in diesem Fall SOLLTEN die Tests deaktiviert werden (d.h. durch Umschalten der check-Bedingung).

Die check-Bedingung

Das Verhalten einiger RPM-Makros hängt vom Vorhandensein und Wert des Makros _with_check ab, d.h. davon, ob in der Spec-Datei %bcond_without check oder %bcond_with check verwendet wird – insbesondere schließt das Makro %cargo_generate_buildrequires nur dev-dependencies (d.h. Abhängigkeiten, die nur zum Kompilieren und/oder Ausführen der Testsuite eines Projekts mit Cargo verwendet werden) ein, wenn die check-Bedingung aktiviert ist.

Darüber hinaus verwenden Pakete für Rust-Crates, die von rust2rpm generiert werden, den Wert dieses Makros, um zu bestimmen, ob das %check-Skript ausgeführt wird.

Pakete für Rust-Crates MÜSSEN diese Bedingung setzen, um unerwartetes Verhalten der %cargo_*-Makros zu vermeiden, indem sie Tests explizit entweder aktivieren oder deaktivieren.

Tests ausführen

Rust-Crates können drei verschiedene Arten von Tests in ihren Test-Suites enthalten:

  • „Unit-Tests“: Diese Tests werden zusammen mit dem Quellcode der Bibliothek/Anwendung im Verzeichnis src/ bereitgestellt und können auf private APIs verweisen (ähnlich wie bei „Glass-Box“-Tests).

  • „Integrationstests“: Diese Tests sind in der Regel separate Dateien im Verzeichnis tests/ und können sich nur auf die öffentliche API der getesteten Bibliothek stützen (ähnlich wie bei „Black-Box“-Tests).

  • „Doc-Tests“: Diese Tests werden automatisch aus Codeblöcken in der Markdown-Dokumentation extrahiert. Dies wird häufig als Mechanismus verwendet, um sicherzustellen, dass Code-Snippets in der Dokumentation für öffentliche Methoden korrekt sind und weiterhin kompiliert werden können.

Standardmäßig werden beim Ausführen von cargo test (d.h. durch Aufruf des Makros %cargo_test) alle drei Testarten ausgeführt. Sie können auch separat aufgerufen werden (beispielsweise weil Teile der Testsuite oder große Datendateien nicht in den veröffentlichten Quellen enthalten sind), indem Filterargumente an den zugrunde liegenden Befehl cargo test übergeben werden:

  • %cargo_test — --lib: nur „Unit-Tests“ für die Bibliotheksschnittstelle ausführen

  • %cargo_test — --bin foo: nur „Unit-Tests“ für die Binärdatei „foo“ ausführen

  • %cargo_test — --doc: nur „Doc-Tests“ ausführen

  • %cargo_test — --test bar: nur „Integrationstests“ für „bar“ ausführen

Dies kann mit zusätzlichen Flags kombiniert werden, um Tests mit bestimmten Namen (oder solche, die eine bestimmte Zeichenkette in ihrem Namen enthalten) zu überspringen, indem das Argument --skip an das Test-Framework übergeben wird (kann mehrfach angegeben werden):

%cargo_test -- --lib -- --skip foo::bar::tests::test1

Standardmäßig verwendet Cargo die Teilzeichenkettensuche, um --skip-Argumente und tatsächliche Namen von Tests abzugleichen. Diese Funktion kann mit dem Flag --exact deaktiviert werden.

Falls Tests übersprungen oder deaktiviert werden, SOLLTE das Paket Kommentare enthalten, die erklären, warum dies der Fall ist, und gegebenenfalls Links zu den entsprechenden Upstream-Fehlermeldungen enthalten.

Nicht-Crate-Rust-Projekte

Dieser Abschnitt gilt für Rust-Projekte, die nicht aus den Quellen auf crates.io paketiert werden.

Die am häufigsten vorkommenden Fälle sind

  • Anwendungen, die keine Bibliotheksschnittstelle bereitstellen,

  • Projekte, die aus mehreren Crates (Cargo-Workspaces) bestehen, die separat veröffentlicht (oder nützlich) sein können oder auch nicht, und

  • Python-Pakete, die in Rust implementiert sind (nachfolgend beschrieben).

Pakete wie dieses DÜRFEN KEINE Crate-Quellen in %{cargo_registry} ausliefern, d.h. sie dürfen keine -devel-Teilpakete ausliefern, die Crate-Quellen enthalten oder Teilpakete haben, die virtuelle „Provides“ für crate(...) = %{version} haben.

Benennung von Paketen

Pakete für Nicht-Crate-Rust-Projekte MÜSSEN gemäß den allgemeinen Benennungsrichtlinien benannt werden, d.h. sie DÜRFEN KEIN rust- Präfix für den Quellpaketnamen verwenden.

Paketquellen

Es gelten die allgemeinen Richtlinien für die Referenzierung von Quellen.

Das Makro %{crates_source} SOLLTE NICHT für Pakete wie dieses verwendet werden, selbst wenn es sich um eine Rust-Crate handelt, die auch auf crates.io veröffentlicht ist.

RPM-Makros

Alle -a- und -n-Flags oder -f-Argumente, die an %cargo_generate_buildrequires übergeben werden, werden während der Abhängigkeitsauflösung auf alle Workspace-Elemente angewendet.

Das Makro %cargo_install SOLLTE NICHT für Pakete wie dieses verwendet werden. Verwenden Sie stattdessen install oder cp, um die erstellten ausführbaren Dateien oder gemeinsam genutzten Bibliotheken bei Bedarf explizit aus target/rpm/* in das Buildroot-Verzeichnis zu kopieren.

Python-Projekte

Python-Pakete, die setuptools_rust oder maturin verwenden, um eine „native“ Python-Erweiterung zu erstellen, müssen zusätzlich zur Einhaltung der Python-Paketbaurichtlinien auch die allgemeinen Regeln für Rust-Pakete anwenden.

Sowohl setuptools_rust als auch maturin erstellen die native Python-Erweiterung durch internen Aufruf von cargo. Daher ist die grundlegende Einrichtung für Projekte, die mit cargo erstellt werden, auch für Pakete wie dieses erforderlich.

Dies beinhaltet den Aufruf von %cargo_prep in %prep, um die Bauumgebung für Cargo einzurichten, und die Verwendung von %cargo_generate_buildrequires, um dynamisch die entsprechenden BuildRequires für Rust-Crate-Abhängigkeiten zu generieren.

Zusätzlich MÜSSEN %cargo_license und/oder %cargo_license_summary verwendet werden, um die Lizenzen zu ermitteln, die für die statisch verlinkte Python-Erweiterung gelten.

Der Paketierer MUSS außerdem sicherstellen, dass die Standard-Compiler-Flags an rustc übergeben werden und dass die Debug-Informationen während des Bauprozesses nicht aufgrund von Einstellungen in der setuptools-rust- oder maturin-Konfiguration entfernt werden.

Gemischte Rust-/C-/C++-Projekte

Die Handhabung von Projekten, die sowohl C/C++- als auch Rust-Code enthalten, hängt davon ab, wie das Kompilieren des Rust-Codes in das Bausystem des Projekts integriert ist.

Unabhängig von der konkreten Konfiguration MÜSSEN die korrekten Compiler-Flags an rustc übergeben werden, und das License-Tag des Pakets, das die Rust-Komponente enthält, MUSS die Lizenzen statisch verlinkter Crates berücksichtigen.

Bauen mit Cargo intern

Projekte mit Bausystemen, die intern Cargo aufrufen, um Rust-Komponenten zu erstellen, MÜSSEN die gleichen Richtlinien befolgen wie andere Projekte, die Rust-Code mit Cargo erstellen.

In Paketen MUSS sichergestellt sein, dass die Cargo-Aufrufe, die intern zum Bausystem des Projekts gehören, keine Flags oder Argumente übergeben, die zu den Standard-Compiler-Flags oder den Cargo-Optionen, die im Makro %cargo_build festgelegt oder mit %cargo_prep konfiguriert werden, inkompatibel sind.

Erstellung direkt mit Meson

Neuere Versionen von meson bieten nur eingeschränkte Unterstützung für das Erstellen von Crate-Abhängigkeiten ohne Cargo. Um die paketierten Crate-Abhängigkeiten zu verwenden, müssen die Crate-Quellen manuell mit der lokalen Registry überschrieben werden.

Bauen gemeinsam genutzter Bibliotheken mit cargo-c

Obwohl es derzeit nicht möglich ist, Rust-Crates als gemeinsam genutzte Bibliotheken zu erstellen, können Rust-Projekte eine C-kompatible öffentliche API definieren, so dass sie als gemeinsam genutzte Standardbibliotheken mit einer C-ABI erstellt werden können.

In den meisten Fällen werden solche Bibliotheken mit cargo-c erstellt, das praktische Wrapper (cargo-cbuild und cargo-cinstall) sowohl zum Erstellen als auch zum Installieren von gemeinsam genutzten Bibliotheken bereitstellt (einschließlich Unterstützung für das Generieren und Installieren von Header-Dateien und pkg-config-Dateien).

Das Paket cargo-c enthält RPM-Makros für diese Funktionalität (%cargo_cbuild und %cargo_cinstall), die die gleichen Argumente wie ihre Cargo-Pendants akzeptieren.

RPM-Makros

Der Prozess des Erstellens und Installierens von Rust-Projekten ist mit mehreren RPM-Makros nahezu vollständig automatisiert:

  • %cargo_prep: Dieses Makro MUSS im %prep-Scriptlet aufgerufen werden, nachdem die Quellen entpackt wurden. Es konfiguriert die Bauumgebung für Cargo und fügt eine Cargo-Konfigurationsdatei ein, die die Standard-Compiler-Flags festlegt und die lokale Crate-Registry als Ersatz für crates.io konfiguriert.

  • %cargo_generate_buildrequires: Dieses Makro MUSS im Scriptlet %generate_buildrequires aufgerufen werden, außer beim Kompilieren mit Vendored-Abhängigkeiten. Basierend auf den Metadaten in Cargo.toml generiert es automatisch Abhängigkeiten zu anderen Rust-Crates.

  • %cargo_build: Dieses Makro DARF NUR im %build-Scriptlet aufgerufen werden, es sei denn, der Bauprozess wird auf andere Weise abgewickelt, d.h. „cargo build“ wird intern von Bauskripten aufgerufen. Es führt cargo build mit den entsprechenden Befehlszeilenargumenten aus. Der Aufruf dieses Makros KANN übersprungen werden, wenn die Crate von der aktuellen CPU-Architektur nicht unterstützt wird.

  • %cargo_install: Dieses Makro DARF NUR im %install-Scriptlet für Crates aufgerufen werden, die eine Bibliotheksschnittstelle bereitstellen. Es führt cargo package aus und installiert den resultierenden Verzeichnisbaum in %{buildroot}/%{crate_instdir} (d. h. %{buildroot}/%{cargo_registry}/%{crate}-%{version}/). Für Crates mit bin-Zielen installiert es alle erstellten ausführbaren Dateien in %{buildroot}/%{_bindir}. Falls erstellte ausführbare Dateien an einem anderen Ort installiert werden müssen, können sie nach dem Aufruf von %cargo_install verschoben werden. Alternativ kann %cargo_install durch manuelle Installationsschritte (Kopieren aus target/rpm/*) ersetzt werden. Um die Installation von ausführbaren Dateien durch dieses Makro zu verhindern, kann das Makro %cargo_install_bin auf 0 gesetzt werden. Um die Installation von Bibliotheksquellen durch dieses Makro zu verhindern, kann das Makro %cargo_install_lib auf 0 gesetzt werden.

  • %cargo_test: Dieses Makro DARF NUR im Scriptlet %check aufgerufen werden. Es führt cargo test mit den entsprechenden Befehlszeilenargumenten aus. Der Aufruf dieses Makros KANN übersprungen werden, wenn die Bibliothek auf der aktuellen CPU-Architektur nicht unterstützt wird oder Tests generell deaktiviert sind.

  • %cargo_license / %cargo_license_summary: Diese Makros MÜSSEN im %build-Scriptlet nach %cargo_build aufgerufen werden, wenn Crates mit Binärzielen erstellt werden. Sie geben die Liste der Lizenzen der Crates aus, die statisch in eine erstellte ausführbare Datei oder gemeinsam genutzte Bibliothek gelinkt sind (siehe License-Tags).

  • %cargo_vendor_manifest: Dieses Makro MUSS im %build-Scriptlet nach %cargo_build aufgerufen werden, wenn Rust-Projekte mit Vendored-Abhängigkeiten erstellt werden. Es schreibt eine maschinenlesbare Liste aller Vendored-Abhängigkeiten in die Datei cargo-vendor.txt, die unbedingt als %license-Datei in das Paket eingebunden werden MUSS, das die statisch gelinkten ausführbaren Dateien enthält.

Alle Pakete für Rust-Crates MÜSSEN entweder %bcond check 1 oder %bcond check 0 setzen. Der Wert dieses Makros beeinflusst, ob %cargo_generate_buildrequires Abhängigkeiten einbindet, die zum Erstellen und Ausführen von Tests erforderlich sind.

Nicht-Crate-Pakete können entweder %bcond check verwenden oder das Flag -t an das Makro %cargo_generate_buildrequires übergeben, um Testabhängigkeiten in die BuildRequires-Generierung einzubeziehen.

Alle %cargo_*-Makros (außer %cargo_prep und %cargo_vendor_manifest) akzeptieren eine Reihe optionaler Flags/Argumente, mit denen die an Cargo übergebenen Funktionsmerkmal-Flags gesteuert werden können (in der Regel, um optionale/nicht standardmäßige Funktionen zu aktivieren):

  • -a: Sorgt dafür, dass das Flag --all-features an Cargo übergeben wird und das Makro %cargo_generate_buildrequires Abhängigkeiten mit allen aktivierten optionalen Funktionsmerkmalen auflöst.

  • -n: Sorgt dafür, dass die --no-default-features-Flags an Cargo übergeben werden und das Makro %cargo_generate_buildrequires Abhängigkeiten auflöst, wobei alle Standard- und optionalen Funktionsmerkmale deaktiviert sind.

  • -f foo,bar: Sorgt dafür, dass das Argument --features foo,bar an Cargo übergeben wird und das Makro %cargo_generate_buildrequires Abhängigkeiten mit den aktivierten zusätzlichen Funktionsmerkmalen foo und bar auflöst. Dieses Argument akzeptiert eine durch Kommas getrennte Liste von Funktionsmerkmal-Namen (oder Namen optionaler Abhängigkeiten).

Die Optionen -a und -n schließen sich gegenseitig aus und können daher nicht gleichzeitig verwendet werden. Auch die Option -a und das Argument -f sind inkompatibel, da durch die Übergabe von -a bereits alle Funktionsmerkmale aktiviert werden. Die Verwendung der Option -n in Kombination mit der gezielten Aktivierung bestimmter Funktionsmerkmale über das Argument -f ist jedoch zulässig.

Es gibt einige häufige Situationen, in denen das Übergeben dieser Flags oder Argumente notwendig ist:

  • Um die Testsuite einer Crate zu erstellen und auszuführen, müssen unter Umständen zusätzliche Funktionsmerkmale und/oder optionale Abhängigkeiten aktiviert werden. In diesem Fall MÜSSEN die benötigten Funktionsmerkmale durch Übergabe der entsprechenden Flags an alle %cargo_*-Makros aktiviert werden, es sei denn, die erforderlichen optionalen Abhängigkeiten sind noch nicht paketiert.

  • Manche Anwendungen unterstützen zusätzliche/nicht standardmäßige Funktionsmerkmale durch die Übergabe von Funktionsmerkmal-Flags. Wenn Anwendungen mit diesen aktivierten Funktionsmerkmalen erstellt werden sollen, müssen die erforderlichen Funktionsmerkmale durch Übergabe der entsprechenden Flags an alle %cargo_*-Makros (einschließlich %cargo_license und %cargo_license_summary) aktiviert werden.

Beachten Sie, dass das Flag -n nur in Ausnahmefällen verwendet werden sollte, beispielsweise wenn ein anderes Backend als das standardmäßig aktivierte aktiviert wird. Es DARF NICHT verwendet werden, um fehlende Abhängigkeiten zu vermeiden, die Teil des "default"-Funktionsumfangs einer Crate sind.

Wenn Sie die Flags -a oder -n oder das Argument -f an ein %cargo_build- und/oder %cargo_install-Makro übergeben, MÜSSEN dieselben Flags auch an %cargo_license und %cargo_license_summary (falls vorhanden) übergeben werden. Anderenfalls passen die Liste der generierten Lizenzen und die generierte Lizenzübersicht, die beim Kompilieren der Anwendung oder Bibliothek verwendet werden, nicht zusammen.

Vorlagen

Nicht-Crate-Rust-Projekt

Name:           my-awesome-project
Version:        25.11.26
Release:        %autorelease
Summary:        My Awesome Rust Project

SourceLicense:  WTFPL
# FIXME: Ausgabe von %%cargo_license_summary hier einfügen
License:        %{shrink:
    WTFPL AND
    ...
}
# LICENSE.dependencies enthält eine vollständige Aufschlüsselung der Lizenzen

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-Projekt

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}/rustypackage-%{version}.tar.gz

BuildRequires:  python3-devel
BuildRequires:  cargo-rpm-macros

%global _description %{expand:
My Rusty Python Package.}

%description %_description

%package     -n python3-rustypackage
Summary:        %{summary}

# FIXME: paste output of %%cargo_license_summary here
License:        %{shrink:
    WTFPL AND
    ...
}
# LICENSE.dependencies contains a full license breakdown

%description -n python3-rustypackage %_description

%prep
%autosetup -n rustypackage-%{version} -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 -l rustypackage

%check
%pyproject_check_import
# %%pytest
# %%cargo_test

%files -n python3-rustypackage -f %{pyproject_files}
%doc README.md

%changelog
%autochangelog

Überprüfen Sie, ob die Projektlizenzdatei(en) und die Datei LICENSE.dependencies wie erwartet in den erstellten Paketen enthalten sind (rpm -qL -p <Pfad zum RPM-Paket>), wenn Sie %pyproject_save_files -l verwenden.