Flutter Engine 编译与调试

1,027 阅读5分钟

Flutter Engine 调试

进行 engine 定制化 ,下载引擎的过程中,需要网络良好,不过失败了,可以继续下载,需要科学上网。

1.depot_tools 安装

git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git 配置环境变量:export PATH=$PATH:/path/to/depot_tools

2.源码 Clone (实际这里只是下载一份源码,建议还是从 gitee 下载,这样速度快一些)

  • fork Flutter Engine 到自己的 Github 仓库
  • 创建引擎存放目录、添加 .gclient 文件
mkdir engine 
cd engine 
touch .gclient
  • gclient 内容如下(替换为自己的 Github Flutter Engine 仓库)
// url 改为自己的仓库( fork 过的) 
solutions = [ { "managed": False, "name": "src/flutter", "url": "git@github.com:<YOUR_NAME>/engine.git", "custom_deps": {}, "deps_file": "DEPS", "safesync_url": "", }, ]
  • gclient sync
gclient sync
  • 同步代码过程较为漫长(总共22G左右),当进度为100%时,依然会下载4~5个G的内容,请不要中断,可以在活动监视器中观察网络使用情况。

3.与官方仓库关联

  • 查看当前远程仓库
cd src/flutter 
git remote -v 
origin git@github.com:<YOUR_NAME>/engine.git (fetch) 
origin git@github.com:<YOUR_NAME>/engine.git (push)
  • 添加指向官方仓库的 upstream
git remote add upstream git@github.com:flutter/engine.git
  • 查看 origin 和 upstream
cd src/flutter 
git remote -v 
origin <git@github.com>:\<YOUR\_NAME>/engine.git (fetch) 
origin <git@github.com>:\<YOUR\_NAME>/engine.git (push)
upstream git@github.com:flutter/engine.git (fetch) 
upstream git@github.com:flutter/engine.git (push)

-从原仓库拉取代码并直接合并代码

git pull upstream
  1. 匹配版本
  • 在实际开发中,一般不直接使用 master 的代码直接编译,都是需要获取指定版本的 engine 代码。可以通过本地安装的 Flutter SDK 版本来获取所对应的 engine 版本
fvm global 3.22.2 //切换通道到制定版本 
cat dev/flutter/bin/internal/engine.version //获取当前版本commit id edd8546116457bdf1c5bdfb13ecb9463d2bb5ed4
  • 切换分支同步代码
cd engine/src/flutter 
git reset --hard edd8546116457bdf1c5bdfb13ecb9463d2bb5ed4 
//本次 sync 时间较长(本人梯子6-8M/s要等待大概10分钟左右),依然要同步4~5个G的内容,请耐心等待。我的经验是关注活动监视器网络情况,如果收到数据速度小于梯子正常速度,中断后再次执行同步命令即可。 
gclient sync -D --with\_branch\_heads --with\_tags -v

5.编译

  • 创建编译的工程 (其他参数还可以拼接)
    • Android
./flutter/tools/gn --android --unoptimized 
./flutter/tools/gn --android --unoptimized --android-cpu=arm64 ./flutter/tools/gn --android --runtime-mode=release 
./flutter/tools/gn --android --android-cpu=arm64 --runtime-mode=release
// 这里编译了多个版本,如果测试学习编译一个也行,这样还快点
// 启动的核心数,最好不要超了 cpu 的核心数 
ninja -C out/android_debug_unopt -j 6 
ninja -C out/android_debug_unopt_arm64 -j 6 
ninja -C out/android_release -j 6 
ninja -C out/android_release_arm64 -j 6
  • 增量编译
    • 各平台首次编译时间较长,大概30-60分钟,以后改动代码后再次编译为增量更新,大大缩短编译时间

6.测试编译后的引擎

  • 创建 flutter 应用
flutter create exec_customer_engine
  • 在pubspec.yaml文件添加如下依赖
dependency_overrides:
  sky_engine:
    path: /Users/alan/.fvm/engine/src/out/android_debug_unopt_arm64/gen/dart-pkg/sky_engine
  • 添加启动参数
//一定要添加 --local-engine-host ,否则会失败 
//--local-engine-src-path 引擎的路径 
// --local-engine 引擎的版本 
// --local-engine-host 引擎的 host 
cd exec_customer_engine flutter run --local-engine-src-path /Users/alan/.fvm/engine/src --local-engine=android_debug_unopt_arm64 --local-engine-host=android_debug_unopt_arm64
  • 或将启动参数配置在 IDE 中

image.png

  1. 验证引擎
  • 如何验证编译 App 的时候,确实是用了我们自编译的 Flutter Engine 呢?我们可以修改 Flutter Eingine ,加一些日志输出看看。使用 Xcode 打开 engine/src/out/android_debug_unopt_arm64/flutter_engine.xcodeproj 工程文件 然后,打开 代码文件 engine/src/flutter/shell/common/engine.cc 源代码文件。 image.png
  • 再次编译引擎
ninja -C out/android\_debug\_unopt\_arm64
  • 再次使用自编译的引起启动 app ,观察控制台输出 image.png
  1. Engine源码调试
  • 对于Engine的调试相对来说就会复杂一些,简单说明下,调试时候,源码在PC端,而代码运行在具体设备(Android/IOS等),那么挑食过程必然有数据通信和通信协议,比如Java代码调试常见的Socket和JDWP,而Engine是基于C++开发,很多步骤要手动完成。此外会准备一个Flutter项目方便后续调试,下面提到的 com.example.exec_customer_engine 是一个Flutter的默认计数器demo项目。
  1. Engine 源码调 LLDB 配置步骤大致如下
  • lldb 位置
// step 1 lldb 位置
/Users/alan/.fvm/engine/src/third\_party/android\_tools/ndk/toolchains/llvm/prebuilt/darwin-x86\_64/lib/clang/17.0.2/lib/linux/aarch64/lldb-server
// step 2 找到lldb-server所在目录位置,并将其推送到手机中 我用的是Flutter Engine 编译产物目录下的lldb-server,执行adb命令,将lldb-server push到临时文件夹
adb push /Users/alan/.fvm/engine/src/third\_party/android\_tools/ndk/toolchains/llvm/prebuilt/darwin-x86\_64/lib/clang/17.0.2/lib/linux/aarch64/lldb-server /data/local/tmp/lldb-server
// step 3 将lldb-server复制到app的私有目录 执行命令如下
adb shell run-as com.example.exec\_customer\_engine cp -F /data/local/tmp/lldb-server /data/data/com.example.exec\_customer\_engine/lldb-server
// step 4 赋予lldb-server可执行权限
adb shell run-as com.example.exec\_customer\_engine \chmod a+x /data/data/com.example.exec\_customer\_engine/lldb-server
// step 5 启动lldb-server,这样设备就可以接受相应调试命令了
adb shell "run-as com.example.exec\_customer\_engine sh -c '/data/data/com.example.exec\_customer\_engine/lldb-server platform --server --listen unix-abstract:///data/data/com.example.exec\_customer\_engine/debug.socket'"
// step 6 获取待调试应用进程id 先使用自己编译的Flutter Engine启动demo工程 在demo目录下执行命令
flutter run --local-engine-src-path /Users/alan/.fvm/engine/src --local-engine=android\_debug\_unopt\_arm64 --local-engine-host=android\_debug\_unopt\_arm64
// step 7 执行命令获取pid
adb shell pidof com.example.exec\_customer\_engine

到这里 LLDB 的配置就完成了,我们可以基于上面的配置,通过 VS Code 断点调试 Flutter Engine 源码

  1. 使用 VS Code 源码调试
  • 使用 VS Code 打开 Engine 所在目录
  • 配置 launch.json 文件
{ "version": "0.2.0", "configurations": \[ { "type": "lldb", "request": "attach", "name": "android\_attach", "pid": "16649", "initCommands": \[ "platform select remote-android", "platform connect unix-abstract-connect:///data/data/com.example.exec\_customer\_engine/debug.socket" ], "postRunCommands": \[ "add-dsym /Users/alan/.fvm/engine/src/out/android\_debug\_unopt\_arm64/libflutter.so", "settings set target.source-map /Users/alan/.fvm/engine/src/out/android\_debug\_unopt\_arm64 /Users/alan/.fvm/engine/src/" ] } ] }
  • 设置断点,F5 开始调试。 我将断点打到了 src/flutter/lib/ui/text/paragraph.cc 点击 demo 的+号,触发断点 image.png
  1. 参考: