Generatory źródeł

Na tej stronie znajdziesz ogólne informacje o tym, jak obsługiwany jest generowany kod źródłowy i jak można go używać w systemie kompilacji.

Wszystkie generatory źródeł oferują podobne funkcje systemu kompilacji. Trzy obsługiwane przez system kompilacji przypadki użycia generowania kodu źródłowego to generowanie połączeń C za pomocą bindgen, interfejsów AIDL i interfejsów protobuf.

Zbiór danych generowanych przez źródło

Każdy moduł Rust, który generuje kod źródłowy, może pełnić funkcję skrzynki – tak samo jak jeśli została określona jako rust_library. (Oznacza to, że można go zdefiniować jako we właściwościach rustlibs, rlibs i dylibs). Optymalne wykorzystanie w kodzie platformy jest wykorzystanie wygenerowanego źródła jako skrzynki. Mimo że Makro include! jest obsługiwane w przypadku wygenerowanego źródła. Jego głównym przeznaczeniem jest obsługują kod innej firmy, który znajduje się w domenie external/.

W niektórych przypadkach kod platformy nadal może używać wygenerowanego źródła za pomocą include!(), np. gdy używasz modułu genrule do wygenerowania źródła w unikalny sposób.

Użycie funkcji include!() do uwzględnienia wygenerowanego źródła

Użycie wygenerowanego źródła w formie klatki jest omówione w przykładach w każdym z poniższych. (odpowiedniej). W tej sekcji dowiesz się, jak odwoływać się do wygenerowanego źródła za pomocą makra include!(). Ten proces jest podobny w przypadku wszystkich źródeł generatorów prądu.

Warunek wstępny

Ten przykład opiera się na założeniu, że zdefiniujesz atrybut rust_bindgen (libbuzz_bindgen) i przejdź do instrukcji dołączania wygenerowanego źródła za korzystanie z makra include!(). Jeśli nie, przejdź do sekcji Defining a rust bindgen module (Definiowanie modułu Rust bindgen)libbuzz_bindgen, a potem wróć tutaj.

Pamiętaj, że te fragmenty pliku kompilacji dotyczą wszystkich generatorów źródeł.

Etapy uwzględniania wygenerowanego źródła

Utwórz obiekt 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) }
}

Powody, dla których tworzone są wygenerowane źródła

W przeciwieństwie do kompilatorów C/C++ rustc akceptuje tylko jeden plik źródłowy reprezentujący punkt wejścia do pliku binarnego lub biblioteki. Oczekuje się, że drzewo źródłowe jest tak sformatowane, aby można było automatycznie wykryć wszystkie wymagane pliki źródłowe. Oznacza to, że wygenerowane źródło należy umieścić w źródle drzewo lub za pomocą dyrektywy include w źródle:

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

Społeczność Rust opiera się na skryptach w usłudze build.rs i założeniach dotyczących w środowisku Cargo, aby obejść z tą różnicą. Podczas kompilacji polecenie cargo ustawia zmienną środowiskową OUT_DIR w których skrypt build.rs ma umieścić wygenerowany kod źródłowy. Użyj następujące polecenie, aby dołączyć kod źródłowy:

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

Stanowi to wyzwanie dla Soong, ponieważ wyniki każdego modułu znajdują się własnego katalogu out/1. Nie ma ani jednego OUT_DIR, w którym zależności zwracają wygenerowane źródło.

W przypadku kodu platformy AOSP preferuje pakowanie wygenerowanego źródła w skrzynkę, którą można zaimportować, z kilku powodów:

  • zapobieganie kolizjom nazw wygenerowanych plików źródłowych;
  • Ogranicz powtarzalny kod. i zameldował(a) się w całym drzewie, które wymaga konserwacji. Każdy szablon, który jest wymagany do skompilowania wygenerowanego źródła w crate, może być zarządzany centralnie.
  • Unikaj ukrytych2 interakcji między wygenerowanym kodem a otaczającą go skrzynką.
  • Zmniejsz obciążenie pamięci i dysku, dynamicznie łącząc często używane źródła wygenerowane.

W rezultacie wszystkie typy modułów generowania źródła Rust na Androidzie generują kod. które można skompilować i wykorzystać jako skrzynię. Soong nadal obsługuje zewnętrzne paczki bez modyfikacji, jeśli wszystkie wygenerowane zależności źródłowe modułu zostaną skopiowane do pojedynczej katalogu na moduł, podobnie jak w przypadku Cargo. W takich przypadkach Soong ustawia zmienną środowiskową OUT_DIR na ten katalog podczas kompilowania modułu, aby można było znaleźć wygenerowane źródło. Ze względu na już podane powody zalecamy jednak używanie tego mechanizmu w kodzie platformy tylko wtedy, gdy jest to absolutnie konieczne.


  1. Nie powoduje to żadnych problemów w przypadku języka C/C++ i podobnych języków, że ścieżka do wygenerowanego źródła jest przekazywana bezpośrednio do kompilatora.

  2. Funkcja include! działa na podstawie uwzględniania tekstowego, dlatego może odwoływać się do wartości z otaczającej ją przestrzeni nazw, zmień ją lub użyj konstruktorów takich jak #![foo]. Utrzymanie tych niejawnych interakcji może być trudne. Zawsze stosuj gdy wymagana jest interakcja z pozostałymi częściami skrzyni.