刚换到 M1 芯片的 Mac 时,我做的第一件事就是拉下一个 iOS 工程跑pod install,结果一连串报错直接把终端刷成了天书。那段时间在群里问了一圈,发现几乎每台 M1 上都倒下过一批人,报错五花八门,但根源都指向同一个东西:CocoaPods 这套 Ruby gem 生态在 Apple Silicon 上的架构兼容性。
这篇文章就聚焦一个主题:M1(以及后续的 M2、M3 系列)上pod install报错的完整解法。我会把我在实际项目里碰到的典型报错、排查思路、最终能稳定跑通的配置都写出来,也会解释每一步操作背后的逻辑。不管你是刚拿到 M1 的新手,还是从 Intel 迁移过来的老开发,这套流程照着走基本都能把问题解决。
1. 先说清楚:M1 上 pod install 到底是怎么挂掉的
很多人在 M1 上第一次装 CocoaPods,会用 Intel 时代的旧习惯:把 Terminal 切换到 Rosetta 模式、装一个 x86_64 的 Homebrew,然后sudo gem install cocoapods。这套操作在某些场景下确实能跑通,但会让你的开发环境变得很奇怪,而且会引出更多问题。
1.1 这不等同于 Intel Mac 的普通报错
M1 上的报错和 Intel 时代最常见的“gem 权限不足”“源不对”完全不一样。Intel 机器上的问题大多是 Ruby 版本太低、gem 源被墙、或者某个依赖包没装全,通常搜几个sudo gem install命令就能解决。M1 的问题则集中在 CPU 指令集差异上:Intel 装的是 x86_64 的二进制,M1 上 Apple Silicon 原生跑的是 arm64。
问题就在这个“差异”上。如果你用的是 M1 原生 Terminal,系统的/usr/bin/ruby是给这个 arm64 环境使用的,但很多 gem 的原生扩展库(尤其是ffi)默认安装时会去编译 x86_64 版本,结果在 arm64 的 Ruby 进程中加载失败,直接抛出一堆晦涩的崩溃信息。
更麻烦的是,很多人为了解决这个问题会切换到 Rosetta 终端,这样 gem 又被装到了另一个架构的路径下。两边架构的 gem 互相污染,最后就是pod命令时而存在时而不存在,报错信息还每次都不一样。
1.2 我踩过的典型报错清单
先把常见报错摆出来,方便你对号入座:
| 报错信息 | 出现阶段 | 根因 |
|---|---|---|
bad CPU type in executable | 安装 gem 后运行 pod | 当前 shell 是 arm64,但 pod/pod 依赖的原生扩展是 x86_64 |
Errno::EPERM: Operation not permitted @ apply2files | pod install执行中途 | 旧版 activesupport / 系统文件权限冲突 |
xcrun: error: invalid active developer path | 任何需要编译的阶段 | Xcode 命令行工具没装好或路径失效 |
Failed to build gem native extension/ffi编译失败 | gem install时 | Ruby 原生扩展编译失败,多半是 ffi 或相关依赖不兼容 |
pod: command not found | 换终端后 | gem 装到了不同架构/不同用户目录下,PATH 不一致 |
Killed或进程崩溃无输出 | pod install执行时 | 内存/线程模型问题,常见于 Rosetta 环境 |
看到这些报错不要慌。它们没有一个需要重装系统或换电脑来修,只需要搞清楚“你的机器上到底跑的是哪套 Ruby、哪套 gem、哪套依赖库”,然后统一架构,问题自然消失。
2. 为什么推荐“优先走原生 arm64”而非无脑 Rosetta
先说结论:现在的 CocoaPods 版本(1.11.0 之后)已经完整支持 Apple Silicon,你完全可以用原生 arm64 环境跑pod install,不需要把终端切成 Rosetta。只有极少数场景才需要退回 x86_64,比如某些老旧的闭源第三方 SDK 只发布了 x86_64 的静态库并硬性要求在 x86 环境编译。遇到这种情况我会单独开一个 Rosetta shell,其余情况下都推荐原生 arm64。
2.1 底层原因
CocoaPods 本身是 Ruby gem,而 gem 能不能在 arm64 下跑,取决于它的原生扩展。核心依赖ffi在 1.15.0 版本之后提供了 arm64-darwin 的预编译二进制,这是官方社区修复的里程碑。紧随其后的依赖如activesupport也早已消除对旧版 Ruby 的依赖。所以,只要你装的 CocoaPods 版本足够新,就完全没必要再委屈自己跑 x86 环境。
我见过两个看起来很唬人但其实是自欺欺人的操作:
第一,把整个 Homebrew 装成 x86_64。这样 brew 和 gem 全走 Rosetta,确实兼容,但每次运行都损耗性能,而且以后所有安装都要用arch -x86_64前缀,连路径都分裂成/usr/local(x86)和/opt/homebrew(arm64)两套。
第二,装两套 Ruby 两套 gemset,一套跑 arm64、一套跑 x86。这在多项目管理里是灾难,你永远搞不清当前终端用的是哪边。
2.2 什么时候必须从 x86 转成 Apple Silicon
坦白讲,2023 年之后的方案已经很少需要手动切 Rosetta 了。我只坚持一种例外情况:如果你的某个 Pod 依赖在pod install时强制生成x86_64的二进制 Framework,并且供应商没有给出 Apple Silicon 版本,那么只在这一步上临时用 Rosetta 终端执行:
arch -x86_64 pod install注意,这只影响当前命令行进程,不会污染你的环境。跑完这个命令之后,我还是会把默认终端切回 arm64 模式,在原生环境下继续日常开发。
3. 完整操作:从零把 CocoaPods 在 M1 上装到能跑
下面我把一次完整的、最稳妥的 CocoaPods 安装流程写出来。这套流程我在 M1 和 M2 上都实测过,适用于目前绝大多数 iOS 项目。
3.1 第一步:确定 Apple Silicon 需要的系统环境
首先,确认你的 Mac 是 arm64 架构:
uname -m如果输出arm64,说明你在 Apple Silicon 原生终端里。如果输出x86_64,说明当前终端运行在 Rosetta 下,需要先退出 Rosetta 模式重开 Terminal。
接着确认 Xcode 命令行工具:
xcode-select -p正常应该输出类似/Applications/Xcode.app/Contents/Developer。如果输出报错,或者提示找不到路径,执行:
sudo xcode-select --reset sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer这一步卡住的概率很高,因为不少人是刚装了 Xcode 但命令行工具还未完成初始化,pod install一跑到编译阶段就直接挂掉。
3.2 第二步:通过 Ruby 版本管理器安装 Ruby
我强烈不建议直接用系统自带的/usr/bin/ruby。它是 Apple 为系统工具保留的 Ruby 2.6,不仅版本旧,而且权限管理很别扭,sudo gem install装完以后经常因为系统签名问题写不进 /usr/bin 外的地方。
用 Ruby 版本管理器是最干净的方案。我常用的是rbenv,因为它没有rvm那么重,也不要求 shell 函数注入。先安装 rbenv:
brew install rbenv ruby-build然后初始化:
rbenv init按提示把eval "$(rbenv init -)"写进~/.zshrc。
接下来安装一个较新的 Ruby。这里有个参数要特别注意:不要装 2.6 之前的老版本,也不要装太新的预览版。我当时用的版本是 3.x 稳定版,通过:
rbenv install 3.2.2 rbenv global 3.2.2rbenv global会切换当前用户默认 Ruby,不会影响系统其他脚本,所以很安全。安装完成后检查:
ruby -v which rubywhich ruby应该指向~/.rbenv/shims/ruby,而不是/usr/bin/ruby。如果还在系统路径,说明rbenv init没生效,检查~/.zshrc里的顺序,或者直接重启终端。
3.3 第三步:更换 gem 源并安装 CocoaPods
国内网络环境下,建议先把 gem 源换成可访问的镜像源,避免下载依赖时卡死。命令行执行:
gem sources --remove https://rubygems.org/ gem sources --add 你的可用镜像源地址这里我不过分推荐某个具体源,你自己验证可用即可。换源后安装 CocoaPods:
gem install cocoapods安装完成后,关键一步是重新加载 shim:
rbenv rehash pod --version如果pod输出了版本号,那么基础安装就成功了。这里我可以分享一个容易踩的坑:rbenv rehash忘记执行会导致pod命令明明存在但就是找不到,大多数“装完后 command not found”其实都是这一步漏了。
3.4 修复 ffi 与原生扩展的构建问题
很多 M1 用户的pod install报错,其实发生在gem install cocoapods过程中或者首次运行时的 ffi 编译失败。我在 Rosetta 环境下遇到过这种情况,在原生 arm64 下也遇到过。
如果编译 ffi 失败,可以尝试指定安装带预编译二进制的版本:
gem install ffi -v 1.15.5然后再装 CocoaPods:
gem install cocoapods -v 最新的稳定版本号如果你们项目对这个 ffi 和我一样遇到过Failed to build gem native extension,还有一个很阴间的细节:旧版本的 Command Line Tools 会在 arm64 上错误地选择 x86_64 的编译器。那就升级命令行工具:
sudo rm -rf /Library/Developer/CommandLineTools sudo xcode-select --install升级完之后重新编译一次 ffi,基本都能过。注意不要删掉 Xcode 自带的 developer path,只清理和重装 CommandLineTools 就够。
4. 实操过程与核心环节实现
理论讲完,下面是一套可以“照着抄”的完整实操流程。我会穿插说明每步检查的目的,这样你能根据自己的报错卡在哪一步来动态调整。
4.1 全终端路径检查
在项目目录跑pod install之前,先给终端做一个体检:
uname -m # 必须是 arm64,除非你明确要用 Rosetta which ruby # 必须是 rbenv shims 路径 ruby -v # 建议 >= 2.7 which pod # 必须是 gem 安装到的路径 bundle --version # 如果项目有 Gemfile这四行命令的输出确定了下面几件事:CPU 架构、Ruby 解释器来源、pod 可执行文件位置、依赖管理方式。任何一个输出不符合预期,都不要急着开跑,先修正环境比事后排查快得多。
如果你的项目里有Gemfile,我建议在bundle init生成的 Gemfile 里补一行 ruby 版本约束,避免同事在不同 Mac 上用不同版本的 Ruby 导致 Podfile.lock 里记录出不一致的结果。
4.2 安装脚本和配置解析
我习惯把下面的内容写成一个setup.sh脚本,放到团队仓库根目录,新成员 clone 完项目直接执行:
#!/bin/zsh set -e # 检查架构 if [[ $(uname -m) != "arm64" ]]; then echo "请在 Apple Silicon 原生终端中运行" exit 1 fi # 检查 Xcode 路径 xcode-select -p >/dev/null 2>&1 || echo "请先安装 Xcode" # 安装依赖 brew install rbenv ruby-build eval "$(rbenv init -)" rbenv install 3.2.2 --skip-existing rbenv global 3.2.2 rbenv rehash # 安装 CocoaPods gem install bundler bundle install bundle exec pod install --repo-update注意这里有两个细节。第一,我用bundle exec pod install而不是裸pod install,这样能确保项目使用的是 Gemfile 中锁定的 CocoaPods 版本,而不是系统里哪个“最新”版本。第二,--repo-update只在你首次拉取项目时加,日常开发不要每次加,否则每次都会去拉全量 spec 仓库,非常耗时。
团队协作时,如果你们不打算引入 Gemfile,至少把 CocoaPods 大版本固定下来。可以写.swift-version或.ruby-version,帮助不同成员的 rbenv 自动切到正确的 Ruby 版本。
4.3 pod install 实战
环境配好后,进入工程目录拉依赖:
cd /path/to/your/ios_project pod install我见过 M1 上跑pod install一次失败,第二次就成功的情况,原因是第一次执行时 Homebrew 会做brew update,网络慢的话会超时。如果你在国内网络环境,可以先把 pod repo 更新限制住:
git config --global --add url.你的镜像地址.insteadOf https://github.com/但这属于网络基础设施配置,不建议在团队脚本里写死,只在个人环境手动调整即可。
第一次pod install成功之后,之后每次安装依赖都会快很多。如果中途你还是遇到坑就往下一节看,基本都是同一个根因的不同变体。
5. 常见问题与排查技巧实录
这里整理我在 M1 上遇到的、以及在帮别人排查过程中见过的典型问题。
5.1zsh: bad CPU type in executable与 x86 gem 残留
这个报错非常典型:说明当前终端是 arm64,但你执行的某个可执行文件是 x86_64 架构,macOS 拒绝运行。它经常出现在你曾经在 Rosetta 终端里安装过 CocoaPods、后来换回原生终端的环境里。
排查思路是两个方向。第一,移除 Rosetta 环境下的 gem 残留:
arch -x86_64 gem list cocoapods如果列出来有版本,说明 Rosetta 环境装过一份。对应清理方式是直接在 Rosetta 模式下卸载:
arch -x86_64 gem uninstall cocoapods第二,检查当前pod可执行文件的架构:
file $(which pod)如果输出里包含x86_64,说明 gem 装错了环境。最干净的解决办法是删掉重新用 arm64 装一遍,不要保留两个架构的版本。
5.2Errno::EPERM: Operation not permitted @ apply2files
这个报错曾经是我排查最久的问题。它发生在pod install中段,看着像是文件权限不够,但用户已经是管理员权限。最后定位到是旧版activesupportgem 在复制文件时调用了系统不支持的 API。
解决方案是升级 activesupport:
gem uninstall activesupport gem install activesupport -v 6.1.7.3或者更简单,直接把 CocoaPods 升级到新版,新版依赖的 activesupport 不再触发这个 API。实践下来,升级 CocoaPods 到 1.12 以上后,Errno::EPERM基本绝迹。
5.3xcrun: error: invalid active developer path
这个报错多数发生在升级系统或升级 Xcode 之后。CocoaPods 要调用 xcrun 去查询 SDK 路径,而 CommandLineTools 的路径失效了。
执行:
sudo xcode-select --reset之后验证:
xcode-select -p xcrun --show-sdk-path如果xcrun --show-sdk-path还是空,就需要重装命令行工具。注意重装后重新执行gem install cocoapods或重新编译原生扩展,否则 ffi 的编译缓存里还记录着旧路径。
5.4 Ruby 2.6 已停止维护后怎么办?
M1 上市时的 macOS 自带 Ruby 2.6,很多人接手老项目时还被锁定在这个版本。Ruby 2.6 对 Apple Silicon 的支持不完整,而且官方早已停止维护,再遇到 ffi 之类的原生依赖构建问题,几乎是无解的。
我的建议是不要守旧,直接在容器里用 rbenv 装新 Ruby。如果你实在拗不过项目里的.ruby-version约束,我的操作习惯是:在项目根目录放一个.ruby-version文件指向项目要求的版本,然后用rbenv local切版本。项目内部版本切换和全局版本互不干扰,老项目也能在新机器上安心跑。
5.5 需要注意 Pods 的架构信息
最后提醒一个非常容易被忽略的点:M1 上pod install成功不等于构建成功。当你执行 Xcode 构建时,如果遇到:
Building for iOS Simulator, but the linked library ... was built for iOSUndefined symbols for architecture x86_64
这通常是 Pod 里的静态库架构不匹配。CocoaPods 是帮你构建依赖的,但最终产物是否支持模拟器 arm64,取决于每个 Pod 的设置。你可以打开 Xcode 的Build Settings查看VALID_ARCHS和EXCLUDED_ARCHS。
我的经验做法:在 Podfile 顶部为 framework 设置:
post_install do |installer| installer.pods_project.targets.each do |target| target.build_configurations.each do |config| config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0' end end end这里顺手把部署版本统一到 12.0 以上,避免某些老依赖在 iOS 版本上出现兼容问题。架构问题如果还出现,再在 Build Settings 里确认ONLY_ACTIVE_ARCH是否为 YES,模拟器 Debug 下应保持 YES,避免同时编译多个架构导致报错。
6. 把配置固化下来
到此,M1 上pod install已经能跑通了。但环境的稳定性还需要你把配置固化。我最后贴一段日常项目里会直接复制进~/.zshrc的环境配置。
6.1 添加环境变量和 PATH
export PATH="$HOME/.rbenv/shims:$PATH" eval "$(rbenv init -)" export LDFLAGS="-L/opt/homebrew/opt/openssl@3/lib" export CPPFLAGS="-I/opt/homebrew/opt/openssl@3/include"openssl环境变量是为了保证某些原生 gem 在编译时能找到正确的头文件和库文件。M1 的 Homebrew 默认装到/opt/homebrew,如果你发现装 gem 时经常卡在 openssl 相关的编译报错,这两个环境变量就能救你一次。
我个人还喜欢加一个函数,用来一键进入 Rosetta 环境处理老依赖:
alias x86="arch -x86_64 zsh"这样只要极少数的老 SDK 场景才切 x86,平时都是原生环境。
6.2 日常操作脚本模板
每次新建 iOS 工程,我建议按以下步骤走,能避免一大半环境问题:
git clone 项目地址 cd 项目目录 rbenv local 3.2.2 gem install bundler bundle install bundle exec pod install如果你的项目没有 Gemfile,就直接:
gem install cocoapods pod install记得在项目的Podfile里加上固定版本注释,例如团队成员统一使用 1.12.1,避免有人 auto-update 到不同版本造成 Podfile.lock 冲突。
6.3 关于团队协作的建议
最后给新入行的开发者一句实际经验:碰到 M1 上的 pod 问题,不要在群里发一张终端报错截图就伸手要答案,把uname -m、which ruby、which pod、pod --version四条命令的结果贴出来,别人帮你定位的速度会快十倍。
我在实际项目中还发现一个额外的好处:用 rbenv 管理 Ruby 之后,团队里不再出现“我机器上明明装好了,怎么你那边就报错”的失配问题。每个人都有独立 Ruby 版本、独立 gemset,pod 相关的问题能从环境层面降到真正的依赖树层面,这对长期维护项目来说非常值得。
个人体会是:M1 上的 CocoaPods 问题,90% 以上是环境构建不一致造成的,真正需要改代码或改 Podfile 的情况反而是少数。只要统一终端架构、统一 Ruby 版本管理、统一 gem 源,日常开发就不会再被 pod 支配。再遇到新的报错时,先问自己一句:当前这个进程是让哪个架构跑的,有没有把 gem 装到对应的 Ruby 里?搞清楚这一点,后面就都是按图索骥了。