Na tej stronie znajdziesz ogólne informacje o tym, jak obsługiwane są wygenerowane źródła i jak można ich używać w systemie kompilacji.
Wszystkie generatory kodu źródłowego zapewniają podobne funkcje systemu kompilacji. 3 obsługiwane przypadki użycia generowania kodu źródłowego w systemie kompilacji to generowanie powiązań C za pomocą narzędzia bindgen, interfejsów AIDL i interfejsów protobuf.
Crates from generated source
Każdy moduł Rusta, który generuje kod źródłowy, może być używany jako pakiet, dokładnie tak, jakby był zdefiniowany jako rust_library
. (Oznacza to, że można go zdefiniować jako zależność we właściwościach rustlibs
, rlibs
i dylibs
). Najlepszym sposobem wykorzystania kodu platformy jest używanie wygenerowanego źródła jako pakietu. Chociaż makro include!
jest obsługiwane w przypadku wygenerowanego źródła, jego głównym celem jest obsługa kodu firmy zewnętrznej znajdującego się w external/
.
W niektórych przypadkach kod platformy może nadal używać wygenerowanego źródła za pomocą makra include!()
, np. gdy używasz modułu genrule
do generowania źródła w unikalny sposób.
Użyj include!(), aby uwzględnić wygenerowany kod źródłowy
Korzystanie z wygenerowanego źródła jako pakietu jest opisane w przykładach na stronach poszczególnych modułów. W tej sekcji dowiesz się, jak odwoływać się do wygenerowanego źródła za pomocą makra include!()
. Pamiętaj, że ten proces jest podobny w przypadku wszystkich generatorów źródeł.
Warunek wstępny
Ten przykład zakłada, że masz zdefiniowany rust_bindgen
moduł (libbuzz_bindgen
) i możesz przejść do kroków dotyczących uwzględniania wygenerowanego źródła w celu użycia makra include!()
. Jeśli nie, przejdź do sekcji Definiowanie modułu rust bindgen, utwórz libbuzz_bindgen
, a potem wróć tutaj.
Pamiętaj, że części tego pliku kompilacji mają zastosowanie do wszystkich generatorów kodu źródłowego.
Instrukcje dotyczące uwzględniania wygenerowanego źródła
Utwórz plik external/rust/hello_bindgen/Android.bp
z tą zawartością:
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",
],
}
Utwórz plik external/rust/hello_bindgen/src/bindings.rs
z tą zawartością:
#![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"));
Utwórz plik external/rust/hello_bindgen/src/lib.rs
z tą zawartością:
mod bindings;
fn main() {
let mut x = bindings::foo { x: 2 };
unsafe { bindings::fizz(1, &mut x as *mut bindings::foo) }
}
Dlaczego skrzynki dla wygenerowanego źródła
W przeciwieństwie do kompilatorów C/C++ kompilator rustc
akceptuje tylko jeden plik źródłowy reprezentujący punkt wejścia do pliku binarnego lub biblioteki. Oczekuje, że drzewo źródłowe jest tak skonstruowane, aby wszystkie wymagane pliki źródłowe można było automatycznie wykryć. Oznacza to, że wygenerowane źródło musi znajdować się w drzewie źródłowym lub być udostępniane za pomocą dyrektywy include w źródle:
include!("/path/to/hello.rs");
Społeczność Rust polega na skryptach build.rs
i założeniach dotyczących środowiska kompilacji Cargo, aby radzić sobie z tą różnicą.
Podczas kompilacji polecenie cargo
ustawia OUT_DIR
zmienną środowiskową, w której skrypty build.rs
mają umieszczać wygenerowany kod źródłowy. Aby uwzględnić kod źródłowy, użyj tego polecenia:
include!(concat!(env!("OUT_DIR"), "/hello.rs"));
Stanowi to wyzwanie dla Soong, ponieważ dane wyjściowe każdego modułu są umieszczane w osobnym out/
katalogu1. Nie ma pojedynczego OUT_DIR
, w którym zależności generują swoje źródło.
W przypadku kodu platformy AOSP preferuje pakowanie wygenerowanego źródła w pakiet, który można zaimportować, z kilku powodów:
- Zapobiega kolizjom wygenerowanych nazw plików źródłowych.
- Ogranicz powtarzający się kod w całym drzewie, który wymaga konserwacji. Wszelkie standardowe fragmenty kodu, które są wymagane do skompilowania wygenerowanego kodu źródłowego do pakietu, można utrzymywać centralnie.
- Unikaj niejawnych2 interakcji między wygenerowanym kodem a otaczającym go pakietem.
- Zmniejsz obciążenie pamięci i dysku, dynamicznie łącząc często używane wygenerowane źródła.
Dzięki temu wszystkie typy modułów generowania kodu źródłowego w Rust w Androidzie tworzą kod, który można skompilować i używać jako pakietu.
Soong nadal obsługuje zewnętrzne pakiety bez modyfikacji, jeśli wszystkie wygenerowane zależności źródłowe modułu zostaną skopiowane do jednego katalogu na moduł, podobnie jak w przypadku Cargo. W takich przypadkach Soong ustawia zmienną środowiskową OUT_DIR
na ten katalog podczas kompilowania modułu, dzięki czemu można znaleźć wygenerowany kod źródłowy.
Z opisanych już powodów zalecamy jednak używanie tego mechanizmu w kodzie platformy tylko wtedy, gdy jest to absolutnie konieczne.
-
Nie stanowi to problemu w przypadku języków C/C++ i podobnych, ponieważ ścieżka do wygenerowanego kodu źródłowego jest przekazywana bezpośrednio do kompilatora. ↩
-
Ponieważ funkcja
include!
działa na zasadzie wstawiania tekstu, może odwoływać się do wartości z przestrzeni nazw, która ją zawiera, modyfikować tę przestrzeń nazw lub używać konstrukcji takich jak#![foo]
. Utrzymanie tych niejawnych interakcji może być trudne. Zawsze preferuj makra, gdy interakcja z resztą pakietu jest naprawdę wymagana. ↩