欢迎关注微信公众号:FSA全栈行动 👋
一、前言
在之前发布的【Flutter - iOS编译加速】一文中,我们提到升级至 Xcode16
之后,iOS
的编译速度慢到令人发指,随后探索发现是 xcrun cc snapshot_assembly.S snapshot_assembly.o
这一汇编耗时变长了。而就在几天前,有人在相关的 issue
中留言了他篡改使用 Xcode 15
的 cc
来提升编译速度的步骤,详情可见 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
- 将
Xcode 15.4
内部的默认工具链复制到~/Library/Developer/Toolchains
目录。 - 将当前工作目录切换到
~/Library/Developer/Toolchains
目录。 - 将复制过来的
XcodeDefault.xctoolchain
重命名为Xcode15.4.xctoolchain
,方便区分。 - 修改
.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 | 【可选】简短的显示名称 |
注:在
DisplayName
和ShortDisplayName
都不设置时,名字会显示为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()
四、改进
直接修改 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
设置为 .xctoolchain
的 Identifier
,如: 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... 进行安装。
这一步会做如下几个操作
- 将
/Applications/Xcode-15.4.0.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain
拷贝至~/Library/Developer/Toolchains/Xcode-15.4.0.xctoolchain
。 - 将
Xcode-15.4.0.xctoolchain/ToolchainInfo.plist
中的Identifier
设置为Xcode-15.4.0
。 - 添加
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
的工具链中,cc
是 clang
的替身

而不同版本的 clang
对同一份 .S
进行汇编,还是有可能生成内容不一样的 .o
的。不过我自己对比 Xcode 16
和 Xcode 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
环境变量即可。
七、资料
- Change toolchain for Xcode
- Using a Custom Toolchain in Xcode
- how-toolchain-works-macos
- extract-xcode-toolchain
如果文章对您有所帮助, 请不吝点击关注一下我的微信公众号:FSA全栈行动, 这将是对我最大的激励. 公众号不仅有
Flutter
技术,还有AI
、Android
,iOS
,Python
等文章, 可能有你想要了解的技能知识点哦~