Flutter - Xcode16 还原编译速度

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

一、前言

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

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

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

M4 只要四分多钟,真香~

二、调整

以下是他提供的修改步骤

shell 复制代码
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
diff 复制代码
- 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 【可选】简短的显示名称

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

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

scss 复制代码
# 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()

摘自: github.com/llvm/llvm-p...

四、改进

直接修改 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 搜索该命令,如果找到同名命令,则执行该命令。

shell 复制代码
xcrun --find cc

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

如果我们将 TOOLCHAINS 设置为 .xctoolchainIdentifier,如: clang.Xcode15.4

shell 复制代码
export TOOLCHAINS=clang.Xcode15.4

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

shell 复制代码
xcrun --find cc
/Users/lxf/Library/Developer/Toolchains/Xcode15.4.xctoolchain/usr/bin/cc

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

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

dart 复制代码
//   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 参数,用于设置环境变量。

dart 复制代码
//   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

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

shell 复制代码
brew tap LinXunFeng/tap && brew install condor

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

shell 复制代码
brew update && brew reinstall condor

Pub Global

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

shell 复制代码
dart pub global activate condor_cli

2、拷贝 xctoolchain

shell 复制代码
condor optimize-build xctoolchain-copy --xcode Xcode-15.4.0

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

这一步会做如下几个操作

  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 源码进行修改,使 flutter build 具备重定向 cc 的能力而已,在有配置 CONDOR_TOOLCHAINS 环境变量时才会生效,否则则使用默认的 cc

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

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

到这里你就可以在 Xcode 16 上感受到 Xcode 15 的编译速度了 🥳

六、是否有影响

Xcode 的工具链中,ccclang 的替身

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

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

shell 复制代码
cmp /Users/lxf/cc15/snapshot_assembly.o /Users/lxf/cc16/snapshot_assembly.o

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

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

七、资料

如果文章对您有所帮助, 请不吝点击关注一下我的微信公众号:FSA全栈行动, 这将是对我最大的激励. 公众号不仅有 Flutter 技术,还有 AIAndroidiOSPython 等文章, 可能有你想要了解的技能知识点哦~

相关推荐
鱼樱前端7 分钟前
Webpack 在前端工程化中的核心应用解析-构建老大
前端·javascript
Moment7 分钟前
多人协同编辑算法 —— CRDT 算法 🐂🐂🐂
前端·javascript·面试
小付同学呀12 分钟前
前端快速入门学习4——CSS盒子模型、浮动、定位
前端·css·学习
FlutterDevs1 小时前
Flutter 2025 年产品路线图发布
flutter
OpenTiny社区2 小时前
TinyPro 中后台管理系统使用指南——让页面搭建变得如此简单!
前端·vue.js·开源
我有一只臭臭2 小时前
webpack配置解析
前端·webpack
我有一只臭臭2 小时前
Vue中webpack的使用
前端·vue.js·webpack
bst@微胖子2 小时前
Flutter之设计与主题&字体
android·flutter
Tee xm3 小时前
清晰易懂的 Flutter 开发环境搭建教程
linux·windows·flutter·macos·安装