Generadores de fuentes

En esta página, se proporciona una vista general de cómo se admite la fuente generada y cómo se puede usar en el sistema de compilación.

Todos los generadores de fuentes proporcionan funciones similares del sistema de compilación. Los tres casos de uso de generación de fuentes admitidos por el sistema de compilación son generar vinculaciones de C mediante bindgen y las interfaces de AIDL y protobuf.

Contenedores de la fuente generada

Cada módulo Rust que genera código fuente se puede usar como contenedor, igual que si se hubiera definido como rust_library. (Significa que se puede definir como una dependencia en las propiedades rustlibs, rlibs y dylibs). El mejor patrón de uso para el código de la plataforma es emplear la fuente generada como contenedor. Aunque la macro include! es compatible con la fuente generada, su objetivo principal es admitir el código de terceros que reside en external/.

Existen casos en los que el código de la plataforma podría seguir usando la fuente generada mediante la macro include!(), como cuando se usa un módulo genrule para generar una fuente de forma única.

Cómo usar include!() para incluir la fuente generada

El uso de la fuente generada como contenedor se incluye en los ejemplos de cada página de módulo específica (respectiva). En esta sección, se muestra cómo hacer referencia a la fuente generada a través de la macro include!(). Ten en cuenta que este proceso es similar para todos los generadores de fuentes.

Requisito previo

Este ejemplo se basa en la suposición de que definiste un módulo rust_bindgen (libbuzz_bindgen) y puedes continuar con los Pasos para incluir la fuente generada a fin de usar la macro include!(). Si aún no lo hiciste, ve a Cómo definir un módulo rust_bindgen, crea libbuzz_bindgen y, luego, regresa aquí.

Ten en cuenta que las partes del archivo de compilación se aplican a todos los generadores de fuentes.

Pasos para incluir la fuente generada

Crea external/rust/hello_bindgen/Android.bp con el siguiente contenido:

rust_binary {
   name: "hello_bzip_bindgen_include",
   srcs: [
         // The primary rust source file must come first in this list.
         "src/lib.rs",

         // The module providing the bindgen bindings is
         // included in srcs prepended by ":".
         ":libbuzz_bindgen",
    ],

    // Dependencies need to be redeclared when generated source is used via srcs.
    shared_libs: [
        "libbuzz",
    ],
}

Crea external/rust/hello_bindgen/src/bindings.rs con el siguiente contenido:

#![allow(clippy::all)]
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(unused)]
#![allow(missing_docs)]

// Note that "bzip_bindings.rs" here must match the source_stem property from
// the rust_bindgen module.
include!(concat!(env!("OUT_DIR"), "/bzip_bindings.rs"));

Crea external/rust/hello_bindgen/src/lib.rs con el siguiente contenido:

mod bindings;

fn main() {
    let mut x = bindings::foo { x: 2 };
    unsafe { bindings::fizz(1, &mut x as *mut bindings::foo) }
}

Por qué crear contenedores para la fuente generada

A diferencia de los compiladores de C/C++, rustc solo acepta un único archivo fuente que representa un punto de entrada a un objeto binario o una biblioteca. Se espera que el árbol de fuentes esté estructurado de modo que todos los archivos fuente necesarios se puedan descubrir automáticamente. Esto significa que la fuente generada se debe colocar en el árbol de fuentes o se debe proporcionar a través de una directiva de inclusión en la fuente:

include!("/path/to/hello.rs");

La comunidad de Rust utiliza las secuencias de comandos de build.rs y las suposiciones sobre el entorno de compilación de Cargo para funcionar con esta diferencia. Cuando se compila, el comando cargo establece una variable de entorno OUT_DIR en la que se espera que las secuencias de comandos build.rs coloquen el código fuente generado. Usa el siguiente comando para incluir el código fuente:

include!(concat!(env!("OUT_DIR"), "/hello.rs"));

Esto presenta un desafío para Soong, ya que los resultados de cada módulo se colocan en su propio directorio de out/1. No hay un único OUT_DIR donde las dependencias colocan la fuente generada.

Para el código de la plataforma, AOSP prefiere empaquetar la fuente generada en un contenedor que se pueda importar por varias razones:

  • Evita conflictos entre los nombres de los archivos fuente generados.
  • Reduce el código estándar registrado en todo el árbol que requiere mantenimiento. Cualquier código estándar necesario para convertir la compilación de la fuente generada en un contenedor puede mantenerse de forma central.
  • Evita las interacciones implícitas2 entre el código generado y el contenedor circundante.
  • Para reducir la presión sobre la memoria y el disco, vincula de forma dinámica las fuentes generadas de uso frecuente.

Como resultado, todos los tipos de módulos de generación de fuentes de Rust de Android crean código que se puede compilar y usar como contenedor. Soong todavía admite contenedores de terceros sin modificaciones si todas las dependencias de la fuente generada para un módulo se copian en un único directorio por módulo, similar a Cargo. En estos casos, Soong establece la variable de entorno OUT_DIR en ese directorio cuando compila el módulo para que se pueda encontrar la fuente generada. Sin embargo, por las razones ya descritas, la práctica recomendada es solo usar este mecanismo en el código de la plataforma cuando sea absolutamente necesario.


  1. Esto no presenta problemas para C/C++ ni lenguajes similares, ya que la ruta de acceso a la fuente generada se proporciona directamente al compilador. 

  2. Dado que include! funciona con la inclusión textual, podría hacer referencia a valores del espacio de nombres adjunto, modificar el espacio de nombres o usar construcciones como #![foo]. Estas interacciones implícitas pueden ser difíciles de mantener. Siempre elige las macros cuando sea realmente necesario interactuar con el resto del contenedor.