Generatory źródeł

Ta strona zapewnia ogólny widok sposobu obsługi wygenerowanego źródła i sposobu jego wykorzystania w systemie kompilacji.

Wszystkie generatory źródeł zapewniają podobną funkcjonalność systemu kompilacji. Trzy przypadki użycia generacji źródła obsługiwane przez system kompilacji generują powiązania C przy użyciu interfejsów bindgen, AIDL i interfejsów protobuf.

Skrzynie z wygenerowanego źródła

Każdy moduł Rusta generujący kod źródłowy może służyć jako skrzynia, 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 wzorcem użycia kodu platformy jest wykorzystanie wygenerowanego źródła jako skrzynki. Chociaż include! makro jest obsługiwane dla wygenerowanego źródła, jego głównym celem jest obsługa kodu strony trzeciej znajdującego się w external/ .

Istnieją przypadki, w których kod platformy może nadal używać wygenerowanego źródła poprzez makro include!() , na przykład gdy używasz modułu genrule do generowania źródła w unikalny sposób.

Użyj include!(), aby uwzględnić wygenerowane źródło

Użycie wygenerowanego źródła jako skrzynki jest opisane w przykładach na każdej konkretnej (odpowiedniej) stronie modułu. W tej sekcji pokazano, jak odwoływać się do wygenerowanego źródła za pomocą include!() . Należy pamiętać, że ten proces jest podobny dla wszystkich generatorów źródła.

Warunek wstępny

Ten przykład opiera się na założeniu, że zdefiniowano moduł rust_bindgen ( libbuzz_bindgen ) i można przejść do kroków włączania wygenerowanego źródła do użycia makra include!() . Jeśli jeszcze tego nie zrobiłeś, przejdź do Definiowanie modułu rdzy , utwórz libbuzz_bindgen i wróć tutaj.

Należy pamiętać, że fragmenty tego pliku kompilacji mają zastosowanie do wszystkich generatorów źródeł.

Kroki włączania wygenerowanego źródła

Utwórz plik external/rust/hello_bindgen/Android.bp z następującą 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 następującą 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 external/rust/hello_bindgen/src/lib.rs z następującą zawartością:

mod bindings;

fn main() {
    let mut x = bindings::foo { x: 2 };
    unsafe { bindings::fizz(1, &mut x as *mut bindings::foo) }
}

Po co skrzynie dla wygenerowanego ź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, że drzewo źródłowe będzie skonstruowane w taki sposób, aby wszystkie wymagane pliki źródłowe mogły zostać automatycznie wykryte. Oznacza to, że wygenerowane źródło musi zostać umieszczone w drzewie źródeł lub dostarczone poprzez dyrektywę include w źródle:

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

Aby poradzić sobie z tą różnicą, społeczność Rust polega na skryptach build.rs i założeniach dotyczących środowiska kompilacji Cargo. Podczas kompilacji polecenie cargo ustawia zmienną środowiskową OUT_DIR , w której skrypty build.rs mają umieszczać wygenerowany kod źródłowy. Użyj następującego polecenia, aby dołączyć kod źródłowy:

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

Stanowi to wyzwanie dla Soonga, ponieważ wyjścia każdego modułu są umieszczane w ich własnym katalogu out/ 1 . Nie ma ani jednego OUT_DIR , w którym zależności wyprowadzają wygenerowane źródło.

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

  • Zapobiegaj kolizjom wygenerowanych nazw plików źródłowych.
  • Zmniejsz liczbę wpisanych kodów standardowych w całym drzewie wymagającym konserwacji. Dowolny schemat wymagany do skompilowania wygenerowanego źródła w skrzynkę może być zarządzany centralnie.
  • Unikaj ukrytych interakcji między wygenerowanym kodem a otaczającą skrzynią.
  • Zmniejsz obciążenie pamięci i dysku poprzez dynamiczne łączenie często używanych generowanych źródeł.

W rezultacie wszystkie typy modułów generowania źródeł Rust w systemie Android generują kod, który można skompilować i wykorzystać jako skrzynkę . Soong nadal obsługuje skrzynki innych firm bez modyfikacji, jeśli wszystkie wygenerowane zależności źródłowe modułu zostaną skopiowane do jednego katalogu dla poszczególnych modułów, podobnie jak Cargo. W takich przypadkach Soong ustawia zmienną środowiskową OUT_DIR na ten katalog podczas kompilacji modułu, aby można było znaleźć wygenerowane źródło. Jednak z już opisanych powodów najlepszą praktyką jest używanie tego mechanizmu w kodzie platformy tylko wtedy, gdy jest to absolutnie konieczne.


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

  2. Ponieważ include! działa poprzez włączenie tekstu, może odwoływać się do wartości z otaczającej przestrzeni nazw, modyfikować przestrzeń nazw lub używać konstrukcji takich jak #![foo] . Utrzymanie tych ukrytych interakcji może być trudne. Zawsze preferuj makra, gdy naprawdę wymagana jest interakcja z resztą skrzynki.

,

Ta strona zapewnia ogólny widok sposobu obsługi wygenerowanego źródła i sposobu jego wykorzystania w systemie kompilacji.

Wszystkie generatory źródeł zapewniają podobną funkcjonalność systemu kompilacji. Trzy przypadki użycia generowania źródła obsługiwane przez system kompilacji generują powiązania C przy użyciu interfejsów bindgen, AIDL i interfejsów protobuf.

Skrzynie z wygenerowanego źródła

Każdy moduł Rusta generujący kod źródłowy może służyć jako skrzynia, 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 wzorcem użycia kodu platformy jest wykorzystanie wygenerowanego źródła jako skrzynki. Chociaż include! makro jest obsługiwane dla wygenerowanego źródła, jego głównym celem jest obsługa kodu strony trzeciej znajdującego się w external/ .

Istnieją przypadki, w których kod platformy może nadal używać wygenerowanego źródła poprzez makro include!() , na przykład gdy używasz modułu genrule do generowania źródła w unikalny sposób.

Użyj include!(), aby uwzględnić wygenerowane źródło

Użycie wygenerowanego źródła jako skrzynki jest opisane w przykładach na każdej konkretnej (odpowiedniej) stronie modułu. W tej sekcji pokazano, jak odwoływać się do wygenerowanego źródła za pomocą include!() . Należy pamiętać, że ten proces jest podobny dla wszystkich generatorów źródła.

Warunek wstępny

Ten przykład opiera się na założeniu, że zdefiniowano moduł rust_bindgen ( libbuzz_bindgen ) i można przejść do kroków włączania wygenerowanego źródła do użycia makra include!() . Jeśli jeszcze tego nie zrobiłeś, przejdź do Definiowanie modułu rdzy , utwórz libbuzz_bindgen i wróć tutaj.

Należy pamiętać, że fragmenty tego pliku kompilacji mają zastosowanie do wszystkich generatorów źródeł.

Kroki włączania wygenerowanego źródła

Utwórz plik external/rust/hello_bindgen/Android.bp z następującą 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 następującą 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 external/rust/hello_bindgen/src/lib.rs z następującą zawartością:

mod bindings;

fn main() {
    let mut x = bindings::foo { x: 2 };
    unsafe { bindings::fizz(1, &mut x as *mut bindings::foo) }
}

Po co skrzynie dla wygenerowanego ź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, że drzewo źródłowe będzie skonstruowane w taki sposób, aby wszystkie wymagane pliki źródłowe mogły zostać automatycznie wykryte. Oznacza to, że wygenerowane źródło musi zostać umieszczone w drzewie źródeł lub dostarczone poprzez dyrektywę include w źródle:

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

Aby poradzić sobie z tą różnicą, społeczność Rust polega na skryptach build.rs i założeniach dotyczących środowiska kompilacji Cargo. Podczas kompilacji polecenie cargo ustawia zmienną środowiskową OUT_DIR , w której skrypty build.rs mają umieszczać wygenerowany kod źródłowy. Użyj następującego polecenia, aby dołączyć kod źródłowy:

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

Stanowi to wyzwanie dla Soonga, ponieważ wyjścia każdego modułu są umieszczane w ich własnym katalogu out/ 1 . Nie ma ani jednego OUT_DIR , w którym zależności wyprowadzają wygenerowane źródło.

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

  • Zapobiegaj kolizjom wygenerowanych nazw plików źródłowych.
  • Zmniejsz liczbę wpisanych kodów standardowych w całym drzewie wymagającym konserwacji. Dowolny schemat wymagany do skompilowania wygenerowanego źródła w skrzynkę może być zarządzany centralnie.
  • Unikaj ukrytych interakcji między wygenerowanym kodem a otaczającą skrzynią.
  • Zmniejsz obciążenie pamięci i dysku poprzez dynamiczne łączenie często używanych generowanych źródeł.

W rezultacie wszystkie typy modułów generowania źródeł Rust w systemie Android generują kod, który można skompilować i wykorzystać jako skrzynkę . Soong nadal obsługuje skrzynki innych firm bez modyfikacji, jeśli wszystkie wygenerowane zależności źródłowe modułu zostaną skopiowane do jednego katalogu dla poszczególnych modułów, podobnie jak Cargo. W takich przypadkach Soong ustawia zmienną środowiskową OUT_DIR na ten katalog podczas kompilacji modułu, aby można było znaleźć wygenerowane źródło. Jednak z już opisanych powodów najlepszą praktyką jest używanie tego mechanizmu w kodzie platformy tylko wtedy, gdy jest to absolutnie konieczne.


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

  2. Ponieważ include! działa poprzez włączenie tekstu, może odwoływać się do wartości z otaczającej przestrzeni nazw, modyfikować przestrzeń nazw lub używać konstrukcji takich jak #![foo] . Utrzymanie tych ukrytych interakcji może być trudne. Zawsze preferuj makra, gdy naprawdę wymagana jest interakcja z resztą skrzynki.

,

Ta strona zapewnia ogólny widok sposobu obsługi wygenerowanego źródła i sposobu jego wykorzystania w systemie kompilacji.

Wszystkie generatory źródeł zapewniają podobną funkcjonalność systemu kompilacji. Trzy przypadki użycia generowania źródła obsługiwane przez system kompilacji generują powiązania C przy użyciu interfejsów bindgen, AIDL i interfejsów protobuf.

Skrzynie z wygenerowanego źródła

Każdy moduł Rusta generujący kod źródłowy może służyć jako skrzynia, 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 wzorcem użycia kodu platformy jest wykorzystanie wygenerowanego źródła jako skrzynki. Chociaż include! makro jest obsługiwane dla wygenerowanego źródła, jego głównym celem jest obsługa kodu strony trzeciej znajdującego się w external/ .

Istnieją przypadki, w których kod platformy może nadal używać wygenerowanego źródła poprzez makro include!() , na przykład gdy używasz modułu genrule do generowania źródła w unikalny sposób.

Użyj include!(), aby uwzględnić wygenerowane źródło

Użycie wygenerowanego źródła jako skrzynki jest opisane w przykładach na każdej konkretnej (odpowiedniej) stronie modułu. W tej sekcji pokazano, jak odwoływać się do wygenerowanego źródła za pomocą include!() . Należy pamiętać, że ten proces jest podobny dla wszystkich generatorów źródła.

Warunek wstępny

Ten przykład opiera się na założeniu, że zdefiniowano moduł rust_bindgen ( libbuzz_bindgen ) i można przejść do kroków włączania wygenerowanego źródła do użycia makra include!() . Jeśli jeszcze tego nie zrobiłeś, przejdź do Definiowanie modułu rdzy , utwórz libbuzz_bindgen i wróć tutaj.

Należy pamiętać, że fragmenty tego pliku kompilacji mają zastosowanie do wszystkich generatorów źródeł.

Kroki włączania wygenerowanego źródła

Utwórz plik external/rust/hello_bindgen/Android.bp z następującą 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 następującą 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 external/rust/hello_bindgen/src/lib.rs z następującą zawartością:

mod bindings;

fn main() {
    let mut x = bindings::foo { x: 2 };
    unsafe { bindings::fizz(1, &mut x as *mut bindings::foo) }
}

Po co skrzynie dla wygenerowanego ź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, że drzewo źródłowe będzie skonstruowane w taki sposób, aby wszystkie wymagane pliki źródłowe mogły zostać automatycznie wykryte. Oznacza to, że wygenerowane źródło musi zostać umieszczone w drzewie źródeł lub dostarczone poprzez dyrektywę include w źródle:

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

Aby poradzić sobie z tą różnicą, społeczność Rust polega na skryptach build.rs i założeniach dotyczących środowiska kompilacji Cargo. Podczas kompilacji polecenie cargo ustawia zmienną środowiskową OUT_DIR , w której skrypty build.rs mają umieszczać wygenerowany kod źródłowy. Użyj następującego polecenia, aby dołączyć kod źródłowy:

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

Stanowi to wyzwanie dla Soonga, ponieważ wyjścia każdego modułu są umieszczane w ich własnym katalogu out/ 1 . Nie ma ani jednego OUT_DIR , w którym zależności wyprowadzają wygenerowane źródło.

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

  • Zapobiegaj kolizjom wygenerowanych nazw plików źródłowych.
  • Zmniejsz liczbę wpisanych kodów standardowych w całym drzewie wymagającym konserwacji. Dowolny schemat wymagany do skompilowania wygenerowanego źródła w skrzynkę może być zarządzany centralnie.
  • Unikaj ukrytych interakcji między wygenerowanym kodem a otaczającą skrzynią.
  • Zmniejsz obciążenie pamięci i dysku poprzez dynamiczne łączenie często używanych generowanych źródeł.

W rezultacie wszystkie typy modułów generowania źródeł Rust w systemie Android generują kod, który można skompilować i wykorzystać jako skrzynkę . Soong nadal obsługuje skrzynki innych firm bez modyfikacji, jeśli wszystkie wygenerowane zależności źródłowe modułu zostaną skopiowane do jednego katalogu dla poszczególnych modułów, podobnie jak Cargo. W takich przypadkach Soong ustawia zmienną środowiskową OUT_DIR na ten katalog podczas kompilacji modułu, aby można było znaleźć wygenerowane źródło. Jednak z już opisanych powodów najlepszą praktyką jest używanie tego mechanizmu w kodzie platformy tylko wtedy, gdy jest to absolutnie konieczne.


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

  2. Ponieważ include! działa poprzez włączenie tekstu, może odwoływać się do wartości z otaczającej przestrzeni nazw, modyfikować przestrzeń nazw lub używać konstrukcji takich jak #![foo] . Utrzymanie tych ukrytych interakcji może być trudne. Zawsze preferuj makra, gdy naprawdę wymagana jest interakcja z resztą skrzynki.