news 2026/9/10 11:58:36

Android 构建集成:在 Soong 中用 rust_binary 构建 CXX Rust 桥接模块

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Android 构建集成:在 Soong 中用 rust_binary 构建 CXX Rust 桥接模块

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模块中rustlibsstatic_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文件)中,这套流程需要三个层面的配合:

  1. Rust 侧桥接声明:由#[cxx::bridge]模块给出(详见 The Bridge Module);
  2. C++ 侧胶水代码生成:由genrule调用cxxbridge工具生成(详见 android-cpp-genrules.md);
  3. 最终可执行目标:由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_staticgenerated_headersgenerated_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.hmain.rs.cc
  • cc_library_staticblobstore_cppsrcs是 src/blobstore.cc(C++ 业务实现,如BlobstoreClient类),并通过generated_headers/generated_sources把两个genrule的产物编入自身。注意generated_headers中额外包含的cxx-bridge-header是 CXX 所需的公共头,而blobstore_bridge_header才是本项目桥接生成的头;
  • rust_binaryblobstore通过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++ 类型以UniquePtrPin<&mut>等方式安全地暴露给 Rust 侧调用。

构建时的类型映射要点

在编写桥接签名时,应遵循 Additional Types 章节给出的映射表,避免使用无法直接跨语言传递的类型:

Rust 类型C++ 类型
Stringrust::String
&strrust::Str
CxxStringstd::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_libraryrust_ffi等模块形态,rustlibsstatic_libs的搭配逻辑保持一致;
  • 桥接模块中的unsafe extern "C++"段是 CXX 安全模型的一部分:跨语言边界的调用由 CXX 生成的胶水代码承担不安全的底层细节,业务代码无需手写unsafe块来操作裸指针。

总结

在 Android 上构建 CXX 桥接代码的标准流程可以归纳为三步:

  1. 在 Rust 源文件中用#[cxx::bridge]声明桥接模块(示例见 src/main.rs);
  2. 用两个genrule+cxxbridge工具生成 C++ 侧头文件与源文件,并作为generated_headers/generated_sources注入cc_library_static(示例见 Android.bp);
  3. 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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 11:57:59

OpenClaw中文文档与实战:安装部署、接入微信飞书及模型配置指南

说句实在话&#xff0c;OpenClaw 的中文资料目前主要靠社区贡献&#xff0c;官方文档入口也经常跟着版本更新变动。所以这篇文章我不会只甩给你一个地址&#xff0c;而是把“怎么找文档”和“拿到文档后怎么实操”一起讲清楚&#xff0c;包括安装部署、接微信飞书、配置模型、写…

作者头像 李华
网站建设 2026/9/10 11:56:59

JaDE自适应差分进化优化SVM参数实现时序预测与Matlab代码

1. 时序预测困局&#xff1a;SVM参数选不好&#xff0c;核函数再先进也白搭做时间序列预测的人&#xff0c;应该都经历过这种尴尬&#xff1a;数据清洗得干干净净&#xff0c;特征也构造得差不多&#xff0c;拿SVM一跑&#xff0c;效果却远不如隔壁用线性回归的同事。问题通常不…

作者头像 李华
网站建设 2026/9/10 11:56:46

PyTorch中OneCycleLR学习率调度的原理与实践

1. OneCycleLR&#xff1a;PyTorch中的学习率控制黑科技第一次在ResNet训练中尝试OneCycleLR时&#xff0c;我盯着验证集准确率曲线从82%飙升至89%的那一刻&#xff0c;就彻底被这个学习率调度器的魔力征服了。作为PyTorch中最具实战价值的工具之一&#xff0c;OneCycleLR完美诠…

作者头像 李华
网站建设 2026/9/10 11:54:58

Arduino ESP32 从零搭建:5 步完成核心包安装、串口识别与首次上传

Arduino ESP32 从零搭建&#xff1a;5 步完成核心包安装、串口识别与首次上传 【免费下载链接】arduino-esp32 Arduino core for the ESP32 family of SoCs 项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32 这份指南写给刚收到第一块 ESP32 开发板、准…

作者头像 李华
网站建设 2026/9/10 11:50:45

CANN/GE数据类型转换算子接口

aclopCast 【免费下载链接】ge GE&#xff08;Graph Engine&#xff09;是面向昇腾的图编译器和执行器&#xff0c;提供了计算图优化、多流并行、内存复用和模型下沉等技术手段&#xff0c;加速模型执行效率&#xff0c;减少模型内存占用。 GE 提供对 PyTorch、TensorFlow 前端…

作者头像 李华