System kompilacji obsługuje generowanie powiązań powiązań za pomocą interfejsu rust_bindgen typ modułu. Bindgen udostępnia powiązania FFI Rust w bibliotekach C (z niektórymi ograniczona obsługa języka C++, co wymaga ustawienia właściwości cppstd).

Podstawowe użycie rust_bindgen

Poniżej pokazujemy, jak zdefiniować moduł, który korzysta z powiązania. jak wykorzystać go jako skrzynię. Jeśli chcesz używać bindgen za pomocą makra include!(), np. w przypadku kodu zewnętrznego, zapoznaj się ze stroną Generatory źródeł.

Przykładowa biblioteka C do wywołania z systemu Rust

Poniżej znajduje się przykładowa biblioteka C, która definiuje strukturę i funkcję do wykorzystania w języku 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 ciąg wszystkie istotne 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 flagach bindgen znajdziesz w sekcji Dostosowywanie wygenerowanych bindowań w podręczniku bindgen.

Jeśli w tej sekcji zdefiniowałeś/zdefiniowałaś moduł rust_bindgen jako warunek wstępny do korzystania z makra include!(), wróć do sekcji Warunek wstępny na stronie „Generatory źródeł”. Jeśli nie, przejdź do kolejnych sekcji.

Używanie wiązań jako zbioru

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 zwykle zawierają kilka wygenerowanych testów układu, aby zapobiec niezgodności układu pamięci. AOSP zaleca zdefiniowanie modułu testowego dla testów oraz że są one uruchamiane w ramach standardowego zestawu testów w projekcie.

Testowy plik binarny dla tych można łatwo utworzyć, definiując moduł rust_test w 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, podpisy funkcji i powiązane stałe. W związku z tym zwykle nie trzeba było ich dynamicznie łączyć. Wyłączyliśmy dynamiczne powiązanie dla tych modułów, dzięki czemu użycie ich w usłudze rustlibs spowoduje automatyczne wybranie statycznego wariantu.

Domyślnie moduły rust_bindgen mają właściwość visibility o wartości [":__subpackages__"], który zezwala wyłącznie na moduły w tej samej sekcji Android.bp lub znajdujących się niżej w hierarchii katalogów. Ma to 2 cel:

  • Nie zaleca się używania nieprzetworzonych połączeń C w innych miejscach w drzewie.
  • Pozwala to uniknąć problemów z łączeniem diamentów w przypadku połączeń statycznych i dynamicznych.

Zwykle należy zapewnić bezpieczną bibliotekę owijającą wokół wygenerowanego modułu, który został dodany w tym samym drzewie katalogów co powiązania, które są przeznaczone 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.

Ważne właściwości rust_bindgen

Właściwości zdefiniowane poniżej są oprócz 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.

stem, name, crate_name

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. Zobacz Ważne właściwości biblioteki Rust w celach informacyjnych.

wrapper_src

To jest ścieżka względna do pliku nagłówka opakowania, który zawiera nagłówki wymagane do tych powiązań. 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 w C++ musi zawierać z innego rozszerzenia, ustaw właściwość cpp_std tak, aby zastępuje działanie domyślne który zakładamy, że jest to plik C.

source_stem

To nazwa wygenerowanego pliku źródłowego. To pole musi nawet jeśli używasz wiązań jako klatki, ponieważ stem kontroluje tylko 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, więc kolizje w source_stem powodowałyby zastąpienie wygenerowanych plików źródłowych w katalogu OUT_DIR.

c_std

Jest to ciąg znaków określający wersję C, która ma zostać użyta. Prawidłowe wartości są wymienione poniżej:

  • Konkretna wersja, 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 ta zasada jest skonfigurowana, rozszerzenie pliku jest ignorowane, a nagłówek powinien być nagłówkiem C. Nie można ustawić tej opcji równocześnie z wartością 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:

  • Konkretna wersja, np. "gnu++11"
  • "experimental", czyli wartość zdefiniowana przez system kompilacji w build/soong/cc/config/global.go, może używać wersje robocze, np. C++1z gdy są dostępne.
  • Wartość nieskonfigurowana lub "", która wskazuje, że należy używać wartości domyślnej 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 ustawić tej opcji równocześnie z wartością c_std.

kalosze

cflags zawiera listę ciągów flag Clang wymaganych do prawidłowej interpretacji nagłówki.

custom_bindgen

W zaawansowanych zastosowaniach bindgen może być używany jako biblioteka, która udostępnia interfejs API, którym można manipulować w ramach niestandardowego binarnego Rusta. 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 obsługiwana przez samą bibliotekę bindgen. Aby zobaczyć przykład użycie znajdziesz na stronie external/rust/crates/libsqlite3-sys/android/build.rs.

Dodatkowo możesz kontrolować pełen zestaw właściwości biblioteki. i kompilację biblioteki, choć rzadko trzeba je zdefiniować lub zmieniać.

handle_static_inline i static_inline_library

Te 2 usługi powinny być używane razem i umożliwiać tworzenie otoki statycznych funkcji wbudowanych, które można uwzględnić w wyeksportowanym powiązań.

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