Flutter - Xcode16 还原编译速度
# 一、前言
在之前发布的【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
- 将
Xcode 15.4
内部的默认工具链复制到~/Library/Developer/Toolchains
目录。 - 将当前工作目录切换到
~/Library/Developer/Toolchains
目录。 - 将复制过来的
XcodeDefault.xctoolchain
重命名为Xcode15.4.xctoolchain
,方便区分。 - 修改
.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()
# 四、改进
直接修改 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 进行安装。
这一步会做如下几个操作
- 将
/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
源码进行修改,使其具备重定向 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
环境变量即可。
# 七、资料

- 01
- AI - 免费的 Cursor 平替方案03-30
- 02
- Android - 2025年安卓真的闭源了吗03-28
- 03
- AI - RooCode 解限使用 Copilot Claude 3.703-18