xcodebuild 命令行构建
# xcodebuild 命令行构建(iOS 常用实测版)
实测环境:Xcode 27.0 (Build 27A266a) / macOS Apple Silicon 重点内容:构建 / 测试 / 打包归档 / CI 脚本 / 常见报错排查
# xcodebuild 是什么
xcodebuild 是 Xcode 的命令行构建入口:Xcode GUI 里按 Command - R 时,底层跑的就是它。所有 CI(fastlane、Jenkins、GitLab CI)构建 iOS 项目最终都落到这条命令。
三个核心价值:
- 自动化——脚本 / CI 无人值守构建、测试、打包
- 可复现——参数固定后构建结果不依赖任何人本机的 Xcode GUI 状态
- 可定制——命令行可覆盖任意 build settings,不用改工程文件
xcodebuild -version # Xcode 27.0 / Build version 27A266a
xcrun xcodebuild -version # 等价写法(经 xcrun 入口)
2
未设置
DEVELOPER_DIR时,系统工具通过xcode-select选择开发者目录;设置该环境变量可覆盖当前命令及其子进程的选择,不改变系统默认目录。多版本共存时同时检查环境变量与工具版本(见 xcrun)。
# 帮助与自查体系
命令细节不用背,按需查:
xcodebuild -help # 完整帮助(选项极多,配合 grep 用)
xcodebuild -usage # 简要用法
man xcodebuild # 手册(含 destination 语法说明)
2
3
# 核心概念(先搞清楚再敲命令)
| 概念 | 说明 | 命令行对应 |
|---|---|---|
| project | .xcodeproj 工程文件,可独立构建 | -project MyApp.xcodeproj |
| workspace | .xcworkspace,多 project + Pod 依赖的容器(用了 CocoaPods 必须用它) | -workspace MyApp.xcworkspace |
| scheme | 构建方案:包含哪些 target、跑哪些测试、用什么配置 | -scheme MyApp |
| configuration | 构建配置,默认 Debug / Release | -configuration Release |
| destination | 构建目标:模拟器 / 真机 / 泛平台 | -destination '...' |
两条铁律:
- workspace 和 scheme 几乎总是成对出现(
-workspace必配-scheme) - destination 不写会随机挑一台可用设备,CI 上必须显式指定
# 基础查询命令
# 查工程里有哪些 scheme / target / configuration
xcodebuild -list -workspace MyApp.xcworkspace
# 实测输出示例:
# Schemes:
# MyApp
# MyAppTests
# Pods-MyApp
# 查已装 SDK
xcodebuild -showsdks
# 查 scheme 支持哪些 destination(报"找不到 destination"时先跑这个)
xcodebuild -showdestinations -workspace MyApp.xcworkspace -scheme MyApp
# 查 build settings 生效值(排查签名/路径问题的第一入口)
xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -showBuildSettings | grep -E "PRODUCT_BUNDLE_IDENTIFIER|DEVELOPMENT_TEAM|PROVISIONING"
# 加 -json 输出机器可读格式(脚本解析用)
xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -showBuildSettings -json
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# destination 写法(最高频踩坑区)
# 模拟器:按名称指定(推荐,CI 稳定)
-destination 'platform=iOS Simulator,name=iPhone 15 Pro'
# 模拟器:按 UDID 指定(多开时最精确,UDID 来自 xcrun simctl list)
-destination 'platform=iOS Simulator,id=80CCEF25-6BD0-4C3A-9A4E-896064E088F7'
# 真机:按 UDID
-destination 'platform=iOS,id=00008130-00162C3C2891401C'
# 真机:泛平台(不需要插真机,打包/签名场景必用)
-destination 'generic/platform=iOS'
# macOS
-destination 'platform=macOS'
2
3
4
5
6
7
8
9
10
多个 key 用逗号连接:platform=iOS Simulator,name=iPhone 15 Pro,OS=17.0。
真机 vs 模拟器架构不同:模拟器产物在
Debug-iphonesimulator/(含模拟器 slice),真机产物在Debug-iphoneos/,两者互不通用——装包时对应关系见 xcrun 章节。
# 构建
# 基本构建
# 标准构建(模拟器)
xcodebuild -workspace MyApp.xcworkspace -scheme MyApp \
-configuration Debug \
-destination 'platform=iOS Simulator,name=iPhone 15 Pro' \
build
# 单 project 工程(没用 CocoaPods 时)
xcodebuild -project MyApp.xcodeproj -scheme MyApp build
# 清理后重建(排查诡异缓存问题)
xcodebuild ... clean build
2
3
4
5
6
7
8
9
10
11
成功最后一行是 ** BUILD SUCCEEDED **,失败是 ** BUILD FAILED **——脚本里用退出码 $? 判断即可(成功为 0)。
# 覆盖 build settings(不改工程文件)
# 任意 build setting 都可以用 key=value 在命令行覆盖
xcodebuild ... build \
CODE_SIGN_IDENTITY="iPhone Distribution" \
DEVELOPMENT_TEAM=XXXXXXXXXX \
COMPILER_INDEX_STORE_ENABLE=NO # 关索引写入,CI 提速
2
3
4
5
# 指定产物目录(CI 必备)
# 固定 DerivedData 路径,方便脚本取产物 / 复用缓存
xcodebuild ... build -derivedDataPath build/DerivedData
# 产物位置:build/DerivedData/Build/Products/<configuration>-<platform>/MyApp.app
2
3
不指定时产物在默认 DerivedData:~/Library/Developer/Xcode/DerivedData/<工程名>-<hash>/,路径带 hash 不稳定,脚本里一律用 -derivedDataPath 或 -showBuildSettings 查 BUILT_PRODUCTS_DIR。
# 测试
# 跑单测(scheme 需包含测试 target)
xcodebuild test -workspace MyApp.xcworkspace -scheme MyAppTests \
-destination 'platform=iOS Simulator,name=iPhone 15 Pro'
# 只跑指定用例
xcodebuild test ... -only-testing:MyAppTests/LoginTests
xcodebuild test ... -only-testing:MyAppTests/LoginTests/testLoginSuccess
# 排除某些用例
xcodebuild test ... -skip-testing:MyAppTests/UITests
# 指定 test plan
xcodebuild test ... -testPlan UnitTests
# 失败自动重试(CI 拦偶现失败)
xcodebuild test ... -retry-tests-on-failure
# 只测不构建(复用已有产物,配合 build-for-testing)
xcodebuild build-for-testing ...
xcodebuild test-without-building ...
# 测试报告:xcresult(Xcode 13+ 默认 v3)
xcodebuild test ... -resultBundlePath build/Test.xcresult
# 用 GUI 看:open build/Test.xcresult
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 打包上架(archive → ipa)
# 第一步:archive 归档
xcodebuild archive \
-workspace MyApp.xcworkspace -scheme MyApp \
-configuration Release \
-destination 'generic/platform=iOS' \
-archivePath build/MyApp.xcarchive \
-allowProvisioningUpdates # 自动管理签名时,允许联网更新描述文件
2
3
4
5
6
产物 MyApp.xcarchive 是目录,内含 Products/Applications/MyApp.app(真机架构、已签名)。
# 第二步:导出 ipa
# 需要 ExportOptions.plist 描述导出方式
xcodebuild -exportArchive \
-archivePath build/MyApp.xcarchive \
-exportPath build/ipa \
-exportOptionsPlist ExportOptions.plist
# 产物:build/ipa/MyApp.ipa
2
3
4
5
6
ExportOptions.plist 最小可用模板(App Store 渠道):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<string>app-store-connect</string>
<key>teamID</key>
<string>XXXXXXXXXX</string>
<key>signingStyle</key>
<string>automatic</string>
<key>uploadSymbols</key>
<true/>
</dict>
</plist>
2
3
4
5
6
7
8
9
10
11
12
13
14
method 常用值对照:
| method | 用途 |
|---|---|
app-store-connect | App Store / TestFlight 上架 |
ad-hoc | 内测分发(需设备注册在描述文件内) |
enterprise | 企业证书分发 |
development | 开发证书 |
手动签名时把
signingStyle换成manual,并加provisioningProfiles/signingCertificate字段。
# 静态库 / xcframework
# 构建 framework / 静态库:换 target 即可
xcodebuild -workspace MyLib.xcworkspace -scheme MyLib \
-destination 'generic/platform=iOS' -configuration Release build
# 合并模拟器 + 真机架构为 xcframework(跨平台分发标准产物)
xcodebuild -create-xcframework \
-framework build/sim/MyLib.framework \
-framework build/device/MyLib.framework \
-output build/MyLib.xcframework
2
3
4
5
6
7
8
9
# CI 常用技巧
# 1. 日志降噪:只输出警告和错误
xcodebuild ... build -quiet
# 2. 日志美化(xcodebuild 原始日志极难读)
brew install xcbeautify
xcodebuild ... build 2>&1 | xcbeautify
# 旧工具 xcpretty 同类,xcbeautify 更快更准
# 3. 关闭索引写入(CI 无人用 IDE,省 20%+ 时间)
COMPILER_INDEX_STORE_ENABLE=NO
# 4. 并行构建
-parallelizeTargets -jobs 8
# 5. 跳过不可用的 scheme action(如 scheme 配了 run 但 CI 只想构建)
-skipUnavailableActions
# 6. SPM 依赖固定目录(CI 缓存 SPM checkout)
-resolvePackageDependencies -clonedSourcePackagesDirPath .spm-cache
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 常见问题排查
# error: does not contain a scheme named "Xxx"
- scheme 名先用
xcodebuild -list核对,注意大小写 - scheme 标记为 hidden:Xcode → Product → Scheme → Manage Schemes 勾选 Shared(CI 上必须 Shared,否则
.xcscheme不入库) - workspace / project 传错了:Pods 工程必须用
-workspace,单独构建某个 Pod 用-project Pods/Pods.xcodeproj
# Unable to find a destination / Multiple matches for destination
# 先看 scheme 实际支持哪些 destination
xcodebuild -showdestinations -workspace MyApp.xcworkspace -scheme MyApp
2
- 模拟器名拼错(如系统没有
iPhone 15 Pro这台设备):用xcrun simctl list devices available核对 - 多台设备匹配同名称:改用
id=<UDID>精确指定 - 真机未插 / 未信任 / 未开开发者模式:连接问题见 xcrun 章节排查
# 签名相关报错(requires a provisioning profile / Signing for X requires a development team)
# 自动签名 + 联网更新描述文件
xcodebuild ... -allowProvisioningUpdates
# 命令行显式覆盖(CI 常用)
xcodebuild ... build \
CODE_SIGN_IDENTITY="iPhone Distribution" \
DEVELOPMENT_TEAM=XXXXXXXXXX \
PROVISIONING_PROFILE_SPECIFIER="match AppStore com.example.MyApp"
2
3
4
5
6
7
8
- 先
-showBuildSettings | grep -E "CODE_SIGN|DEVELOPMENT_TEAM|PROVISIONING"看当前生效值 - CI 上建议
security unlock-keychain解锁钥匙串,否则签名会卡住或失败
# 构建结果与 GUI 不一致(本地好的 CI 挂了)
- GUI 走的 scheme / configuration / destination 与命令行不同——用
-showBuildSettings对比CONFIGURATION、PLATFORM_NAME、SDKROOT - 缓存脏了:
xcodebuild clean或直接删-derivedDataPath指定的目录(GUI 是Command Shift - K) - Xcode 版本不同:CI 与本机各跑一次
xcodebuild -version对比
# 卡在 "Waiting for first launch" / 首次安装弹窗
新装 Xcode 首次 CLI 构建需要完成首次启动协议:
xcodebuild -runFirstLaunch # 安装组件并同意 license
xcodebuild -license status # 单查 license 状态
2
# 产物路径找不到
# 查产物目录的权威方式
xcodebuild ... -showBuildSettings | grep -E "BUILT_PRODUCTS_DIR|TARGET_BUILD_DIR"
2
规则:BUILT_PRODUCTS_DIR = <DerivedData>/Build/Products/<configuration>-<platform>/,其中 platform 为 iphonesimulator / iphoneos。
# 与其他工具的关系
| 工具 | 关系 |
|---|---|
| Xcode GUI | GUI 构建就是调 xcodebuild,日志在 Report Navigator 里 |
| fastlane | gym/scan 底层就是拼 xcodebuild archive/test 参数 |
| xcrun | xcodebuild 本身也是经 xcrun 定位执行的(见 xcrun 章节) |
| xcode-select | 多 Xcode 版本切换后,xcodebuild 版本跟着变 |