Quellgeneratoren

Auf dieser Seite erhalten Sie einen Überblick darüber, wie generierte Quellcodes unterstützt und im Build-System verwendet werden können.

Alle Quellgeneratoren bieten ähnliche Build-Systemfunktionen. Die drei vom Build-System unterstützten Anwendungsfälle für die Quellgenerierung sind die Generierung von C-Bindungen mit bindgen, AIDL-Schnittstellen und Protobuf-Schnittstellen.

Crates aus generierter Quelle

Jedes Rust-Modul, das Quellcode generiert, kann als Kiste verwendet werden, genau so, als wäre es als rust_library definiert. Das bedeutet, dass es als Abhängigkeit in den Properties rustlibs, rlibs und dylibs definiert werden kann. Das beste Nutzungsmuster für Plattformcode besteht darin, generierte Quellcodedateien als Crate zu verwenden. Das include!-Makro wird zwar für generierte Quellen unterstützt, sein Hauptzweck besteht jedoch darin, Drittanbietercode zu unterstützen, der sich in external/ befindet.

In einigen Fällen wird für Plattformcode möglicherweise weiterhin generierter Quellcode über das include!()-Makro verwendet, z. B. wenn Sie ein genrule-Modul verwenden, um Quellcode auf einzigartige Weise zu generieren.

Verwenden Sie „include!()“, um die generierte Quelle einzuschließen

Die Verwendung der generierten Quelle als Crate wird in den Beispielen auf den jeweiligen Modulseiten behandelt. In diesem Abschnitt wird gezeigt, wie Sie mit dem Makro include!() auf die generierte Quelle verweisen. Dieser Vorgang ist für alle Quellengeneratoren ähnlich.

Voraussetzung

In diesem Beispiel wird davon ausgegangen, dass Sie ein rust_bindgen-Modul (libbuzz_bindgen) definiert haben. Sie können mit den Schritten zum Einfügen der generierten Quelle fortfahren, um das include!()-Makro zu verwenden. Falls noch nicht geschehen, rufen Sie Ein Rust-Bindgen-Modul definieren auf, erstellen Sie libbuzz_bindgen und kehren Sie dann hierher zurück.

Die Teile der Builddatei gelten für alle Quellgeneratoren.

Schritte zum Einbeziehen der generierten Quelle

Erstellen Sie external/rust/hello_bindgen/Android.bp mit folgendem Inhalt:

rust_binary {
   name: "hello_bzip_bindgen_include",
   srcs: [
         // The primary rust source file must come first in this list.
         "src/lib.rs",

         // The module providing the bindgen bindings is
         // included in srcs prepended by ":".
         ":libbuzz_bindgen",
    ],

    // Dependencies need to be redeclared when generated source is used via srcs.
    shared_libs: [
        "libbuzz",
    ],
}

Erstellen Sie external/rust/hello_bindgen/src/bindings.rs mit folgendem Inhalt:

#![allow(clippy::all)]
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(unused)]
#![allow(missing_docs)]

// Note that "bzip_bindings.rs" here must match the source_stem property from
// the rust_bindgen module.
include!(concat!(env!("OUT_DIR"), "/bzip_bindings.rs"));

Erstellen Sie external/rust/hello_bindgen/src/lib.rs mit folgendem Inhalt:

mod bindings;

fn main() {
    let mut x = bindings::foo { x: 2 };
    unsafe { bindings::fizz(1, &mut x as *mut bindings::foo) }
}

Warum Crates für generierte Quellen verwendet werden

Im Gegensatz zu C/C++-Compilern akzeptiert rustc nur eine einzige Quelldatei, die einen Einstiegspunkt in eine Binärdatei oder Bibliothek darstellt. Es wird davon ausgegangen, dass der Quellbaum so strukturiert ist, dass alle erforderlichen Quelldateien automatisch gefunden werden können. Das bedeutet, dass die generierte Quelle entweder im Quellbaum platziert oder über eine Include-Anweisung in der Quelle bereitgestellt werden muss:

include!("/path/to/hello.rs");

Die Rust-Community verwendet build.rs-Scripts und Annahmen zur Cargo-Build-Umgebung, um mit diesem Unterschied zu arbeiten. Beim Erstellen legt der Befehl cargo die Umgebungsvariable OUT_DIR fest, in die build.rs-Scripts den generierten Quellcode ablegen sollen. Verwenden Sie den folgenden Befehl, um Quellcode einzufügen:

include!(concat!(env!("OUT_DIR"), "/hello.rs"));

Dies stellt eine Herausforderung für Soong dar, da die Ausgaben für jedes Modul in einem eigenen out/-Verzeichnis1 abgelegt werden. Es gibt keine einzelne OUT_DIR, in der Abhängigkeiten ihre generierte Quelle ausgeben.

Für Plattformcode wird in AOSP aus mehreren Gründen bevorzugt eine generierte Quelle in einem Crate verpackt, das importiert werden kann:

  • Verhindern, dass generierte Quelldateinamen kollidieren.
  • Reduzieren Sie den Standardcode, der im gesamten Baum eingecheckt ist und gewartet werden muss. Alle Boilerplate-Texte, die erforderlich sind, damit die generierte Quelle in einen Crate kompiliert werden kann, können zentral verwaltet werden.
  • Vermeiden Sie implizite2-Interaktionen zwischen generiertem Code und dem umgebenden Crate.
  • Reduzieren Sie die Belastung von Arbeitsspeicher und Laufwerk, indem Sie häufig verwendete generierte Quellen dynamisch verknüpfen.

Daher generieren alle Modultypen der Rust-Quellcodegenerierung von Android Code, der kompiliert und als Crate verwendet werden kann. Soong unterstützt weiterhin Crates von Drittanbietern ohne Modifikation, wenn alle generierten Quellabhängigkeiten für ein Modul in ein einzelnes Verzeichnis pro Modul kopiert werden, ähnlich wie bei Cargo. In solchen Fällen legt Soong beim Kompilieren des Moduls die Umgebungsvariable OUT_DIR auf dieses Verzeichnis fest, damit die generierte Quelle gefunden werden kann. Aus den bereits genannten Gründen sollten Sie diesen Mechanismus jedoch nur dann im Plattformcode verwenden, wenn es unbedingt erforderlich ist.


  1. Bei C/C++ und ähnlichen Sprachen ist das kein Problem, da der Pfad zur generierten Quelle direkt an den Compiler übergeben wird.

  2. Da include! durch Texteinschluss funktioniert, kann es auf Werte aus dem umschließenden Namespace verweisen, den Namespace ändern oder Konstrukte wie #![foo] verwenden. Diese impliziten Interaktionen können schwierig zu pflegen sein. Bevorzugen Sie Makros, wenn eine Interaktion mit dem Rest der Kiste wirklich erforderlich ist.