Flutter Engine 源码仓库导读:引擎架构分层与 et / felt 引擎开发工具链完整指南
Flutter Engine 是 Flutter 生态中承载一切上层能力的"可移植运行时":它独立于 UI 框架存在,为托管 Flutter 应用提供动画与图形、文件与网络 I/O、无障碍、插件架构,以及完整的 Dart 运行时与编译工具链。本文以引擎仓库的入口文档 engine/src/README.md 为骨架,结合仓库内的真实源码与工具文档,系统讲解引擎与框架的关系、引擎代码在 monorepo 中的物理位置,并手把手带你掌握引擎开发的两把核心工具——面向 Web 引擎的 felt 与面向移动/桌面引擎的 et,覆盖从构建、测试到本地调试的完整闭环。
读完本文,你将能够:理解 Flutter Engine 在整个技术栈中的定位与仓库目录映射;正确配置 et/felt 的运行环境;使用构建配置(build configuration)概念区分并构建 host/target 引擎;通过 GN 目标表达式精确定制构建产物;运行 C++/Dart 单元测试;以及使用本地引擎产物直接驱动 Flutter 应用进行真机或浏览器调试。
Flutter Engine 是什么:可移植运行时的职责边界
按照引擎仓库根 README(engine/src/README.md)的权威定义,Flutter Engine 是一个用于托管 Flutter 应用的可移植运行时(portable runtime)。它实现 Flutter 的核心底层库,包括:
- 动画与图形(animation and graphics):负责场景树的渲染管线与帧调度;
- 文件与网络 I/O(file and network I/O):提供跨平台的底层异步 I/O 能力;
- 无障碍支持(accessibility support):把语义树桥接到各操作系统的无障碍框架;
- 插件架构(plugin architecture):支持通过平台通道与原生宿主双向通信;
- Dart 运行时与编译工具链(a Dart runtime and compile toolchain):既能在 JIT/debug 模式跑解释执行,也能做 AOT 编译产物。
值得强调的是引擎与框架的边界:绝大多数 Flutter 开发者日常打交道的是 Flutter Framework(提供现代响应式框架、以及一整套平台/布局/基础 widget),而引擎位于框架之下,两者由 packages/flutter 等上层库通过 dart:ui 与引擎交互。开发者只有在需要改动渲染、图形、Dart 运行时或某平台底层能力时,才会直接进入引擎仓库工作。
在仓库中定位引擎:目录结构与源码真实映射
本仓库是 Flutter 的 monorepo,引擎代码并不在根目录,而是集中在 engine/src/ 下(引擎入口文档即 engine/src/README.md,其兄弟文件还有 engine/src/BUILD.gn、engine/src/AUTHORS)。引擎主体源码位于 engine/src/flutter,几个与职责描述直接对应的核心目录包括:
| 引擎职责 | 仓库目录(相对仓库根) | 说明 |
|---|---|---|
| 渲染/场景图相关 | engine/src/flutter/flow、engine/src/flutter/display_list、engine/src/flutter/skia | 保留绘制模型与指令 |
| 全新 GPU 渲染后端 | engine/src/flutter/impeller | Impeller 渲染器源码、测试与构建规则所在 |
| 平台嵌入层(shell) | engine/src/flutter/shell | 平台视图、平台通道与运行时生命周期管理;例如 Android 平台相关代码在 engine/src/flutter/shell/platform/android,其中 android_jar 目标定义于其 BUILD.gn |
| C++ 基础库 | engine/src/flutter/fml | 消息循环、线程、文件等底层基础设施 |
Dart 侧 dart:ui 库 |
engine/src/flutter/lib | Web 引擎单独位于 engine/src/flutter/lib/web_ui |
| Dart 运行时适配 | engine/src/flutter/runtime | Dart 运行时封装、Dart 入口 |
| 构建/CI 定义 | engine/src/flutter/ci/builders | 大量 *.json 构建配置文件与格式说明 |
| 引擎开发工具 | engine/src/flutter/tools、engine/src/flutter/bin | 引擎工具脚本,et 入口即位于 engine/src/flutter/bin/et |
引擎开发工具矩阵:et 与 felt 的分工
引擎 README 用一张表概括了两大官方工具的使用场景:
| 目标平台 | 工具 | 说明文档(仓库内相对路径) |
|---|---|---|
| Web | felt |
engine/src/flutter/lib/web_ui/README.md(felt 即 "Flutter Engine Local Tester") |
| 移动或桌面(Mobile or Desktop) | et |
engine/src/flutter/tools/engine_tool/README.md(engine tool) |
其中 et 的官方定位是"为构建和在 Flutter 引擎中工作提供统一命令行接口(unified interface)";felt 则聚焦于提升 Web 引擎的本地开发体验。下面分别展开两者的完整用法。
使用 et:移动与桌面引擎的构建、测试与本地运行
环境配置与自检
et 要求你已具备可用的引擎源码检出与受支持平台的开发环境。启动前建议把 et 所在目录加入 PATH,即引擎仓库下的 bin 目录(仓库内实际路径为 engine/src/flutter/bin,其中已包含 et 与 Windows 用的 et.bat):
PATH=$PATH:/path/to/engine/flutter/bin
验证安装是否可用,直接运行 et help,正常输出如下(该命令输出原样来自工具文档):
$ et help
A command line tool for working on the Flutter Engine.
This is a community supported project, file a bug or feature request:
https://flutter.dev/to/engine-tool-bug.
Usage: et <command> [arguments]
Global options:
-h, --help Print this usage information.
-v, --verbose Prints verbose output
Available commands:
build Builds the engine
fetch Download the Flutter engine's dependencies
format Formats files using standard formatters and styles.
lint Lint the engine repository.
query Provides information about build configurations and tests.
run Run a Flutter app with a local engine build.
test Runs a test target
Run "et help <command>" for more information about a command.
可见 et 提供 build/fetch/format/lint/query/run/test 七类子命令,涵盖从依赖拉取到产物运行的完整链路。
理解构建配置(build configuration)这一核心概念
et 的许多命令都依赖构建配置(build configuration),通常通过 --config(简写 -c)显式指定。一份构建配置至少包含三要素:
- 运行平台约束(
drone_dimensions):标明该构建可在何种机器/设备上运行; - 编译期 GN 参数(
gn):用于配置本次构建的编译开关; - 人类可读的名称与描述(
name、description)。
这些配置在仓库中集中定义于 engine/src/flutter/ci/builders,分为两类:一类是任务特定的 CI 配置文件(如 engine/src/flutter/ci/builders/mac_unopt.json),另一类是仅用于本地开发迭代的 engine/src/flutter/ci/builders/local_engine.json。命令行中按"名字"引用它们,et 会自动补全路径:
# 隐式引用 ci/builders/mac_unopt.json 中的配置
et build --config ci/host_debug_unopt_arm64
# 隐式引用 ci/builders/local_engine.json 中的配置
et build --config host_debug_unopt_arm64
⚠️ 注意:每个构建配置(也叫"变体")会在
$ENGINE/src/out下生成一组独立的输出文件(如$ENGINE/src/out/host_debug),体积可达数 GB 且累积很快。建议定期用et cleanup(见下文高级特性)自动清理旧输出目录。
构建配置文件的底层格式规范可以阅读 engine/src/flutter/ci/builders/README.md,它定义了"引擎构建定义语言":每个 JSON 文件由 builds、tests、generators、archives 组合而成,builds 内部一条记录则至少包含 name、drone_dimensions、gn 等字段,并由 tools/gn(仓库内即 engine/src/flutter/tools/gn)生成后续 Ninja 构建文件。
构建 host 引擎(本机桌面引擎)
最常见且默认的操作是构建 host 变体——即在当前本地桌面操作系统上运行的引擎。例如:ARM64 macOS 笔记本上构建 ARM64 macOS 桌面引擎;x64 Linux 桌面则构建 x64 Linux 桌面引擎。
# 构建当前平台的(host)debug 构建
et build
# 与上面等价
et build --config host_debug
host 引擎适合以下场景:
- 想独立于特定设备/平台去测试、调试或迭代功能;
- 正在开发与当前(桌面)平台强相关的功能;
- 想要把 host 引擎与 target 引擎组合起来运行一个 Flutter 应用。
构建 target 引擎(Android/iOS 等目标平台引擎)
Flutter 引擎同时支持多种 target 引擎——即非当前桌面操作系统的目标引擎,例如 Android 或 iOS。在 macOS 上可以这样构建 iOS 模拟器/真机引擎:
# 构建 iOS 真机(非模拟器)引擎
et build --config ios_debug
# 构建 iOS 模拟器引擎
et build --config ios_debug_sim
按约定,target 引擎的名称不以 host 开头。target 引擎适合:开发特定目标平台的能力,或将 host 与 target 引擎组合运行 Flutter 应用。
构建指定 GN 目标:精确控制编译范围
默认情况下 et build 会构建整个引擎。例如下面两条命令完全等价:
et build --config host_debug
et build --config host_debug //flutter/...
虽然缓存通常会避免重建未变更部分,但当依赖树无法推断"到底哪些需要重编"时,你可以提供 GN 目标的完整路径来构建指定目标:
# 只构建 "flutter.jar" 产物
et build --config android_debug_unopt_arm64 //flutter/shell/platform/android:android_jar
三种目标表达式的差异值得记住:
- 单个目标:
//flutter/shell/platform/android:android_jar——只构建该 target; - 目录内递归构建:
//flutter/shell/platform/...——构建该目录下所有目标的全部依赖; - 目录内非递归构建:
//flutter/shell/platform:all——构建该目录下(不含子目录递归)的全部目标。
# 递归构建 //flutter/shell/platform 下所有目标
et build --config android_debug_unopt_arm64 //flutter/shell/platform/...
# 非递归构建 //flutter/shell/platform 下所有目标
et build --config android_debug_unopt_arm64 //flutter/shell/platform:all
运行 C++ 测试与 Dart 测试
C++ 单元测试可以用 et test 边重建边运行,同样支持 /... 与 :all 两种范围表达式:
et test //flutter/impeller:impeller_unittests
非 C++ 测试目前支持有限。Dart 单元测试也有初步支持(与 C++ 不同,当前并不强制要求为 Dart 测试声明 BUILD.gn 目标,多数包也没有声明,因此该命令会随着 GN 的更广泛采用而逐渐通用化):
et test //flutter/tools/engine_tool/...
运行格式化与 Lint
对改动过的文件运行全部格式化器:
et format
有时依赖或工具的变更会使你并未改动的"脏"文件也通过不了格式检查,此时可对所有文件做全量格式化(会慢很多):
# 检查 *所有* 文件,速度会慢得多!
et format --all
全局 linter 与格式化器类似,用 et lint 运行;当前 linter 总是作用于整个仓库。
使用 et run 以本地引擎运行 Flutter 应用
正常情况下,用预编译引擎运行 Flutter 应用只需:
cd to/project/dir
flutter run
但当你在引擎源码上迭代时,往往想用上面构建出的引擎产物(host + target)来驱动应用。此时进入应用目录改用 et run 即可:
cd to/project/dir
et run
ℹ️ 注意:
et run会在必要时重建 host 与 target 构建,这一步可能相当耗时。
高级特性
启用远程构建执行(RBE)
Google 员工可选择使用远程构建执行(RBE)来大幅加速构建——复用先前构建/缓存的产物,并把编译任务委托给高配远程虚拟机。启用 RBE 后,默认情况下 et 构建会尽量走 RBE,这也隐式要求网络连通。可用 --build-strategy 为单条命令临时切换本地/远程策略:
# 纯本地构建,对部分增量构建可能更快,且不要求联网
et build --build-strategy=local
# 纯远程构建,对本地机器负载更小,需要较快的网络
et build --build-strategy=remote
若要完全禁用 RBE,可用 --no-rbe:
et build --no-rbe
⚠️ 禁用 RBE 会令构建上下文失效:此前(开启 RBE 时)构建的产物不会被复用。除非你在排查工具或 RBE 配置本身的问题,否则更推荐用
--build-strategy=local。
使用自定义引擎配置
多数场景应直接用预置配置(它们已被 CI 广泛测试)。如果确实需要构建未预置的配置,先自问两个问题:
-
我的配置是否代表一组应该在 CI 上测试或供他人复用的 flag 组合? 如果是,最佳做法是把构建加入 engine/src/flutter/ci/builders(作为 CI 构建或仅作为 local_engine.json 本地引擎构建),这样对其他人可复现、有文档、且能在
et中直接使用。 -
我的配置是否只用于一次性测试/验证? 如果是,可在既有配置模板之上,通过
--gn-args传入任意额外的 GN 参数(即那些本应由 tools/gn 解析的参数)。例如启用链接时优化(LTO):et build --config host_release --lto再如使用从源码构建的 Dart SDK(Dart SDK/VM 开发者常用):
et build --config host_debug --gn-args="--no-prebuilt-dart-sdk"
提示:关于构建配置格式的更多信息见 engine/src/flutter/ci/builders/README.md。
回收旧输出目录
et cleanup 会删除默认 30 天内未被访问的旧输出目录,可用 --untouched-since 自定义时间点,并建议先用 --dry-run 预览将被删除的内容:
# 删除所有超过 30 天的输出目录
et cleanup
# 预览上述命令会删除哪些输出目录
et cleanup --dry-run
# 删除所有最后访问时间早于 2023 年(以 2024-01-01 为界)的输出目录
et cleanup --untouched-since=2024-01-01
使用 felt:Web 引擎的构建、测试与调试
环境准备与路径配置
Web 引擎源码位于 engine/src/flutter/lib/web_ui。首次搭建工作区,在完成引擎开发环境准备后,建议把以下目录加入 PATH:
engine/src/flutter/lib/web_ui/dev(即仓库内 engine/src/flutter/lib/web_ui/dev,其中包含felt与felt.bat),以便随处运行felt命令;- Flutter SDK 的
bin目录,以便随处运行dart与flutter命令。
felt 的用法是 felt SUBCOMMAND;可用 felt help 列出子命令,用 felt help SUBCOMMAND 查看某个子命令的帮助。
felt build:构建 Web 引擎目标
build 子命令构建 Web 引擎的 gn/ninja 目标。可以在命令行逐个指定目标;若不指定则构建所有 Web 引擎目标。常见目标包括:
sdk—— flutter_web_sdk 本身;canvaskit—— Flutter 自带的 CanvasKit 版本;canvaskit_chromium—— 针对 Chromium 系浏览器优化的 CanvasKit 版本;skwasm—— 实验性的 Skia WASM 模块渲染器。
这些步骤的输出既供单元测试使用,也可配合 flutter 命令的 --local-web-sdk=wasm_release 使用。build 命令还接受 --profile 或 --debug,用于改变产物的构建 profile。
# 构建全部 Web 引擎目标,然后用它运行 Flutter 应用
felt build
cd path/to/some/app
flutter run -d chrome --local-web-sdk=wasm_release
# 只构建 sdk 与 canvaskit 两个目标
felt build sdk canvaskit
felt test:编译与运行 Web 引擎测试
test 子命令会编译并/或运行 Web 引擎单元测试套件。默认情况下,felt test 编译并运行宿主系统兼容的所有套件。常用 action 类 flag 用于挑选测试流水线中的具体环节(可指定多个、也可不指定;不指定则执行全部动作):
--compile—— 编译测试 bundle;--copy-artifacts—— 复制测试运行所需的构建产物(可搭配--profile或--debug从对应构建目录而非 release 目录复制);--run—— 运行单元测试。
其余辅助 flag:
--list—— 仅列出所有测试套件与测试 bundle 后退出,不编译不运行;--verbose—— 输出额外调试信息;--start-paused—— 打开浏览器窗口并在测试开始前暂停,方便先设断点再启动套件。
还有一组用于筛选测试套件的 flag(不同类型的过滤是逻辑 AND 关系;同一类型的多个过滤 flag 之间是逻辑 OR 关系):
--browser:只跑在这些浏览器上的套件,合法值chrome、firefox、safari、edge;--compiler:只跑使用该编译器的套件,合法值dart2js、dart2wasm;--renderer:只跑使用该渲染器的套件,合法值canvaskit、skwasm;--suite:按套件名运行;--bundle:运行针对特定测试 bundle 的套件。
test 命令也可直接接收一批具体测试文件路径;若给出则只编译并运行这些测试,否则运行全部:
# 在所有兼容浏览器上运行全部测试套件
felt test
# 在所有兼容浏览器上运行指定测试文件
felt test test/engine/util_test.dart
# 一次运行多个测试文件
felt test test/engine/util_test.dart test/engine/alarm_clock_test.dart
# 只运行以 dart2wasm 编译的套件
felt test --compiler dart2wasm
# 只运行在 Chrome 与 Safari 中执行的套件
felt test --browser chrome --browser safari
优化本地构建并发
构建各步骤的并发度可通过环境变量调节:FELT_COMPILE_CONCURRENCY 指定用于编译测试的并发编译进程数,默认值为 8。
调试 Web 引擎
先在本地构建 Web 引擎产物,然后在 debug 模式下用本地产物运行 Flutter 应用:
felt build
运行应用有两种方式可选:
- 方式一:从命令行拉起 Chrome 窗口
退出flutter run --local-web-sdk=wasm_release --debug -d chromeflutter run会同时关闭应用的 Chrome 窗口。 - 方式二:在 8080 端口启动 Web Server
然后浏览器访问flutter run --local-web-sdk=wasm_release --debug -d web-server --web-port 8080http://localhost:8080查看应用。当你希望重启flutter run时保留浏览器窗口,或需要调试flutter run不支持的浏览器(如 Firefox、Safari)时,这种方式更合适。
打开 Web 引擎源码:在 Chrome 窗口中右键并选择 Inspect 打开 Chrome DevTools,切到 Sources 面板,Flutter Web 引擎的 Dart 源码位于 localhost:<port> > lib > _engine > engine,可直接在 Dart 源文件上打断点,并用 Chrome 调试器查看变量值。
构建 CanvasKit 与 Skwasm
要在本地构建 CanvasKit/Skwasm,需要先把 gclient 配置调整为启用 Emscripten SDK(编译 CanvasKit/Skwasm 的工具链),之后执行 gclient sync 拉取并激活 Emscripten SDK。构建命令为:
felt build canvaskit
这会构建到 out/wasm_debug。随后执行 felt test 时会自动探测到你已构建的 CanvasKit,并用它(而不是从 CIPD 拉取的版本)来跑测试。引擎内与 Emscripten SDK 版本相关的维护点位于 engine/src/flutter/tools/activate_emsdk.py(EMSDK_VERSION 定义处)。
使用本地构建的 Dart SDK
只需设置 DART_SDK_DIR 环境变量即可让 felt 使用本地 Dart SDK:
DART_SDK_DIR=path/to/dart-sdk/ felt test
注意:提供的 Dart SDK 用于运行 felt 自身,以及编译和运行测试;但 felt build 走的是 gn 构建,不受 DART_SDK_DIR 影响。
引擎测试与 CI 构建定义:从本地到持续集成
引擎 README 把"测试"单独列为开发者应了解的入口。在仓库层面,引擎的测试组织与 CI 构建定义有两条脉络:
- 本地测试命令行:如上文所述,C++ 测试用
et test //flutter/...:<target>(如//flutter/impeller:impeller_unittests),Dart 测试用et test //flutter/tools/engine_tool/...等;Web 引擎套件则由felt test驱动。 - CI 构建定义(引擎 v2):所有引擎构建与测试的"配方"以 JSON 形式存放在 engine/src/flutter/ci/builders。该目录下的 README(engine/src/flutter/ci/builders/README.md)完整定义了"Flutter 引擎构建定义语言":顶层 JSON 由
builds、tests、generators、archives组成;builds内每个子构建描述gn参数、ninja目标、本地测试、产出物归档(默认会归档到 CAS)、drone_dimensions(选择运行机器)等。例如一份典型 build 记录的字段骨架为:
{
"archives": [],
"drone_dimensions": [],
"gclient_variables": {},
"gn": [],
"name": "host_debug",
"generators": [],
"ninja": {},
"tests": [],
"postsubmit_overrides": {}
}
配合仓库根目录引擎代码(engine/src/flutter/BUILD.gn)及各模块下 BUILD.gn 中声明的测试 target,你可以从"本地一条 et test 命令"一直追到"CI 上某配置文件里声明的同名测试脚本",形成完整可追踪的测试链路。
引擎工具开发与贡献须知
如果你希望为 et 本身贡献代码,工具 README(engine/src/flutter/tools/engine_tool/README.md)给出几条关键约定:
- Dart 代码遵循 Flutter 仓库风格指南(超出代码格式化层面的约定);
- 不要在
main.dart之外直接调用dart:io,对系统的访问一律通过Environment对象完成; - 所有命令都必须有单元测试;需要 fake 实现就编写 fake 实现;
- 新增或变更功能时,同步更新该 README;
- "以终为始"——先从本工具应有的接口出发设计,再修改底层脚本/工具以提供支撑 API。
运行 et 自身测试:
et test //flutter/tools/engine_tool/...
至此,从引擎的定义与仓库布局,到 et 的构建/测试/运行、felt 的 Web 引擎构建与调试,再到 CI 构建定义语言,你已经掌握了一条完整的 Flutter Engine 本地开发主线。无论你是要改一条渲染路径、调一个平台通道,还是想为引擎贡献首个补丁,都可以基于本文给出的命令与文件路径直接开工。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00