Generatori di origine

Questa pagina fornisce una panoramica generale di come viene supportata l'origine generata e di come può essere utilizzata nel sistema di compilazione.

Tutti i generatori di origine forniscono funzionalità del sistema di compilazione simili. I tre casi d'uso di generazione di origine supportati dal sistema di compilazione sono la generazione di binding C utilizzando bindgen, interfacce AIDL e interfacce protobuf.

Crate da origine generata

Ogni modulo Rust che genera codice sorgente può essere utilizzato come crate, esattamente come se fosse definito come rust_library. (Ciò significa che può essere definito come dipendenza nelle proprietà rustlibs, rlibs e dylibs.) Il modello di utilizzo migliore per il codice della piattaforma è impiegare l'origine generata come crate. Sebbene la macro include! sia supportata per l'origine generata, il suo scopo principale è supportare il codice di terze parti che risiede in external/.

Esistono casi in cui il codice della piattaforma potrebbe comunque utilizzare l'origine generata tramite la macro include!(), ad esempio quando utilizzi un modulo genrule per generare l'origine in modo univoco.

Utilizzare include!() per includere l'origine generata

L'utilizzo dell'origine generata come crate è trattato negli esempi di ogni pagina del modulo specifico (rispettivo). Questa sezione mostra come fare riferimento all'origine generata tramite la macro include!(). Tieni presente che questa procedura è simile per tutti i generatori di origine.

Prerequisito

Questo esempio si basa sul presupposto che tu abbia definito un rust_bindgen modulo (libbuzz_bindgen) e che tu possa procedere ai passaggi per includere l'origine generata per l'utilizzo dellainclude!() macro. In caso contrario, vai a Definire un modulo rust bindgen, crea libbuzz_bindgen e poi torna qui.

Tieni presente che le parti del file di compilazione sono applicabili a tutti i generatori di origine.

Passaggi per includere l'origine generata

Crea external/rust/hello_bindgen/Android.bp con i seguenti contenuti:

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 i seguenti contenuti:

#![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 i seguenti contenuti:

mod bindings;

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

Perché i crate per l'origine generata

A differenza dei compilatori C/C++, rustc accetta un solo file di origine che rappresenta un punto di ingresso per un file binario o una libreria. Si aspetta che l'albero di origine sia strutturato in modo che tutti i file di origine richiesti possano essere rilevati automaticamente. Ciò significa che l'origine generata deve essere inserita nell'albero di origine o fornita tramite una direttiva include nell'origine:

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

La community Rust si basa sugli script build.rs e sui presupposti sull' ambiente di build di Cargo per gestire questa differenza. Durante la compilazione, il comando cargo imposta una variabile di ambiente OUT_DIR in cui gli script build.rs devono inserire il codice sorgente generato. Utilizza il seguente comando per includere il codice sorgente:

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

Questo rappresenta una sfida per Soong, poiché gli output di ogni modulo vengono inseriti in una propria directory out/1. Non esiste un singolo OUT_DIR in cui le dipendenze restituiscono l'origine generata.

Per il codice della piattaforma, AOSP preferisce il packaging dell'origine generata in un crate che può essere importato, per diversi motivi:

  • Impedire che i nomi dei file di origine generati entrino in conflitto.
  • Ridurre il codice boilerplate archiviato nell'albero che richiede manutenzione. Qualsiasi boilerplate necessario per compilare l'origine generata in un crate può essere gestito centralmente.
  • Evitare interazioni implicite2 tra il codice generato e il crate circostante.
  • Ridurre la pressione su memoria e disco collegando dinamicamente le origini generate di uso comune.

Di conseguenza, tutti i tipi di moduli di generazione di origine Rust di Android producono codice che può essere compilato e utilizzato come crate. Soong supporta ancora i crate di terze parti senza modifiche se tutte le dipendenze di origine generate per un modulo vengono copiate in una singola directory per modulo, in modo simile a Cargo. In questi casi, Soong imposta la variabile di ambiente OUT_DIR su quella directory durante la compilazione del modulo, in modo che l'origine generata possa essere trovata. Tuttavia, per i motivi già descritti, è una best practice utilizzare questo meccanismo nel codice della piattaforma solo quando è assolutamente necessario.


  1. Questo non presenta problemi per C/C++ e linguaggi simili, poiché il percorso dell'origine generata viene fornito direttamente al compilatore.

  2. Poiché include! funziona tramite inclusione testuale, potrebbe fare riferimento a valori dello spazio dei nomi racchiuso , modificare lo spazio dei nomi o utilizzare costrutti come #![foo]. Queste interazioni implicite possono essere difficili da gestire. Preferisci sempre le macro quando l'interazione con il resto del crate è effettivamente necessaria.