源生成器

此页面提供了有关如何支持生成的源以及如何在构建系统中使用它的高级视图。

所有源代码生成器都提供类似的构建系统功能。三个构建系统支持的源代码生成用例是使用 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 的其余部分交互时,总是更喜欢宏。