Das Build-System unterstützt das Generieren von Bindgen-Bindungen über den Modultyp rust_bindgen. Bindgen stellt Rust-FFI-Bindungen für C-Bibliotheken bereit (mit eingeschränkter C++-Unterstützung, für die die cppstd-Eigenschaft festgelegt werden muss).

Grundlegende Verwendung von rust_bindgen

Im Folgenden finden Sie ein Beispiel dafür, wie Sie ein Modul definieren, das „bindgen“ verwendet, und wie Sie dieses Modul als Crate verwenden. Wenn Sie bindgen-Bindungen über ein include!()-Makro verwenden müssen, z. B. für externen Code, lesen Sie die Seite Source Generators (Quellgeneratoren).

Beispiel für eine C-Bibliothek, die von Rust aufgerufen werden kann

Im Folgenden finden Sie ein Beispiel für eine C-Bibliothek, in der eine Struktur und eine Funktion für die Verwendung in Rust definiert werden.

external/rust/libbuzz/libbuzz.h

typedef struct foo {
    int x;
} foo;

void fizz(int i, foo* cs);

external/rust/libbuzz/libbuzz.c

#include <stdio.h>
#include "libbuzz.h"

void fizz(int i, foo* my_foo){
    printf("hello from c! i = %i, my_foo->x = %i\n", i, my_foo->x);
}

rust_bindgen-Modul definieren

Definieren Sie einen Wrapper-Header, external/rust/libbuzz/libbuzz_wrapper.h, der alle relevanten Header enthält:

// Include headers that are required for generating bindings in a wrapper header.
#include "libbuzz.h"

Definieren Sie die Datei Android.bp als external/rust/libbuzz/Android.bp:

cc_library {
    name: "libbuzz",
    srcs: ["libbuzz.c"],
}

rust_bindgen {
     name: "libbuzz_bindgen",

     // Crate name that's used to generate the rust_library variants.
     crate_name: "buzz_bindgen",

     // Path to the wrapper source file.
     wrapper_src: "libbuzz_wrapper.h",

     // 'source_stem' controls the output filename.
     // This is the filename that's used in an include! macro.
     //
     // In this case, we just use "bindings", which produces
     // "bindings.rs".
     source_stem: "bindings",

     // Bindgen-specific flags and options to customize the bindings.
     // See the bindgen manual for more information.
     bindgen_flags: ["--verbose"],

     // Clang flags to be used when generating the bindings.
     cflags: ["-DSOME_FLAG"],

     // Shared, static, and header libraries which export the necessary
     // include directories must be specified.
     //
     // These libraries will also be included in the crate if static,
     // or propagated to dependents if shared.
     // static_libs: ["libbuzz"]
     // header_libs: ["libbuzz"]
     shared_libs: ["libbuzz"],
}

Weitere Informationen zur Verwendung von bindgen-Flags finden Sie im bindgen-Handbuch im Abschnitt Customizing Generated Bindings (Generierte Bindungen anpassen).

Wenn Sie diesen Abschnitt verwendet haben, um ein rust_bindgen-Modul als Voraussetzung für die Verwendung des include!()-Makros zu definieren, kehren Sie auf der Seite „Quellgeneratoren“ zu Voraussetzung zurück. Falls nicht, fahren Sie mit den nächsten Abschnitten fort.

Bindungen als Crate verwenden

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

rust_binary {
   name: "hello_bindgen",
   srcs: ["main.rs"],

   // Add the rust_bindgen module as if it were a rust_library dependency.
   rustlibs: ["libbuzz_bindgen"],
}

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

//! Example crate for testing bindgen bindings

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

Rufen Sie abschließend m hello_bindgen auf, um das Binärprogramm zu erstellen.

Bindgen-Bindungen testen

Bindgen-Bindungen enthalten in der Regel eine Reihe von generierten Layouttests, um Konflikte beim Speicherlayout zu vermeiden. AOSP empfiehlt, dass Sie für diese Tests ein Testmodul definieren und dass die Tests als Teil der normalen Testsuite Ihres Projekts ausgeführt werden.

Eine Test-Binärdatei für diese kann einfach erstellt werden, indem ein rust_test-Modul in external/rust/hello_bindgen/Android.bp definiert wird:

rust_test {
    name: "bindings_test",
    srcs: [
        ":libbuzz_bindgen",
    ],
    crate_name: "buzz_bindings_test",
    test_suites: ["general-tests"],
    auto_gen_config: true,

    // Be sure to disable lints as the generated source
    // is not guaranteed to be lint-free.
    clippy_lints: "none",
    lints: "none",
}

Sichtbarkeit und Verknüpfung

Generierte Bindungen sind in der Regel sehr klein, da sie aus Typdefinitionen, Funktionssignaturen und zugehörigen Konstanten bestehen. Daher ist es in der Regel ineffizient, diese Bibliotheken dynamisch zu verknüpfen. Wir haben die dynamische Verknüpfung für diese Module deaktiviert, sodass bei der Verwendung über rustlibs automatisch eine statische Variante ausgewählt wird.

Standardmäßig haben rust_bindgen-Module die visibility-Eigenschaft [":__subpackages__"]. Dadurch können nur Module in derselben Android.bp-Datei oder darunter in der Verzeichnishierarchie darauf zugreifen. Das hat zwei Vorteile:

  • Dadurch wird die Verwendung von Raw-C-Bindungen an anderer Stelle im Baum verhindert.
  • So werden Probleme mit der Diamantverknüpfung bei einer Mischung aus statischer und dynamischer Verknüpfung vermieden.

Normalerweise sollten Sie eine sichere Wrapper-Bibliothek für das generierte Modul bereitstellen, das Sie im selben Verzeichnisbaum wie die Bindungen hinzugefügt haben und das für andere Entwickler vorgesehen ist. Wenn das für Ihren Anwendungsfall nicht funktioniert, können Sie der Sichtbarkeit zusätzliche Pakete hinzufügen. Achten Sie beim Hinzufügen zusätzlicher Sichtbarkeitsbereiche darauf, dass Sie nicht zwei Bereiche hinzufügen, die in Zukunft möglicherweise mit demselben Prozess verknüpft werden, da die Verknüpfung sonst fehlschlagen kann.

Wichtige rust_bindgen-Attribute

Die unten definierten Properties gelten zusätzlich zu den wichtigen gemeinsamen Properties, die für alle Module gelten. Diese sind entweder besonders wichtig für Rust-Bindgen-Module oder weisen ein einzigartiges Verhalten auf, das für den Modultyp rust_bindgen spezifisch ist.

stem, name, crate_name

rust_bindgen generiert Bibliotheksvarianten. Daher gelten für die Eigenschaften stem, name und crate_name dieselben Anforderungen wie für die rust_library-Module. Weitere Informationen finden Sie unter Wichtige Eigenschaften von Rust-Bibliotheken.

wrapper_src

Dies ist der relative Pfad zu einer Wrapper-Headerdatei, die die für diese Bindungen erforderlichen Header enthält. Die Dateiendung bestimmt, wie der Header interpretiert wird, und legt fest, welches -std-Flag standardmäßig verwendet wird. Es wird davon ausgegangen, dass es sich um einen C-Header handelt, es sei denn die Erweiterung ist entweder .hh oder .hpp. Wenn Ihr C++-Header eine andere Erweiterung haben muss, legen Sie die Eigenschaft cpp_std fest, um das Standardverhalten zu überschreiben, bei dem davon ausgegangen wird, dass die Datei eine C-Datei ist.

source_stem

Dies ist der Dateiname für die generierte Quelldatei. Dieses Feld muss definiert werden, auch wenn Sie die Bindungen als Crate verwenden, da die stem-Eigenschaft nur den Ausgabedateinamen für die generierten Bibliotheksvarianten steuert. Wenn ein Modul von mehreren Quellgeneratoren (z. B. bindgen und protobuf) als Quelle und nicht als Crates über rustlibs abhängt, müssen Sie dafür sorgen, dass alle Quellgeneratoren, die Abhängigkeiten dieses Moduls sind, eindeutige source_stem-Werte haben. Abhängige Module kopieren Quellen aus allen SourceProvider-Abhängigkeiten, die in srcs definiert sind, in ein gemeinsames OUT_DIR-Verzeichnis. Kollisionen in source_stem führen dazu, dass die generierten Quelldateien im OUT_DIR-Verzeichnis überschrieben werden.

c_std

Dies ist ein String, der angibt, welche C-Standardversion verwendet werden soll. Gültige Werte sind unten aufgeführt:

  • Eine bestimmte Version, z. B. "gnu11".
  • "experimental", ein Wert, der vom Build-System in build/soong/cc/config/global.go definiert wird, kann Entwurfsversionen wie C++1z verwenden, wenn sie verfügbar sind.
  • Nicht festgelegt oder "", was angibt, dass die Standardeinstellung des Build-Systems verwendet werden soll.

Wenn diese Option festgelegt ist, wird die Dateiendung ignoriert und davon ausgegangen, dass es sich bei dem Header um einen C-Header handelt. Dieser Wert kann nicht gleichzeitig mit cpp_std festgelegt werden.

cpp_std

cpp_std ist ein String, der angibt, welche C-Standardversion verwendet werden soll. Gültige Werte:

  • Eine bestimmte Version, z. B. "gnu++11"
  • "experimental", ein Wert, der vom Build-System in build/soong/cc/config/global.go definiert wird, kann Entwurfsversionen wie C++1z verwenden, wenn sie verfügbar sind.
  • Nicht festgelegt oder "", was angibt, dass die Standardeinstellung des Build-Systems verwendet werden soll.

Wenn diese Option festgelegt ist, wird die Dateiendung ignoriert und davon ausgegangen, dass es sich bei dem Header um einen C++-Header handelt. Dieser Wert kann nicht gleichzeitig mit c_std festgelegt werden.

cflags

cflags enthält eine Stringliste mit Clang-Flags, die zum korrekten Interpretieren der Header erforderlich sind.

custom_bindgen

Für erweiterte Anwendungsfälle kann bindgen als Bibliothek verwendet werden, die eine API bereitstellt, die als Teil einer benutzerdefinierten Rust-Binärdatei bearbeitet werden kann. Das Feld custom_bindgen enthält den Modulnamen eines rust_binary_host-Moduls, das die bindgen API anstelle des normalen bindgen-Binärprogramms verwendet.

Dieses benutzerdefinierte Binärprogramm muss Argumente ähnlich wie bindgen erwarten, z. B.

$ my_bindgen [flags] wrapper_header.h -o [output_path] -- [clang flags]

Der Großteil davon wird von der bindgen-Bibliothek selbst übernommen. Ein Beispiel für diese Verwendung finden Sie unter external/rust/crates/libsqlite3-sys/android/build.rs.

Außerdem ist der vollständige Satz von Bibliothekseigenschaften verfügbar, um die Kompilierung der Bibliothek zu steuern. Diese müssen jedoch nur selten definiert oder geändert werden.

handle_static_inline und static_inline_library

Diese beiden Attribute sind für die gemeinsame Verwendung vorgesehen und ermöglichen die Erstellung von Wrappern für statische Inlinefunktionen, die in die exportierten Bindgen-Bindungen aufgenommen werden können.

Um sie zu verwenden, legen Sie handle_static_inline: true fest und setzen Sie static_inline_library auf ein entsprechendes cc_library_static, das das rust_bindgen-Modul als Quelleneingabe definiert.

Verwendungsbeispiele:

    rust_bindgen {
        name: "libbindgen",
        wrapper_src: "src/any.h",
        crate_name: "bindgen",
        stem: "libbindgen",
        source_stem: "bindings",

        // Produce bindings for static inline fucntions
        handle_static_inline: true,
        static_inline_library: "libbindgen_staticfns"
    }

    cc_library_static {
        name: "libbindgen_staticfns",

        // This is the rust_bindgen module defined above
        srcs: [":libbindgen"],

        // The include path to the header file in the generated c file is
        // relative to build top.
        include_dirs: ["."],
    }