前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Rust + Android 的集成开发设计

Rust + Android 的集成开发设计

作者头像
niqin.com
发布于 2022-09-01 07:42:53
发布于 2022-09-01 07:42:53
2.3K00
代码可运行
举报
文章被收录于专栏:Rust 生态与实践Rust 生态与实践
运行总次数:0
代码可运行

现在,不只是互联网时代,更是移动互联网时代。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 指令提供:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
include!("/path/to/hello.rs");

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 集成到现有的构建系统中,我们也希望继续讨论。

谢谢您的阅读,欢迎交流。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-05-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Rust 生态与实践 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
4 条评论
热度
最新
跪求 你这个编码主题风格包
跪求 你这个编码主题风格包
33点赞举报
你好,现奉上团队vscode相关配置
你好,现奉上团队vscode相关配置
回复回复点赞举报
Gist Id: adff035853cbec6d6d7be4809443de5d token:ae5e36c894b909bb2577acbde083a739c92fa038
Gist Id: adff035853cbec6d6d7be4809443de5d token:ae5e36c894b909bb2577acbde083a739c92fa038
回复回复点赞举报
查看全部3条回复
推荐阅读
JavaScript 时间与日期
知识点: 1.Date类型 2.通用的方法 3.格式化方法 4.组件方法 JavaScript提供了Date类型来处理时间和日期。Date类型内置一系列获取和设置日期时间信息的方法。 一.Date类型 Date类型是在早期Java中java.util.Date类基础上构建的。为此,Date类型使用UTC (Coordinated Universal Time,国际协调时间[又称世界统一时间]) 1970年1月1日午夜(零时)开始经过的毫秒来保存日期。在使用这种数据存储格式的条件下,Dat
汤高
2018/01/11
1.9K0
关于Date对象那些事
获取当前日期时间,我们知道通过方法 new Date() 就可以得到,但是获取到是一个标准格式时间 Fri Jan 18 2019 13:56:47 GMT+0800 (中国标准时间)。但通常我们更渴望得到 2019-01-18 13:56:47 或者 2019/01/18 13:56:47 这种格式。
celineWong7
2020/11/05
8580
vue 手写一个时间选择器
最近研究了 DatePicker 的实现原理后做了一个 vue 的 DatePicker 组件,今天带大家一步一步实现 DatePicker 的 vue 组件。
小皮咖
2019/11/05
2.5K0
JavaScript 学习-20.Date 日期对象
前言 JavaScript Date日期对象用于处理日期和时间。 创建日期 有四种方式初始化日期 new Date() // 当前日期和时间 new Date(milliseconds) //返回从 1970 年 1 月 1 日至今的毫秒数 new Date(dateString) new Date(year, month, day, hours, minutes, seconds, milliseconds) 实例化一个日期 var a = new Date
上海-悠悠
2022/05/25
9910
js中日期格式化常用方法
任何语言中都会有对时间相关处理的方法,JavaScript中当然也有很多时间相关的方法,但是平时我们用到的很少,像我就是,总是记不住,用到的时候就去搜一下,但是网上推荐的总是类似的文章,有时候就会错过很多好用的方法,今天就来总结一下相关的方法,用的时候再也不愁了。
十里青山
2022/08/22
6.3K0
nodejs时间工具类
/** * * @fmt 格式化字符串 * @Date 为需要格式化的日期 * * 示例:format(new Date(),'yyyy-MM-dd hh:mm:ss'); * 返回值为字
用户1141560
2017/12/26
1.7K0
Js中Date对象
JavaScript的Date对象是用于处理日期和时间的全局对象,Date对象基于Unix Time Stamp,即自1970年1月1日UTC起经过的毫秒数。
WindRunnerMax
2020/10/26
21.6K0
Javascript日期时间总结(转)
从后台返回的C#时间为:/Date(-62135596800000)/,这个是C#的DateTime.MinValue; 要在html页面展示,一个方法是后端先处理成yyyy-MM-dd HH:mm:ss的格式,前端直接展示。 如果后端不做处理,就需要前端来做处理了,下面就是看前端处理的这种情况。
山河木马
2019/03/05
4.9K0
Javascript日期时间总结(转)
一起Polyfill系列:让Date识别ISO 8601日期时间格式
一、什么是ISO 8601日期时间格式   ISO 8601是国际标准化组织制定的日期时间表示规范,全称是《数据存储和交换形式·信息交换·日期和时间的表示方法》。   示例:   1. 2014-12-12T00:00:00.000Z   2. 2014-12-12T00:00:00.000+08   3. 2014-12-12T00:00:00.000+0800   4. 2014-12-12T00:00:00.000+08:00   5. 2004-W17-3   6. 0001-165 详细说明请参考
^_^肥仔John
2018/01/18
1.7K0
JavaScript中Date对象的那些事儿
任何事情都离不开时间,太阳每天升起的时间,每天你上班的时间,中午吃饭时间等等。在编程生涯中,无时无刻都有一个时间来引导,如数据创建时间(createTime),更新时间(updateTime)等。今天来说说JavaScript的Date对象。
Javanx
2019/09/05
9350
JavaScript中Date对象的那些事儿
js中进行数字,超大金额(千位符),日期时间格式化处理
最近遇到一个需求,对于社区里讨论的帖子展示一个访问量的计数显示问题,当超过多少页面访问量时,就让其显示xxx万,xx亿
itclanCoder
2020/10/28
4.4K0
js中进行数字,超大金额(千位符),日期时间格式化处理
时间与日期处理
主要有以下类: NSDate -- 表示一个绝对的时间点 NSTimeZone -- 时区信息 NSLocale -- 本地化信息 NSDateComponents -- 一个封装了具体年月日、时秒分、周、季度等的类 NSCalendar -- 日历类,它提供了大部分的日期计算接口,并且允许您在NSDate和NSDateComponents之间转换 NSDateFormatter -- 用来在日期和字符串之间转换 NSDate NSDate用来表示公历的GMT时间(格林威治时间)。 有下面几种初始化方法:
猿人谷
2018/01/17
3.3K0
时间与日期处理
为什么到了时间你的活动还没开始——探究Date对象
假设有一个活动,原计划定的是12月25日早上8点开始,结果苹果用户到了早上8点却看见活动按钮还是灰色的,而且PC、安卓都是正常。这种情况如果发生,首先往哪个方向考虑呢?
lhyt
2019/09/08
1.6K0
JavaScript 日期
JavaScript 日期 JavaScript 日期输出 默认情况下,JavaScript将使用浏览器的时区并将日期格式显示为全文本字符串: Tue Apr 02 2019 09:01:19 GMT+0800 创建 Date 对象 Date对象由 Date() 构造函数创建 new Date() new Date():使用当前日期和时间创建新的日期对象: var date = new Date(); new Date(year,month,…) 格式:new Date(year,month,day,ho
Mirror王宇阳
2020/11/13
1.2K0
js常用方法和一些封装 -- 时间相关(附案例详解)
昨天上传的时候少了一个方法,现在补上: //日期转换 function transformDate(date){ if(typeof date =="string"){ return new Date(date.replace(/-/ig,"/").replace("T"," ")); }else{ return date; } } 勘误:_center()方法中timeBox应改为dom,封装方法的时候忘了改过来了,现在已更正。 本文介绍一些时间操
剽悍一小兔
2018/05/17
1.9K0
JS前端Date(日期)
Date日期对象是一个构造函数,主要用来获取时间和对时间进行一系列操作。Date 类型将日期保存为自协调世界时(UTC,Universal Time Coordinated)时间 1970 年 1 月 1 日午夜(零时)至今所经过的毫秒数。
IT工作者
2022/05/13
10.5K0
前端开发:JS获取当前日期方法(各种获取时间的奇葩需求汇集)
在前端开发过程中,有时候会需要获取当前时间和已知时间做对比的需求,也有单纯的获取当前时间然后传给后台,还有各种需要把获取到的当前时间转化成特定的时间格式传给后台,这虽然不是一个复杂的操作,但是不同的产品需求不一样,有时候还需要转时区。总之,就是各种稀奇古怪的需求,那么本篇博文就来分享一下关于在前端开发过程中获取当前时间或者日期相关的各种操作。
三掌柜
2022/04/12
8K0
前端开发:JS获取当前日期方法(各种获取时间的奇葩需求汇集)
需要知道的JS的日期知识,都在这了
JS中的 Date 很奇怪。当我们需要处理日期和时间的时候比较麻烦,经常借助像date-fns和 Moment 这样的库。
前端小智@大迁世界
2019/07/02
2.7K0
JS基础-JS内置对象Date详解
year:表示年份的整数值。0到99会被映射至1900年至1999年,其它值代表实际年份。
用户10106350
2022/10/28
3.9K0
几个JavaScript极短日期时间代码片段(你值得拥有)
日期是不是今天,我们只需要判断 日期的 年月日 是否与 当前日期的 年月日一致即可,所以我们的常规代码片段如下:
coder_koala
2021/11/10
1.2K0
几个JavaScript极短日期时间代码片段(你值得拥有)
推荐阅读
相关推荐
JavaScript 时间与日期
更多 >
LV.2
上海观安前端负责人
目录
  • 在大型项目中集成 Rust
  • 无需嵌套的(nested)构建系统
    • 无需 build.rs 脚本
  • 为什么支持过程宏(proc_macro),而非构建脚本(build.rs)?
  • 源码生成 crate
    • 默认为动态链接(dynamic linkage)
    • 当前状态
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档