Questa pagina fornisce una visione generale di come l'origine generata è supportata e di come può essere utilizzata 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 definita come dipendenza nelle proprietà rustlibs
, rlibs
e dylibs
. Il pattern di utilizzo migliore per il codice della piattaforma è utilizzare il codice sorgente generato come crate. Sebbene la macro include!
sia supportata per il codice sorgente generato, il suo scopo principale è supportare il codice di terze parti che si trova in external/
.
In alcuni casi, il codice della piattaforma potrebbe comunque utilizzare il codice sorgente generato tramite la macro include!()
, ad esempio quando utilizzi un modulo genrule
per generare il codice sorgente in modo univoco.
Utilizza include!() per includere il codice sorgente generato
L'utilizzo del codice sorgente generato come crate è trattato negli esempi di ogni pagina del modulo specifico. 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 presuppone che tu abbia definito un rust_bindgen
modulo (libbuzz_bindgen
) e puoi procedere alla sezione Passaggi per includere il codice sorgente generato
per utilizzare la macroinclude!()
. 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 rappresenta un punto di contatto per un file binario o una libreria. Si presume che l'albero di origine sia strutturato in modo da poter rilevare automaticamente tutti i file di origine richiesti. 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");
Per funzionare con questa differenza, la community di Rust dipende dagli script build.rs
e dalle ipotesi
sull'ambiente di compilazione Cargo.
Durante la creazione, il comando cargo
imposta una variabile di ambiente OUT_DIR
in cui si prevede che gli script build.rs
inseriscano il codice sorgente generato. Utilizza il seguente comando per includere il codice sorgente:
include!(concat!(env!("OUT_DIR"), "/hello.rs"));
Ciò rappresenta una sfida per Chooseg, in quanto gli output per ogni modulo vengono inseriti nella
propria directory out/
1. Non esiste un singolo OUT_DIR
in cui
le dipendenze generano il codice sorgente generato.
Per il codice della piattaforma, AOSP preferisce impacchettare il codice sorgente generato in un crate che può essere importato, per diversi motivi:
- Impedisci la collisione dei nomi dei file di origine generati.
- Riduci il codice boilerplate checked-in nell'intera struttura ad albero che richiede manutenzione. Qualsiasi boilerplate necessario per trasformare la compilazione dell'origine generata in una cassa può essere sottoposto a manutenzione centralizzata.
- 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.
Soong supporta ancora i pacchetti di terze parti senza modifiche se tutte le dipendenze delle origini 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 il codice sorgente generato possa essere trovato.
Tuttavia, per i motivi già descritti, è buona norma utilizzare questo meccanismo nel codice della piattaforma solo se assolutamente necessario.
-
Questo non presenta problemi per C/C++ e linguaggi simili, poiché il percorso del codice sorgente generato viene fornito direttamente al compilatore. ↩
-
Poiché
include!
funziona tramite inclusione di testo, potrebbe fare riferimento ai valori dello spazio dei nomi contenente, 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. ↩