1. 程式人生 > >Flutter原始碼剖析(一):原始碼獲取與構建

Flutter原始碼剖析(一):原始碼獲取與構建

## 概述 本文介紹了Flutter原始碼的獲取與構建,後面會另有文章介紹Flutter原始碼的版本管理、開發環境搭建等主題。 ## 準備工作 Flutter原始碼分為兩個部分: * [flutter/flutter](https://github.com/flutter/flutter)是框架層,為開發者提供各種介面,主要是dart程式碼。 * [flutter/engine](https://github.com/flutter/engine)是引擎層,負責Flutter的渲染以及宿主的互動。 相關依賴的安裝可參考官方文件:[Setting up the Engine development environment · flutter/flutter Wiki](https://github.com/flutter/flutter/wiki/Setting-up-the-Engine-development-environment)。以我的Mac為例,如JDK等一般都已經安裝,無需擔心。 ## 原始碼下載 `flutter/flutter`可以直接通過git下載,但是`flutter/engine`需要通過`gclient`工具獲取,因為`engine`有很多依賴,`gclient`可以很好地處理這些依賴,簡化原始碼管理流程。 首先,新建一個目錄,下載`flutter`框架程式碼: ```bash $ mkdir flutter_source_code $ cd flutter_source_code $ git clone https://github.com/flutter/flutter.git Cloning into 'flutter'... remote: Enumerating objects: 12, done. remote: Counting objects: 100% (12/12), done. remote: Compressing objects: 100% (12/12), done. remote: Total 272396 (delta 0), reused 6 (delta 0), pack-reused 272384 Receiving objects: 100% (272396/272396), 116.98 MiB | 2.48 MiB/s, done. Resolving deltas: 100% (210440/210440), done. ``` 獲取`depot_tools`工具(這個一開始是用來管理`chromium`原始碼的): ```bash $ git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git Cloning into 'depot_tools'... remote: Sending approximately 34.14 MiB ... remote: Total 40539 (delta 27803), reused 40539 (delta 27803) Receiving objects: 100% (40539/40539), 34.14 MiB | 5.04 MiB/s, done. Resolving deltas: 100% (27803/27803), done. ``` 設定環境變數(每次構建之前都要設定,也可以寫入系統配置): ```bash export PATH=$PATH:`pwd`/depot_tools ``` 開始拉取程式碼(這一步比較耗時) ```bash $ gclient sync [18:04:43] ...... remote: Enumerating objects: 25, done. remote: Counting objects: 100% (25/25), done. remote: Compressing objects: 100% (22/22), done. remote: Total 209672 (delta 10), reused 13 (delta 3), pack-reused 209647 Receiving objects: 100% (209672/209672), 196.61 MiB | 3.74 MiB/s, done. Resolving deltas: 100% (153791/153791), done. Syncing projects: 31% (33/104) src/third_party/vulkan [0:03:59] Still working on: [0:03:59] src/ios_tools [0:03:59] src/third_party/angle [0:03:59] src/third_party/dart [0:03:59] src/third_party/icu ...... [0:12:48] Still working on: [0:12:48] src/third_party/dart Syncing projects: 100% (104/104), done. Running hooks: 100% ( 9/ 9) dart package config ________ running 'vpython src/flutter/tools/run_third_party_dart.py' in '/Users/vimerzhao/WorkProject/flutter_source_code' Resolving dependencies... (1.7s) + charcode 1.1.3 + collection 1.14.13 + meta 1.2.3 + package_config 1.9.3 + path 1.7.0 + pub_semver 1.4.4 + source_span 1.7.0 + string_scanner 1.0.5 + term_glyph 1.1.0 + yaml 2.2.1 Changed 10 dependencies! ``` 需要注意的是,`Syncing projects: 100% (104/104), done`之後,會繼續下載一些大檔案,可能命令列沒有輸出,一定不能強制退出,可以通過資源管理器檢視網路的流量,確定`cipd`是否在下載: ![](http://images.vimerzhao.top/gclient-sync-cipd.png) (因為git不是很擅長下載大檔案,所以產生了cipd這個程式來做這些工作) 此時的目錄結構: ```bash $ tree -L 1 . ├── depot_tools # 原始碼管理工具 ├── flutter # flutter framework目錄 └── src # flutter engine以及相關依賴所在目錄 ``` framework的版本和engine的版本是一一對應的,framework的分支規則如下: * `stable`是當前的穩定分支,無特殊情況,推薦開發者使用該分支作為flutter sdk * `master`包含最新的特性,但是不穩定 * 每個版本會打上對應的tag 截止到 2020-10-29 ,最新的較穩定版本是 1.22.0,於是我們也先切到這個版本(不是必選的,但是個人認為:基於一個明確的版本編譯和修改原始碼似乎更合適)。 ```bash $ cd flutter $ git checkout 1.22.0 $ cat bin/internal/engine.version 5babba6c4d25fa237bbf755ab85c9a0c50b3c6ec ``` `engine.version`這個檔案指定了framework對應的engine版本,接下來,我們進入engine目錄切換到這次commit。 ```bash $ cd ../src/flutter $ git reset --hard 5babba6c4d25fa237bbf755ab85c9a0c50b3c6ec HEAD is now at 5babba6c4 Flutter 1.22.0-12.3.pre engine cherrypicks (#21466) ``` 此時,我們需要執行以下命令: ```bash $ gclient sync --with_branch_heads --with_tags Syncing projects: 100% (104/104), done. ...... Running hooks: 100% ( 8/ 8) dart package config ...... Running hooks: 100% (8/8), done. ``` 後面這兩個引數的含義比較晦澀,參考Chromium的說明,其含義是: >Checkout all the submodules at their branch DEPS revisions 因為切換分支之後,某些依賴的版本可能有更改,所以需要再次sync一下,直接在`src/flutter`目錄執行即可。 以上就完成了原始碼環境的搭建,下面正式開始編譯。 ## 原始碼編譯 首先我們退回到src目錄,然後通過`gn`生成`ninja`需要的元資料: ```bash $ cd .. $ pwd /Users/vimerzhao/WorkProject/flutter_source_code/src $ ./flutter/tools/gn --unoptimized --android --runtime-mode debug --android-cpu arm Generating GN files in: out/android_debug_unopt Generating Xcode projects took 75ms Done. Made 438 targets from 197 files in 1960ms ``` 對於編譯引數,[Compiling the engine · flutter/flutter Wiki](https://github.com/flutter/flutter/wiki/Compiling-the-engine)有詳細介紹,在此不做贅述,這裡我構建的是一個未優化、Android平臺、debug版本、arm 32位的 engine。 此時,檢視out目錄,可以看到: ```bash $ ls out/ android_debug_unopt compile_commands.json ``` `compile_commands.json`可以作為IDE的索引檔案,提供類/函式/變數的跳轉等能力,後面會說到。 然後就可以開始正式的編譯了: ```bash $ ninja -C out/android_debug_unopt ninja: Entering directory `out/android_debug_unopt' [38/3844] ACTION //flutter/shell/platform/android:flutter_shell_java(//build/toolchain/android:clang_arm) 警告: ../../third_party/android_tools/sdk/build-tools/30.0.1/core-lambda-stubs.jar(java/lang/invoke/LambdaMetafactory.class): 主版本 53 比 52 新, 此編譯器 支援最新的主版本。 建議升級此編譯器。 注: 某些輸入檔案使用或覆蓋了已過時的 API。 注: 有關詳細資訊, 請使用 -Xlint:deprecation 重新編譯。 1 個警告 [3844/3844] STAMP obj/default.stamp $ ls out/android_debug_unopt [19:35:15] all.xcodeproj flutter_embedding_debug-sources.jar.md5.stamp lib.stripped args.gn flutter_embedding_debug.jar libflutter.so armeabi_v7a_debug.jar flutter_embedding_debug.jar.md5.stamp libflutter.so.TOC armeabi_v7a_debug.pom flutter_embedding_debug.pom obj build.ninja flutter_icu toolchain.ninja build.ninja.d flutter_patched_sdk vm_outline_strong.dill clang_x64 gen vm_platform_strong.dill flutter.jar gyp-mac-tool vm_platform_strong.dill.d flutter_embedding_debug-sources.jar icudtl.dat zip_archives ``` 其中,`flutter_embedding_debug.jar`是Android嵌入層程式碼,`libflutter.so`是flutter的引擎層程式碼,通過這兩個檔案,可以在Android工程混合接入Flutter程式碼。 ## 原始碼使用 建立一個工程: ```bash $ pwd /Users/vimerzhao/WorkProject/flutter_source_code $ ls depot_tools flutter src $ ./flutter/bin/flutter create flutter_demo Downloading Dart SDK from Flutter engine 5babba6c4d25fa237bbf755ab85c9a0c50b3c6ec... % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 172M 100 172M 0 0 4381k 0 0:00:40 0:00:40 --:--:-- 4723k Building flutter tool... Downloading Material fonts... 0.5s Downloading Gradle Wrapper... 0.1s Downloading package sky_engine... 0.3s Downloading flutter_patched_sdk tools... 2.7s Downloading flutter_patched_sdk_product tools... 2.1s Downloading darwin-x64 tools... 8.2s Downloading libimobiledevice... 0.0s Downloading usbmuxd... 0.7s Downloading libplist... 0.0s Downloading openssl... 0.2s ...... Creating project flutter_demo... flutter_demo/ios/Runner.xcworkspace/contents.xcworkspacedata (created) flutter_demo/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (created) ...... flutter_demo/.idea/runConfigurations/main_dart.xml (created) flutter_demo/.idea/libraries/Dart_SDK.xml (created) flutter_demo/.idea/libraries/KotlinJavaRuntime.xml (created) flutter_demo/.idea/modules.xml (created) flutter_demo/.idea/workspace.xml (created) Running "flutter pub get" in flutter_demo... 2.3s Wrote 71 files. All done! ...... Run "flutter doctor" for information about installing additional components. In order to run your application, type: $ cd flutter_demo $ flutter run ``` 使用flutter並指定本地engine執行(不指定則會拉遠端的、已經構建好的engine)。 ``` $ ../flutter/bin/flutter run --local-engine-src-path ~/WorkProject/flutter_source_code/src --local-engine=android_debug_unopt No Flutter engine build found at /Users/vimerzhao/WorkProject/flutter_source_code/src/out/host_debug_unopt. $ ../flutter/bin/flutter run --local-engine-src-path ~/WorkProject/flutter_source_code/src --local-engine=host_debug_unopt Launching lib/main.dart on DUK AL20 in debug mode... Oops; flutter has exited unexpectedly: "Invalid argument(s): Cannot find executable for /Users/vimerzhao/WorkProject/flutter_source_code/src/out/host_debug_unopt/dart-sdk/bin/dart.". A crash report has been written to /Users/vimerzhao/WorkProject/flutter_source_code/flutter_demo/flutter_02.log. ... FAILURE: Build failed with an exception * Where: Script '/Users/vimerzhao/WorkProject/flutter_source_code/flutter/packages/flutter_tools/gradle/flutter.gradle' line: 904 * What went wrong: Execution failed for task ':app:compileFlutterBuildDebug'. > Process 'command '/Users/vimerzhao/WorkProject/flutter_source_code/flutter/bin/flutter'' finished with non-zero exit value 1 * Try: Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights. * Get more help at https://help.gradle.org BUILD FAILED in 1s Running Gradle task 'assembleDebug'... Running Gradle task 'assembleDebug'... Done 1.8s https://git.io/JTDf0 $../flutter/bin/flutter run --local-engine-src-path ~/WorkProject/flutter_source_code/src --local-engine=h ost_debug_unopt Launching lib/main.dart on DUK AL20 in debug mode... Error: Error when reading '../src/out/host_debug_unopt/gen/frontend_server.dart.snapshot': No such file or directory the Dart compiler exited unexpectedly. the Dart compiler exited unexpectedly. Running Gradle task 'assembleDebug'... ``` 總的來說,遇到一些官方文件上沒有提到的問題: 1. 輸入`android_debug_unopt` 卻提示找不到 `host_debug_unopt`,這個莫名其妙,只能先改一下之前構建的資料夾名稱了。 2. 找不到`dart-sdk`,flutter會下載到`bin/cache`目錄,只能自己手動copy一份到報錯的目錄。 3. 找不到`frontend_server.dart.snapshot`,在flutter目錄用`find . -name "frontend_server.dart.snapshot"`找到,然後手動copy一份。 解決這三個問題之後,終於可以運行了。 ``` $ ../flutter/bin/flutter run --local-engine-src-path ~/WorkProject/flutter_source_code/src --local-engine=host_debug_unopt Launching lib/main.dart on DUK AL20 in debug mode... Running Gradle task 'assembleDebug'... Running Gradle task 'assembleDebug'... Done 18.1s ✓ Built build/app/outputs/flutter-apk/app-debug.apk. Installing build/app/outputs/flutter-apk/app.apk... 3.5s Waiting for DUK AL20 to report its views... 8ms Syncing files to device DUK AL20... 210ms Flutter run key commands. r Hot reload.