探究Xcode命令⾏⽤法三:xcodebuild打包实践(上)
本⽂还是 adat 项⽬的延伸,开始介绍打包实践。打包相关的内容繁多,作者把它分成了多篇⽂章,本⽂主要是概念部分。理解这些概念对于⾃⼰动⼿编写打包命令⾄关重要。如果你使⽤ fastlane、bitrise 或其他构建⼯具来打包,但对于某些配置项不是很理解,看完这篇⽂章,或许会很有帮助。
内容概览
了解架构
xcodebuild 命令中打包相关的⽤法
ExportOptions.plist 详解
了解架构
芯⽚设计采⽤什么指令集架构,在其上运⾏的软件就要⽀持对应的指令集架构。常见的指令集架构有x86系列和arm系列。
x86 和 arm
x86 是复杂指令集架构,由Intel公司设计,主要应⽤在桌⾯计算机和服务器。有16 位(16-bit)版本、32 位(32-bit)版本、64 位(64-bit)版本等,早期的i386是 32 位的 x86 架构,显然x86_64就是 64 位的 x86 架构。
arm 是精简指令集架构,由Arm公司设计,主要⽤于移动设备,⽐ x86 架构更加省电。同样,arm 架构也有 32 位和 64 位之分。Arm 公司发布的第⼀代ARM1就已经是 32 位架构,后来的ARMv3、ARMv7依然是 32 位,直到 2011 年发布的ARMv8-A才开始⽀持 64
位。
armv7 和 arm64
armv7,即ARMv7-A架构,是 32 位 arm 架构之⼀,因此不能统称为 arm32。例如 Apple 最早的 A 系列芯⽚ A4 以及后⾯的
A5/A5X/A6/A6X 芯⽚均采⽤ARMv7-A架构。A4 芯⽚服务了 iPhone 4 和第⼀代 iPad,A6 服务了 iPhone 5/iPhone 5c,直到 A6X 服务完第四代 iPad,armv7 就结束了它的使命,从 iPhone 5s 搭载的 A7 芯⽚开始,都是 64 位 arm 的天下。
arm64 是 64 位 arm 架构的统称,即ARMv8-A及以后版本的 64 位系列架构,并⾮是某⼀个架构。例
如 Apple 的
A7/A8/A8X/A9/A9X/A10/A10X 芯⽚采⽤ARMv8-A架构,A11芯⽚采⽤ARMv8.2-A架构,A12/A12X/A12Z 芯⽚采⽤ARMv8.3-
A架构,⽽在最新的 iPhone 13 系列⼿机上搭载的 A15 芯⽚则采⽤ARMv8.5-A架构。M 系列芯⽚ M1/M1 Pro/M1 Max 均是⽀持
arm64 架构的芯⽚。
M1 和 Rosetta 2
这么多架构,我们并未针对每⼀种架构输出⼆进制,⽽是通常将 Build Settings 的ARCHS项设置为armv7 arm64来统⼀处理。⽽能够统⼀处理的前提,则是每⼀个版本的架构都兼容前⼀个版本的。ARCHS值默认由$(ARCHS_STANDARD) 决定,它由 Xcode 根据项⽬⽀持的平台和版本⾃动确定。例如在 M1 芯⽚的 Mac 上利⽤ Xcode 13.1 创建的 macOS 项⽬,$(ARCHS_STANDARD) 实际值是arm64,在 Intel 芯⽚的 Mac 上创建的 macOS 项⽬,其值是x86_64 arm64。⽽在 Intel 芯⽚的 Mac 上利⽤低版本的 Xcode 创建的 macOS 项⽬,其值是x86_64。
如果应⽤只⽀持 x86_64 架构,它是不能直接跑在 M1 芯⽚上的,那么 Mac AppStore 上海量的应⽤⽆
法在 M1 设备上运⾏。缺失应⽤⽣态⽀持,直接导致 M1 设备⼏乎⽆⼈购买。⽽ M1 设备⼜是苹果打开⾃研芯⽚ Apple Silicon 市场的切⼊点,那么这条路注定很艰难。为了解决这个问题,Apple 创造了Rosetta 2⼯具,它⽀持 x86_64 应⽤运⾏在 arm64 架构的芯⽚上。这是⼀项伟⼤的技术,解决了前⾯的问题。但这种转换运⾏始终不可能和在 x86_64 架构的芯⽚上直接运⾏相媲美,最好还是开发者提供⽀持 arm64 架构的应⽤程序,这也是 Apple ⼤⼒呼吁开发者修改⾃⼰的应⽤程序以增加对 Apple Silicon ⽀持的原因。
现在不难理解为什么 iOS 应⽤可以在搭载 M1 的 MacBookPro 上运⾏了。因为芯⽚架构不同的鸿沟已经被填平,剩下软件层进⾏适配就相对容易了。
Universal binaries 和 Fat file
为了让⼀个应⽤程序在不同的架构上都可以运⾏,将⽀持不同架构的⼆进制打包在⼀起,就形成了通⽤⼆进制(Universal)⽂件,最终形成通⽤应⽤程序。作者使⽤的是 Intel 芯⽚的 Mac,导航到应⽤程序⽬录,在系统⽇历上右键显⽰简介,可以看到种类后标注有(通⽤),表明此应⽤是通⽤应⽤程序,可以同时在 Intel 芯⽚的 Mac 和 Apple Silicon 上运⾏。同样的⽅式查看Visual Studio,种类后标注的
是(Intel),表明这个版本的 VS 只能在 Intel 芯⽚的 Mac 上运⾏。
通过lipo命令查看系统⽇历中的⼆进制⽂件⽀持的架构:
$ lipo -info /System/Applications/Calendar.app/Contents/MacOS/Calendar
Architectures in the fat file: /System/Applications/Calendar.app/Contents/MacOS/Calendar are: x86_64 arm64e
同样的⽅式查看Visual Studio:
$ lipo -info /Applications/Visual Studio.app/Contents/MacOS/VisualStudio
Non-fat file: /Applications/Visual Studio.app/Contents/MacOS/VisualStudio is architecture: x86_64
从上⾯两个命令的输出中可以看到,⽇历的⼆进制包含x86_64 arm64e两个架构,是胖⽂件(Fat file),⽽Visual Studio的⼆进制只
有x86_64⼀个架构,不是胖⽂件(Non-fat file)。
xcodebuild 命令中打包相关的⽤法
打包配置
-configuration NAME
指定 Build Settings 的变体名称,Xcode ⼯程默认创建了 Debug 和 Release 两个变体。例如指定 Release 变体,则写法为-configuration Release。
-xcconfig PATH
指定 Build Settings 的配置⽂件,所有在 Xcode -> Build Settings ⾯板中的配置,都可以在配置⽂件中
指定。配置⽂件优先级最⾼,会覆盖 Build Settings ⾯板中的配置和命令⾏单独传⼊的配置。
关于变体和配置⽂件的详细说明,请参考
-arch ARCH
针对指定的架构进⾏构建。例如-arch arm64,将只构建 arm64 架构的⼆进制。
-sdk SDK
指定 Base SDK 的规范名称(Canonical Name)或完整路径。通过xcodebuild -showsdks -json查看所有可⽤的 SDK 的完整信息。
⽰例,指定 Base SDK 的完整路径:
$ xcodebuild \
-deproj \
-scheme App \
-destination generic/platform=iOS \
-sdk /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.0.sdk
或者指定规范名称:-sdk iphoneos15.0
账号授权
-allowProvisioningUpdates
允许 xcodebuild 和 Apple Developer 后台交互。对于⾃动签名的项⽬,xcodebuild 可以⾃动创建或更新证书、AppID 和描述⽂件,对于⼿动签名的项⽬,xcodebuild 会下载缺失的描述⽂件或更新过期的描述⽂件。
有两种⽅式可以搭配该选项使⽤:
在 Xcode 偏好设置⾯板中登录开发者账号,后续⽆论是从 Xcode 构建还是利⽤ xcodebuild 命令构建,都将获得 Apple Developer 后台交互权限。
不登录开发者账号,通过 API 密钥进⾏交互。API 密钥由-authenticationKeyPath、-authenticationKeyID和-authenticationKeyIssuerID三者共同决定。
-allowProvisioningDeviceRegistration
允许 xcodebuild 将当前设备注册到 Apple Developer 后台。依赖-allowProvisioningUpdates。
-authenticationKeyPath PATH
指定 API 密钥⽂件的绝对路径,不能是相对路径。密钥⽂件是.p8格式。导航到 App Store Connect -> ⽤户和访问 -> 密钥 -> App Store Connect API, 创建⼀个密钥,点击下载 API 密钥按钮进⾏下载。
注意,创建密钥需要指定权限,权限较低⽆法操作发布证书。另外,密钥⽂件只能下载⼀次,请妥善保管。如果新创建的密钥没有下载⼊⼝,刷新⼀下⽹页就会显⽰。
-authenticationKeyID KEY_ID
密钥 ID。导航到 App Store Connect API,选择⼀个密钥,点击拷贝密钥 ID即可。
-authenticationKeyIssuerID ISSUER_ID
创建认证令牌的发放者。导航到 App Store Connect API,Issuer ID下⾯的字符串即是。
打包操作
-archivePath PATH
指定.xcarchive⽂件的路径。
-exportPath PATH
指定导出⽬录,⽬录中可能包含AppStoreInfo.plist,DistributionSummary.plist,ExportOptions.plist,manifest.plist,OnDemandResources⽂件夹,Packaging.log,安装包⽂件等。后续会针对这些⽂件
⼀⼀说明。
-exportOptionsPlist PATH
提供导出操作需要的参数,决定导出⾏为。后⽂会详细说明。
archive
将 Xcode 项⽬导出为.xcarchive归档⽂件,由-archivePath指定⾃定义路径,不指定则默认导出到~/Library/Developer/Xcode/Archives/路径的当前⽇期⽬录下。
-exportArchive
将归档⽂件导出为安装包或上传⾄ AppStore。必须依赖-archivePath和-exportOptionsPlist,对于导出操作,还需指定-exportPath。
-exportNotarizedApp
将公证过的归档⽂件导出为安装包。
-create-xcframework
从已经编译好的Framework或Library创建.xcframework⽂件。.xcframework⽂件可以同时包含多个平台和架构的⼆进制,并直接嵌⼊Xcode 项⽬,Xcode 会根据当前运⾏环境⾃动抽取匹配的⼆进制参与构建,⽆需再写脚本剥离动态库中的模拟器架构。
ExportOptions.plist 详解
ExportOptions.plist 描述了导出操作,内部的配置项和⼿动在 Xcode 中导出需要勾选或输⼊的内容相同。由于是命令⾏操作,不允许和Xcode 界⾯交互,只能将这些内容通过⽂件形式先确定下来。在终端中执⾏xcodebuild -h会在输出的最后⼀部分显⽰所有可⽤的键。
分发⽅式
method
指定分发⽅式,例如分发到 AppStore 或沙盒测试。值是 String 类型,所有可⽤值:app-store,validation,package,ad-
xcode怎么打开hoc,enterprise,development,developer-id,mac-application。归档⽂件类型不同,可⽤值也会相应变化。
destination
将 App 上传到 App Store Connect 还是导出到本地,默认是导出到本地。值是 String 类型,所有可⽤值:export,upload。上传操作需要和 App Store Connect 交互,参见上⽂的账号授权部分。
generateAppStoreInformation
是否⽣成AppStoreInfo.plist⽂件。值是 Bool 类型,YES 或 NO,默认为 NO。在 Linux 和 Windows 中利⽤iTMSTransporter上传 App 时必须要指定该⽂件,⽽在 macOS 中上传时不需要。
Bitcode
uploadBitcode
是否上传 bitcode 并允许 AppStore 从 bitcode 重新编译 App,仅限 AppStore 导出操作。值是 Bool 类型,YES 或 NO,默认是YES。需要在 Xcode 中启⽤Enable Bitcode,并且归档⽂件内确实携带 bitcode。
如果项⽬中使⽤了任何⼀个不包含 bitcode 的三⽅库,会导致最终的 App 也⽆法⽀持 bitcode,所以这个步骤常常被开发者所忽略,但对Apple ⽽⾔却是⾮常重要的。当有新的硬件或软件变更时,需要 App 进⾏更新适配⽽不是兼容运⾏,就很依赖开发者了,直接导致 Apple 受限于开发者变得被动,很难推进⾃我更新。但是如果留有 App 的 bitcode,Apple 可以在后台默默重新编译 App 以适配最新的
软硬件,这样就摆脱开发者的限制了。
compileBitcode
是否需要 Xcode 以和 AppStore 相同的⽅式从 bitcode 重新编译 App,只对⾮ AppStore 导出操作有效。需要在 Xcode 中启⽤Enable Bitcode,并且归档⽂件内确实携带 bitcode。值是 Bool 类型,YES 或 NO,默认是 YES。
On-Demand Resources
onDemandResourcesAssetPacksBaseURL
指定按需下载资源(On-Demand Resources,以下简称 ODR)所在的服务器地址,只对⾮ AppStore 导出操作有效。值是 String 类型,具体为资源所在的服务器域名和⽬录。xcodebuild 会将它写⼊AssetPackManifest.plist的URL部分。AssetPackManifest.plist ⽂件可以在导出成功的 App 的 Main Bundle 内到。如果是 AppStore 导出,ODR 会被 AppStore 所托管,因此该值⽆效。
embedOnDemandResourcesAssetPacksInBundle
是否将 ODR 嵌⼊包内,只对⾮ AppStore 导出操作有效。值是 String 类型,true 或 false。默认是 true,当指定
了onDemandResourcesAssetPacksBaseURL时,默认为 false。如果选择嵌⼊包内,则 App ⽆需从服务器下载 ODR 即可直接使⽤。当运⾏与 ODR ⽆关的测试或 ODR 托管服务器不可⽤时,这个选项⾮常有⽤。如果不嵌⼊,会额外导出 OnDemandResources ⽂件夹,包含所有 ODR 资源和⼀个 AssetPackManifest.plist ⽂件。
Over-the-air Installation
manifest
从浏览器安装 App(Over-the-air Installation)时需要的配置,只针对⾮ AppStore 导出操作。值是字典(Dictionary)类型,需要指定字典项的三个键:software-package(App 包地址),display-image(57*57像素的展⽰图⽚),full-size-image(512*512像素的原始图⽚)。如果使⽤了 ODR,还需指定 ODR 的 AssetPackManifest.plist ⽂件地址:asset-pack-manifest。启⽤该项,会额外导
出manifest.plist⽂件。
关于 manifest.plist ⽂件的解析和使⽤,会放在后续的上传与分发相关的⽂章中。关注作者,第⼀时间获取:
签名&证书&描述⽂件
signingStyle
导出 App 时使⽤的签名⽅式,⼿动签名或⾃动签名。值是 String 类型,所有可⽤值:manual,automatic。如果使⽤⾃动签名并导出了归档⽂件,那么导出 App 可以是⼿动签名也可以是⾃动签名。如果使⽤⼿动签名导出归档⽂件,就只能使⽤⼿动签名⽅式导出 App,此时该值会被忽略。
provisioningProfiles
指定 App 使⽤的所有描述⽂件,仅⽤于⼿动签名导出操作。值是字典类型,字典项的键是 BundleID,值是描述⽂件名或其 UUID。如果App 使⽤了⼀个应⽤扩展(Application Extension),则这个字典会有两项,⼀项是 App 的,另⼀项是应⽤扩展的。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论