Módulos de vinculación bindgen

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 una estructura 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 relevantes:

// 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 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 en build/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 en build/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 similares a 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.