Auf dieser Seite erhalten Sie einen allgemeinen Überblick darüber, wie generierter Quellcode unterstützt wird und wie er im Build-System verwendet werden kann.
Alle Quellgeneratoren bieten ähnliche Buildsystemfunktionen. Die drei vom Build-System unterstützten Anwendungsfälle für die Quellcode-Generierung sind das Generieren von C-Bindungen mit bindgen, AIDL-Schnittstellen und Protobuf-Schnittstellen.
Crates aus generiertem Quellcode
Jedes Rust-Modul, das Quellcode generiert, kann als Crate verwendet werden, genau wie wenn es als rust_library
definiert wäre. Das bedeutet, dass sie als Abhängigkeit in den Attributen rustlibs
, rlibs
und dylibs
definiert werden kann. Am besten verwenden Sie generierten Quellcode als Crate. Das Makro include!
wird zwar für generierten Quellcode unterstützt, sein Hauptzweck ist jedoch die Unterstützung von Drittanbietercode, der sich in external/
befindet.
Es gibt Fälle, in denen im Plattformcode möglicherweise weiterhin generierter Quellcode über das Makro include!()
verwendet wird, z. B. wenn Sie ein genrule
-Modul verwenden, um Quellcode auf einzigartige Weise zu generieren.
Mit „include!()“ generierten Quellcode einfügen
Die Verwendung von generiertem Quellcode als Crate wird in den Beispielen auf jeder Modulseite behandelt. In diesem Abschnitt wird gezeigt, wie Sie mit dem Makro include!()
auf generierten Quellcode verweisen. Dieses Verfahren ist für alle Quellgeneratoren ähnlich.
Voraussetzung
In diesem Beispiel wird davon ausgegangen, dass Sie ein rust_bindgen
-Modul (libbuzz_bindgen
) definiert haben und mit den Schritten zum Einbinden der generierten Quelle für die Verwendung desinclude!()
-Makros fortfahren können. Falls noch nicht geschehen, definieren Sie ein „rust bindgen“-Modul, erstellen Sie libbuzz_bindgen
und kehren Sie dann hierher zurück.
Die Build-Datei-Abschnitte gelten für alle Quellgeneratoren.
Schritte zum Einbinden generierter Quellen
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 generierten Quellcode?
Im Gegensatz zu C/C++-Compilern akzeptiert rustc
nur eine einzelne Quelldatei, die einen Einstiegspunkt für eine Binärdatei oder Bibliothek darstellt. Dabei wird davon ausgegangen, dass der Quellbaum so strukturiert ist, dass alle erforderlichen Quelldateien automatisch erkannt werden können. Das bedeutet, dass generierter Quellcode entweder im Quellcodebaum platziert oder über eine Include-Anweisung im Quellcode bereitgestellt werden muss:
include!("/path/to/hello.rs");
Die Rust-Community ist auf build.rs
-Scripts und Annahmen zur Cargo-Build-Umgebung angewiesen, um mit diesem Unterschied umzugehen.
Beim Erstellen wird mit dem Befehl cargo
eine OUT_DIR
-Umgebungsvariable festgelegt, in die build.rs
-Skripts generierten Quellcode einfügen 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 Ausgaben für jedes Modul in einem eigenen out/
-Verzeichnis platziert werden.1 Es gibt kein einzelnes OUT_DIR
, in dem Abhängigkeiten ihren generierten Quellcode ausgeben.
Für Plattformcode wird in AOSP das Verpacken von generiertem Quellcode in einem Crate bevorzugt, das importiert werden kann. Das hat mehrere Gründe:
- Kollisionen von generierten Quelldateinamen verhindern
- Standardcode, der im gesamten Baum eingecheckt wird und Wartung erfordert, wird reduziert. Jeglicher Boilerplate-Code, der erforderlich ist, damit der generierte Quellcode in eine Kiste kompiliert werden kann, kann zentral verwaltet werden.
- Vermeiden Sie implizite2 Interaktionen zwischen generiertem Code und dem umgebenden Crate.
- Reduzieren Sie die Belastung von Arbeitsspeicher und Festplatte, indem Sie häufig verwendete generierte Quellen dynamisch verknüpfen.
Daher erzeugen alle Quellgenerierungsmodultypen von Android Rust Code, der kompiliert und als Crate verwendet werden kann.
Soong unterstützt weiterhin Drittanbieter-Crates ohne Änderungen, 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 die Umgebungsvariable OUT_DIR
beim Kompilieren des Moduls auf dieses Verzeichnis fest, damit die generierte Quelle gefunden werden kann.
Aus den bereits beschriebenen Gründen ist es jedoch am besten, diesen Mechanismus nur im Plattformcode zu verwenden, wenn er unbedingt erforderlich ist.
-
Für C/C++ und ähnliche Sprachen stellt dies kein Problem dar, da der Pfad zum generierten Quellcode direkt an den Compiler übergeben wird. ↩
-
Da
include!
durch textuelle Einbeziehung funktioniert, kann es Werte aus dem einschließenden Namespace referenzieren, den Namespace ändern oder Konstrukte wie#![foo]
verwenden. Diese impliziten Interaktionen können schwer aufrechtzuerhalten sein. Verwenden Sie immer Makros, wenn eine Interaktion mit dem Rest des Crate wirklich erforderlich ist. ↩