- 跨平台
- 前端
- 移动开发
- OpenHarmony
【免费下载链接】flutter_flutter
本仓库是 Flutter SDK 与 Flutter Engine 的 OpenHarmony 适配版本,由 CPF-Flutter 团队维护。开发者可使用熟悉的 Flutter 技术栈开发 OpenHarmony 应用,3.35.7 及以后的适配版本可基于本仓库源码构建支持 OpenHarmony 的 Flutter Engine。
本文围绕 flutter_flutter 仓库中 engine/src/flutter/docs/rbe/rbe.md 的官方文档展开,系统讲解如何在构建 Flutter Engine 时启用 RBE(Remote Build Execution,远程构建执行)。文章将带你完成
.gclient配置、CIPD/gcloud 双重认证、通过et工具发起 RBE 构建、按需禁用 RBE 与 LTO,并深入解读仓库中 DEPS、et工具与构建配置 JSON 的底层实现,最后给出完整的故障排查手段,帮助你真正上手"本地开发、远端编译、缓存复用"的高效引擎构建工作流。
适用范围与前置条件
在动手之前必须先明确:根据官方文档 rbe.md 的说明,这套 RBE 方案预期仅对 Google 内部员工(Googlers)在 corp Linux、macOS 和 Windows 主机(含 cloudtop 实例)上可用。原因在于 RBE 的配置文件存放在受权限管控的flutter_internalCIPD bucket 中,需要 Googler 身份的 CIPD 认证才能拉取。
从仓库源码看,这一限制体现在两处:
- 根目录 DEPS 中
use_rbe变量默认值为False,并注释明确写道:"When this is true, the Flutter Engine's configuration files and scripts for RBE will be downloaded from CIPD. This option is only usable by Googlers."(该选项仅 Googlers 可用)。 - 引擎工具的源码 environment.dart 中,
hasRbeConfigInTree()通过检查engine/src/flutter/build/rbe目录是否存在来判断"当前环境是否支持远程构建",而该目录正是由上述 CIPD 依赖在gclient sync时按需下载的。
对于非 Googler 的开发者(包括基于本仓库进行 OpenHarmony 适配的开发者),RBE 部分可能无法直接使用,但本文中关于.gclient的custom_vars结构、et构建工具的用法、GN/Ninja 构建参数以及local_engine.json的自定义方式,仍然适用于常规的本地引擎构建,值得完整阅读。
快速开始:在.gclient中启用 RBE
第一步:编辑.gclient文件
启用 RBE 的第一步是在仓库根目录的.gclient文件中,于custom_vars一节加入"use_rbe": True。官方文档给出的完整示例为:
solutions = [ { "managed": False, "name": "src/flutter", "url": "git@github.com:zanderso/engine.git", "custom_deps": {}, "custom_vars": { "use_rbe": True, }, "deps_file": "DEPS", "safesync_url": "", }, ]各字段含义:
| 字段 | 说明 |
|---|---|
managed | 是否由 gclient 管理该 solution 的版本,引擎通常设为False |
name | solution 在本地的目录名(引擎通常为src/flutter) |
url | 引擎源码仓库地址,替换为你实际使用的仓库即可 |
custom_deps | 附加依赖项,通常为空对象 |
custom_vars | 传递给 DEPS 解析的变量,use_rbe: True即在此处声明 |
deps_file | gclient 依赖描述文件,即根目录的DEPS |
safesync_url | 安全同步地址,本地开发通常留空 |
在 flutter_flutter 仓库中,OpenHarmony 适配版本的 gclient 配置模板位于 engine/scripts/ohos.gclient,其 solution 结构与上例完全一致(custom_deps、deps_file、managed、name、safesync_url、url字段一一对应),只是deps_file指向DEPS_ohos,url指向 OpenHarmony 镜像仓库。可以看到custom_vars正是这类"按需开关依赖下载"的标准挂载点——原文档中下载 web 引擎所需 emsdk 也是通过取消注释该处的custom_vars来实现。
第二步:CIPD 认证
完成上述编辑后,必须以 Googler 身份通过 CIPD 认证,以便从flutter_internalCIPD bucket 下载 RBE 配置:
cipd auth-login这一步对应到源码层面,就是让gclient sync能够以合法身份解析 DEPS 中声明的两个关键 CIPD 依赖组:
- reclient 二进制(
infra/rbe/client/${{platform}}):按主机平台(linux-x64、mac-arm64、mac-x64、windows-x64)分别下载到engine/src/flutter/buildtools/<平台>/reclient目录,版本由 DEPS 中的reclient_version变量(当前为git_revision:29a9d3cb597b6a7d67fa3e9aa8a7cab1c81232ee)决定; - RBE 配置(
flutter_internal/rbe/reclient_cfgs):下载到engine/src/flutter/build/rbe目录,这正是et工具判断"当前环境是否支持 RBE"的目录。
其中 Windows 平台的 reclient 依赖条件还额外要求download_windows_deps为真,Linux/macOS 则分别以host_os+host_cpu组合作为condition。
第三步:执行gclient sync -D
认证成功后,运行:
gclient sync -D-D参数会删除未在 DEPS 中声明、或不再需要的本地依赖,确保工作区状态与依赖声明严格一致。执行完毕后,engine/src/flutter/build/rbe与engine/src/flutter/buildtools/<平台>/reclient应已就位,et工具即可识别当前环境支持 RBE。
运行 RBE 构建:统一经由et工具
为什么必须使用et
官方文档明确规定:引擎仓库中所有 RBE 构建都必须通过et工具发起,其入口脚本是//flutter/bin/et。原因在于et会在ninja调用前后正确地初始化与关闭本地 RBE proxy(reproxy)——如果绕过et直接调用ninja,RBE 的本地代理生命周期无人管理,构建必然失败。
从仓库源码看,engine/src/flutter/bin/et 是一个 Bash 入口脚本:它根据uname -s/uname -m判定宿主机平台(linux/macos×arm64/x64),拼接出平台名后调用对应预编译 Dart SDK 执行tools/engine_tool/bin/et.dart;若发现tools/engine_tool/.dart_tool目录不存在,会提示你"必须先运行gclient sync -D"。Windows 上对应的是 engine/src/flutter/bin/et.bat。
gcloud 认证:绑定 RBE worker pool 所在项目
运行 RBE 构建前,还必须通过gcloudSDK 认证到拥有 RBE worker pool 的 Google Cloud 项目:
gcloud init --project flutter-rbe-prodmacOS 上有一个易踩的坑:运行gcloud前必须确保python3在 PATH 中且不来自 homebrew 等第三方来源,which python3应返回/usr/bin/python3,否则可能引发认证相关异常。
若bootstrap报错找不到Application Default Credentials(应用默认凭据),则执行:
gcloud auth application-default login列出可用构建
et工具可用的构建由//flutter/ci/builders目录下的构建配置 JSON 文件决定。查看命令:
et help build只列出适合本地开发的构建;要查看全部构建:
et help build --verboseverbose 列表中带ci/或ci\前缀的构建,正是 CI 上以完全相同 GN 参数与 Ninja 目标运行的构建,可用于本地复现 CI 行为。
这一行为的源码实现在 build_plan.dart:configureArgParser会读取当前平台可运行的构建配置,非 verbose 模式下隐藏ci/前缀配置以缩短帮助文本;--config(缩写-c)参数会校验输入是否为合法构建名。
运行构建与验证
运行某个配置的构建:
et build -c host_debuget默认会为本地开发选择合适的构建(源码中_defaultHostDebug()的默认值是host_debug,见 build_plan.dart)。RBE 工作正常时,日志中应出现类似输出:
[2024-04-22T09:58:48.643][windows/host_debug: GN]: OK [2024-04-22T09:58:59.361][windows/host_debug: RBE startup]: OK两行分别代表 GN 配置阶段与 RBE proxy 启动阶段成功。这也是判断 RBE 是否真正接通的第一个检查点。
禁用 RBE:--no-rbe
如果希望本次构建走本地,给build命令加--no-rbe标志:
et build -c host_debug --no-rbe源码层面,RBE 开关的处理逻辑在 build_plan.dart:当--rbe被显式开启但环境中不存在 RBE 配置(即engine/src/flutter/build/rbe目录缺失)时,会直接抛出FatalError("RBE requested but configuration not found"),并附带引导说明。同时,--rbe与--lto、--no-rbe、--no-lto属于保留 GN 参数(reservedGnArgs),不能通过--gn-args传入,否则会触发reservedGnArgsError提示改用et的直接参数(见 build_plan.dart)。
控制 LTO:--lto
LTO(链接时代码优化)在本地构建中既慢又少有用武之地,因此et在所有构建中默认关闭 LTO——即使某个构建配置里显式指定了 LTO 也会被覆盖。需要时显式传入:
et build -c host_debug --lto源码中这一默认逻辑位于 build_plan.dart:若用户未显式解析--lto,则默认取!build.gn.contains('--no-lto')的反值,最终在toGnArgs()中生成--lto或--no-lto传给 GN(见 build_plan.dart)。
自定义构建:编辑构建配置 JSON
除开关 RBE 与 LTO 外,build命令目前不支持在命令行层面自定义构建参数。如果需要自定义 GN 标志或 Ninja 目标,官方做法是本地修改构建配置 JSON 文件,尤其是专为本地引擎开发准备的//flutter/ci/builders/local_engine.json。
在 flutter_flutter 仓库中,engine/src/flutter/ci/builders/local_engine.json 收录了大量常用本地构建。以其中的macos/host_debug为例,其结构为:
{ "cas_archive": false, "drone_dimensions": [ "os=Mac-14|Mac-15", "device_type=none" ], "gclient_variables": { "download_android_deps": false, "download_jdk": false, "use_rbe": true }, "gn": [ "--runtime-mode", "debug", "--no-stripped", "--no-lto", "--xcode-symlinks", "--rbe", "--no-goma" ], "name": "macos/host_debug", "description": "Builds a debug mode engine for a macOS host.", "ninja": { "config": "host_debug", "targets": [] }, "properties": { "$flutter/osx_sdk": { "sdk_version": "16c5032a" } } }各字段的作用(完整规范见 engine/src/flutter/ci/builders/README.md 中的 Build Definition Language 说明):
name:构建名,et build -c <name>中的<name>即取自此处;命名约定为<平台>/<配置>,et会按当前平台对名称做"mangling"处理(见 build_utils.dart,如macos/host_debug在 macOS 上简化为host_debug)。drone_dimensions:选择执行该子构建的 CI bot(os、device_type等),本地运行时用于表达目标平台属性。gclient_variables:传给gclient sync的变量,如use_rbe: true、download_android_deps: false。gn:传给tools/gn脚本的 GN 参数列表,如--rbe、--no-goma、--no-lto、--runtime-mode debug。仓库中 DEPS 的reclient_version与这里配合,决定了实际拉取的 reclient 工具链版本。ninja.config:GN 生成的输出目录名(对应out/<config>)。ninja.targets:额外指定要构建的 Ninja 目标,为空数组表示构建该配置的全部默认目标。properties.$flutter/osx_sdk.sdk_version:macOS 构建使用的 Xcode "Build version" 值。这一点与后文"构建缓慢"排错一节直接相关——本地 macOS 开发者应使用与 CI bot 相同的 Xcode 版本。
何时不应放入local_engine.json:如果一个构建的产出物要用于 CI 归档或测试,就不应放入local_engine.json,而应放入与其用途匹配的专用 JSON 文件(如 linux_host_engine.json、windows_host_engine.json 等)。反之,若某个本地常用配置在local_engine.json中缺失、且会被多人长期使用,官方建议将该配置检入仓库并让其跑在 CI 上——这能让 RBE 远端缓存保持"温暖"(warm),从而显著加速本地构建。仓库中 30 余个ci/builders/*.json文件正是这一机制的产物。
et工具的底层构建流程
把以上命令行行为串起来看,et build的实际执行路径是:
- build_command.dart 读取
BuildPlan(含useRbe、useLto、并发数、额外 GN 参数); - build_utils.dart 的
runBuild()组装 GN 参数(RBE 关闭时追加--no-rbe),构造BuildRunner并传入RbeConfig; BuildRunner依次执行 GN 配置、启动 RBE proxy(bootstrap)、运行 ninja、关闭 proxy,整个生命周期由et统一托管——这正是官方文档强调"所有 RBE 构建必须经由et"的实现根基。
保持与 RBE 的兼容性:rebase_path()的相对路径约束
RBE 对编译命令中的路径非常敏感。官方文档给出两条硬性约束:
- 编译命令中的所有路径必须是相对路径;
- 这些相对路径必须全部解析到引擎源码树(engine source tree)内部的文件。
落到 GN 层面,这意味着:只有确定结果不会用于编译或链接命令时,rebase_path()才可以省略第二个参数(即基准路径参数)。仓库树中确实存在大量省略第二参数的rebase_path()用法,但安全的经验法则是:不要新增单参数形式的rebase_path()。
在 flutter_flutter 仓库中可以看到规范用法示例,例如 engine/src/flutter/BUILD.gn 中的rebase_path(inputs[0], root_build_dir)明确传入root_build_dir作为基准,确保产物路径相对构建目录输出,从而满足 RBE 对相对路径的要求;engine/src/flutter/build/archives/BUILD.gn 同样传入了$root_build_dir基准。为 RBE 新增 GN 规则或修改编译参数时,请始终遵循这一模式。
故障排查
错误:获取凭据失败(Error obtaining credentials)
运行et build时若出现如下错误:
E0815 09:30:03.169505 1413247 main.go:147] Error obtaining credentials: application default credentials were invalid: could not get valid Application Default Credentials token: oauth2: cannot fetch token: 400 Bad Request检查~/.config/gcloud/application_default_credentials.json,确认你是否登录了错误的项目或账号(例如同时登录了多个账号导致默认凭据混乱)。修复方式为重置默认凭据:
gcloud auth application-default login构建缓慢(Slow builds)
RBE 构建变慢通常有几个原因,按常见程度排序:
- 远端缓存是冷的(cold):缓存温暖时,一次编译步骤仅需从缓存下载已编译的翻译单元(compiled TU);缓存冷时,远端 worker 必须真实执行编译命令,耗时自然更长。
- worker pool 过载:过载时部分编译命令会回退到本地执行,同样更慢。
- 网络带宽受限:带宽不足时 RBE 会吞吐受限(文档作者曾观察到在家中因 RBE 占满低配 Comcast Business 连接而导致构建缓慢)。
此外还有两个平台相关的针对性建议:
- macOS 开发者:确保本机 Xcode 版本与 CI bot 一致。用
xcodebuild -version查看 "Build version" 值,应与 local_engine.json 中所运行构建的sdk_version(例如16c5032a)匹配。 - Google corp macOS 设备:各类后台与监控进程可能拖慢构建(RBE 与非 RBE 皆然),可按文档指引关闭部分此类进程;同时应按照相关说明将引擎源码目录加入 Spotlight 隐私列表,关闭 Spotlight 对源码目录的扫描。
临时降级为本地构建的快捷方式:当 RBE 变慢时,非 RBE 构建(尤其是增量构建)往往更快。你可以在不失效现有构建产物的情况下,通过环境变量禁用远端执行:
RBE_exec_strategy=localProxy 状态与调试日志
⚠️警告:由于
et会在执行构建时启动与停止本地 RBE proxy,以下命令仅在构建运行期间有效。
查询本地 RBE proxy 的状态:
buildtools/mac-arm64/reclient/reproxystatus该命令会输出已完成的 action 数、进行中的 action 数,以及远端执行、本地执行、远端缓存命中三项统计。示例输出:
$ reproxystatus Reproxy(unix:///tmp/reproxy.sock) is OK Actions completed: 4405 (750 cache hits, 3075 racing locals, 580 racing remotes) Actions in progress: 11 QPS: 12解读:
cache hits:远端缓存命中数,缓存命中越多构建越快;racing locals/racing remotes:RBE 的 racing 模式下本地与远端执行的 action 数(racer 会同时启动本地与远端执行、取先完成者);QPS:每秒处理的 action 数。
RBE 日志位于系统/tmp目录:/tmp/reproxy.{INFO,WARNING,ERROR}与/tmp/bootstrap.{INFO,WARNING,ERROR}。这些日志文件是排查 RBE 问题的一手资料。
CI 运行中,RBE 日志还可以通过构建页面中teardown remote execution > collect rbe logs步骤下的链接获取:
依赖分析失败(Dependency analysis failures)
上述日志对排查 RBE 的依赖分析(dependency analysis)失败很有价值。依赖分析失败的原因多种多样,但本地开发中常见的一种是源文件本身存在某种格式错误(malformed)。排查手段很简单:关闭 RBE 做一次本地构建,即可确认问题是否与 RBE 的依赖分析相关。
参考资料与延伸阅读
关于 RBE 工具链的进一步信息,可结合 flutter_flutter 仓库内的以下资源继续深入:
- RBE 工具链本体(reproxy、rewrapper、bootstrap 等)属于 reclient 项目;这些工具文档稀缺,其命令行参数以源码为准。本地对应的工具二进制由 DEPS 中
reclient_version(infra/rbe/client/${{platform}})与flutter_internal/rbe/reclient_cfgs两个 CIPD 依赖决定,并在gclient sync时下载到 engine/src/flutter/buildtools 与engine/src/flutter/build/rbe目录。 - RBE 版本管理:本地开发使用的 RBE 版本在根目录 DEPS 中声明,需要不定期手动滚动升级;CI 使用的版本在 LUCI recipe 中配置,同样需要手动滚动。两者升级节奏与 clang 升级相互关联。
- 构建配置体系:engine/src/flutter/ci/builders/README.md 完整介绍了 Build Definition Language(builds、tests、generators、archives 的 JSON 结构),是理解
local_engine.json及全部 CI 构建配置的权威文档。 et工具源码:engine/src/flutter/tools/engine_tool/lib/src/build_plan.dart、build_utils.dart、environment.dart 分别实现了构建计划解析、构建执行与 RBE 环境探测,可作为扩展et行为时的阅读起点。- 仓库构建配置全集:engine/src/flutter/ci/builders 目录下的 30 余个 JSON 文件分别覆盖 Linux/macOS/Windows 主机的 Android、iOS、host、web(wasm)等引擎构建变体,其中
--no-rbe的 web 构建(如linux/wasm_release)展示了同一套配置体系下关闭 RBE 的写法。
如果你同时关注 flutter_flutter 的 OpenHarmony 适配侧构建流程,engine/scripts/ohos.gclient 提供了本仓库 OpenHarmony 版本的 gclient 模板(deps_file指向DEPS_ohos),其中custom_vars的结构与本文第一节完全一致,可作为对照参考。
- 跨平台
- 前端
- 移动开发
- OpenHarmony
【免费下载链接】flutter_flutter
本仓库是 Flutter SDK 与 Flutter Engine 的 OpenHarmony 适配版本,由 CPF-Flutter 团队维护。开发者可使用熟悉的 Flutter 技术栈开发 OpenHarmony 应用,3.35.7 及以后的适配版本可基于本仓库源码构建支持 OpenHarmony 的 Flutter Engine。
相关推荐
Flutter Engine RBE 远程构建配置指南:基于 `et` 工具的完整实践
Flutter Engine RBE 远程构建配置指南:基于 et 工具的完整实践 导读 本文是围绕 Flutter Engine 仓库中 docs/rbe/r
跨平台移动开发前端图形学Flutter Engine 构建定义语言(Build Definition Language)详解:在 flutter_flutter 仓库中用 JSON 定义 CI 构建、分片与产物归档
Flutter Engine 构建定义语言(Build Definition Language)详解:在 flutter_flutter 仓库中用 JSON 定
跨平台前端移动开发OpenHarmonyFlutter Engine 引擎仓库 Pre-submit 与 Post-submit 持续集成机制深度解析(flutter_flutter 仓库)
Flutter Engine 引擎仓库 Pre submit 与 Post submit 持续集成机制深度解析(flutter_flutter 仓库) 本文基于
跨平台前端移动开发OpenHarmony
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考