Android 构建集成:在 Soong 中用 rust_binary 构建 CXX Rust 桥接模块
【免费下载链接】comprehensive-rustThis is the Rust course used by the Android team at Google. It provides you the material to quickly teach Rust.项目地址: https://gitcode.com/GitHub_Trending/co/comprehensive-rust
本篇技术指南基于 comprehensive-rust 课程中《Building in Android》章节,讲解在 Android 的 Soong 构建系统中如何把 CXX 生成的 Rust↔C++ 桥接代码编译为可运行的rust_binary。你将掌握rust_binary模块中rustlibs与static_libs的职责分工、配合genrule生成 C++ 桥接头文件/源文件的标准流程,并得到一个可直接参考的完整Android.bp实例,为在 Android 平台上安全地双向调用 Rust 与 C++ 代码打下构建层面的基础。
CXX 与 Android 构建的整体关系
With C++ 章节指出,Android 平台上 Rust 与 C++ 的互操作依靠 CXX crate(课程在 third_party/cxx 目录中内置了该 crate 的副本与示例)。CXX 的核心机制是"桥接模块"(bridge module):在 Rust 源码中通过#[cxx::bridge]属性宏声明一个ffi模块,描述两个语言互相暴露的函数签名与类型,然后由 CXX 的代码生成器分别产出 Rust 侧与 C++ 侧的胶水代码,实现类型安全、内存安全的双向调用。
在 Android 的构建体系(Soong,基于 Blueprint/Android.bp文件)中,这套流程需要三个层面的配合:
- Rust 侧桥接声明:由
#[cxx::bridge]模块给出(详见 The Bridge Module); - C++ 侧胶水代码生成:由
genrule调用cxxbridge工具生成(详见 android-cpp-genrules.md); - 最终可执行目标:由
rust_binary把 Rust 代码、CXX 运行库(libcxx)与编译好的 C++ 静态库链接到一起——这正是本文的核心文档 android-build-rust.md 所演示的内容。
核心模块:一个最小可用的 rust_binary 定义
原文档给出的最小构建单元如下(JavaScript 语法即 Soong 模块定义语言):
rust_binary { name: "cxx_test", srcs: ["lib.rs"], rustlibs: ["libcxx"], static_libs: ["libcxx_test_cpp"], }逐字段拆解其含义与作用:
name: "cxx_test":模块名,最终生成的二进制产物名即cxx_test,也是其他模块依赖它的标识;srcs: ["lib.rs"]:Rust 源文件,其中必须包含用#[cxx::bridge]标注的桥接模块(通常放在ffi子模块中,参见 The Bridge Module);rustlibs: ["libcxx"]:链接 CXX 的 Rust 侧运行库。libcxx是 Android 平台预置的 CXX crate(即cxx库),它为桥接代码提供运行时支持(内存布局、类型转换、异常处理等)。没有它,#[cxx::bridge]展开出的 Rust 代码将无法编译;static_libs: ["libcxx_test_cpp"]:链接一个cc_library_static类型的 C++ 静态库。该库包含桥接所需的 C++ 侧实现(例如BlobstoreClient的 C++ 类实现)以及由cxxbridge生成的桥接源码。
一句话概括构建链路:Rust 代码(lib.rs)+ CXX 运行库(libcxx)+ 已生成的 C++ 桥接代码与业务实现(cc_library_static)→ 链接成单一可执行文件。
前置步骤:用 genrule 生成 C++ 桥接胶水
rust_binary依赖的cc_library_static并非空手而来。CXX 需要为 C++ 侧也生成一份匹配桥接声明的头文件和源文件,这一步由 android-cpp-genrules.md 中的两个genrule完成:
// Generate a C++ header containing the C++ bindings // to the Rust exported functions in lib.rs. genrule { name: "libcxx_test_bridge_header", tools: ["cxxbridge"], cmd: "$(location cxxbridge) $(in) --header > $(out)", srcs: ["lib.rs"], out: ["lib.rs.h"], } // Generate the C++ code that Rust calls into. genrule { name: "libcxx_test_bridge_code", tools: ["cxxbridge"], cmd: "$(location cxxbridge) $(in) > $(out)", srcs: ["lib.rs"], out: ["lib.rs.cc"], }要点说明:
cxxbridge是 CXX 提供的独立命令行工具,在 Android 平台被内置为 Soong tool,可直接通过tools: ["cxxbridge"]引用;- 第一个
genrule追加--header参数,产出 C++ 头文件(声明 C++ 侧接口,供 C++ 业务代码 include);第二个genrule不带该参数,产出 C++ 源文件(实现 Rust 调进来的桩代码); - 命名约定:若 Rust 源文件为
lib.rs,则头文件为lib.rs.h、源文件为lib.rs.cc。这一约定并非强制,但 Android 构建体系中的后续环节会按此约定消费产物; - 这两个产物通过
cc_library_static的generated_headers与generated_sources属性注入,见下文的完整实例。
完整实例:仓库内置的 blobstore 项目
课程仓库在 third_party/cxx/blobstore/Android.bp 中提供了一个可直接对照的完整示例,把上述两节内容串成一条完整的构建链:
cc_library_static { name: "blobstore_cpp", srcs: ["src/blobstore.cc"], generated_headers: [ "cxx-bridge-header", "blobstore_bridge_header" ], generated_sources: ["blobstore_bridge_code"], } genrule { name: "blobstore_bridge_header", tools: ["cxxbridge"], cmd: "$(location cxxbridge) $(in) --header > $(out)", srcs: ["src/main.rs"], out: ["main.rs.h"], } genrule { name: "blobstore_bridge_code", tools: ["cxxbridge"], cmd: "$(location cxxbridge) $(in) > $(out)", srcs: ["src/main.rs"], out: ["main.rs.cc"], } rust_binary { name: "blobstore", srcs: ["src/main.rs"], rustlibs: ["libcxx"], static_libs: ["blobstore_cpp"], }可以看到它与课程文档中的最小示例完全同构,只是多了一层真实的业务实现:
genrule输入的是src/main.rs,因为该示例的桥接模块声明在 third_party/cxx/blobstore/src/main.rs 中。产物按命名约定为main.rs.h与main.rs.cc;cc_library_static(blobstore_cpp)的srcs是 src/blobstore.cc(C++ 业务实现,如BlobstoreClient类),并通过generated_headers/generated_sources把两个genrule的产物编入自身。注意generated_headers中额外包含的cxx-bridge-header是 CXX 所需的公共头,而blobstore_bridge_header才是本项目桥接生成的头;rust_binary(blobstore)通过rustlibs: ["libcxx"]获得 CXX Rust 运行库、通过static_libs: ["blobstore_cpp"]获得 C++ 静态库(含生成代码与业务实现),三者在链接期合并为最终可执行文件。
桥接声明的样子
third_party/cxx/blobstore/src/main.rs 中的桥接模块演示了三种典型的桥接内容,读者可参照其结构编写自己的lib.rs:
#[allow(unsafe_op_in_unsafe_fn)] #[cxx::bridge(namespace = "org::blobstore")] mod ffi { // 共享结构体:两个语言都可见的字段 struct BlobMetadata { size: usize, tags: Vec<String>, } // Rust 侧暴露给 C++ 的类型与签名 extern "Rust" { type MultiBuf; fn next_chunk(buf: &mut MultiBuf) -> &[u8]; } // C++ 侧暴露给 Rust 的类型与签名 unsafe extern "C++" { include!("include/blobstore.h"); type BlobstoreClient; fn new_blobstore_client() -> UniquePtr<BlobstoreClient>; fn put(self: Pin<&mut BlobstoreClient>, parts: &mut MultiBuf) -> u64; fn tag(self: Pin<&mut BlobstoreClient>, blobid: u64, tag: &str); fn metadata(&self, blobid: u64) -> BlobMetadata; } }对照 Rust Bridge Declarations 与 Generated C++ 章节可以理解:extern "Rust"段声明引用父模块作用域内的 Rust 类型/函数,CXX 据此生成对应的 C++ 头文件声明(生成头与 Rust 源文件同路径、扩展名为.rs.h);unsafe extern "C++"段则通过include!引入 C++ 头,并把BlobstoreClient等 C++ 类型以UniquePtr、Pin<&mut>等方式安全地暴露给 Rust 侧调用。
构建时的类型映射要点
在编写桥接签名时,应遵循 Additional Types 章节给出的映射表,避免使用无法直接跨语言传递的类型:
| Rust 类型 | C++ 类型 |
|---|---|
String | rust::String |
&str | rust::Str |
CxxString | std::string |
&[T]/&mut [T] | rust::Slice |
Box<T> | rust::Box<T> |
UniquePtr<T> | std::unique_ptr<T> |
Vec<T> | rust::Vec<T> |
CxxVector<T> | std::vector<T> |
这些类型可用于共享结构体字段以及extern函数的参数与返回值。特别需要注意:Rust 的String并不直接对应std::string——原因在于std::string不保证 UTF-8 不变式、两者内存布局不同无法直接跨语言传递,且std::string的移动构造语义与 Rust 的 move 语义不匹配,不能按值传给 Rust。
调试与注意事项
结合 The Bridge Module 的说明,构建或运行桥接代码时还应留意:
- 查看生成的 Rust 代码:常规 Cargo 工程可用
cargo expand(如cargo expand ::ffi只展开ffi模块)查看宏展开后的桥接代码,但该方式不适用于 Android 工程——Android 工程应直接查看构建产物; - 查看生成的 C++ 代码:在 Cargo 工程中可检查
target/cxxbridge目录;在 Android 工程中则对应genrule产出的*.rs.h/*.rs.cc文件; rust_binary仅适用于产出可执行文件。若你的需求是把桥接代码编入共享库(供其他模块加载),则应将上述思路迁移到rust_library或rust_ffi等模块形态,rustlibs与static_libs的搭配逻辑保持一致;- 桥接模块中的
unsafe extern "C++"段是 CXX 安全模型的一部分:跨语言边界的调用由 CXX 生成的胶水代码承担不安全的底层细节,业务代码无需手写unsafe块来操作裸指针。
总结
在 Android 上构建 CXX 桥接代码的标准流程可以归纳为三步:
- 在 Rust 源文件中用
#[cxx::bridge]声明桥接模块(示例见 src/main.rs); - 用两个
genrule+cxxbridge工具生成 C++ 侧头文件与源文件,并作为generated_headers/generated_sources注入cc_library_static(示例见 Android.bp); - 用
rust_binary聚合 Rust 源文件、rustlibs: ["libcxx"]与static_libs: ["<你的 cc_library_static>"],得到最终可执行文件。
把最小示例与仓库内置的 blobstore 工程(Android.bp、src/main.rs)对照研读,即可在 Android 平台上搭建出结构正确、可编译、可运行的 Rust↔C++ 互操作构建配置。
【免费下载链接】comprehensive-rustThis is the Rust course used by the Android team at Google. It provides you the material to quickly teach Rust.项目地址: https://gitcode.com/GitHub_Trending/co/comprehensive-rust
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考