Flutter xcode_backend分析

本文详细分析了Flutter编译iOS应用时的xcode_backend.sh脚本,涵盖Build、Thin和Embed过程。主要内容包括:编译App.framework、 ThinAppFrameworks、EmbedFlutterFrameworks等,探讨了如何在iOS工程中嵌入Flutter框架并签名。
摘要由CSDN通过智能技术生成

我从来不相信什么懒洋洋的自由,我向往的自由是通过勤奋和努力实现的更广阔的人生,那样的自由才是珍贵的、有价值的;我相信一万小时定律,我从来不相信天上掉馅饼的灵感和坐等的成就。做一个自由又自律的人,靠势必实现的决心认真地活着。

前言

xcode_backend.sh位于$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh,是Flutter编译iOS产物的一个关键部分,本篇文章用于分析该脚本。

为何要分析?

当我们创建完毕Flutter Module,并且通过官方的方式引入了Flutter框架后,我们会在Target->Build Phases->Run Script中可以看到这么两句话:

"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" build
"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" embed

从字面上看,第一步是build编译,第二个是embed嵌入Framework到宿主APP。

  • Embed 的意思是嵌入,但是这个嵌入并不是嵌入 app 可执行文件,而是嵌入 app 的 bundle 文件。

因此,如果我们要了解Flutter混合编译的来龙去脉,就需要分析下xcode_backend到底做了什么东西。

主函数入口

# 主函数入口
if [[ $# == 0 ]]; then # 如果不带参数则直接执行BuildApp函数
  # Backwards-compatibility: if no args are provided, build.
  BuildApp
else # 否则执行case语句
  case $1 in
    "build")
      BuildApp ;; 							# 编译
    "thin")
      ThinAppFrameworks ;;			# 只合并需要的架构
    "embed")
      EmbedFlutterFrameworks ;;	# Embed
  esac
fi

因此,Xcode_backend包含了三部分的实现,即build、thin、embed。

踩坑的本机环境

注意对比下我的环境和你的环境是否一样,有些问题在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

文章总结

xcode_backend.sh的主要作用:

  • iOS工程直接依赖Flutter工程,每次编译的时候都会执行Target->Build Phases->Run Script的xcode_backend.sh脚本。

在以下指令中:

"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" build
"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" embed
  • xcode_backend.sh做的事情:
    • 导入Flutter引擎的对应模式版本(BuildApp所做的事情)
    • 编译Dart代码为App.framework(BuildApp所做的事情)
    • 编译flutter_assets,并内嵌到App.framework(BuildApp所做的事情)
    • 复制资源,并签名(EmbedFlutterFrameworks所做的事情)

Build

主要包含几个部分的工作:

  • 检查路径和资源是否存在

    • 目录不存在就创建
    • Flutter 引擎不存在则报错
  • 检查输入的变量是否符合

  • 拷贝Flutter引擎到工程目录下,${SOURCE_ROOT}/Flutter或者${project_path}/.ios/Flutter

  • 编译App.framework

    • Debug模式:生成App.framework,并生成dSYM
    • Release/Profile模式:生成App.framework
  • 编译资源包

编译App.framework

为了方便大家阅读,特意将几个重要的命令提取出来:

Release/Profile

# 执行Flutter的编译命令
RunCommand "${FLUTTER_ROOT}/bin/flutter" --suppress-analytics           \
  ${verbose_flag}                                                       \
  build aot                                                             \
  --output-dir="${build_dir}/aot"                                       \
  --target-platform=ios                                                 \
  --target="${target_path}"                                             \
  --${build_mode}                                                       \
  --ios-arch="${archs}"                                                 \
  ${local_engine_flag}                                                  \
  ${track_widget_creation_flag}
生成dSYM

Release/Profile模式下,默认会生成符号表dSYM,用于符号的还原。方便崩溃的时候分析问题所在。不需要打入App.framework。因此,这里会将dSYM从App.framework中剥离。

# 生成 dSYM 文件
RunCommand xcrun dsymutil -o "${build_dir}/dSYMs.noindex/App.framework.dSYM" "${app_framework}/App"

StreamOutput " ├─Stripping debug symbols..."
# 剥离调试符号表
RunCommand xcrun strip -x -S "${derived_dir}/App.framework/App"
StreamOutput "done"

Debug

  • Debug模式会包含程序的JIT编译快照
RunCommand eval "$(echo "static const int Moo = 88;" | xcrun clang -x c \
    ${
     arch_flags} \
    -dynamiclib \
    -Xlinker -rpath -Xlinker '@executable_path/Frameworks' \
    -Xlinker -rpath -Xlinker '@loader_path/Frameworks' \
    -install_name '@rpath/App.framework/App' \
    -o "${derived_dir}/App.framework/App" -)"
  • static const int Moo = 88;这一句我暂时也不知道什么用的,先放着,之后分析。╮(╯▽╰)╭

编译资源包

# 编译资源包,若是debug模式则会包含flutter代码的JIT编译快照,此时app.framework中不含dart代码
StreamOutput " ├─Assembling Flutter resources..."
RunCommand "${FLUTTER_ROOT}/bin/flutter" --suppress-analytics             \
  ${verbose_flag}                                                         \
  build bundle                                                            \
  --target-platform=ios                                                   \
  --target="${target_path}"                                               \
  --${build_mode}                                                         \
  --depfile="${build_dir}/snapshot_blob.bin.d"                            \
  --asset-dir="${derived_dir}/flutter_assets"                             \
  ${precompilation_flag}                                                  \
  ${local_engine_flag}                                                    \
  ${track_widget_creation_flag}

完整源码

BuildApp() {
   
  # xcode工程根目录,SOURCE_ROOT这个变量来自xcode工程环境
  local project_path="${SOURCE_ROOT}/.."

  # FLUTTER_APPLICATION_PATH flutter工程目录,该变量来自Generated.xcconfig文件
  # 若FLUTTER_APPLICATION_PATH不为空则,赋值给project_path
  if [[ -n "$FLUTTER_APPLICATION_PATH" ]]; then
    project_path="${FLUTTER_APPLICATION_PATH}"
  fi

  # flutter的程序入口文件目录
  local target_path="lib/main.dart"
  if [[ -n "$FLUTTER_TARGET" ]]; then
    target_path="${FLUTTER_TARGET}"
  fi

  # Use FLUTTER_BUILD_MODE if it's set, otherwise use the Xcode build configuration name
  # This means that if someone wants to use an Xcode build config other than Debug/Profile/Release,
  # they _must_ set FLUTTER_BUILD_MODE so we know what type of artifact to build.
  # 获取编译模式
  # 根据编译模式设置相应变量
  # artifact_variant是后续拷贝flutter引擎的时候使用,决定引擎的版本
  # 在podhelper.rb中已经把flutter引擎集成进去了,不过依赖的是flutter工程本身编译模式引入的版本,可能不同
  # 所以在这个脚本之中希望能够重新引入相应模式的engine
  local build_mode="$(echo "${FLUTTER_BUILD_MODE:-${CONFIGURATION}}" | tr "[:upper:]" "[:lower:]")"
  local artifact_variant="unknown".
  case "$build_mode" in
    release*) build_mode="release"; artifact_variant="ios-release";;
    profile*) build_mode="profile
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值