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 używa bindgen, oraz sposób używania tego modułu jako pakietu. Jeśli musisz używać powiązań bindgen za pomocą
makra include!(), np. w przypadku kodu zewnętrznego, zapoznaj się z sekcją Generatory kodu źródłowego.
Przykładowa biblioteka C do wywoływania z poziomu 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);
}
Definiowanie modułu rust_bindgen
Zdefiniuj nagłówek otoki, 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 Dostosowywanie wygenerowanych powiązań w podręczniku bindgen.
Jeśli ta sekcja została użyta do zdefiniowania modułu rust_bindgen jako warunku wstępnego
użycia makra include!(), wróć do sekcji Warunek wstępny
na stronie Generatory kodu źródłowego. W przeciwnym razie przejdź do następnych sekcji.
Używanie powiązań jako pakietu
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) }
}
Następnie wywołaj polecenie m hello_bindgen, aby skompilować plik binarny.
Testowanie powiązań bindgen
Powiązania bindgen zwykle zawierają kilka wygenerowanych testów układu, które zapobiegają niezgodnościom układu pamięci. AOSP zaleca zdefiniowanie modułu testowego na potrzeby tych testów oraz uruchamianie testów w ramach normalnego zestawu testów projektu.
Aby utworzyć plik binarny testu, zdefiniuj 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 łączenie
Wygenerowane powiązania są zwykle małe, ponieważ składają się z definicji typów, sygnatur funkcji i powiązanych stałych. W rezultacie dynamiczne łączenie tych bibliotek jest nieefektywne. Wyłączyliśmy dynamiczne łączenie tych modułów, aby używanie ich z rustlibs automatycznie wybierało wariant statyczny.
Domyślnie moduły rust_bindgen mają właściwość visibility ustawioną na
[":__subpackages__"], co umożliwia modułom w tym samym pliku Android.bp
lub modułom znajdującym się pod nim w hierarchii katalogów wyświetlanie go. Ma to 2 cele:
- Zniechęca do używania surowych powiązań C w innych miejscach drzewa.
- Zapobiega problemom z łączeniem diamentowym w przypadku połączenia statycznego i dynamicznego.
W tym samym drzewie katalogów co powiązania dodaj bezpieczną bibliotekę otoki wokół wygenerowanego modułu, która jest przeznaczona do używania przez innych deweloperów. Jeśli to nie zadziała w Twoim przypadku, możesz dodać
dodatkowe pakiety do visibility.
Dodając dodatkowe zakresy widoczności, nie dodawaj 2 zakresów, które mogą być w przyszłości połączone w tym samym procesie, ponieważ może to spowodować niepowodzenie łączenia.
Ważne właściwości rust_bindgen
Właściwości zdefiniowane w tej sekcji są dodatkiem do
ważnych właściwości wspólnych, które mają zastosowanie do wszystkich modułów. Są one ważne dla modułów Rust bindgen lub wykazują unikalne zachowanie charakterystyczne dla typu modułu rust_bindgen.
stem, name, crate_name
rust_bindgen tworzy warianty bibliotek, więc mają one takie same wymagania
jak moduły rust_library w przypadku właściwości stem, name i crate_name. Więcej informacji znajdziesz w sekcji Ważne właściwości biblioteki Rust.
wrapper_src
Jest to ścieżka względna do pliku nagłówka otoki, który zawiera nagłówki wymagane do tych powiązań. Rozszerzenie pliku określa sposób interpretowania nagłówka i domyślnie używaną flagę -std. Zakłada się, że jest to nagłówek C, chyba że rozszerzenie to .hh lub .hpp. Jeśli nagłówek 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
Jest to nazwa 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 wygenerowanych wariantów biblioteki.
Jeśli moduł zależy od wielu generatorów kodu źródłowego (np. bindgen i protobuf) jako źródła, a nie jako pakietów za pomocą rustlibs, upewnij się, że wszystkie generatory kodu źródłowego, 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, które są zdefiniowane 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
Jest to ciąg znaków reprezentujący wersję standardu C, która ma zostać użyta. Prawidłowe wartości są wymienione tutaj:
- Konkretna wersja, np.
gnu11. experimental, czyli wartość zdefiniowana przez system kompilacji wbuild/soong/cc/config/global.go, może używać wersji roboczych, takich jak C++1z, gdy są dostępne- Nieskonfigurowana 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 ustawić tego ustawienia jednocześnie z cpp_std.
cpp_std
cpp_std to ciąg znaków reprezentujący wersję standardu C, która ma zostać użyta. Prawidłowe wartości są wymienione tutaj:
- Konkretna wersja, np.
gnu++11. experimental, czyli wartość zdefiniowana przez system kompilacji wbuild/soong/cc/config/global.go, może używać wersji roboczych, takich jak C++1z, gdy są dostępne- Nieskonfigurowana 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 ustawić tego ustawienia jednocześnie z c_std.
cflags
cflags to lista ciągów znaków zawierająca flagi Clang wymagane do prawidłowego interpretowania nagłówków.
custom_bindgen
W zaawansowanych przypadkach użycia bindgen można używać jako biblioteki, która udostępnia 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 używa interfejsu API bindgen zamiast zwykłego pliku binarnego bindgen.
Ten niestandardowy plik binarny musi oczekiwać argumentów w podobny sposób jak bindgen, np.
$ my_bindgen [flags] wrapper_header.h -o [output_path] -- [clang flags]
Większość z tych czynności jest obsługiwana przez samą bibliotekę bindgen. Przykład użycia
znajdziesz w pliku external/rust/crates/libsqlite3-sys/android/build.rs.
Dodatkowo dostępny jest pełny zestaw właściwości biblioteki, które umożliwiają kontrolowanie kompilacji biblioteki, chociaż 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 otok dla statycznych funkcji inline, które można uwzględnić w eksportowanych powiązaniach bindgen.
Aby ich użyć, ustaw handle_static_inline: true i ustaw static_inline_library na odpowiednią wartość cc_library_static, która definiuje moduł rust_bindgen jako dane wejściowe źródła.
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 functions
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: ["."],
}