Flutter定制引擎

 

由于flutter官方引擎有时候会有一些问题,但是又不好直接大范围升级flutter版本。且flutter引擎是相对独立的版块,所以定制引擎就有了市场。

背景

2.8.1flutter引擎层在iOS的platform view层,shell调用时机有问题。导致线上会有相应的引擎层crash。 官方已经修复,但是需要升级至2.10.3+,我们暂时不升级,采用iOS自定义引擎的方式解决。

官方issue 官方已经修复了改问题,并已经合并代码到master

修复的pull request:https://github.com/flutter/engine/pull/30835/commits

bufix的描述:https://github.com/flutter/flutter/issues/95844

合并到引擎commit记录:https://github.com/flutter/engine/commit/fb3ee7f2b5e6e537a2a83c9fe2cf733cd9c6ec06

在高版本,2.10.2(2.10.3+)以上此类问题已修复。

2.8.1 Cherrypicks:https://github.com/flutter/engine/pull/30355

一 环境准备

  • git
  • Python
  • Xcode
  • depot_tools

    depot_tools 安装(需要全局VPN否则很慢)

    创建目录 /Users/xx/Desktop/flutter-engine

    cd /Users/xx/Desktop/flutter-engine
    git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git 
    

    配置环境 (~/.zshrc或者~/.bashrc)

    export PATH=/Users/xx/Desktop/flutter-engine/depot_tools:$PATH
    

    depot_tools安装后的几个工具,gclient、gn和ninja工具介绍

    | 工具 | 描述 | 作用 | | — | — | — | | gclient | 可以理解为代码以及依赖管理工具 | 它的作用类似 git 的 submodule,用来将多个git仓库组成一个solution进行管理。 | | gn和ninja | 构建工具,类似(make+makefile,这个我们也接触很少) | gn元构建系统(meta build system),只输出 Ninja 构建文件。ninja需要gn输出NinjaBuild files。 |

二 环境配置

gclient 配置

/Users/xxx/Desktop/flutter-engine目录engine文件夹创建.gclient文件

solutions = [
  {
    "managed": False,
    "name": "src/flutter",
    "url": "git@gitlab.xxx.cn:Group/xx_flutter_engine.git@hash-adafbaaf",
    "custom_deps": {},
    "deps_file": "DEPS",
    "safesync_url": "",
  },
]

url字段是你需要的引擎commit。

同步依赖

cd /Users/xxx/Desktop/flutter-engine/engine
gclient sync

执行这一步,国内很多用户会很慢,所以建议先下从flutter引擎的git仓库下载好项目,然后把内容(不包括名称)复制到src/flutter下面,再执行gclient sync,而且建议开全局代理,在zsh文件中(笔者使用的是zsh)添加:

#export http_proxy=http://127.0.0.1:9999
#export https_proxy=http://127.0.0.1:9999

这里的9999是vpn全局的端口号,#是笔者使用完注释回去。否则会影响其他网络

三 编译产物

执行gn,生成源码工程

cd到src目录下:

cd /Users/xxx/Desktop/flutter-engine/engine/src

# --simulator就是模拟器
# --ios-cpu=arm就是armv7,不指定默认就是arm64
flutter/tools/gn --ios --unoptimized

在src/out/ios_debug_unopt目录下,会生成一个 Xcode 项目,用于debug时候使用引擎源代码。

flutter/tools/gn --unoptimized

会生成src/out/host_debug_unopt,我在debug引擎时候发现需要里面的dart-sdk,所以这个也需要编译一下。如果没有修改dart层代码就不需要执行这一步。

不同命令的组合:

gn mode & arch out
flutter/tools/gn –unoptimized   src/out/host_debug_unopt
flutter/tools/gn –ios –unoptimized debug arm64 src/out/ios_debug_unopt
flutter/tools/gn   src/out/host_debug
flutter/tools/gn –ios –simulator debug simulator src/out/ios_debug_sim
flutter/tools/gn –ios –ios-cpu arm debug armv7 src/out/ios_debug_arm
flutter/tools/gn –ios debug arm64 src/out/ios_debug
flutter/tools/gn –ios –ios-cpu arm –runtime-mode profile profile armv7 src/out/ios_profile_arm
flutter/tools/gn –ios –runtime-mode profile profile arm64 src/out/ios_profile
flutter/tools/gn –ios –ios-cpu arm –runtime-mode release release armv7 src/out/ios_release_arm
flutter/tools/gn –ios –runtime-mode release release arm64 src/out/ios_release

执行ninja,生成framework产物

ninja -C out/ios_debug_unopt && ninja -C out/host_debug_unopt

编译过程很漫长,可以在ninja后面加上参数-j 2,避免编译占用过多的电脑资源,影响你开发。

四 创建xcframework和gen_snapshots

编译完成之后,就要创建最后的Flutter.xcframework 官方也有提供python脚本工具,src/flutter/sky/tools目录下的:create_ios_framework.py和create_macos_gen_snapshots.py

创建Flutter.xcframework:release版本加–dsym参数会生成dysm符号表

cd  ~/Desktop/flutter-engine/engine/src/flutter/sky/tools

# debug版本Flutter.xcframework
python create_ios_framework.py --dst  ~/Desktop/flutter-engine/engine/src/out/vd/ios --arm64-out-dir  ~/Desktop/flutter-engine/engine/src/out/ios_debug --armv7-out-dir  ~/Desktop/flutter-engine/engine/src/out/ios_debug_arm --simulator-out-dir  ~/Desktop/flutter-engine/engine/src/out/ios_debug_sim

# profile版本Flutter.xcframework
python create_ios_framework.py --dst  ~/Desktop/flutter-engine/engine/src/out/vd/ios-profile --arm64-out-dir  ~/Desktop/flutter-engine/engine/src/out/ios_profile --armv7-out-dir  ~/Desktop/flutter-engine/engine/src/out/ios_profile_arm --simulator-out-dir  ~/Desktop/flutter-engine/engine/src/out/ios_debug_sim

# release版本Flutter.xcframework
python create_ios_framework.py --dst  ~/Desktop/flutter-engine/engine/src/out/vd/ios-release --arm64-out-dir  ~/Desktop/flutter-engine/engine/src/out/ios_release --armv7-out-dir  ~/Desktop/flutter-engine/engine/src/out/ios_release_arm --simulator-out-dir  ~/Desktop/flutter-engine/engine/src/out/ios_debug_sim --dsym


# 每个版本都加上了模拟器的部分,便于使用方模拟器调试

创建gen_snapshots

cd  ~/Desktop/flutter-engine/engine/src/flutter/sky/tools

# debug版本gen_snapshot_arm64和gen_snapshot_armv7
python create_macos_gen_snapshots.py --dst  ~/Desktop/flutter-engine/engine/src/out/vd/ios --arm64-out-dir  ~/Desktop/flutter-engine/engine/src/out/ios_debug --armv7-out-dir  ~/Desktop/flutter-engine/engine/src/out/ios_debug_arm

# profile版本gen_snapshot_arm64和gen_snapshot_armv7
python create_macos_gen_snapshots.py --dst  ~/Desktop/flutter-engine/engine/src/out/vd/ios-profile --arm64-out-dir  ~/Desktop/flutter-engine/engine/src/out/ios_profile --armv7-out-dir  ~/Desktop/flutter-engine/engine/src/out/ios_profile_arm

# release版本gen_snapshot_arm64和gen_snapshot_armv7
python create_macos_gen_snapshots.py --dst  ~/Desktop/flutter-engine/engine/src/out/vd/ios-release --arm64-out-dir  ~/Desktop/flutter-engine/engine/src/out/ios_release --armv7-out-dir  ~/Desktop/flutter-engine/engine/src/out/ios_release_arm

# 模拟器是JIT的,不需要gen_snapshot

五 总结

最终我们需要armv7、arm64、x86-64的产物,组合命令如下:

//1 执行这一部分命令生成不同架构的产物
cd ~/Desktop/flutter-engine/engine/src

./flutter/tools/gn --unoptimized	

./flutter/tools/gn --ios --runtime-mode=debug --simulator	
./flutter/tools/gn --ios --runtime-mode=release	
./flutter/tools/gn --ios --runtime-mode=release	--ios-cpu=arm

ninja -C out/host_debug_unopt && ninja -C out/ios_debug_sim && ninja -C out/ios_release && ninja -C out/ios_release_arm


//2 执行这一部分命令生成包含所有架构的产物
cd ~/Desktop/flutter-engine/engine/src/flutter/sky/tools

python3 create_ios_framework.py \
--dst ~/Desktop/flutter-engine/engine/src/out/vd/ios-release \
--arm64-out-dir ~/Desktop/flutter-engine/engine/src/out/ios_release \
--armv7-out-dir ~/Desktop/flutter-engine/engine/src/out/ios_release_arm \
--simulator-out-dir ~/Desktop/flutter-engine/engine/src/out/ios_debug_sim \
--dsym

//2 执行这一部分命令生成gen_snapshots文件
python3 create_macos_gen_snapshots.py \
--dst ~/Desktop/flutter-engine/engine/src/out/vd/ios-release \
--arm64-out-dir ~/Desktop/flutter-engine/engine/src/out/ios_release \
--armv7-out-dir ~/Desktop/flutter-engine/engine/src/out/ios_release_arm

整个编译过程工程目录结构:

flutter_engine1

/flutter-engine:自己创建的引擎目录 /flutter-engine/depot_tools:depot_tools工具目录 /flutter-engine/engine:引擎目录 /flutter-engine/engine/.gclient:gclient配置文件 /flutter-engine/engine/src:https://github.com/flutter/engine代码存放目录 /flutter-engine/engine/src/out:不同架构源码工程目录 /flutter-engine/engine/src/out/vd/ios-release:聚合产物目录

参考文档

【官方】Compiling the engine: https://github.com/flutter/flutter/wiki/Compiling-the-engine#compiling-for-ios-from-macos

【官方】Creating an iOS Bitcode enabled app: https://github.com/flutter/flutter/wiki/Creating-an-iOS-Bitcode-enabled-app

【官方】符号表:https://console.cloud.google.com/storage/browser/flutter_infra_release/flutter/b3af521a050e6ef076778bcaf16e27b2521df8f8;tab=objects?prefix=&forceOnObjectsSortingFiltering=false