Xcode 构建必备认知

Xcode 构建必备认知

Posted by WTJ on September 12, 2022

目录

内容概览

命令行用法文档

  • 命令行使用手册
  • 如何利用文档写一条命令
  • Target、Configuration 和 Scheme 到底是什么东西?
  • Project、Workspace 又是什么东西?
  • xcodebuild 基础命令
  • 精彩预告

命令行用法文档

这里的 “用法” 取自英文的 “Usage”,是用法的简要形式。获取命令的 “用法文档” 取决于命令自身,因此没有固定的写法,工作经验告诉我们有下面几种常见写法(注意 <命令> 后有空格):

  • <命令> -h
  • <命令> help
  • <命令> --help
  • <命令> -usage

或者直接使用空命令尝试,如果命令出错,输出错误信息的同时一般会输出正确的用法。如果命令没有出错,表示该命令无需参数或使用了默认参数,可以通过阅读使用手册来获取。如果还是找不到,最后寻求官方网站的帮助。

示例,查看git命令的用法文档:

$ git --help

命令执行后,会将简要用法直接输出在当前界面:

$ git --help
usage: git [--version] [--help] [-C <path>] [-c <name>=<value>]
           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--bare]
           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
           <command> [<args>]
...
See 'git help git' for an overview of the system.

用法文档适用于快速查看命令用法。比如工作中突然忘记了一些选项,或某些选项是组合单词太长,不确定是否输入正确,就可以快速看一眼,然后继续工作。

命令行使用手册

这里的 “使用手册” 取自英文的 “Manual”,是一种更为详细的文档形式。macOS 使用BSD General Commands Manual,一般包含NAME、SYNOPSIS、DESCRIPTION、EXAMPLES、SEE ALSO 等常用的部分。

含义 描述
NAME 名字 一句话描述命令的名称
SYNOPSIS 概要 遵循命令行语法的格式,列举常用功能对应的命令行写法
DESCRIPTION 描述 命令的详细说明,一一列举每个参数的名称、简写方式、意义、组合用法、注意点等,参数包括:选项 option、标记 flag、值 value
EXAMPLES 示例 实现某个功能的具体写法示例
SEE ALSO 参见 相关联的其他命令

使用方式:

man <命令>

示例,查看echo命令的使用手册:

$ man echo

命令执行后,自动切换到vim的命令模式,并输出文档详情:

ECHO(1)    BSD General Commands Manual    ECHO(1)

NAME
     echo -- write arguments to the standard output

SYNOPSIS
     echo [-n] [string ...]

DESCRIPTION
     The echo utility writes any specified operands, separated by single blank (` ') characters and followed by a newline (`\n') character, to the standard output.

     The following option is available:

     -n    Do not print the trailing newline character.  This may also be achieved by appending `\c' to the end of the string, as is done by iBCS2 compatible systems.
...

SEE ALSO
     builtin(1), csh(1), printf(1), sh(1)
...

BSD    April 12, 2003    BSD
(END)

在命令模式下输入字母q退出命令模式,同时退出了使用手册界面。注意字母 q 输入成功后会立即退出。

尝试用 man git 获取使用手册,再用 git –help 获取用法文档,对比两者有何不同。

使用手册适用于学习或研究某个命令的阶段。因为非常详尽,往往有好几页,例如man git、man xcodebuild,不方便快速查看,但是对于深究是很有帮助的,特别是DESCRIPTION、EXAMPLES部分。

如何利用文档写一条命令

首先要读懂文档的内容,但是文档中的诸多符号成为阅读障碍,需要先解决它。

命令行符号

命令也是一个程序,它必须能够识别参数并根据参数调整功能。命令行文档利用约定的符号和顺序来识别参数,描述和限定命令行的用法。下面是一些常见符号:

符号 含义 描述
[ ] 可选 某个选项、标记或参数可以不用指定,多个可选组合使用,可以实现完全不同的功能
| 互斥 使用 | 连接的多个选项,只能选择其中一个使用
重复 选项、标记、参数或它们的组合可以多次出现在命令行中
< > 必选 必须指定,使用时将 < > 及内部的文本整体替换为具体值
无符号 不可修改 文档中怎么写的,使用时原样写入

命令行符号使用示例

在 Terminal 中执行:

$ xcodebuild -help

提取输出中的一行:

xcodebuild -list [[-project <projectname>]|[-workspace <workspacename>]] [-json]

按照这个说明,结合符号的含义,我们知道:

  • xcodebuild是命令,-list是选项,两者没有符号修饰,因此必须要出现在命令行中。
  • -project 和-workspace 以|连接,是互斥关系,如果要选择只能二选一。两者分别被[ ]包裹,代表可选,因此都可以不选。两者之外又被一个大的[ ]包裹,表示这一个整体可以完全忽略。分别被< >包裹,表示必须指定。整个意思是:要么指定-project,其后跟 project 路径,要么指定-workspace,其后跟 workspace 路径,要么整个忽略。
  • -json 是一个可选的功能开关,命令行中指定-json以 json 格式输出,不指定则原样输出。

上面三条的写法数分别是:1,3,2,组合起来共计有 6 种写法(顺序调整不计入写法数)。列举几种可能的写法:

  • xcodebuild -list
  • xcodebuild -list -project /Users/username/Networking.xcodeproj
  • xcodebuild -list -workspace /Users/username/A.xcworkspace -json

至此,我们对命令行有了基本的认识。熟记符号,多加练习,以后看到陌生的命令,应该不会再恐惧了。还有一些命令行符号没有列举,感兴趣的话可以去探索一番,例如{ }、( )。

Target、Configuration 和 Scheme 到底是什么东西?

Target、Configuration 和Scheme决定了最终产物,下面分别介绍。

Target

  • 以 Xcode 13.1 为例,创建一个 App 项目就自动创建了一个 Target,如果勾选 Include Tests,还会自动创建两个 Target:单元测试 Target 和 UI 测试 Target。另外 Xcode -> File -> New -> Target 可以手动创建 Target。实际上,Target 描述了最终产物的形态,是 App、Framework、Bundle 或者是 Extension,因此 Target 的最终产物可能是 xxx.app、xxx.framework、xxx.bundle、xxx.appex。
  • 在 Xcode -> Targets 列表中切换 Target,会发现每个 Target 都有自己的 Build Settings、Build Phases 和 Build Rules,修改一个 Target 的这些配置项,不会影响其他 Target(受关联影响的配置项除外)。例如,我们非常熟悉的 Build Settings 下的Other Linker Flags,Build Phases 下的Run Script。实际上,Target 确实定义了 Build Settings、Build Phases 和 Build Rules,就像自身的属性一样,修改它们就会影响 Target 的最终产物。例如,修改一个 Framework Target 的 Build Settings 下的Mach-O Type,可以决定最终生成静态库还是动态库。

fc1c3e5f798f417f98f8d0ad0ec15d8d~tplv-k3u1fbpfcp-zoom-in-crop-mark-3024-0-0-0 image

一个大型项目往往有多个 Target,从 Github 克隆 facebook-ios-sdk 项目,检出 tag v12.0.2 ,可以看到 FBSDKCoreKit 这个 Project 下,有 7 个 Target:

a9a964f050114fb9a4d7b4c781f807a6~tplv-k3u1fbpfcp-zoom-in-crop-mark-3024-0-0-0 image

查看产物,我们可以自己编译整个项目,也可以在 facebook-ios-sdk 项目的 Release 部分,下载官方帮我们编译好的产物:

207463c29b9346e5aa34996ace43bc97~tplv-k3u1fbpfcp-zoom-in-crop-mark-3024-0-0-0 image

可以看到,一个文件内,确实包含了多个平台和架构的产物。

Configuration

使用 Android Studio 进行 Android 开发的读者,应该很熟悉构建变体这个概念,Configuration就是 Xcode 中 Target 的 Build Settings 的变体,每个变体都有自己的配置,这些配置影响最终产物。新建一个 App 项目,默认创建了两个变体:Debug和Release。

变体可以独立修改。例如在 Release 变体中生成调试符号文件,用于分析生产环境报告的崩溃信息,而在 Debug 变体中禁用这项配置,以减少开发阶段的构建时间:

679d6e34a27c4de3b66815bada8bbcd9~tplv-k3u1fbpfcp-zoom-in-crop-mark-3024-0-0-0 image

在 Xcode -> Project 中管理变体:修改变体名称,新增变体,设置命令行构建默认变体,设置变体配置文件:

151410f1fe4144459270f741c60acb50~tplv-k3u1fbpfcp-zoom-in-crop-mark-3024-0-0-0 image

变体配置文件可以覆盖 Xcode 中 Build Settings 的配置,便于统一环境配置和版本管理,为大型项目的团队协作提供了很好的支持。变体配置文件后缀为.xcconfig,文件内容格式为:Key=Value,Key 来自 Build Settings,Value 可以继承默认值,也可以重写。在文件内还能引用其他变体配置文件。

示例,下面是 FBSDKCoreKit-Dynamic.xcconfig 的内容:

...
#include "Shared/Platform/iOS.xcconfig"
#include "Shared/Target/DynamicFramework.xcconfig"
#include "Shared/Version.xcconfig"

PRODUCT_NAME = FBSDKCoreKit
PRODUCT_BUNDLE_IDENTIFIER = com.facebook.sdk.FBSDKCoreKit

CURRENT_PROJECT_VERSION = $(FBSDK_PROJECT_VERSION)

INFOPLIST_FILE = $(SRCROOT)/FBSDKCoreKit/Info.plist
MODULEMAP_FILE = $(SRCROOT)/FBSDKCoreKit/FBSDKCoreKit.modulemap

Scheme

Target 和 Configuration 只是定义了产物,并不生成产物,Scheme 的作用就是驱动产物的生成。Scheme 给 Target 绑定了一系列操作,并为每个操作绑定了 Configuration:

68e8c9d3f222401d9b838e19ab667a3d~tplv-k3u1fbpfcp-zoom-in-crop-mark-3024-0-0-0 image

在 Xcode -> Product 执行Build、Run、Test、Profile、Analyze、Archive的操作时,实际执行了当前 Scheme 上对应的操作。当执行不同的操作时,同一个 Target 和 Configuration 的产物也会不同,例如Run的结果是运行到设备上,Archive输出归档文件。

Scheme 是一个操作列表,每个操作对应一条构建路

Scheme Action Target Configuration Product
App Build App Debug App.app
  Run App Debug 运行到设备
  Test AppTests、AppUITests Debug 运行测试用例
  Profile App Release 动态分析
  Analyze App Debug 静态分析
  Archive App Release App.xcarchive

Project、Workspace 又是什么东西?

Project和Workspace是 Xcode 项目的两种组织方式,下面分别介绍。

Project

Project 是直接容器,直接管理项目中所有的代码文件、资源、脚本、依赖库,以及 Target、Configuration 和 Scheme。以 Project 方式组织的项目的入口文件后缀是.xcodeproj。 一个 Project 可以引用其他 Project 作为自己的依赖项,和 Workspace 的操作方式一样。

Workspace

Workspace 是间接容器,它引用 Project 后才有意义。 当然在 Workspace 内也能够创建代码文件、添加资源等,如果这些文件不被任何 Project 使用,那么它们就只是一堆普通文件而已,不会起任何作用。以 Workspace 方式组织的项目的入口文件后缀是.xcworkspace。

示例,Workspace A 引用 App(一个 App Project) 和 Networking(一个 Framework Project),找到项目的.xcworkspace文件,右键显示包内容,查看contents.xcworkspacedata文件内容:

<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "group:Networking/Networking.xcodeproj">
   </FileRef>
   <FileRef
      location = "group:App/App.xcodeproj">
   </FileRef>
</Workspace>

可以看到, Workspace 把多个 Project 组织在一起了,而且没有做什么特殊的工作。 通过 Workspace 来组织 Project,能够非常便捷地在 Project 之间建立依赖。 示例,App Project 要依赖 Framework Project 的产物 Networking.framework,只需在 App Project 中找到 Framework Project,并添加其产物 Networking.framework,整个过程像添加系统库一样:

5948ad4487984768bc453c3679174055~tplv-k3u1fbpfcp-zoom-in-crop-mark-3024-0-0-0 image

注意,workspace 并非只是一个空壳,它也有自己的设置,导航到 Xcode -> File -> Workspace Settings:

f8d1475d26fd4baead7ca0241f947530~tplv-k3u1fbpfcp-zoom-in-crop-mark-3024-0-0-0 image

在构建系统中,workspace 必须搭配 scheme 使用。

Target、Configuration、Scheme、Project、Workspace这些概念在模块化中非常重要,后续会有文章介绍 iOS 模块化的实现方案。

xcodebuild 基础命令

执行 xcodebuild 命令前,要切换到包含.xcodeproj或.xcworkspace文件所在的目录,或在命令中通过-project、-workspace指定。如果目录下有多个.xcodeproj文件,默认使用第一个。

查看 SDK 信息

xcodebuild -version -sdk

示例:

$ xcodebuild -version -sdk
iPhoneOS15.0.sdk - iOS 15.0 (iphoneos15.0)
SDKVersion: 15.0
Path: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.0.sdk
PlatformVersion: 15.0
PlatformPath: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform
BuildID: 84856584-0587-11EC-B99C-6807972BB3D4
ProductBuildVersion: 19A339
ProductCopyright: 1983-2021 Apple Inc.
ProductName: iPhone OS
ProductVersion: 15.0

...

Xcode 13.0
Build version 13A233

输出了 Xcode 版本和所有 SDK 信息:iOS、iOS Simulator、DriverKit、macOS、tvOS、tv Simulator、watchOS、watch Simulator。在命令后附加-json,将以 json 格式输出信息。

这个命令没有指定 Project 或 Workspace,是因为它获取的是构建系统的信息,和具体项目无关。

查看 Project 信息

xcodebuild -list -project App/App.xcodeproj

示例,以 json 格式输出 App Project 信息:

$ xcodebuild -list -project App/App.xcodeproj -json
{
  "project" : {
    "configurations" : [
      "Debug",
      "Release"
    ],
    "name" : "App",
    "schemes" : [
      "App"
    ],
    "targets" : [
      "App"
    ]
  }
}

输出了 App Project 的 Target、Configuration 和 Scheme,这些信息非常重要,在上文已经分析过。

查看 Build Settings 信息

xcodebuild -project App/App.xcodeproj -showBuildSettings -destination "generic/platform=iOS"

示例:

$ xcodebuild -project App/App.xcodeproj -showBuildSettings -destination "generic/platform=iOS"
Command line invocation:
    /Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -project App/App.xcodeproj -showBuildSettings -destination generic/platform=iOS

User defaults from command line:
    IDEPackageSupportUseBuiltinSCM = YES

Build settings for action build and target App:
    ACTION = build
    ...
    ARCHS = arm64
    ...
    BUILD_DIR = /Users/username/Library/Developer/Xcode/DerivedData/App-ejgzkwsxgbtdqdefiyltxpffjdbg/Build/Products
    ...
    CODE_SIGN_STYLE = Automatic
    ...

输出了从命令行对 Project 执行 Build 操作所使用的 Build Settings 信息。

对 App Project 执行 Clean 操作

输出:

$ xcodebuild -project App/App.xcodeproj -scheme App -destination "generic/platform=iOS" clean
Command line invocation:
    /Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -project App/App.xcodeproj -scheme App -destination generic/platform=iOS clean

User defaults from command line:
    IDEPackageSupportUseBuiltinSCM = YES

note: Using new build system
note: Build preparation complete

** CLEAN SUCCEEDED **

命令执行成功,输出没有警告,Build 目录也已删除,非常完美。

Clean 操作指定的 -destination 可以是 iOS 平台,也可以是 iOS Simulator 平台,任选一种,都可以将 Build 目录删除。选择后者的写法为:-destination “generic/platform=iOS Simulator”