zhengxiaoyong

Flutter Engine与SDK的定制化与编译

概述

对于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获取:

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

并配置到环境变量中:

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

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

1
2
3
$ ./flutter/tools/gn
gn gen --check in out/path
$ ninja -C out/path

检出Flutter Engine源码

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

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

配置代理

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

1
2
3
4
[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/engine
Hook '/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配置文件进行配置:

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

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

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

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

配置gclient依赖与编译分支

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

1
2
3
4
5
6
7
8
9
10
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所依赖的所有代码:

1
2
MacBook-Pro-3:~ Sunzxyong$ cd /path/to/engine
MacBook-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目录,然后依次执行:

1
2
3
MacBook-Pro-3:~ Sunzxyong$ cd /path/to/engine/src/flutter
MacBook-Pro-3:flutter Sunzxyong$ git reset --hard f1f19bba8f0089490962316867bd222727510ac5
MacBook-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的编译选项:

1
2
3
4
5
6
7
8
9
10
11
12
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目录,然后进行编译:

1
2
3
MacBook-Pro-3:~ Sunzxyong$ cd /path/to/engine/src
MacBook-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产物

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# 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 x64
ninja -C out/android_debug_unopt
ninja -C out/android_debug_unopt_arm64
ninja -C out/android_debug_unopt_x64
ninja -C out/host_debug_unopt
ninja -C out/host_debug_unopt_arm64
ninja -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 x64
ninja -C out/android_profile_unopt
ninja -C out/android_profile_unopt_arm64
ninja -C out/android_profile_unopt_x64
ninja -C out/host_profile_unopt
ninja -C out/host_profile_unopt_arm64
ninja -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 x64
ninja -C out/android_release_unopt
ninja -C out/android_release_unopt_arm64
ninja -C out/android_release_unopt_x64
ninja -C out/host_release_unopt
ninja -C out/host_release_unopt_arm64
ninja -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 x64
ninja -C out/android_debug
ninja -C out/android_debug_arm64
ninja -C out/android_debug_x64
ninja -C out/host_debug
ninja -C out/host_debug_arm64
ninja -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 x64
ninja -C out/android_profile
ninja -C out/android_profile_arm64
ninja -C out/android_profile_x64
ninja -C out/host_profile
ninja -C out/host_profile_arm64
ninja -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 x64
ninja -C out/android_release
ninja -C out/android_release_arm64
ninja -C out/android_release_x64
ninja -C out/host_release
ninja -C out/host_release_arm64
ninja -C out/host_release_x64

编译IOS产物

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# 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 arm64
ninja -C out/ios_debug_unopt_arm
ninja -C out/ios_debug_unopt
ninja -C out/host_debug_unopt_arm
ninja -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 arm64
ninja -C out/ios_profile_unopt_arm
ninja -C out/ios_profile_unopt
ninja -C out/host_profile_unopt_arm
ninja -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 arm64
ninja -C out/ios_release_unopt_arm
ninja -C out/ios_release_unopt
ninja -C out/host_release_unopt_arm
ninja -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 arm64
ninja -C out/ios_debug_arm
ninja -C out/ios_debug
ninja -C out/host_debug_arm
ninja -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 arm64
ninja -C out/ios_profile_arm
ninja -C out/ios_profile
ninja -C out/host_profile_arm
ninja -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 arm64
ninja -C out/ios_release_arm
ninja -C out/ios_release
ninja -C out/host_release_arm
ninja -C out/host_release

编译产物输出路径

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

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

Flutter SDK编译

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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.2s
Wrote 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源码集传入而生效,如下:

1
"$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

文件,代码如下:

1
2
3
4
5
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以及工作目录,接着就可以直接运行

关注我

坚持原创技术分享,您的支持将鼓励我继续创作!