El sistema de compilación admite la generación de vinculaciones bindgen a través del tipo de módulo rust_bindgen
. Bindgen brinda vinculaciones de FFI de Rust a bibliotecas de C (además de compatibilidad limitada con C++, que requiere la configuración de la propiedad cppstd
).
Uso básico de rust_bindgen
A continuación, se muestra un ejemplo de cómo definir un módulo que usa bindgen y cómo usar ese módulo como contenedor. Si necesitas usar vinculaciones bindgen por medio de una macro include!()
(por ejemplo, para código externo), consulta la página Generadores de fuentes.
Ejemplo de biblioteca de C para llamar desde Rust
A continuación, se muestra un ejemplo de biblioteca de C que define un struct y una función para usar en 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);
}
Cómo definir un módulo rust_bindgen
Define un encabezado de wrapper, external/rust/libbuzz/libbuzz_wrapper.h
, que incluya todos los encabezados correspondientes:
// Include headers that are required for generating bindings in a wrapper header.
#include "libbuzz.h"
Define el archivo Android.bp
como 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"],
}
A fin de obtener más información para usar las marcas bindgen, consulta la sección Cómo personalizar las vinculaciones generadas del manual sobre bindgen.
Si usaste esta sección a fin de definir un módulo rust_bindgen
como requisito previo para usar la macro include!()
, vuelve a consultar Requisito previo en la página Generadores de fuentes. De lo contrario, continúa con las siguientes secciones.
Cómo usar vinculaciones como contenedor
Crea external/rust/hello_bindgen/Android.bp
con el siguiente contenido:
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 el siguiente contenido:
//! 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) }
}
Por último, llama a m hello_bindgen
para compilar el objeto binario.
Cómo probar las vinculaciones bindgen
Por lo general, las vinculaciones bindgen incluyen una serie de pruebas generadas de diseño para evitar discrepancias en el diseño de memoria. AOSP recomienda que cuentes con un módulo de prueba que se defina para estas pruebas y que estas se ejecuten como parte del paquete normal de pruebas de tu proyecto.
Se puede producir un objeto binario de prueba para estos fácilmente si se define un módulo rust_test
en 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",
}
Visibilidad y vinculación
Las vinculaciones generadas suelen ser muy pequeñas, ya que consisten en definiciones de tipos, firmas de funciones y constantes relacionadas. Como resultado, vincular estas bibliotecas de forma dinámica suele ser una pérdida. Inhabilitamos la vinculación dinámica para estos módulos, de modo que, cuando se usen mediante rustlibs
, se seleccione automáticamente una variante estática.
De forma predeterminada, los módulos rust_bindgen
tienen una propiedad visibility
de [":__subpackages__"]
, que solo permitirá que vean los módulos en el mismo archivo Android.bp
o aquellos debajo de él en la jerarquía de directorios. Esto tiene dos propósitos:
- Reduce el uso de vinculaciones C sin procesar en otras partes del árbol.
- Evita problemas de vinculación de diamantes con una combinación de vinculación estática y dinámica.
Por lo general, debes proporcionar una biblioteca de wrapper segura alrededor del módulo generado que agregaste en el mismo árbol de directorios que las vinculaciones. La idea es que la usen otros desarrolladores. Si esto no funciona para tu caso de uso, puedes agregar paquetes adicionales a la visibilidad. Cuando agregues alcances de visibilidad adicionales, ten en cuenta que no debes agregar dos alcances que puedan estar vinculados en el mismo proceso en el futuro, ya que es posible que no se vinculen.
Propiedades notables de rust_bindgen
Las propiedades que se definen a continuación se agregan a las propiedades comunes importantes que se aplican a todos los módulos. Estas son particularmente importantes para los módulos bindgen de Rust o presentan un comportamiento único específico para el tipo de módulo rust_bindgen
.
stem, name, crate_name
rust_bindgen
produce variantes de biblioteca, por lo que comparten los mismos requisitos con los módulos rust_library
para las propiedades stem
, name
y crate_name
. Como referencia, consulta las Propiedades notables de la biblioteca de Rust.
wrapper_src
Es la ruta de acceso relativa a un archivo de encabezado de wrapper que incluye los encabezados necesarios para estas vinculaciones. La extensión de archivo determina cómo interpretar el encabezado y determina qué marca -std
se usará de forma predeterminada. Se supone que es un encabezado de C, a menos que la extensión sea .hh
o .hpp
. Si el encabezado de C++ debe tener otra extensión, configura la propiedad cpp_std
para anular el comportamiento predeterminado que supone que el archivo es de C.
source_stem
Es el nombre de archivo para el archivo de fuente generado. Este campo debe definirse, incluso si usas las vinculaciones como contenedor, ya que la propiedad stem
solo controla el nombre de archivo de salida para las variantes de biblioteca generadas.
Si un módulo depende de varios generadores de fuentes (por ejemplo, bindgen
y protobuf
) como fuente en lugar de como contenedores a través de rustlibs
, debes asegurarte de que todos los generadores que sean dependencias de ese módulo tengan valores source_stem
únicos. Los módulos dependientes copian las fuentes de todas las dependencias SourceProvider
que se definen en srcs
a un directorio OUT_DIR
común, por lo que los conflictos en source_stem
causarían que se reemplacen los archivos de fuente generados en el directorio OUT_DIR
.
c_std
Es una string que representa la versión estándar de C que se usará. A continuación, se mencionan los valores válidos:
- Una versión específica, como
"gnu11"
"experimental"
, que es un valor definido por el sistema de compilación enbuild/soong/cc/config/global.go
, puede usar versiones preliminares, como C++1z, cuando estén disponibles- No establecido o
""
, que indica que se debe usar la configuración predeterminada del sistema de compilación
Si se configura, se ignora la extensión de archivo, y se supone que el encabezado es de C. No se puede configurar al mismo tiempo que cpp_std
.
cpp_std
cpp_std
es una string que representa la versión estándar de C que se usará. Valores válidos:
- Una versión específica, como
"gnu++11"
"experimental"
, que es un valor definido por el sistema de compilación enbuild/soong/cc/config/global.go
, puede usar versiones preliminares, como C++1z, cuando estén disponibles- No establecido o
""
, que indica que se debe usar la configuración predeterminada del sistema de compilación
Si se configura, se ignora la extensión de archivo, y se supone que el encabezado es de C++. No se puede configurar al mismo tiempo que c_std
.
cflags
cflags
brinda una lista de strings de marcas Clang que se necesitan para interpretar los encabezados de manera correcta.
custom_bindgen
Para casos de uso avanzados, bindgen se puede usar como una biblioteca, lo que brinda una API que se puede manipular como parte de un objeto binario personalizado de Rust. El campo custom_bindgen
toma el nombre de módulo de un rust_binary_host
, que usa la API de bindgen en lugar del objeto binario bindgen
normal.
Este objeto binario personalizado debe esperar argumentos al igual que bindgen
, como los siguientes:
$ my_bindgen [flags] wrapper_header.h -o [output_path] -- [clang flags]
La mayor parte de esto se controla mediante la propia biblioteca de bindgen
. Para ver un ejemplo de este uso, visita external/rust/crates/libsqlite3-sys/android/build.rs.
Además, el conjunto completo de propiedades de la biblioteca está disponible para controlar la compilación de esta, aunque, por lo general, no es necesario definirlas ni cambiarlas.
handle_static_inline and static_inline_library
Estas dos propiedades están pensadas para utilizarse juntas y permiten la producción de wrappers para funciones intercaladas estáticas que se pueden incluir en las vinculaciones de bindgen exportadas.
Para utilizarlas, establece handle_static_inline: true
y static_inline_library
en un cc_library_static
correspondiente que defina el módulo rust_bindgen
como entrada de fuente.
Ejemplo de uso:
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"],
include_dirs: ["src/"],
}