Generatori di origine

Questa pagina fornisce una visione generale di come l'origine generata è supportata e e come può essere usata nel sistema di compilazione.

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

Casse dall'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 dipendenza nelle proprietà rustlibs, rlibs e dylibs. Utilizzo ottimale per il codice della piattaforma prevede l'utilizzo di un'origine generata come una cassa. Sebbene La macro include! è supportata per l'origine generata; il suo scopo principale è Supportano il codice di terze parti che risiede in external/.

In alcuni casi il codice della piattaforma potrebbe continuare a utilizzare il codice sorgente generato tramite Macro include!(), ad esempio quando utilizzi un modulo genrule per generare un'origine in modo peculiare.

Utilizza include!() per includere l'origine generata

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

Prerequisito

Questo esempio si basa sul presupposto che tu abbia definito un valore rust_bindgen (libbuzz_bindgen) e può andare alla sezione Passaggi per includere l'origine generata per utilizzare la macro include!(). 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é le casse per il codice sorgente generato

A differenza dei compilatori C/C++, rustc accetta un solo file di origine che rappresentano un punto di ingresso di un file binario o di una libreria. Si presume che l'albero di origine sia strutturato in modo che tutti i file di origine richiesti possano essere rilevati automaticamente. Ciò significa che il codice sorgente generato deve essere inserito nell'albero di origine o fornito tramite una direttiva include nel codice sorgente:

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

La community di Rust si basa su build.rs script e sulle ipotesi dell'ambiente di costruzione Cargo, per evitare questa differenza. Durante la compilazione, il comando cargo imposta una variabile di ambiente OUT_DIR in cui gli script build.rs dovrebbero inserire il codice sorgente generato. Utilizza la seguente comando per includere il codice sorgente:

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

Questo rappresenta una sfida per Soong, in quanto gli output di ciascun modulo vengono posizionati nella rispettiva directory out/1. Non esiste un solo OUT_DIR in cui delle dipendenze restituiscono l'origine generata.

Per il codice della piattaforma, AOSP preferisce impacchettare il codice sorgente generato in un crate che può essere importato, per diversi motivi:

  • Evitare collisioni tra i nomi dei file di origine generati.
  • Riduci il codice boilerplate checked-in nell'intera struttura ad albero che richiede manutenzione. Qualsiasi boilerplate necessario per compilare il codice sorgente generato in un crate può essere gestito centralmente.
  • Evita interazioni implicite2 tra il codice generato e la cassa circostante.
  • Riduci la pressione su memoria e disco collegando in modo dinamico le origini generate di uso comune.

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


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

  2. Poiché include! funziona tramite inclusione di testo, potrebbe fare riferimento ai valori dello spazio dei nomi che lo racchiude, modificarlo o utilizzare costrutti come #![foo]. Queste interazioni implicite possono essere difficili da gestire. Prediligi sempre le macro quando l'interazione con il resto della cassetta è davvero necessaria.