目录
- 第一章 – Xcode 测试实践
- 第二章 – Xcode 打包实践
- 第三章 – Xcode 构建必备认知
- 第四章 – Xcode Build Setting
- 第五章 – Xcode 命令构建的场景问题
内容概览
命令行工具包是什么?
- macOS 10.9 及以后的版本上的 Xcode 不再提供命令行工具下载入口,应该如何安装?
- 如何卸载命令行工具?
- 多个 Xcode 并存的情况下,如何查看命令行工具使用的 Xcode 版本?
- 如何修改命令行工具使用的 Xcode 版本?
- 如何使用命令行构建 Xcode 项目?
- 如果有多个 Configurations ,如何给 xcodebuild 设置一个默认的 Configuration ?
- 如何利用命令行实施单元测试?
- 如何利用命令行实现 Xcode 中的 Build For Testing 和 Test Without Building 功能?
- exportOptionsPlist 文件可以包含哪些配置?
- 如何归档并导出应用?
命令行工具包是什么?
命令行工具包是一个小型的自包含工具包,可以独立下载,可以在 macOS 上进行命令行开发。它由 macOS SDK 和命令行工具(例如 Clang,位于 /Library/Developer/CommandLineTools)组成。 macOS 10.9 及以后的版本上的 Xcode 不再提供命令行工具下载入口,应该如何安装?
在 macOS 10.9 及以后的版本上,Xcode 的偏好设置中的下载面板不再支持命令行工具直接下载,可以使用下面任意一种方式来安装:
安装 Xcode
Xcode 已经包含命令行工具,因此只要安装了 Xcode,就无需额外安装命令行工具。
从 Apple 开发者网站下载
下载地址:developer.apple.com/download/al… ,需要使用 Membership Account 登录,搜索并下载和系统版本匹配的命令行工具版本。
在 macOS 10.9 及以后,当有新的命令行版本可用时,将会收到软件更新的通知。
通过 Terminal 安装
在 Terminal 中执行如下命令进行安装:
xcode-select --install
macOS 自带 xcode-select 命令,在 /usr/bin 目录下。
如何卸载命令行工具?
- 删除 Xcode 应用
- 删除 /Library/Developer/CommandLineTools 目录
多个 Xcode 并存的情况下,如何查看命令行工具使用的 Xcode 版本?
在 Terminal 中执行如下命令:
xcode-select --print-path
示例:
$ xcode-select --print-path
/Applications/Xcode.app/Contents/Developer
表示当前命令行工具使用的Xcode位于 /Applications/Xcode.app
如何修改命令行工具使用的 Xcode 版本?
在 Terminal 中执行如下命令:
sudo xcode-select -switch <path/to/>Xcode.app
示例:
$ sudo xcode-select -switch /Applications/Xcode.app
表示当前命令行工具使用的Xcode位于 /Applications/Xcode.app
如何修改命令行工具使用的 Xcode 版本?
在 Terminal 中执行如下命令:
sudo xcode-select -switch <path/to/>Xcode.app
实例:
$ sudo xcode-select -switch /Applications/Xcode.app
如何使用命令行构建 Xcode 项目?
使用 xcodebuild 命令,它可以对 Xcode 项目和工作空间进行编译、查询、分析、测试和归档。如果操作的是 Xcode 项目,需要指定至少一个 target 或一个 scheme,而如果是工作空间,则只需指定一个 scheme。在 Terminal 中执行 man xcodebuild 可以查看 xcodebuild 使用手册。xcodebuild 执行的结果默认存储位置定义在:Xcode > Preferences > Locations。
注意:在执行命令前,要先切换到包含项目或工作空间的目录下,或指定完整的项目或工作空间的路径。
列出工作空间包含的所有 scheme
xcodebuild -list -workspace <your_workspace_name>.xcworkspace
示例:
$ cd /Users/username/Desktop/MyApplication
$ xcodebuild -list -workspace MyApplication.xcworkspace
Information about workspace "MyApplication":
Schemes:
iOSApp
tvOSApp
macOSApp
iOSWithWatchApp
iOSWithWatchApp WatchKit App
列出项目包含的所有 target 、configuration 和 scheme
xcodebuild -list -project MyProject.xcodeproj
示例:
$ cd /Users/username/Desktop/MyProject
$ xcodebuild -list -project MyProject.xcodeproj
Information about project "MyProject":
Targets:
iOS
iOSTests
iOSUITests
watchOS App
watchOS App Extension
tvOS
tvOSTests
tvOSUITests
macOS
macOSTests
macOSUITests
Build Configurations:
Debug
Release
If no build configuration is specified and -scheme is not passed then "Debug" is used.
Schemes:
iOS
watchOS App
tvOS
macOS
编译项目中的一个 scheme
xcodebuild -scheme <your_scheme_name> build
示例:
$ xcodebuild -scheme tvOS build
=== BUILD TARGET tvOS OF PROJECT MyProject WITH CONFIGURATION Debug ===
...
注意:xcodebuild 支持编译、分析、测试和归档等操作,当不指定任何操作时,默认执行编译操作
构建时指定 configuration 文件
xcodebuild -target <your_target_name> -xcconfig <your_configuration_file>.xcconfig
示例:
$ xcodebuild -target iOS -xcconfig configuration.xcconfig
Build settings from configuration file 'configuration.xcconfig':
IPHONEOS_DEPLOYMENT_TARGET = 10
SWIFT_TREAT_WARNINGS_AS_ERRORS = YES
=== BUILD TARGET watchOS Extension OF PROJECT MyProject WITH THE DEFAULT CONFIGURATION (Debug) ===
...
=== BUILD TARGET watchOS App OF PROJECT MyProject WITH THE DEFAULT CONFIGURATION (Debug) ===
...
=== BUILD TARGET iOS OF PROJECT MyProject WITH THE DEFAULT CONFIGURATION (Debug) ===
...
修改 xcodebuild 的输出位置
可以在命令行中指定 SYMROOT(构建路径,存放 debug 包和 .dSYM 文件)和 DSTROOT(安装位置,存放 release 包和 .dSYM 文件)的值来达到修改目的。
修改 debug 版本的输出位置:
$ xcodebuild -scheme iOS SYMROOT="/Users/username/Desktop/DebugLocation"
Build settings from command line:
SYMROOT = "/Users/username/Desktop/DebugLocation"
=== BUILD TARGET watchOS Extension OF PROJECT MyProject WITH CONFIGURATION Debug ===
...
=== BUILD TARGET watchOS App OF PROJECT MyProject WITH CONFIGURATION Debug ===
...
=== BUILD TARGET iOS OF PROJECT MyProject WITH CONFIGURATION Debug ===
...
修改 release 版本的输出位置:
$ xcodebuild -scheme iOS DSTROOT="/Users/username/Desktop/ReleaseLocation" archive
Build settings from command line:
DSTROOT = /Users/username/Desktop/ReleaseLocation
=== BUILD TARGET watchOS Extension OF PROJECT MyProject WITH CONFIGURATION Release ===
...
=== BUILD TARGET watchOS App OF PROJECT MyProject WITH CONFIGURATION Release ===
...
=== BUILD TARGET iOS OF PROJECT MyProject WITH CONFIGURATION Release ===
...
如果有多个 Configurations ,如何给 xcodebuild 设置一个默认的 Configuration ?
导航到 Project > Info > Configurations ,选择一个 configuration 即可:
如何利用命令行实施单元测试?
xcodebuild 也支持单元测试,使用如下命令实现:
xcodebuild test [-workspace <your_workspace_name>]
[-project <your_project_name>]
-scheme <your_scheme_name>
-destination <destination-specifier>
[-only-testing:<test-identifier>]
[-skip-testing:<test-identifier>]
test 操作需要同时指定 scheme 和 destination。 -workspace 指向 .xcworkspace 文件路径。 -project 指向 .xcodeproj 文件路径。 -destination 确定了单元测试的运行环境,
是对用于测试的设备、模拟器或 Mac 的描述,用 key=value 来确定,多个键值对以逗号分隔。 -only-testing 是可选的,指定了只测试哪些用例,-skip-testing 也是可选的,指定了跳过测试哪些用例, 是对测试用例的描述,格式为:
TestTarget[/TestClass[/TestMethod]]
TestTarget 是测试 Target 的名称,必须要指定。TestClass 和 TestMethod 都是可选的,表示要进行测试的类和方法。
对于 macOS 应用,destinationspecifier 用 platform 和 arch 两个值表示
Key | Description | Value |
---|---|---|
platform | 测试运行的平台 | macOS |
arch | 测试使用的架构 | i386 或 x86_64 |
示例,在 macOS 平台的 x86_64 架构上进行测试:
xcodebuild test -scheme macOS -destination 'platform=macOS,arch=x86_64'```
对于 iOS 和 tvOS 应用,destinationspecifier 用 platform、name 和 id 表示,name 和 id 只能指定其中一个
Key | Description | Value |
---|---|---|
platform | 测试运行的平台 | iOS(针对 iOS 应用),tvOS(针对 tvOS 应用) |
name | 测试使用的设备全称 | Xcode 设备管理器中显示的设备名称 |
id | 测试使用的设备ID | Xcode 设备管理器中显示的设备ID |
示例,在 ID 为 965058a1c30d845d0dcec81cd6b908650a0d701c 的 iOS 设备上进行测试:
xcodebuild test -workspace MyApplication.xcworkspace -scheme iOSApp -destination 'platform=iOS,id=965058a1c30d845d0dcec81cd6b908650a0d701c'
示例,在名为 iPhone 的 iOS 设备上进行测试:
$ xcodebuild test -workspace MyApplication.xcworkspace -scheme iOSApp -destination 'platform=iOS,name=iPhone'
...
=== BUILD TARGET iOSApp OF PROJECT iOSApp WITH CONFIGURATION Debug ===
...
=== BUILD TARGET iOSAppTests OF PROJECT iOSApp WITH CONFIGURATION Debug ===
...
=== BUILD TARGET iOSAppUITests OF PROJECT iOSApp WITH CONFIGURATION Debug ===
...
Test Suite 'All tests' started at ...
Test Suite 'iOSAppTests.xctest' started at ...
Test Suite 'SecondTestClass' started at ...
Test Case '-[iOSAppTests.SecondTestClass testExampleA]' started.
Test Case '-[iOSAppTests.SecondTestClass testExampleB]' started.
Test Suite 'SecondTestClass' passed at ...
...
Test Suite 'iOSAppTests' started at ...
Test Case '-[iOSAppTests.iOSAppTests testExample]' started.
Test Case '-[iOSAppTests.iOSAppTests testPerformanceExample]' started.
Test Suite 'iOSAppTests' passed at ...
...
Test Suite 'iOSAppUITests.xctest' started at ...
Test Suite 'iOSAppUITests' started at ...
Test Case '-[iOSAppUITests.iOSAppUITests testLabels]' started.
Test Case '-[iOSAppUITests.iOSAppUITests testToolbar]' started.
...
** TEST SUCCEEDED **
示例,在名为 iPhone 的 iOS 设备上进行测试,但跳过对 iOSAppUITests 的测试:
xcodebuild test -workspace MyApplication.xcworkspace -scheme iOSApp -destination 'platform=iOS,name=iPhone' -skip-testing:iOSAppUITests
示例,在名为 iPhone 的 iOS 设备上进行测试,但只测试 iOSAppTests 中的 SecondTestClass 类的 testExampleB 方法:
$ xcodebuild test -workspace MyApplication.xcworkspace -scheme iOSApp -destination 'platform=iOS,name=iPhone' -only-testing:iOSAppTests/SecondTestClass/testExampleB
...
=== BUILD TARGET iOSApp OF PROJECT iOSApp WITH CONFIGURATION Debug ===
...
=== BUILD TARGET iOSAppTests OF PROJECT iOSApp WITH CONFIGURATION Debug ===
...
=== BUILD TARGET iOSAppUITests OF PROJECT iOSApp WITH CONFIGURATION Debug ===
...
Test Suite 'Selected tests' started at ...
Test Suite 'iOSAppTests.xctest' started at ...
Test Suite 'SecondTestClass' started at ...
Test Case '-[iOSAppTests.SecondTestClass testExampleB]' started.
Test Case '-[iOSAppTests.SecondTestClass testExampleB]' passed (0.007 seconds).
Test Suite 'SecondTestClass' passed at ...
...
** TEST SUCCEEDED **
对于 iOS 模拟器和 tvOS 模拟器应用,destinationspecifier 用 platform、name、id 和 OS 表示,name 和 id 只能指定其中一个,OS 是可选的
Key | Description | Value
-- | -- | --
platform | 测试运行的平台 | iOS Simulator(针对 iOS 应用),tvOS Simulator(针对 tvOS 应用)
name | 测试使用的模拟器全称 | Xcode 设备管理器中显示的模拟器名称
id | 测试使用的模拟器ID | Xcode 设备管理器中显示的模拟器ID
OS | iOS 或 tvOS 的版本号,或 latest 字符串 | 具体的 iOS 版本、 tvOS 版本,或者是 latest
示例,在名为 iPad Pro (12.9 inch)、版本号为 10.2 的 iOS 模拟器上进行测试:
xcodebuild test -scheme iOS -destination 'platform=iOS Simulator,name=iPad Pro (12.9-inch),OS=10.2'
示例,在 ID 为 D6FA2C2A-E297-406A-AA22-624B4834ACB2 的 tvOS 模拟器上进行测试:
xcodebuild test -scheme tvOS -destination 'platform=tvOS Simulator,id=D6FA2C2A-E297-406A-AA22-624B4834ACB2'
一个测试用例允许指定多个 -destination 选项,xcodebuild 将会按顺序在每个 -destination 指定的环境上测试。 示例,在 iOS 模拟器和 iPod touch 上依次进行测试:
xcodebuild test -scheme iOS -destination 'platform=iOS Simulator,name=iPhone 6s,OS=10.3' -destination 'platform=iOS,name=iPod touch'
如何利用命令行实现 Xcode 中的 Build For Testing 和 Test Without Building 功能?
xcodebuild 的 build-for-testing 操作实现了 Xcode菜单中的 Product > Build For > Testing 功能,需要指定一个 scheme
具体命令:
xcodebuild build-for-testing [-workspace <your_workspace_name>]
[-project <your_project_name>]
-scheme <your_scheme_name>
-destination <destination-specifier>
示例,在 ID 为 D6FA2C2A-E297-406A-AA22-624B4834ACB2 的 tvOS 模拟器上构建一个测试:
xcodebuild build-for-testing -scheme tvOS -destination 'platform=tvOS Simulator,id=D6FA2C2A-E297-406A-AA22-624B4834ACB2'
build-for-testing 操作会生成一个 xctestrun 文件,保存在 DerivedData 目录下。
xcodebuild 的 test-without-building 操作实现了 Xcode 菜单中的 Product > Perform Action > Test Without Building 功能,需要指定一个 scheme 或者一个 xctestrun 文件
指定 scheme 的用法:
xcodebuild test-without-building [-workspace <your_workspace_name>]
[-project <your_project_name>]
-scheme <your_scheme_name>
-destination <destination-specifier>
[-only-testing:<test-identifier>]
[-skip-testing:<test-identifier>]
test-without-building 在指定 scheme 进行测试时,它会在构建根目录(SYMROOT)寻找可以测试的包,因此请确保项目已经编译过或构建根目录已经存在可以测试的包。 示例,在名为 iPhone SE、版本为 10.1 的 iOS 模拟器上执行 test-without-building 测试:
xcodebuild test-without-building -workspace MyApplication.xcworkspace -scheme iOSApp -destination 'platform=iOS Simulator,name=iPhone SE,OS=10.1'
指定 xctestrun 文件的用法:
xcodebuild test-without-building -xctestrun <your_xctestrun_name>.xctestrun
-destination <destination-specifier>
[-only-testing:<test-identifier>]
[-skip-testing:<test-identifier>]
test-without-building 在指定 xctestrun 进行测试时,它会寻找 xctestrun 文件中指定的包,因此请确保这些包存在。 示例,在 ID 为 6DC4A7BA-EA7F-40D6-A327-A0A9DF82F7F6 的 iOS 模拟器上测试iOSApp_iphonesimulator.xctestrun 文件指定的包:
$ cat iOSApp_iphonesimulator.xctestrun
<?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>iOSAppTests</key>
<dict>
<key>BundleIdentifiersForCrashReportEmphasis</key>
<array>
<string>com.myapps.iOSApp</string>
<string>com.myapps.iOSAppTests</string>
<string>com.myapps.iOSAppUITests</string>
</array>
…
<key>TestBundlePath</key>
<string>__TESTHOST__/PlugIns/iOSAppTests.xctest</string>
<key>TestHostBundleIdentifier</key>
<string>com.myapps.iOSApp</string>
<key>TestHostPath</key>
<string>__TESTROOT__/Debug-iphonesimulator/iOSApp.app</string>
…
</dict>
<key>iOSAppUITests</key>
…
</dict>
</plist>
$ xcodebuild test-without-building -xctestrun iOSApp_iphonesimulator.xctestrun -destination 'platform=iOS Simulator,id=6DC4A7BA-EA7F-40D6-A327-A0A9DF82F7F6'
示例,在 ID 为 3D95DF14-E8B7-4A05-B65B-78F381B74B22 的 iOS 模拟器上测试iOSApp_iphonesimulator.xctestrun 文件指定的所有包,但跳过对 iOSAppUITests 的测试:
xcodebuild test-without-building -xctestrun iOSApp_iphonesimulator.xctestrun -destination 'platform=iOS Simulator,id=3D95DF14-E8B7-4A05-B65B-78F381B74B22' -skip-testing:iOSAppUITests
注意:build-for-testing 和 test-without-building 也支持持续集成系统。
exportOptionsPlist 文件可以包含哪些配置?
在 Terminal 中执行如下命令进行查看:
xcodebuild -help
示例:
$ xcodebuild -help
Usage: xcodebuild [-project <projectname>] [[-target <targetname>]...|-alltargets] [-configuration <configurationname>] [-arch <architecture>]
...
Available keys for -exportOptionsPlist:
compileBitcode : Bool
...
embedOnDemandResourcesAssetPacksInBundle : Bool
...
iCloudContainerEnvironment
...
manifest : Dictionary
...
method : String
...
onDemandResourcesAssetPacksBaseURL : String
...
teamID : String
...
thinning : String
...
uploadBitcode : Bool
...
uploadSymbols : Bool
如何归档并导出应用?
xcodebuild -project <xcodeprojpath> -scheme <scheme_name> -archivePath <xcarchivepath> archive
指向工程的 .xcodeproj 文件路径。如果操作的是工作空间,将 -project 替换为 -workspace 即可。