Questa pagina fornisce una panoramica di alto livello di come viene supportata l'origine generata e di come può essere utilizzata nel sistema di compilazione.
Tutti i generatori di origine forniscono funzionalità di sistema di compilazione simili. I tre casi d'uso di generazione di codice sorgente supportati dal sistema di compilazione sono la generazione di binding C utilizzando bindgen, interfacce AIDL e interfacce protobuf.
Crates from generated source
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 miglior pattern
di utilizzo del 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 si trova 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.
Utilizza include!() per includere l'origine generata
L'utilizzo dell'origine generata come cassa è 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 questo processo è simile per tutti i generatori di fonti.
Prerequisito
Questo esempio si basa sul presupposto che tu abbia definito un modulo rust_bindgen
(libbuzz_bindgen
) e che tu possa procedere ai passaggi per includere l'origine generata
per utilizzare la macroinclude!()
. In caso contrario, vai a Definizione di un modulo rust bindgen,
crea libbuzz_bindgen
e poi torna qui.
Tieni presente che le parti del file di build sono applicabili a tutti i generatori di origine.
Passaggi per includere la fonte 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 l'origine generata
A differenza dei compilatori C/C++, rustc
accetta solo un singolo file sorgente
che rappresenta un punto di ingresso per un binario o una libreria. Presuppone che la struttura dell'albero delle origini
sia tale che tutti i file di origine richiesti possano essere rilevati automaticamente. Ciò significa che l'origine generata deve essere inserita nell'albero delle origini o fornita tramite una direttiva include nell'origine:
include!("/path/to/hello.rs");
La community Rust si basa su script build.rs
e su presupposti relativi all'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
devono inserire il codice sorgente generato. Utilizza il comando seguente per includere il codice sorgente:
include!(concat!(env!("OUT_DIR"), "/hello.rs"));
Ciò rappresenta una sfida per Soong, in quanto gli output di ogni modulo vengono inseriti nella
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 inserire il codice sorgente generato in una crate che può essere importata, per diversi motivi:
- Impedisci la collisione dei nomi dei file sorgente generati.
- Riduci il codice boilerplate estratto in tutta la struttura ad albero che richiede manutenzione. Qualsiasi boilerplate necessario per compilare il codice sorgente generato in una crate può essere gestito centralmente.
- Evita interazioni implicite2 tra il codice generato e la crate circostante.
- Riduce 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 Rust di Android producono codice
che può essere compilato e utilizzato come crate.
Soong supporta ancora le 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 sia possibile trovare l'origine generata.
Tuttavia, per i motivi già descritti, è una best practice utilizzare
questo meccanismo nel codice della piattaforma solo quando è assolutamente necessario.
-
Ciò 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 testuale, potrebbe fare riferimento a valori dello spazio dei nomi contenitore, modificare lo spazio dei nomi o utilizzare costrutti come#![foo]
. Queste interazioni implicite possono essere difficili da mantenere. Preferisci sempre le macro quando l'interazione con il resto della crate è davvero necessaria. ↩