Questa pagina fornisce una visione di alto livello del supporto del codice sorgente generato e di come può essere utilizzato 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 specifica. 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 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 che tutti i file di origine richiesti possano essere rilevati automaticamente. Ciò significa che il codice sorgente generato deve essere inserito nell'albero
dell'origine o fornito tramite una direttiva include nel codice sorgente:
include!("/path/to/hello.rs");
La community di Rust si basa su script build.rs
e su assunzioni sull'ambiente di compilazione 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
dovrebbero 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 ciascun modulo vengono posizionati nella rispettiva 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 una cassetta che può essere importata, 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 le interazioni implicite2 tra il codice generato e la cassetta circostante.
- Riduci la pressione su memoria e disco collegando dinamicamente 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 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. ↩