Il sistema di compilazione supporta la generazione di associazioni bindgen tramite il tipo di modulo rust_bindgen
. Bindgen fornisce binding FFI Rust alle librerie C (con un supporto C++ limitato, che richiede l'impostazione della proprietà cppstd
).
Utilizzo di base di rust_bindgen
Di seguito è riportato un esempio di come definire un modulo che utilizza bindgen e di come utilizzare questo modulo come crate. Se devi utilizzare i binding bindgen tramite
una macro include!()
, ad esempio per codice esterno, consulta la pagina
Generatori di origine.
Libreria di esempio C da chiamare da Rust
Di seguito è riportato un esempio di libreria C che definisce una struct e una funzione da utilizzare in 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);
}
Definisci un modulo rust_bindgen
Definisci un'intestazione wrapper, external/rust/libbuzz/libbuzz_wrapper.h
, che includa
tutte le intestazioni pertinenti:
// Include headers that are required for generating bindings in a wrapper header.
#include "libbuzz.h"
Definisci il file Android.bp
come 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"],
}
Per scoprire di più sull'utilizzo dei flag bindgen, consulta la sezione del manuale bindgen sulla personalizzazione dei binding generati.
Se hai utilizzato questa sezione per definire un modulo rust_bindgen
come prerequisito per
l'utilizzo della macro include!()
, torna a Prerequisito
nella pagina Generatori di origine. In caso contrario, procedi con le sezioni successive.
Utilizzare i binding come crate
Crea external/rust/hello_bindgen/Android.bp
con i seguenti contenuti:
rust_binary {
name: "hello_bindgen",
srcs: ["main.rs"],
// Add the rust_bindgen module as if it were a rust_library dependency.
rustlibs: ["libbuzz_bindgen"],
}
Crea external/rust/hello_bindgen/src/main.rs
con i seguenti contenuti:
//! 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) }
}
Infine, chiama m hello_bindgen
per creare il binario.
Testare i binding di Bindgen
I binding Bindgen in genere contengono una serie di test di layout generati per evitare mancate corrispondenze del layout di memoria. AOSP consiglia di definire un modulo di test per questi test e di eseguirli nell'ambito della normale suite di test del progetto.
Un binario di test per questi può essere prodotto facilmente definendo un modulo rust_test
in 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",
}
Visibilità e collegamento
I binding generati sono in genere molto piccoli, in quanto sono costituiti da definizioni di tipo,
firme di funzioni e costanti correlate. Di conseguenza, in genere
è uno spreco collegare queste librerie in modo dinamico. Abbiamo disattivato il collegamento dinamico
per questi moduli, in modo che il loro utilizzo tramite rustlibs
selezioni automaticamente una
variante statica.
Per impostazione predefinita, i moduli rust_bindgen
hanno una proprietà visibility
di [":__subpackages__"]
, che consente solo ai moduli nello stesso file Android.bp
o a quelli sottostanti nella gerarchia delle directory di visualizzarlo. Questo ha due
scopi:
- Sconsiglia l'utilizzo di binding C non elaborati altrove nell'albero.
- In questo modo, si evitano problemi di collegamento a diamante con una combinazione di collegamento statico e dinamico.
In genere, devi fornire una libreria wrapper sicura intorno al modulo generato che hai aggiunto nella stessa struttura di directory dei binding, che è destinata all'utilizzo da parte di altri sviluppatori. Se questa soluzione non è adatta al tuo caso d'uso, puoi aggiungere altri pacchetti alla visibilità. Quando aggiungi ulteriori ambiti di visibilità, fai attenzione a non aggiungere due ambiti che potrebbero essere collegati allo stesso processo in futuro, in quanto il collegamento potrebbe non riuscire.
Proprietà importanti di rust_bindgen
Le proprietà definite di seguito si aggiungono alle proprietà comuni importanti
che si applicano a tutti i moduli. Questi sono particolarmente importanti per i moduli Rust
bindgen o mostrano un comportamento unico specifico per il tipo di modulo rust_bindgen
.
stem, name, crate_name
rust_bindgen
produce varianti della libreria, pertanto condividono gli stessi requisiti con
i moduli rust_library
per le proprietà stem
, name
e crate_name
. Per riferimento, vedi
Proprietà notevoli della libreria Rust.
wrapper_src
Questo è il percorso relativo a un file di intestazione wrapper che include le intestazioni richieste
per questi binding. L'estensione del file determina come interpretare l'intestazione
e quale flag -std
utilizzare per impostazione predefinita. Si presume che si tratti di un'intestazione C
a meno che l'estensione non sia .hh
o .hpp
. Se l'intestazione C++ deve avere
un'altra estensione, imposta la proprietà cpp_std
per ignorare il comportamento predefinito
che presuppone che il file sia un file C.
source_stem
Questo è il nome file del file sorgente generato. Questo campo deve
essere definito, anche se utilizzi i binding come crate, poiché la proprietà stem
controlla solo il nome file di output per le varianti della libreria generate.
Se un modulo dipende da più generatori di origine (ad esempio bindgen
e protobuf
)
come origine anziché come crate tramite rustlibs
, devi assicurarti che tutti i generatori di origine
che sono dipendenze di quel modulo abbiano valori source_stem
univoci. I moduli dipendenti
copiano le origini da tutte le dipendenze SourceProvider
definite in
srcs
in una directory OUT_DIR
comune, quindi le collisioni in source_stem
comporterebbero la sovrascrittura dei file di origine generati nella directory OUT_DIR
.
c_std
Si tratta di una stringa che rappresenta la versione dello standard C da utilizzare. I valori validi sono elencati di seguito:
- Una versione specifica, ad esempio
"gnu11"
. "experimental"
, che è un valore definito dal sistema di compilazione inbuild/soong/cc/config/global.go
, può utilizzare versioni bozza come C++1z quando sono disponibili.- Non impostato o
""
, che indica che deve essere utilizzata l'impostazione predefinita del sistema di build.
Se questa opzione è impostata, l'estensione del file viene ignorata e si presume che l'intestazione
sia un'intestazione C. Questo valore non può essere impostato contemporaneamente a cpp_std
.
cpp_std
cpp_std
è una stringa che rappresenta la versione standard C da utilizzare. Valori validi:
- Una versione specifica, ad esempio
"gnu++11"
"experimental"
, che è un valore definito dal sistema di compilazione inbuild/soong/cc/config/global.go
, può utilizzare versioni bozza come C++1z quando sono disponibili.- Non impostato o
""
, che indica che deve essere utilizzata l'impostazione predefinita del sistema di build.
Se questa opzione è impostata, l'estensione del file viene ignorata e si presume che l'intestazione
sia un'intestazione C++. Questo valore non può essere impostato contemporaneamente a c_std
.
cflags
cflags
fornisce un elenco di stringhe di flag Clang necessari per interpretare correttamente le intestazioni.
custom_bindgen
Per i casi d'uso avanzati, bindgen può essere utilizzato come libreria, fornendo un'API che
può essere manipolata nell'ambito di un binario Rust personalizzato. Il campo custom_bindgen
accetta il nome
del modulo di un modulo rust_binary_host
, che utilizza l'API bindgen anziché
il normale binario bindgen
.
Questo binario personalizzato deve prevedere argomenti in modo simile a bindgen
, ad esempio
$ my_bindgen [flags] wrapper_header.h -o [output_path] -- [clang flags]
La maggior parte di queste operazioni viene gestita dalla libreria bindgen
stessa. Per vedere un esempio di questo utilizzo, visita la pagina external/rust/crates/libsqlite3-sys/android/build.rs.
Inoltre, è disponibile l'intero insieme di proprietà della libreria per controllare la compilazione della libreria, anche se raramente è necessario definirle o modificarle.
handle_static_inline e static_inline_library
Queste due proprietà devono essere utilizzate insieme e consentono la produzione di wrapper per le funzioni statiche inline che possono essere incluse nei binding bindgen esportati.
Per utilizzarli, imposta handle_static_inline: true
e static_inline_library
su
un cc_library_static
corrispondente che definisce il modulo rust_bindgen
come
input di origine.
Esempio di utilizzo:
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: ["."],
}