FSA全栈行动 FSA全栈行动
首页
  • 移动端文章

    • Android
    • iOS
    • Flutter
  • 学习笔记

    • 《Kotlin快速入门进阶》笔记
    • 《Flutter从入门到实战》笔记
    • 《Flutter复习》笔记
前端
后端
  • 学习笔记

    • 《深入浅出设计模式Java版》笔记
  • 逆向
  • 分类
  • 标签
  • 归档
  • LinXunFeng
  • GitLqr

公众号:FSA全栈行动

记录学习过程中的知识
首页
  • 移动端文章

    • Android
    • iOS
    • Flutter
  • 学习笔记

    • 《Kotlin快速入门进阶》笔记
    • 《Flutter从入门到实战》笔记
    • 《Flutter复习》笔记
前端
后端
  • 学习笔记

    • 《深入浅出设计模式Java版》笔记
  • 逆向
  • 分类
  • 标签
  • 归档
  • LinXunFeng
  • GitLqr
  • AndroidUI

  • Android第三方SDK

  • Android混淆

  • Android仓库

  • Android新闻

  • Android系统开发

  • Android源码

  • Android注解AOP

  • Android脚本

  • AndroidTv开发

  • AndroidNDK

  • Android音视频

  • Android热修复

  • Android性能优化

  • Android云游戏

  • Android插件化

  • iOSUI

  • iOS工具

  • iOS底层原理与应用

  • iOS组件化

  • iOS音视频

  • iOS疑难杂症

  • iOS之Swift

  • iOS之RxSwift

  • iOS开源项目

  • iOS逆向

  • Flutter开发

    • Dart - 抽象类的实例化
    • Flutter - 打印好用的Debug日志
    • Flutter - 混合开发
    • Flutter - 解决混合开发iOS脚本打包遇到的问题
    • Flutter - 低版本在iOS14上遇到的问题与解决方案
    • Flutter - 解决原生弹窗的触摸事件被Flutter响应的问题
    • Flutter - 实现列表上下拉切换header
    • Flutter - 获取ListView当前正在显示的Widget信息
    • Flutter - 列表滚动定位超强辅助库,墙裂推荐!🔥
    • Flutter - 快速实现聊天会话列表的效果,完美💯
    • Flutter - 聊天输入框更新文本时的必备优化点🔖
    • Flutter - 我给官方提PR,解决run命令卡住问题 😃
    • Flutter - 探索run命令到底做了什么 🤔
    • Flutter - 引擎调试(iOS篇)🛠
    • Flutter - 引擎调试bug到提交PR实战 🐞
    • Flutter - 船新升级😱支持观察第三方构建的滚动视图💪
    • Flutter - 瀑布流交替播放视频 🎞
    • Flutter - IM保持消息位置大升级(支持ChatGPT生成式消息) 🤖
    • Flutter - 滚动视图中的表单防遮挡 🗒
    • Flutter - 秒杀1/2曝光统计 📊
    • 一天内加入 Flutter 和 FlutterCandies 两大组织是什么体验 🧐
    • Flutter - 如何快速搓一个微信通讯录列表(azlist) 📓
    • Flutter - 混编项目集成Shorebird热更新🐦(安卓篇)
    • Flutter - 混编项目集成Shorebird热更新🐦(iOS篇)
    • Flutter - 解决返回原生页面时dispose方法未被触发的问题 🐞
    • Flutter - 升级3.19之后页面多次rebuild?🤨
    • Flutter - 热更新 Shorebird 1.0 正式版来了 🐦
    • Flutter - 使用Pigeon实现视频缓存插件 🐌
    • Flutter - 轻松搞定屏幕旋转功能 😎
    • Flutter - 解决Connection closed before full header was received
    • Flutter - 实现聊天键盘与功能面板的丝滑切换 🍻
    • Flutter - 支持观察NestedScrollView,兼容性更强 😈
    • Flutter - 聊天键盘与面板丝滑切换的强势升级 🍻
    • Flutter - 升级到3.24后页面还会多次rebuild吗?🧐
    • Flutter - 轻松实现PageView卡片偏移效果
    • Flutter - 轻松搞定炫酷视差(Parallax)效果
    • Flutter - 危!3.24版本苹果审核被拒!
    • Flutter - 子部件任意位置观察滚动数据
    • Flutter - iOS编译加速
    • Flutter - Xcode16 还原编译速度
      • 一、前言
      • 二、调整
      • 三、详解
      • 四、改进
      • 五、Condor
        • 1、安装与更新 condor
        • 2、拷贝 xctoolchain
        • 3、cc 重写向
        • 4、应用
      • 六、是否有影响
      • 七、资料
  • 移动端
  • Flutter开发
LinXunFeng
2025-04-05
目录

Flutter - Xcode16 还原编译速度

欢迎关注微信公众号:[FSA全栈行动 👋]

# 一、前言

在之前发布的【Flutter - iOS编译加速】一文中,我们提到升级至 Xcode16 之后,iOS 的编译速度慢到令人发指,随后探索发现是 xcrun cc snapshot_assembly.S snapshot_assembly.o 这一汇编耗时变长了。而就在几天前,有人在相关的 issue 中留言了他篡改使用 Xcode 15 的 cc 来提升编译速度的步骤,详情可见 https://github.com/dart-lang/sdk/issues/43299#issuecomment-2769408562 (opens new window)

我在他的基础上做了优化与封装,只需两句命令即可还原编译速度,在开始详细介绍之前,先展示一下两台构建机优化前后的编译时长记录。

构建机 优化前(min) Release + 二进制依赖(min) Release + 二进制依赖 + 还原编译速度(min)
i7 25+ 14+ 11+
M4 16+ 8+ 4+

M4 只要四分多钟,真香~

# 二、调整

以下是他提供的修改步骤

cp -r /Applications/Xcode-15.4.0.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain ~/Library/Developer/Toolchains
cd ~/Library/Developer/Toolchains
mv XcodeDefault.xctoolchain Xcode15.4.xctoolchain
/usr/libexec/PlistBuddy -c "Add CompatibilityVersion integer 2" Xcode15.4.xctoolchain/ToolchainInfo.plist
/usr/libexec/PlistBuddy -c "Set Identifier clang.Xcode15.4" Xcode15.4.xctoolchain/ToolchainInfo.plist
  1. 将 Xcode 15.4 内部的默认工具链复制到 ~/Library/Developer/Toolchains 目录。
  2. 将当前工作目录切换到 ~/Library/Developer/Toolchains 目录。
  3. 将复制过来的 XcodeDefault.xctoolchain 重命名为 Xcode15.4.xctoolchain,方便区分。
  4. 修改 .xctoolchain/ToolchainInfo.plist 文件,添加 CompatibilityVersion,并将其值设置为整数类型 2,修改 Identifier 的值为 clang.Xcode15.4。
- Future<RunResult> cc(List<String> args) => _run('cc', args);
+ Future<RunResult> cc(List<String> args) => _run('--toolchain', <String>[
+   'clang.Xcode15.4',
+   'cc',
+   ...args,
+ ]);

修改 flutter_tools 源码,将 cc 修改为 --toolchain 来使用 clang.Xcode15.4 下的 cc。

# 三、详解

默认的工具链路径是 Xcode 中的 /Applications/Xcode.app/Contents/Developer/Toolchains,不过也可以将工具链放到 ~/Library/Developer/Toolchains 目录下,这样就可以在不修改 Xcode 应用本身的情况下,使用和管理不同的工具链版本。

接着是修改 .xctoolchain/ToolchainInfo.plist 文件,里面可以设置的一些字段如下:

字段 说明
CFBundleIdentifier 唯一标识
CompatibilityVersion 适配版本,适配 Xcode 时必为 2
DisplayName 【可选】显示名称
ShortDisplayName 【可选】简短的显示名称

注:在 DisplayName 和 ShortDisplayName 都不设置时,名字会显示为 CFBundleIdentifier

关于 CompatibilityVersion 的说明,在网上基本是搜不到的,只有如下这个注释,Xcode 8 及以上,使用 2,否则使用 1。

# Xcode 8 requires CompatibilityVersion 2
set(COMPAT_VERSION 2)
if(XCODE_VERSION VERSION_LESS 8.0.0)
  # Xcode 7.3 (the first version supporting external toolchains) requires
  # CompatibilityVersion 1
  set(COMPAT_VERSION 1)
endif()

摘自: https://github.com/llvm/llvm-project/blob/main/llvm/tools/xcode-toolchain/CMakeLists.txt (opens new window)

# 四、改进

直接修改 flutter_tools 源码并写死 clang.Xcode15.4 太过于粗暴,如果我们为了安全起见,只想打测试包的时候还原编译速度,而打上架包保持原样就不好调整了,所以这里我对他的修改进行了优化。

首先来介绍一下 TOOLCHAINS 这个环境变量,它可以影响 /usr/bin/ 下的命令调用,如 /usr/bin/xcrun

注:Developer Directory 指 /Applications/Xcode.app/Contents/Developer 或者 /Library/Developer/CommandLineTools,可以通过 xcode-select --print-path 进行检查

如果我们没有设置 TOOLCHAINS,根据上述流程图,在调用 /usr/bin/xcrun 时,会根据 Developer Directory 搜索该命令,如果找到同名命令,则执行该命令。

xcrun --find cc

# /Applications/Xcode-16.2.0.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc

如果我们将 TOOLCHAINS 设置为 .xctoolchain 的 Identifier,如: clang.Xcode15.4。

export TOOLCHAINS=clang.Xcode15.4

那么根据上述流程图,则是在 Xcode15.4.xctoolchain 中找到 cc。

xcrun --find cc
/Users/lxf/Library/Developer/Toolchains/Xcode15.4.xctoolchain/usr/bin/cc

根据这一特性,我做了如下调整:

调整 cc 方法,当有配置 CONDOR_TOOLCHAINS 环境变量时,将值取出并赋值给 TOOLCHAINS。

//   Future<RunResult> cc(List<String> args) => _run('cc', args);
  Future<RunResult> cc(List<String> args) {
    final String condorToolchains = platform.environment['CONDOR_TOOLCHAINS'] ?? '';
    final Map<String, String> environment = <String, String>{
      if (condorToolchains.isNotEmpty) "TOOLCHAINS": condorToolchains,
    };
    _run('--find', <String>['cc'], environment: environment).then((RunResult result) {
      printStatus(
        '\n[condor] find cc: ${result.stdout}\n',
      );
    });
    return _run('cc', args, environment: environment);
  }

_run 方法新增 environment 参数,用于设置环境变量。

//   Future<RunResult> _run(String command, List<String> args) {
//     return _processUtils.run(
//       <String>[...xcrunCommand(), command, ...args],
//       throwOnError: true,
//     );
//   }
  Future<RunResult> _run(String command, List<String> args, {Map<String, String>? environment}) {
    return _processUtils.run(
      <String>[...xcrunCommand(), command, ...args],
      throwOnError: true,
      environment: environment,
    );
  }

# 五、Condor

上述步骤还是比较繁琐的,所以这里我将其进行了封装,只需要执行两句命令即可。

# 1、安装与更新 condor

# Homebrew

如果你是首次安装,则执行如下命令

brew tap LinXunFeng/tap && brew install condor

如果不是首次安装,则需要执行如下命令进行更新

brew update && brew reinstall condor

# Pub Global

如果你习惯使用 Pub,或者你的电脑是 Intel 芯,则可以执行如下命令进行安装或更新

dart pub global activate condor_cli

# 2、拷贝 xctoolchain

condor optimize-build xctoolchain-copy --xcode Xcode-15.4.0

--xcode 参数请使用 Xcode 15 在 /Applications/ 下的名字,如果你电脑上没有 Xcode 15,建议使用 https://github.com/XcodesOrg/XcodesApp 进行安装。

这一步会做如下几个操作

  1. 将 /Applications/Xcode-15.4.0.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain 拷贝至 ~/Library/Developer/Toolchains/Xcode-15.4.0.xctoolchain。
  2. 将 Xcode-15.4.0.xctoolchain/ToolchainInfo.plist 中的 Identifier 设置为 Xcode-15.4.0。
  3. 添加 CompatibilityVersion 并设置为 2。

# 3、cc 重写向

该命令会对 flutter_tools 源码进行修改,使其具备重定向 cc 的能力而已,在有配置 CONDOR_TOOLCHAINS 环境变量时才会生效,否则则使用默认的 cc。

# 使用默认 flutter,则不需要传 flutter 参数
condor optimize-build redirect-cc

# 如果你想指定 fvm 下的指定 Flutter 版本
condor optimize-build redirect-cc --flutter fvm spawn 3.24.5

# 4、应用

后续你只需要 export CONDOR_TOOLCHAINS=Xcode-15.4.0 就可以在 Xcode 16 上感受到 Xcode 15 的编译速度了 🥳

如打包前 export

export CONDOR_TOOLCHAINS=Xcode-15.4.0
flutter clean
flutter build ipa

如果你想验证,可以加上 --verbose,并将输出保存到 result.txt

flutter build ipa --verbose > result.txt

命令执行完毕后打开 result.txt,搜索 condor 即可。

或者如果你不需要按需配置,也可以直接在 Run Script 里设置 CONDOR_TOOLCHAINS 环境变量。

验证也很简单,如下图所示,选择当前的 Build 任务,搜索 condor 即可。

# 六、是否有影响

在 Xcode 的工具链中,cc 是 clang 的替身

而不同版本的 clang 对同一份 .S 进行汇编,还是有可能生成内容不一样的 .o 的。不过我自己对比 Xcode 16 和 Xcode 15 生成的 .o 并没有什么不同。

对比 .o 文件,我们可以使用系统自带的 cmp 命令,cmp 是一个用于比较文件的命令行工具,它可以逐字节比较二进制文件。如下所示

cmp /Users/lxf/cc15/snapshot_assembly.o /Users/lxf/cc16/snapshot_assembly.o

cmp 命令执行完成,退出代码为 0,并且没有输出。这表明 cmp 命令没有发现两个文件之间有任何不同之处。因此可以证明这两个 .o 文件的内容是相同的。

即,基于 Xcode 16 来说并没有影响,这种方式生成的 .o 可以用于上架包,如果还是不放心,可以在打上架包时,不设置 CONDOR_TOOLCHAINS 环境变量即可。

# 七、资料

  • Change toolchain for Xcode (opens new window)
  • Using a Custom Toolchain in Xcode (opens new window)
  • how-toolchain-works-macos (opens new window)
  • extract-xcode-toolchain (opens new window)
#Dart#Flutter#Xcode#iOS
Flutter - iOS编译加速

← Flutter - iOS编译加速

最近更新
01
AI - 免费的 Cursor 平替方案
03-30
02
Android - 2025年安卓真的闭源了吗
03-28
03
AI - RooCode 解限使用 Copilot Claude 3.7
03-18
更多文章>
Theme by Vdoing | Copyright © 2020-2025 FSA全栈行动
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×