Flutter Engine与SDK的定制化与编译

4,170 阅读10分钟

概述

对于Flutter SDK相关的定制化,也就是两个地方,分别为Flutter Engine与Flutter SDK,对于定制化后的应用,则需要我们重新编译相关的代码产物,主要为Flutter Engine的编译和Flutter Tools的编译

Flutter Engine编译

编译配置

配置Depot_tools脚本工具集

获取Chromium的depot_tools脚本工具集, depot_tools是Google用来管理Chromium源码的工具集,它包含了一系列实用脚本,如:gclient、ninja、repo等。gclient和repo都是用来检出项目源码的脚本,主要区别是gclient主要依赖于.gclientDEPS这两个配置文件进行多项目模块源码的依赖检出,而repo主要依赖于一个manifest.xml配置文件来进行多项目模块源码检出,gclient定制性相对高些,也更复杂些

获取depot_tools脚本工具集可以通过Git获取:

$ git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git

并配置到环境变量中:

$ export PATH=$PATH:/path/to/depot_tools

构建Flutter Engine产物使用的构建系统是Ninja,Chromium的构建也是使用它,它是一个专注于速度的小型构建系统,即它的输入文件是由更高级别的构建系统生成的产物,而 GN就是一个专门生成Ninja构建文件的元构建系统,所以构建Flutter Engine的步骤是先用GN构建Ninja的输入文件,再由Ninja构建最终产物,即:

$ ./flutter/tools/gngn gen --check in out/path$ ninja -C out/path

检出Flutter Engine源码

Fork并检出Flutter Engine源码,并重新命名为engine,这里需要Fork是因为后续可能会对Flutter Engine进行一些定制化

$ git clone https://github.com/Sunzxyong/flutter

配置代理

在检出Flutter Engine所有源码的过程中,往往会出现如下或其它一些类似异常:

[P27678 17:18:58.373 client.go:311 W] RPC failed transiently. Will retry in 1m4s  {"error":"failed to send request: Post https://chrome-infra-packages.appspot.com/prpc/cipd.Repository/GetInstanceURL: EOF", "host":"chrome-infra-packages.appspot.com", "method":"GetInstanceURL", "service":"cipd.Repository", "sleepTime":"1m4s"}Error: failed to update packages, see the log.Error: Command '/usr/local/Cellar/python@2/2.7.15_3/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python src/tools/buildtools/update.py' returned non-zero exit status 1 in /Users/Sunzxyong/Library/Flutter/engineHook '/usr/local/Cellar/python@2/2.7.15_3/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python src/tools/buildtools/update.py' took 307.37 secs

这些异常的导致几乎都是网络环境被墙问题,所以需要配置下代理,主要配置的地方有两个,一是github的代理,二是终端的http(s)代理

1、配置Github的代理通过编辑hosts配置文件进行配置:

MacBook-Pro-3:~ Sunzxyong$ sudo vim /etc/hosts

然后添加github.com域名与其ip地址的对应关系

2、配置终端的代理终端的代理,我们通过export环境变量的方式,这样只对当前终端进程有效,而不影响后续终端的使用,然后在当前终端切换到Flutter Engine目录便可准备编译,即:

MacBook-Pro-3:~ Sunzxyong$ export http_proxy=http://127.0.0.1:1087MacBook-Pro-3:~ Sunzxyong$ export https_proxy=http://127.0.0.1:1087MacBook-Pro-3:~ Sunzxyong$ cd /Users/Sunzxyong/Library/Flutter/engine

配置gclient依赖与编译分支

engine目录中,新建一个 .gclient文件,内容如下:

solutions = [  {    "managed": False,    "name": "src/flutter",    "url": "git@github.com:Sunzxyong/engine.git",    "custom_deps": {},    "deps_file": "DEPS",    "safesync_url": "",  },]

这里对应的url的值修改为上述自己Fork的Flutter Engine的Git地址即可,其它保持不变

先确保当前目录是Engine目录,然后执行下面命令进行同步Flutter Engine所依赖的所有代码:

MacBook-Pro-3:~ Sunzxyong$ cd /path/to/engineMacBook-Pro-3:engine Sunzxyong$ gclient sync

接下来即是切换Flutter Engine对应的编译分支,因为默认拉下来的是master分支,如果我们对Flutter Engine进行了定制化,肯定不能在master分支上直接进行编译,所以需要切换成我们进行了定制化的分支或当前工程中所使用的Flutter SDK对应的Engine的分支,查看当前Flutter SDK使用的是哪个版本的Engine,在Flutter SDK中的位置为:

bin/internal/engine.version

这个值是无论我们进不进行定制化都得拿到,不进行定制化我们可以直接切换到这个值所对应的版本,而进行定制化的话,因为定制化肯定得基于当前使用的版本作为基线版本进行定制化,所以定制化后要切换分支即是我们自己commit id的值

如下是我获取到v1.2.2版本的Flutter SDK依赖的Engine的commit id的值:

f1f19bba8f0089490962316867bd222727510ac5

接下来进行分支切换,首先切换到engine/src/flutter目录,然后依次执行:

MacBook-Pro-3:~ Sunzxyong$ cd /path/to/engine/src/flutterMacBook-Pro-3:flutter Sunzxyong$ git reset --hard f1f19bba8f0089490962316867bd222727510ac5MacBook-Pro-3:flutter Sunzxyong$ gclient sync --with_branch_heads --with_tags

其中gclient sync --with_branch_heads --with_tags这段命令含义为:

Checkout all the submodules at their branch DEPS revisions.

编译Engine

编译Flutter Engine使用的是GNNinja进行编译,GN编译后生成Ninja的构建文件,Ninja将输入文件进行编译成最终产物,如下是GN的编译选项:

usage: gn [-h] [--unoptimized] [--runtime-mode {debug,profile,release}]          [--dynamic] [--interpreter] [--dart-debug]          [--target-os {android,ios,linux}] [--android]          [--android-cpu {arm,x64,x86,arm64}] [--ios] [--ios-cpu {arm,arm64}]          [--simulator] [--linux-cpu {x64,x86,arm64,arm}]          [--arm-float-abi {hard,soft,softfp}] [--goma] [--no-goma] [--lto]          [--no-lto] [--clang] [--no-clang] [--target-sysroot TARGET_SYSROOT]          [--target-toolchain TARGET_TOOLCHAIN]          [--target-triple TARGET_TRIPLE]          [--toolchain-prefix TOOLCHAIN_PREFIX]          [--operator-new-alignment OPERATOR_NEW_ALIGNMENT] [--enable-vulkan]          [--embedder-for-target] [--coverage] [--out-dir OUT_DIR]

上述的编译选项可以自由组合成非常多种的编译组合,其中--target-os指定编译产物的平台与 --android--ios是等价的,通过GN进行的预编译生成Ninja的构建文件,除了预编译指定平台的构建文件之外,还需要预编译Host的构建文件(即PC编译平台的),当通过Ninja进行最终产物的编译时,会用到指定平台的构建文件和Host的构建文件进行编译

开始进行编译,需要把目录切换到engine的src目录,然后进行编译:

MacBook-Pro-3:~ Sunzxyong$ cd /path/to/engine/srcMacBook-Pro-3:src Sunzxyong$ ./flutter/tools/gn ...MacBook-Pro-3:src Sunzxyong$ ninja -C out/...

其中x86的编译产物,在MacOS上需要XCode版本在9.4以下版本才能进行编译,如果需要x86的产物,可以看官方的编译x86产物的兼容编译配置:Supporting-legacy-platforms

如下是常用的编译组合,包含Android和IOS两个平台以及对应cpu架构类型,分别为:未优化的Debug、未优化的Profile、未优化的Release、优化的Debug、优化的Profile、优化的Release,更多的编译组合请看Flutter's modes

编译Android产物

# unopt-debug# prepare build files for device-side executables../flutter/tools/gn --unoptimized --android --runtime-mode debug --android-cpu arm./flutter/tools/gn --unoptimized --android --runtime-mode debug --android-cpu arm64./flutter/tools/gn --unoptimized --android --runtime-mode debug --android-cpu x64# prepare the build files for host-side executables../flutter/tools/gn --unoptimized --runtime-mode debug --android-cpu arm ./flutter/tools/gn --unoptimized --runtime-mode debug --android-cpu arm64./flutter/tools/gn --unoptimized --runtime-mode debug --android-cpu x64ninja -C out/android_debug_unoptninja -C out/android_debug_unopt_arm64ninja -C out/android_debug_unopt_x64ninja -C out/host_debug_unoptninja -C out/host_debug_unopt_arm64ninja -C out/host_debug_unopt_x64# unopt-profile./flutter/tools/gn --unoptimized --android --runtime-mode profile --android-cpu arm./flutter/tools/gn --unoptimized --android --runtime-mode profile --android-cpu arm64./flutter/tools/gn --unoptimized --android --runtime-mode profile --android-cpu x64./flutter/tools/gn --unoptimized --runtime-mode profile --android-cpu arm./flutter/tools/gn --unoptimized --runtime-mode profile --android-cpu arm64./flutter/tools/gn --unoptimized --runtime-mode profile --android-cpu x64ninja -C out/android_profile_unoptninja -C out/android_profile_unopt_arm64ninja -C out/android_profile_unopt_x64ninja -C out/host_profile_unoptninja -C out/host_profile_unopt_arm64ninja -C out/host_profile_unopt_x64# unopt-release./flutter/tools/gn --unoptimized --android --runtime-mode release --android-cpu arm./flutter/tools/gn --unoptimized --android --runtime-mode release --android-cpu arm64./flutter/tools/gn --unoptimized --android --runtime-mode release --android-cpu x64./flutter/tools/gn --unoptimized --runtime-mode release --android-cpu arm./flutter/tools/gn --unoptimized --runtime-mode release --android-cpu arm64./flutter/tools/gn --unoptimized --runtime-mode release --android-cpu x64ninja -C out/android_release_unoptninja -C out/android_release_unopt_arm64ninja -C out/android_release_unopt_x64ninja -C out/host_release_unoptninja -C out/host_release_unopt_arm64ninja -C out/host_release_unopt_x64# opt-debug./flutter/tools/gn --android --runtime-mode debug --android-cpu arm./flutter/tools/gn --android --runtime-mode debug --android-cpu arm64./flutter/tools/gn --android --runtime-mode debug --android-cpu x64./flutter/tools/gn --runtime-mode debug --android-cpu arm./flutter/tools/gn --runtime-mode debug --android-cpu arm64./flutter/tools/gn --runtime-mode debug --android-cpu x64ninja -C out/android_debugninja -C out/android_debug_arm64ninja -C out/android_debug_x64ninja -C out/host_debugninja -C out/host_debug_arm64ninja -C out/host_debug_x64# opt-profile./flutter/tools/gn --android --runtime-mode profile --android-cpu arm./flutter/tools/gn --android --runtime-mode profile --android-cpu arm64./flutter/tools/gn --android --runtime-mode profile --android-cpu x64./flutter/tools/gn --runtime-mode profile --android-cpu arm./flutter/tools/gn --runtime-mode profile --android-cpu arm64./flutter/tools/gn --runtime-mode profile --android-cpu x64ninja -C out/android_profileninja -C out/android_profile_arm64ninja -C out/android_profile_x64ninja -C out/host_profileninja -C out/host_profile_arm64ninja -C out/host_profile_x64# opt-release./flutter/tools/gn --android --runtime-mode release --android-cpu arm./flutter/tools/gn --android --runtime-mode release --android-cpu arm64./flutter/tools/gn --android --runtime-mode release --android-cpu x64./flutter/tools/gn --runtime-mode release --android-cpu arm./flutter/tools/gn --runtime-mode release --android-cpu arm64./flutter/tools/gn --runtime-mode release --android-cpu x64ninja -C out/android_releaseninja -C out/android_release_arm64ninja -C out/android_release_x64ninja -C out/host_releaseninja -C out/host_release_arm64ninja -C out/host_release_x64

编译IOS产物

其中IOS编译选项中,有个--simulator编译选项,是编译IOS模拟器的产物,如有需要可在下属编译命令中加上该选项

# unopt-debug# prepare build files for device-side executables../flutter/tools/gn --unoptimized --ios --runtime-mode debug --ios-cpu arm./flutter/tools/gn --unoptimized --ios --runtime-mode debug --ios-cpu arm64# prepare the build files for host-side executables../flutter/tools/gn --unoptimized --runtime-mode debug --ios-cpu arm./flutter/tools/gn --unoptimized --runtime-mode debug --ios-cpu arm64ninja -C out/ios_debug_unopt_armninja -C out/ios_debug_unoptninja -C out/host_debug_unopt_armninja -C out/host_debug_unopt# unopt-profile./flutter/tools/gn --unoptimized --ios --runtime-mode profile --ios-cpu arm./flutter/tools/gn --unoptimized --ios --runtime-mode profile --ios-cpu arm64./flutter/tools/gn --unoptimized --runtime-mode profile --ios-cpu arm./flutter/tools/gn --unoptimized --runtime-mode profile --ios-cpu arm64ninja -C out/ios_profile_unopt_armninja -C out/ios_profile_unoptninja -C out/host_profile_unopt_armninja -C out/host_profile_unopt# unopt-release./flutter/tools/gn --unoptimized --ios --runtime-mode release --ios-cpu arm./flutter/tools/gn --unoptimized --ios --runtime-mode release --ios-cpu arm64./flutter/tools/gn --unoptimized --runtime-mode release --ios-cpu arm./flutter/tools/gn --unoptimized --runtime-mode release --ios-cpu arm64ninja -C out/ios_release_unopt_armninja -C out/ios_release_unoptninja -C out/host_release_unopt_armninja -C out/host_release_unopt# opt-debug./flutter/tools/gn --ios --runtime-mode debug --ios-cpu arm./flutter/tools/gn --ios --runtime-mode debug --ios-cpu arm64./flutter/tools/gn --runtime-mode debug --ios-cpu arm./flutter/tools/gn --runtime-mode debug --ios-cpu arm64ninja -C out/ios_debug_armninja -C out/ios_debugninja -C out/host_debug_armninja -C out/host_debug# opt-profile./flutter/tools/gn --ios --runtime-mode profile --ios-cpu arm./flutter/tools/gn --ios --runtime-mode profile --ios-cpu arm64./flutter/tools/gn --runtime-mode profile --ios-cpu arm./flutter/tools/gn --runtime-mode profile --ios-cpu arm64ninja -C out/ios_profile_armninja -C out/ios_profileninja -C out/host_profile_armninja -C out/host_profile# opt-release./flutter/tools/gn --ios --runtime-mode release --ios-cpu arm./flutter/tools/gn --ios --runtime-mode release --ios-cpu arm64./flutter/tools/gn --runtime-mode release --ios-cpu arm./flutter/tools/gn --runtime-mode release --ios-cpu arm64ninja -C out/ios_release_armninja -C out/ios_releaseninja -C out/host_release_armninja -C out/host_release

编译产物输出路径

编译后的产物输出路径在/path/to/engine/src/out目录下,在对应的构建选项目录下分别有 flutter.jarFlutter.framework编译产物,如下所示:

对于编译产物的应用,可以通过--local-engine进行配置引擎的路径或替换Flutter SDK中对应engine

Flutter SDK编译

通常情况下,对于Flutter SDK的定制化场景可能会相对多些,如定制Flutter工程结构,添加flutterw包装器脚本:

Creating project test_module...  test_module/.gitignore (created)  test_module/.idea/libraries/Dart_SDK.xml (created)  test_module/.idea/libraries/Flutter_for_Android.xml (created)  test_module/.idea/modules.xml (created)  test_module/.idea/workspace.xml (created)  test_module/.metadata (created)  test_module/lib/main.dart (created)  test_module/test_module.iml (created)  test_module/test_module_android.iml (created)  test_module/pubspec.yaml (created)  test_module/README.md (created)  test_module/test/widget_test.dart (created)  test_module/flutter/.DS_Store (created)  test_module/flutter/wrapper/flutter-wrapper.properties (created)  test_module/flutterw.bat (created)  test_module/flutterw (created)Running "flutter packages get" in test_module...             7.2sWrote 16 files.All done!

如上是通过修改后Flutter SDK后通过flutter命令创建的一个module工程,自动包含了flutterw脚本

而如何让修改后的Flutter SDK代码生效,主要有两种方式:

1、删除/path/to/flutter/bin/cache/flutter_tools.snapshot文件2、删除/path/to/flutter/bin/cache/flutter_tools.stamp文件

其中文件含义为:

1、flutter_tools.snapshot:当前Flutter SDK的Dart代码的源码集.2、flutter_tools.stamp:当前Flutter SDK的commit id.

而当我们每次运行flutter命令时候,都会校验这两个文件,不存在则会基于当前的Flutter SDK代码重新构建/path/to/flutter/packages/flutter_tools代码生成 flutter_tools.snapshot,最终会通过dart命令,把 flutter_tools.snapshot源码集传入而生效,如下:

"$DART" $FLUTTER_TOOL_ARGS "$SNAPSHOT_PATH" "$@"

Flutter Tools调试

对于修改后Flutter Tools的调试,使用AS直接打开:

/path/to/flutter/packages/flutter_tools

工程,该工程是一个packages工程

而对于我们构建的入口,是在:

/path/to/flutter/packages/flutter_tools/bin/flutter_tools.dart

文件,代码如下:

import 'package:flutter_tools/executable.dart' as executable;void main(List<String> args) {  executable.main(args);}

对应的会通过executable.dart调用main函数执行,也就是进行一系列Command的配置以及执行

所以,进行修改后代码的调试,我们可以直接修改flutter_tools.dart中的 args入参进行调试,在flutter_tools.dart中右键直接运行,或者想改变当前运行的Configurations配置,可以新添加一个 Configurations,添加一个Dart Command Line App,然后指定 flutter_tools.dart以及工作目录,接着就可以直接运行

往期精彩回顾

Flutter动态化探索

Flutter Wrapper Workflow

一种Api兼容性检测方案

长按识别二维码关注我