我们必须全力以赴,同时又不抱持任何希望。不管做什么事,都要当它是全世界最重要的一件事,但同时又知道这件事根本无关紧要。
前言
分析了一堆的内容,其实主要就是为了做Flutter的CI的事情。让我们在本文中探讨如何制作Flutter CI for iOS。
踩坑的本机环境
注意对比下我的环境和你的环境是否一样,有些问题在Flutter的新版本中已经被修复了。
➜ app git:(master) ✗ flutter --version
Flutter 1.9.1+hotfix.6 • channel stable • https://github.com/flutter/flutter.git
Framework • revision cc949a8e8b (3 weeks ago) • 2019-09-27 15:04:59 -0700
Engine • revision b863200c37
Tools • Dart 2.5.0
铺垫
本文基于我的前几篇文章的分析所得,如果你不清楚Flutter的编译产物和编译流程,建议阅读我的前几篇文章:
文章 | 说明 |
---|---|
Flutter build ios产物分析 | 介绍Flutter的编译产物以及官方接入方案的产物的编译、组织流程。 |
Flutter xcode_backend分析 | 承接上文,上文中使用了xcode_backend.sh脚本生成编译产物,本文基于上文做深层次的Flutter混合编译分析和介绍。 |
初始环境
我配置好了一个临时的分析工程(Native接入Flutter的工程目录结构)。
➜ DevelopProjects tree -L 2 temp
temp
├── Android # Android工程
├── flutter_module # Flutter工程
│ ├── README.md
│ ├── build
│ ├── flutterApp.podspec
│ ├── flutter_01.log
│ ├── flutter_module.iml
│ ├── flutter_module_android.iml
│ ├── flutter_podhelper.rb
│ ├── lib
│ ├── pubspec.lock
│ ├── pubspec.yaml
│ └── test
├── iOS # iOS工程
│ ├── Podfile
│ ├── Podfile.lock
│ ├── Pods
│ ├── iOS
│ ├── iOS.xcodeproj
│ └── iOS.xcworkspace
└── startBuild.sh # iOS产物CI编译脚本
10 directories, 11 files
制作过程
先看一眼编译后的东西:
➜ app ✗ tree build -L 3 # 省略了部分内容
build # 编译目录
├── aot
│ ├── App.framework # Flutter业务层代码
│ ├── App.framework.dSYM.noindex
│ ├── app.dill
│ ├── arm64
│ ├── armv7
│ ├── frontend_server.d
│ └── kernel_compile.d
├── dSYMs.noindex
│ └── App.framework.dSYM
├── ios
│ ├── Debug-iphonesimulator # x86_64架构,Debug
│ ├── Release # 合并Debug-iphonesimulator和Release-iphoneos的产物
│ ├── Release
│ │ ├── libFlutterPluginRegistrant.a
│ │ ├── xxx.a
│ │ └── xxx.a
│ └── Release-iphoneos # arm64 armv7,Release
└── libFlutterPlugins.a # 所有的插件及FlutterPluginRegistrant
11 directories, 4 files
启动流程
本脚本可以应用于Jenkints,也可以做成其他的自动化的部分。
根据Flutter build ios产物分析、Flutter xcode_backend分析的分析结果,可以简单定下以下的启动流程,分别为如下流程:
- 获取输入的变量
- 初始化环境变量
- 编译App.framework(Flutter业务层代码的framework,包含了静态资源)
- 生成dSYM(iOS编译后的符号表,用户还原符号)
- Strip dSYM(剥离符号表)
- 编译Flutter资源文件
- 编译Flutter插件(将Flutter的插件连同注册制都编译为一个framework,对外仅仅提供注册制的header)
- unset环境变量
# 主要流程
main() {
# Flutter工程目录
cd ./flutter_module
InitVariable # 获取输入的变量
InitEnv # 初始化环境变量
BuildAppFramework # 编译App.framework
GeneratedSYM # 生成dSYM
StripdSYM # Strip dSYM
BuildFlutterAssets # 编译Flutter资源
BuildFlutterPlugin # 编译Flutter插件
UnsetVariable # unset环境变量
# 退出到原来目录
cd -
}
# 启动脚本
main
根据上述脚本,逐渐填充函数实现即可。
初始化变量
- 如果使用Jenkints的话,可以将下述参数配置:
# 用于命令行的输出
EchoDone() {
echo ""
echo " └─$1"
echo ""
}
# 初始化从外部输入的变量
InitVariable() {
echo " ├──Input variable..."
export FLT_PROJ_BUILD_MODE='release' # profile/release
Echo " ├────FLT_PROJ_BUILD_MODE:${FLT_PROJ_BUILD_MODE}"
export FLT_ARCH='armv7+arm64'
echo " ├────FLT_ARCH:${FLT_ARCH}"
}
##初始化编译环境
InitEnv() {
# 设置Flutter Pub地址
export PUB_HOSTED_URL='https://pub.flutter-io.cn'
echo " ├────PUB_HOSTED_URL=${PUB_HOSTED_URL}"
echo ""
echo " ├────Flutter version:"
echo ""
flutter --version
echo ""
echo " ├────Clean flutter building artifacts"
echo ""
flutter clean
EchoDone "Clean flutter building artifacts done"
echo " ├────Fetch flutter project dependences"
# 更新依赖
flutter pub get
flutter packages get
EchoDone "Fetch flutter project dependences done"
# 工程根目录
export FLT_PROJ="$(pwd)"
# 编译输出目录
export FLT_PROJ_BUILD="${FLT_PROJ}/build"
# Flutter编译的ios工程或者.ios的地址
export FLT_PROJ_iOS="${FLT_PROJ}/ios"
# 如果.ios存在则是Module工程
if [[ -e "${FLT_PROJ}/.ios" ]]; then
unset FLT_PROJ_iOS
export FLT_PROJ_iOS="${FLT_PROJ}/.ios"
fi
# 输出目录
export FLT_PROJ_iOS_FLT="${FLT_PROJ_iOS}/Flutter"
# 输出环境变量,([]:中括号表示可选)
echo " ├────./ -> ${FLT_PROJ}"
echo " ├────./build -> ${FLT_PROJ_BUILD}"
echo " ├────./[.]ios -> ${FLT_PROJ_iOS}"
echo " ├────./[.]ios/Flutter -> ${FLT_PROJ_iOS_FLT}"
# 需要