System kompilacji obsługuje generowanie bindgen za pomocą typu modułu rust_bindgen. Bindgen zapewnia powiązania FFI Rust z bibliotekami C (z nieco 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 przykład użycia tego modułu jako crate. Jeśli chcesz używać bindgen za pomocą makra include!(), np. w przypadku kodu zewnętrznego, zapoznaj się ze stroną Generatory źródeł.

Przykład biblioteki C do wywołania z Rust

Poniżej znajduje się przykładowa biblioteka 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 opakowujący 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 dotyczącej ręcznego tworzenia powiązań w artykule Dostosowywanie wygenerowanych powiązań.

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

Używanie wiązań jako paczki

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 skompilować plik binarny.

Testowanie powiązań Bindgen

Powiązania Bindgen zawierają zwykle wiele wygenerowanych testów układu, aby zapobiegać niezgodności układu pamięci. AOSP zaleca, aby zdefiniowany moduł testów był zdefiniowany na potrzeby tych testów, a testy zostały uruchomione w ramach standardowego zestawu testów w projekcie.

Testowy binarny plik binarny dla tych urządzeń można łatwo wygenerować, definiując moduł rust_test w pliku 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łączenie

Wygenerowane powiązania są zwykle bardzo małe, ponieważ składają się z definicji typów, podpisów funkcji i powiązanych stałych. Dlatego dynamiczne łączenie tych bibliotek zwykle nie jest sensowne. Wyłączyliśmy dynamiczne linkowanie w przypadku tych modułów, aby ich używanie za pomocą rustlibs automatycznie wybierało wariant statyczny.

Domyślnie moduły rust_bindgen mają właściwość visibility o wartości [":__subpackages__"], która pozwala widzieć je tylko modułom w tym samym pliku Android.bp lub w katalogach podrzędnych w hierarchii katalogów. Ma to 2 cele:

  • Nie zaleca się stosowania nieprzetworzonych połączeń C w innych miejscach w drzewie.
  • Umożliwia unikanie problemów z połączeniami diamentowymi dzięki połączeniu łączenia statycznych i dynamicznych.

Zwykle należy udostępnić bezpieczną bibliotekę owijającą wygenerowany moduł w tym samym drzewie katalogów co powiązania, która jest przeznaczona do użytku przez innych programistów. Jeśli to nie zadziała, możesz dodać dodatkowe pakiety do widoczności. Podczas dodawania dodatkowych zakresów widoczności uważaj, aby nie dodawać zakresów, które mogą zostać połączone w ramach tego samego procesu w przyszłości, ponieważ może to spowodować niepowodzenie połączenia.

Właściwości rust_bindgen

Właściwości zdefiniowane poniżej są uzupełnieniem 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 Rustbindgen lub mają unikalne zachowanie specyficzne dla typu modułu rust_bindgen.

łodyga, nazwa, nazwa_skrzynki

rust_bindgen generuje wersje biblioteki, więc ma te same wymagania co moduły rust_library w przypadku właściwości stem, namecrate_name. Informacje o najważniejszych właściwościach biblioteki Rust znajdziesz w poniższej sekcji.

wrapper_src

Jest to ścieżka względna do pliku nagłówka kodu, który zawiera nagłówki wymagane przez te powiązania. Rozszerzenie pliku określa sposób interpretacji nagłówka oraz flagę -std, która ma być używana domyślnie. Zakłada się, że jest to nagłówek C, chyba że rozszerzenie to .hh lub .hpp. Jeśli nagłówek C++ musi mieć inną rozszerzenie, ustaw właściwość cpp_std, aby zastąpić domyślne działanie, które zakłada, że plik jest plikiem C.

source_stem

Jest to nazwa wygenerowanego pliku źródłowego. To pole musi być zdefiniowane, nawet jeśli używasz tych ograniczeń jako zbioru, ponieważ właściwość stem wpływa tylko na nazwę pliku wyjściowego wygenerowanych wariantów biblioteki. Jeśli moduł zależy od wielu generatorów źródłowych (takich jak bindgenprotobuf) jako źródła, a nie jako paczek za pomocą rustlibs, musisz się upewnić, że wszystkie generatory źródłowe, które są zależnościami tego modułu, mają 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, dlatego kolizje w source_stem powodują zastąpienie 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:

  • konkretnej wersji, np. "gnu11";
  • "experimental", która jest wartością zdefiniowaną przez system kompilacji w build/soong/cc/config/global.go, może używać wersji roboczych, takich jak C++1z, gdy są dostępne.
  • Niezdefiniowane lub "", co oznacza, że należy użyć domyślnego systemu kompilacji.

Jeśli to ustawienie jest ustawione, rozszerzenie pliku jest ignorowane, a zakłada się, że nagłówek jest nagłówkiem C. Nie można go ustawić jednocześnie z opcją cpp_std.

cpp_std

cpp_std to ciąg znaków wskazujący, której wersji języka C należy użyć. Prawidłowe wartości:

  • konkretnej wersji, np. "gnu++11"
  • "experimental", która jest wartością zdefiniowaną przez system kompilacji w build/soong/cc/config/global.go, może używać wersji roboczych, takich jak C++1z, gdy są dostępne.
  • Niezdefiniowane lub "", co oznacza, że należy użyć domyślnego systemu kompilacji.

Jeśli to ustawienie jest włączone, rozszerzenie pliku jest ignorowane, a zakłada się, że nagłówek jest nagłówkiem C++. Nie można go ustawić jednocześnie z opcją c_std.

cflags

cflags zawiera listę ciągów znaków z flagami Clang wymaganymi do prawidłowej interpretacji nagłówków.

niestandardowe_wiązanie

W zaawansowanych przypadkach użycia bindgen może służyć jako biblioteka, udostępniając interfejs API, na którym można manipulować jako część niestandardowego pliku binarnego Rust. Pole custom_bindgen przyjmuje nazwę modułu rust_binary_host, który zamiast zwykłego binarnego pliku bindgen używa interfejsu bindgen API.

Ten niestandardowy plik binarny musi oczekiwać argumentów w sposób podobny do bindgen, na przykład

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

Większość tych zadań jest wykonywana przez samą bibliotekę bindgen. Przykład tego użycia znajdziesz na stronie external/rust/crates/libsqlite3-sys/android/build.rs.

Udostępniamy też pełny zestaw właściwości biblioteki, by kontrolować kompilację biblioteki. Rzadko trzeba je jednak zdefiniować lub zmienić.

handle_static_inline i static_inline_library

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

Aby ich użyć, ustaw handle_static_inline: true i static_inline_library na odpowiadający cc_library_static, który 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: ["."],
    }