budshome (blog: 芽之家) shared the aphorism --
不可避而不战,否则即是徒然增加敌方的胜卷。 -- 马基雅维利

[Rust] Rust + Android 的集成开发设计

💥 内容涉及著作权,均归属作者本人。若非作者注明,默认欢迎转载:请注明出处,及相关链接。

Summary: Rust 和 Android 集成开发的官方设计,包括:无需嵌套的(nested)构建系统、用过程宏(proc_macro)而无需构建脚本(build.rs)、以 crate 形式生成源码,以及动态链接 crate 等。

Topics: rust android rust-android-integrate

现在,不只是互联网时代,更是移动互联网时代。Rust 是当前很多程序员“最想学”的程序设计语言,而 Android 则是市场占有率最高的智能手机操作系统。熟悉 Rust 和 Android 的开发人员,对于将 Rust 用在 Android 项目开发中,估计是非常期待的。但是,目前非谷歌官方的集成方式,均复杂而不便,往往尝试后不得不放弃。

2021 年 5 月 11 日,谷歌官方,Android 安全与隐私团队成员 Ivan Lozano,在 Google Online Security 发布了一篇文章 Integrating Rust Into the Android Open Source Project,对 Rust + Android 的集成开发进行了设计阐述,如:无需嵌套的(nested)构建系统、用过程宏(proc_macro)而无需构建脚本(build.rs)、以 crate 形式生成源码,以及动态链接 crate 等。

让我们来看看 Android 官方的集成开发设计方案——

自 2019 年以来,Android 团队一直致力于将 Rust 程序设计语言引入到 Android 开源项目(AOSP)中,以作为 Android 平台原生(native)代码开发时的内存安全替代方案。与任何大型项目一样,引入一种新语言需要仔细考虑。对于 Android 来说,重要的问题是:评估如何将 Rust 最佳地融入到 Android 的构建系统。目前,在 Soong 构建系统中,对 Rust 提供了支持。但是,当 AOSP 迁移到 Bazel 构建系统时(译注:Bazel 是 AOSP 的下一代构建系统,SoongMake 都将迁移),这些设计决策和思路是同样适用的。本文,讨论了一些关键的设计思路,以及一些关于在 Android 构建系统中对 Rust 提供集成支持的决策。

在大型项目中集成 Rust

2019 年的 RustConf 会议上,大型团队中的 Rust 使用研讨会(Rust in Large Organizations Meeting)强调了一些挑战,比如:避开 Cargo 而使用 Rust 编译器(Compiler) rustc,所带来的风险等(译注:指有些大型项目中,避开 Cargo,直接使用 Rust 编译器 rustc。此次研讨会指出社区生态 crate 依赖于 Cargo,避开会带来风险。具体请查阅研讨会链接)。对此,我们有同样的关切。当第三方 crate 可能对广泛的社区更有益时,我们的目标是将 crate 优先引入。同样地,当为 Android 开发的 crate 能够使广泛的 Rust 社区更为受益时,我们希望将其作为独立的库发布。我们相信:Rust 在 Android 中的成功依赖于:最大限度地减少 Android 和 Rust 社区之间的分歧,并希望 Rust 社区能够从 Android 团队的参与中获益。

无需嵌套的(nested)构建系统

Rust 提供了 Cargo 作为默认的构建系统和包管理器,收集依赖项并调用 rustc(Rust 编译器)来构建目标 crate(Rust 语言包)。Soong 在 Android 中扮演了这个角色,并直接调用 rustc。这是基于如下原因:

  • 在 Cargo 中,C 语言的依赖项,通过 build.rs 脚本,以 ad-hoc 模式处理。Soong 已经提供了一种构建 C 语言库,并将其定义为依赖项的机制。而 Android 谨慎地控制编译器版本和全局编译标记,以确保库是以特定的方式构建的。依靠 Cargo,将引入第二个 non-Soong 机制,以定义/构建 C 语言库,该机制不会受到 Soong 中精心选择的编译控制的限制。
  • 通过 Soong 直接调用编译器,可以为 Android 支持的各种构建配置提供所需的稳定性和控制能力(例如,指定特定于目标的依赖项位置,以及要使用的编译标记)。从技术上讲,虽然有可能实现通过 Cargo 调用 rustc,但 Soong 会不明白怎么通过 Cargo.toml(Rust 项目构建清单)发出指令。再者,Cargo 是独立演化的,这将严重限制 Soong 对构建的精确控制能力。
  • 自包含的构建,且对主机配置不敏感,我们称之为炼金术(hermetic builds),这是 Android 可重复构建的必要条件。Cargo,则依赖于 build.rs 脚本,还未提供密封性(译注:指与外部依赖隔绝)保证。
  • 对于保持工程生产力,增量构建尤其重要;构建 Android 需要大量的资源。Cargo 不是为集成到现有的构建系统而设计的,也没有公开它的编译单元。Cargo 构建,所有 crate 的依赖关系图是给定的 Cargo.toml,跨项目则需要多次重构构建相关 crate(译注:Rust 1.51 已经在这方面做出了努力,参见《Rust 1.51.0 已正式发布,及其新特性详述》中小节 Cargo 新特性 resolver)。这对于集成到 Soong 的增量构建支持来说,太粗糙了,后者期望更小的编译单元。这种支持,对于在 Android 中扩大 Rust 的使用,是必要的。

直接使用 Rust 编译器,则可以避免这些问题,并且,这与我们在 AOSP 中编译其它代码的方式是一致的。它提供了对构建过程的最大控制,并简化了与 Android 现有构建系统的集成。不幸的是,避免它会带来一些挑战,并影响许多其它构建系统的决策。因为,Cargo 的使用,在 Rust 生态系统中根深蒂固。

无需 build.rs 脚本

build.rs 脚本编译为 Rust 二进制文件,它在构建过程中执行 Cargo 构建,以及处理预生成任务。通常用于设置生成环境,或者使用其它语言的构建库(例如 C/C++),类似于配置使用其它语言的脚本。

避开 build.rs 脚本,从某种程度上说,也是不依赖于 Cargo。因为支持这些脚本,是需要复制 Cargo 的构建行为和假设的。除此之外,AOSP 也有充分的理由避免构建脚本:

  • build.rs 脚本可以在执行构建的主机上,执行任意代码。从安全的角度来看,这在添加或更新第三方代码时,会带来额外的负担,因为需要仔细检查 build.rs 脚本文件。
  • 第三方 build.rs 脚本,可能无法密封(译注:指与外部依赖隔绝),或以潜在的微妙方式复制。build.rs 文件访问 build 目录之外的文件(例如 /usr/lib),是很常见的。
  • build.rs 最常见的任务是构建 Rust 代码所依赖的 C 语言库,我们已经通过 Soong 支持了。
  • Android 同样避免了其它语言的构建脚本,而是简单地使用它们,以告知 Android.bp 文件的结构。

为什么支持过程宏(proc_macro),而非构建脚本(build.rs)?

为什么我们支持过程宏(proc_macro)?因为它们是编译器插件,在编译器上下文中的主机上执行代码。

虽然 build.rs 是作为一次性代码编写的,用于处理构建单个 crate,但过程宏(proc_macro)在编译器中定义了可重用的功能,这些功能在 Rust 社区中可以得到广泛的依赖。因此,流行的过程宏(proc_macro)通常会得到更好的维护,以及更严格的检查,这使得代码检查过程更易于管理。作为构建过程的一部分,它们也更容易被“沙盒化”,因为它们不太可能具有编译器外部的依赖关系。

过程宏(proc_macro)也是一种语言特性,而不是构建代码的方法。

在 Android 构建系统中,编译器插件支持也有优先权。例如,参阅 Soong 构建系统中的模块 java_plugin

源码生成为 crate

与 C/C++ 编译器不同,rustc 只接受代表二进制或库入口点的单个源文件。它希望源码树的结构能够自动发现所有必需的源文件。这意味着生成的源文件需要放在源码树中,或者通过 include 指令提供:

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

Rust 社区中,依赖 build.rs 脚本和 Cargo 构建环境来绕过这个限制。在构建时,cargo 命令会设置一个 OUT_DIR 环境变量,build.rs 脚本需要将生成的源代码放入该环境变量中。然后可通过以下方式包含该源文件:

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

对于 Soong 来说,这是一个挑战。因为每个模块的输出都放在自己的 out/directory 目录中;在依赖项输出其生成源代码的地方,没有单独的目录。

对于 Android 平台的代码,我们更喜欢将生成的源代码打包到可以引入的 crate 中。这是因为:

  • 防止生成的源文件名发生冲突。
  • 减少在整个源码树中签入(并需要维护)的模板代码。使生成的源代码编译成一个 crate,其所需的任何模板代码都可以集中维护。
  • 避免生成的代码和其它 crate 之间有隐式交互。
  • 通过动态地生成源代码,来减少对内存和磁盘的压力。

因此,所有 Android 中的 Rust 源代码,生成的模块类型和代码,都可以作为一个 crate,以进行编译和使用。

我们仍然支持无需修改的第三方 crate。

默认为动态链接(dynamic linkage)

默认情况下,Rust 生态系统中,假定 crate 将静态链接到二进制文件中。动态库的优势在于升级(无论是安全性还是功能性),以及减少内存使用。Rust 缺乏稳定的二进制接口和跨 crate 信息流的使用,妨碍了在不升级所有依赖代码的情况下升级库。即使系统上的两个不同程序使用同一个 crate,也不太可能由同一个 crate 共享提供,因为 Rust 识别 crate 的精度很高。这使得 Rust 二进制文件更具可移植性,但也会导致更大的磁盘和内存占用。

对于 Android 设备来说,这是个问题,因为静态地将所有 crate 链接到 Rust 的二进制文件中,会导致过度的代码重复(尤其是在标准库中)。因此,默认情况下,我们选择动态链接 crate。允许 crate 在依赖它们的多个二进制文件之间重用,这减少了 Android 平台中 Rust 的总体内存占用。

由于动态链接在 Rust 社区中不常见,因此并非所有第三方 crate 都支持动态编译。有时,我们必须携带小补丁文件。这需要我们与 crate 维护人员一起工作,以增加支持。

当前状态

我们的构建支持 rustc 的所有输出类型(rlibsdylibsproc_macroscdylibsstaticlibs,以及可执行文件)。Rust 模块可以为给定的依赖关系(rlib vs dylib)自动请求适当的 crate。C/C++ 模块可以依赖于 Rust cdylib 或者 staticlib 生成,它们与 C/C++ 库的方式相同。

除了能够构建 Rust 代码外,Android 的构建系统还支持 protobufgRPC,以及 AIDL 生成的 crate。

Rust 社区为开发人员提供了很好的工具,比如 Rust 语言服务器 rust-analyzer。我们在构建系统中集成了对 rust-analyzer 的支持,因此任何支持它的 IDE 都可以为 Android 模块提供代码补全goto 定义

支持基于源代码的代码覆盖构建,以向平台开发人员提供测试。

在不久的将来,我们计划在站点 source.android.com 上添加文档,说明如何在 Soong 中定义和使用 Rust 模块。我们期望 Android 平台对 Rust 语言的支持,能随着 Rust 生态系统的发展而不断发展。而对于将 Rust 集成到现有的构建系统中,我们也希望继续讨论。

谢谢您的阅读!


Rust 生态与实践

Related Articles

  1. [Rust] Rust 官方周报 395 期(2021-06-16)
  2. [Rust] Rust 1.53.0 明日发布,关键新特性一瞥
  3. [Rust] 使用 tide、handlebars、rhai、graphql 开发 Rust web 前端(3)- rhai 脚本、静态/资源文件、环境变量等
  4. [Rust] 使用 tide、handlebars、rhai、graphql 开发 Rust web 前端(2)- 获取并解析 GraphQL 数据
  5. [Rust] 使用 tide、handlebars、rhai、graphql 开发 Rust web 前端(1)- crate 选择及环境搭建
  6. [Rust] Rust 官方周报 394 期(2021-06-09)
  7. [Rust] Rust web 前端库/框架评测,以及和 js 前端库/框架的比较
  8. [WebAssembly] Rust 和 Wasm 的融合,使用 yew 构建 web 前端(4)- 获取 GraphQL 数据并解析
  9. [WebAssembly] Rust 和 Wasm 的融合,使用 yew 构建 web 前端(3)- 资源文件及小重构
  10. [WebAssembly] Rust 和 Wasm 的融合,使用 yew 构建 WebAssembly 标准的 web 前端(2)- 组件和路由
  11. [WebAssembly] Rust 和 Wasm 的融合,使用 yew 构建 WebAssembly 标准的 web 前端(1)- 起步及 crate 选择
  12. [Rust] Rust 官方周报 393 期(2021-06-02)
  13. [Rust] Rust 官方周报 392 期(2021-05-26)
  14. [Rust] Rust 中,对网址进行异步快照,并添加水印效果的实践
  15. [Rust] Rust 官方周报 391 期(2021-05-19)
  16. [Rust] Rust,风雨六载,砥砺奋进
  17. [Rust] 为什么我们应当将 Rust 用于嵌入式开发?
  18. [Rust] Rust 官方周报 390 期(2021-05-12)
  19. [Rust] Rust + Android 的集成开发设计
  20. [Rust] Rust 1.52.1 已正式发布,及其新特性详述
  21. [Rust] 让我们用 Rust 重写那些伟大的软件吧
  22. [Rust] Rust 1.52.0 已正式发布,及其新特性详述
  23. [Rust] Rust 官方周报 389 期(2021-05-05)
  24. [GraphQL] 基于 actix-web + async-graphql + rbatis + postgresql / mysql 构建异步 Rust GraphQL 服务(4) - 变更服务,以及小重构
  25. [Rust] Rust 1.52.0 稳定版预发布测试中,关键新特性一瞥
  26. [Rust] Rust 生态中,最不知名的贡献者和轶事
  27. [Rust] Rust 基金会迎来新的白金会员:Facebook
  28. [Rust] Rustup 1.24.1 已官宣发布,及其新特性详述
  29. [Rust] Rust 官方周报 388 期(2021-04-28)
  30. [Rust] Rust 官方周报 387 期(2021-04-21)
  31. [GraphQL] 构建 Rust 异步 GraphQL 服务:基于 tide + async-graphql + mongodb(4)- 变更服务,以及第二次重构
  32. [Rust] Rustup 1.24.0 已官宣发布,及其新特性详述
  33. [Rust] basedrop:Rust 生态中,适用于实时音频的垃圾收集器
  34. [Rust] Rust 编译器团队对成员 Aaron Hill 的祝贺
  35. [Rust] Jacob Hoffman-Andrews 加入 Rustdoc 团队
  36. [机器人] 为什么应将 Rust 引入机器人平台?以及机器人平台的 Rust 资源推荐
  37. [Rust] rust-lang.org、crates.io,以及 docs.rs 的管理,已由 Mozilla 转移到 Rust 基金会
  38. [Rust] Rust 官方周报 386 期(2021-04-14)
  39. [Rust] Rust 编译器(Compiler)团队 4 月份计划 - Rust Compiler April Steering Cycle
  40. [GraphQL] 基于 actix-web + async-graphql + rbatis + postgresql / mysql 构建异步 Rust GraphQL 服务(3) - 重构
  41. [Rust] 头脑风暴进行中:Async Rust 的未来熠熠生辉
  42. [GraphQL] 基于 actix-web + async-graphql + rbatis + postgresql / mysql 构建异步 Rust GraphQL 服务(2) - 查询服务
  43. [GraphQL] 基于 actix-web + async-graphql + rbatis + postgresql / mysql 构建异步 Rust GraphQL 服务 - 起步及 crate 选择
  44. [Rust] Rust 2021 版本特性预览,以及工作计划
  45. [Rust] Rust 用在生产环境的 42 家公司
  46. [Rust] 构建最精简的 Rust Docker 镜像
  47. [Rust] Rust 官方周报 385 期(2021-04-07)
  48. [Rust] 使用 Rust 做异步数据采集的实践
  49. [Rust] Android 支持 Rust 编程语言,以避免内存缺陷
  50. [Rust] Android 平台基础支持转向 Rust
  51. [Rust] Android 团队宣布 Android 开源项目(AOSP),已支持 Rust 语言来开发 Android 系统本身
  52. [Rust] RustyHermit——基于 Rust 实现的下一代容器 Unikernel
  53. [Rust] Rustic:完善的纯粹 Rust 技术栈实现的国际象棋引擎,多平台支持(甚至包括嵌入式设备树莓派 Raspberry Pi、Buster)
  54. [Rust] Rust 迭代器(Iterator trait )的要诀和技巧
  55. [Rust] 使用 Rust 极致提升 Python 性能:图表和绘图提升 24 倍,数据计算提升 10 倍
  56. [Rust] 【2021-04-03】Rust 核心团队人员变动
  57. [Rust] Rust web 框架现状【2021 年 1 季度】
  58. [Rust] Rust 官方周报 384 期(2021-03-31)
  59. [Rust] Rust 中的解析器组合因子(parser combinators)
  60. [生活] 毕马威(KPMG)调查报告:人工智能的实际采用,在新冠疫情(COVID-19)期间大幅提升
  61. [Python] HPy - 为 Python 扩展提供更优秀的 C API
  62. [Rust] 2021 年,学习 Rust 的网络资源推荐(2)
  63. [Rust] 2021 年,学习 Rust 的网络资源推荐
  64. [生活] 况属高风晚,山山黄叶飞——彭州葛仙山露营随笔
  65. [Rust] Rust 1.51.0 已正式发布,及其新特性详述
  66. [Rust] 为 Async Rust 构建共享的愿景文档—— Rust 社区的讲“故事”,可获奖
  67. [Rust] Rust 纪元第 382 周最佳 crate:ibig 的实践,以及和 num crate 的比较
  68. [Rust] Rust 1.51.0 稳定版本改进介绍
  69. [Rust] Rust 中将 markdown 渲染为 html
  70. [生活] 国民应用 App 的用户隐私数据窥探
  71. [GraphQL] 构建 Rust 异步 GraphQL 服务:基于 tide + async-graphql + mongodb(3)- 重构
  72. [GraphQL] 构建 Rust 异步 GraphQL 服务:基于 tide + async-graphql + mongodb(2)- 查询服务
  73. [GraphQL] 构建 Rust 异步 GraphQL 服务:基于 tide + async-graphql + mongodb(1)- 起步及 crate 选择
  74. [Rust] Rust 操控大疆可编程 tello 无人机

Topics

rust(73)

graphql(16)

rust-官方周报(12)

webassembly(9)

async-graphql(8)

rust-官方博客(8)

tide(7)

rust-web(7)

wasm(6)

this-week-in-rust(6)

yew(6)

mysql(5)

rbatis(5)

android(4)

actix-web(4)

mongodb(3)

json-web-token(3)

jwt(3)

cargo(3)

技术延伸(3)

trunk(3)

handlebars(3)

rhai(3)

用户隐私(2)

学习资料(2)

python(2)

ai(2)

人工智能(2)

postgresql(2)

rust-compiler(2)

rust-基金会(2)

rust-foundation(2)

rustup(2)

rust-toolchain(2)

rust-工具链(2)

rust-游戏开发(2)

rust-区块链(2)

graphql-client(2)

tello(1)

drone(1)

无人机(1)

隐私数据(1)

markdown(1)

html(1)

crate(1)

async(1)

异步(1)

旅游(1)

不忘生活(1)

葛仙山(1)

hpy(1)

python-扩展(1)

正则表达式(1)

解析器组合因子(1)

组合器(1)

regular-expression(1)

parser-combinator(1)

regex(1)

官方更新(1)

rust-工作招聘(1)

rust-技术资料(1)

rust-周最佳-crate(1)

rust-web-框架(1)

rust-web-framework(1)

rust-核心团队(1)

rust-core-team(1)

rust-language-team(1)

pyo3(1)

rust-python-集成(1)

python-性能改进(1)

迭代器(1)

iterator-trait(1)

国际象棋(1)

chess(1)

游戏引擎(1)

game-engine(1)

虚拟化(1)

unikernel(1)

rustyhermit(1)

linux(1)

virtualization(1)

sandboxing(1)

沙箱技术(1)

数据采集(1)

异步数据采集(1)

docker(1)

镜像(1)

生产环境(1)

rust-评价(1)

rust-2021-edition(1)

rust-2021-版本(1)

graphql-查询(1)

vision-doc(1)

愿景文档(1)

代码重构(1)

steering-cycle(1)

方向周期(1)

隐私声明(1)

机器人(1)

robotics(1)

rustdoc(1)

rust-编译器(1)

实时音频(1)

real-time-audio(1)

变更服务(1)

mutation(1)

查询服务(1)

query(1)

rust-贡献者(1)

rust-轶事(1)

rust-稳定版(1)

rust-预发布(1)

rust-测试(1)

安全编程(1)

可信计算(1)

安全代码(1)

secure-code(1)

rust-android-integrate(1)

rust-embedded(1)

rust-嵌入式(1)

rust-生产环境(1)

rust-2021(1)

rust-production(1)

网页快照(1)

网页截图(1)

水印效果(1)

图片水印(1)

rust-wasm(1)

yew-router(1)

css(1)

web-前端(1)

wasm-bindgen(1)

区块链(1)

blockchain(1)

surf(1)

dotenv(1)

标识符(1)

rust-1.53.0(1)

Elsewhere

- Open Source
  1. github/zzy
  2. github/sansx
- Awesome Blog
  1. Sansx's Studio
  2. 曙光磁铁的博客
- Learning & Studying
  1. Rust 学习资料 - 芽之家