源生成器

此頁面提供了有關如何支持生成的源以及如何在構建系統中使用它的高級視圖。

所有源代碼生成器都提供類似的構建系統功能。三個構建系統支持的源代碼生成用例是使用 bindgen、AIDL 接口和 protobuf 接口生成 C 綁定。

來自生成源的板條箱

每個生成源代碼的 Rust 模塊都可以用作 crate,就像它被定義為rust_library 。 (這意味著它可以定義為rustlibsrlibsdylibs屬性中的依賴項。)平台代碼的最佳使用模式是使用生成的源代碼作為 crate。雖然include!生成的源代碼支持宏,其主要目的是支持駐留在external/中的第三方代碼。

在某些情況下,平台代碼可能仍會通過include!()宏使用生成的源代碼,例如當您使用genrule模塊以獨特的方式生成源代碼時。

使用 include!() 包含生成的源代碼

每個特定(相應)模塊頁面中的示例都涵蓋了使用生成的源代碼作為 crate。本節介紹如何通過include!()宏引用生成的源代碼。請注意,此過程對於所有源生成器都是相似的。

先決條件

此示例基於您已定義rust_bindgen模塊 ( libbuzz_bindgen ) 的假設,並且可以繼續執行步驟以包含生成的源代碼以使用include!()宏。如果還沒有,請轉到定義 rust bindgen 模塊,創建libbuzz_bindgen ,然後返回此處。

請注意,此構建文件部分適用於所有源生成器。

包含生成源的步驟

使用以下內容創建external/rust/hello_bindgen/Android.bp

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",
    ],
}

使用以下內容創建external/rust/hello_bindgen/src/bindings.rs

#![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"));

使用以下內容創建external/rust/hello_bindgen/src/lib.rs

mod bindings;

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

為什麼要為生成的源創建板條箱

與 C/C++ 編譯器不同, rustc只接受代表二進製或庫入口點的單個源文件。它期望源樹的結構可以自動發現所有需要的源文件。這意味著生成的源必須要么放在源樹中,要么通過源中的包含指令提供:

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

Rust 社區依賴build.rs腳本和關於 Cargo 構建環境的假設來處理這種差異。當它構建時, cargo命令設置一個OUT_DIR環境變量build.rs腳本應該將生成的源代碼放入其中。使用以下命令包含源代碼:

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

這對 Soong 提出了挑戰,因為每個模塊的輸出都放置在它們自己的out/目錄1中。沒有一個OUT_DIR依賴項輸出其生成的源。

對於平台代碼,AOSP 更喜歡將生成的源代碼打包到可以導入的 crate 中,原因如下:

  • 防止生成的源文件名發生衝突。
  • 減少需要維護的整個樹中的樣板代碼簽入。將生成的源代碼編譯到 crate 中所需的任何樣板文件都可以集中維護。
  • 避免生成的代碼和周圍的 crate 之間的隱式2交互。
  • 通過動態鏈接常用的生成源來減少內存和磁盤的壓力。

因此,所有 Android 的 Rust 源代碼生成模塊類型都會生成可以編譯並用作crate 的代碼。如果一個模塊的所有生成的源依賴項都複製到單個每個模塊的目錄中,Soong 仍然支持第三方 crate 而無需修改,類似於 Cargo。在這種情況下,Soong 在編譯模塊時將OUT_DIR環境變量設置為該目錄,因此可以找到生成的源。然而,由於已經描述的原因,最好只在絕對必要時在平台代碼中使用這種機制。


  1. 這對 C/C++ 和類似語言沒有任何問題,因為生成源的路徑直接提供給編譯器。

  2. 既然include!通過文本包含工作,它可能會引用封閉命名空間中的值、修改命名空間或使用#![foo]之類的結構。這些隱式交互可能難以維護。當確實需要與 crate 的其餘部分交互時,總是更喜歡宏。