System kompilacji obsługuje generowanie powiązań bindgen za pomocą typu modułu rust_bindgen. Bindgen udostępnia powiązania Rust FFI z bibliotekami C (z ograniczoną obsługą C++, która wymaga ustawienia właściwości cppstd).

Podstawowe użycie rust_bindgen

Poniżej znajdziesz przykład definiowania modułu, który korzysta z bindgen, oraz sposób używania tego modułu jako pakietu. Jeśli chcesz używać powiązań bindgen za pomocą makra include!(), np. w przypadku kodu zewnętrznego, zapoznaj się ze stroną Generatory kodu źródłowego.

Przykład biblioteki C, z której można wywoływać funkcje w Rust

Poniżej znajdziesz przykład biblioteki C, która definiuje strukturę i funkcję do użycia w Rust.

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);
}

Zdefiniuj moduł rust_bindgen

Zdefiniuj nagłówek kodu, external/rust/libbuzz/libbuzz_wrapper.h, który zawiera wszystkie odpowiednie nagłówki:

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

Zdefiniuj plik Android.bp jako 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"],
}

Więcej informacji o używaniu flag bindgen znajdziesz w sekcji podręcznika bindgen dotyczącej dostosowywania wygenerowanych powiązań.

Jeśli w tej sekcji zdefiniowano rust_bindgen moduł jako warunek wstępny używania makra include!(), wróć do sekcji Warunek wstępny na stronie Generatory źródeł. Jeśli nie, przejdź do następnych sekcji.

Używanie wiązań jako skrzyni

Utwórz plik external/rust/hello_bindgen/Android.bp z tą zawartością:

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

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

Utwórz plik external/rust/hello_bindgen/src/main.rs z tą zawartością:

//! 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) }
}

Na koniec wywołaj m hello_bindgen, aby utworzyć plik binarny.

Testowanie powiązań Bindgen

Wiązania bindgen zwykle zawierają wiele wygenerowanych testów układu, aby zapobiec niezgodnościom układu pamięci. AOSP zaleca zdefiniowanie modułu testowego dla tych testów i uruchamianie ich w ramach normalnego pakietu testów projektu.

Binarny plik testowy można łatwo utworzyć, definiując rust_testmodułexternal/rust/hello_bindgen/Android.bp:

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",
}

Widoczność i połączenia

Wygenerowane powiązania są zwykle bardzo małe, ponieważ składają się z definicji typów, sygnatur funkcji i powiązanych stałych. W rezultacie dynamiczne łączenie tych bibliotek jest zwykle nieefektywne. Wyłączyliśmy linkowanie dynamiczne w przypadku tych modułów, aby korzystanie z nich za pomocą rustlibs automatycznie wybierało wariant statyczny.

Domyślnie moduły rust_bindgen mają właściwość visibility ustawioną na [":__subpackages__"], co oznacza, że będą one widoczne tylko dla modułów w tym samym pliku Android.bp lub w plikach znajdujących się niżej w hierarchii katalogów. Ma to 2 cele:

  • Zniechęca to do używania w innych miejscach drzewa surowych powiązań C.
  • Pozwala uniknąć problemów z łączeniem w kształcie rombu dzięki połączeniu statycznemu i dynamicznemu.

Zwykle należy udostępnić bezpieczną bibliotekę otoki wokół wygenerowanego modułu dodanego w tym samym drzewie katalogów co powiązania, która jest przeznaczona do użytku przez innych deweloperów. Jeśli to nie działa w Twoim przypadku, możesz dodać kolejne pakiety do widoczności. Dodając dodatkowe zakresy widoczności, uważaj, aby nie dodawać 2 zakresów, które w przyszłości mogą być połączone w tym samym procesie, ponieważ może to spowodować niepowodzenie połączenia.

Ważne właściwości rust_bindgen

Właściwości zdefiniowane poniżej są dodatkowe w stosunku do ważnych wspólnych właściwości, które mają zastosowanie do wszystkich modułów. Są one szczególnie ważne w przypadku modułów Rust bindgen lub wykazują unikalne zachowanie charakterystyczne dla typu modułu rust_bindgen.

stem, name, crate_name

rust_bindgen tworzy warianty biblioteki, więc mają one takie same wymagania jak moduły rust_library w przypadku właściwości stem, namecrate_name. Więcej informacji znajdziesz w sekcji Notable Rust library properties (Ważne właściwości biblioteki Rust).

wrapper_src

Jest to ścieżka względna do pliku nagłówkowego otoki, który zawiera nagłówki wymagane w przypadku tych powiązań. Rozszerzenie pliku określa sposób interpretacji nagłówka i domyślnie wskazuje, którego flagi -std użyć. Zakłada się, że jest to nagłówek C , chyba że rozszerzenie ma wartość .hh lub .hpp. Jeśli plik nagłówkowy C++ musi mieć inne rozszerzenie, ustaw właściwość cpp_std, aby zastąpić domyślne zachowanie, które zakłada, że plik jest plikiem C.

source_stem

To nazwa pliku wygenerowanego pliku źródłowego. To pole musi być zdefiniowane, nawet jeśli używasz powiązań jako pakietu, ponieważ właściwość stem kontroluje tylko nazwę pliku wyjściowego dla wygenerowanych wariantów biblioteki. Jeśli moduł zależy od wielu generatorów źródłowych (np. bindgen i protobuf) jako źródła, a nie jako pakietów za pomocą rustlibs, musisz zadbać o to, aby wszystkie generatory źródłowe, które są zależnościami tego modułu, miały unikalne wartości source_stem. Moduły zależne kopiują źródła ze wszystkich zależności SourceProvider zdefiniowanych w srcs do wspólnego katalogu OUT_DIR, więc kolizje w source_stem spowodują nadpisanie wygenerowanych plików źródłowych w katalogu OUT_DIR.

c_std

To ciąg znaków wskazujący, której wersji standardu C należy użyć. Prawidłowe wartości są wymienione poniżej:

  • konkretną wersję, np. "gnu11";
  • "experimental", czyli wartość zdefiniowana przez system kompilacji w build/soong/cc/config/global.go, może używać wersji roboczych, takich jak C++1z, gdy są one dostępne.
  • Nieustawiona lub "", co oznacza, że należy użyć domyślnego ustawienia systemu kompilacji.

Jeśli to ustawienie jest skonfigurowane, rozszerzenie pliku jest ignorowane, a nagłówek jest traktowany jako nagłówek C. Nie można tego ustawić w tym samym czasie co cpp_std.

cpp_std

cpp_std to ciąg znaków określający, której wersji standardu C należy użyć. Prawidłowe wartości:

  • konkretną wersję, np. "gnu++11";
  • "experimental", czyli wartość zdefiniowana przez system kompilacji w build/soong/cc/config/global.go, może używać wersji roboczych, takich jak C++1z, gdy są one dostępne.
  • Nieustawiona lub "", co oznacza, że należy użyć domyślnego ustawienia systemu kompilacji.

Jeśli to ustawienie jest włączone, rozszerzenie pliku jest ignorowane, a nagłówek jest traktowany jako nagłówek C++. Nie można tego ustawić w tym samym czasie co c_std.

cflags

cflags zawiera listę ciągów znaków z flagami Clang, które są wymagane do prawidłowego interpretowania nagłówków.

custom_bindgen

W zaawansowanych przypadkach użycia narzędzie bindgen może być używane jako biblioteka udostępniająca interfejs API, którym można manipulować w ramach niestandardowego pliku binarnego Rust. Pole custom_bindgen przyjmuje nazwę modułu rust_binary_host, który korzysta z interfejsu API bindgen zamiast ze zwykłego pliku binarnego bindgen.

Ten niestandardowy plik binarny musi oczekiwać argumentów w sposób podobny do bindgen, np.

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

Większość tych czynności wykonuje sama biblioteka bindgen. Przykład takiego użycia znajdziesz w pliku external/rust/crates/libsqlite3-sys/android/build.rs.

Dostępny jest też pełny zestaw właściwości biblioteki, który umożliwia kontrolowanie kompilacji biblioteki, choć rzadko trzeba je definiować lub zmieniać.

handle_static_inline i static_inline_library

Te 2 właściwości są przeznaczone do używania razem i umożliwiają tworzenie otoczek dla statycznych funkcji wbudowanych, które można uwzględnić w eksportowanych powiązaniach bindgen.

Aby ich użyć, ustaw handle_static_inline: truestatic_inline_library na odpowiednią wartość cc_library_static, która definiuje moduł rust_bindgen jako źródło danych wejściowych.

Przykład użycia:

    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: ["."],
    }