大多数浏览器和
Developer App 均支持流媒体播放。
-
探索 Xcode 中的高级项目配置
使用更复杂的 Xcode 项目?您找对地方了。了解如何配置项目以针对多个 Apple 平台进行构建、按平台过滤内容、创建自定义构建规则和文件相关性等。我们将带您了解多平台框架目标,详细说明如何优化您的项目和方案配置,并展示如何有效利用配置设置文件。我们将探索并行构建和隐式相关性、脚本阶段、自定义构建规则、设置输入和输出文件相关性、构建阶段文件列表以及通过聚合目标删除重复工作的配置方案。最后,了解有关构建设置编辑器、级别工作原理和配置设置文件语法的更多信息。
资源
相关视频
WWDC21
-
下载
♪ (探索Xcode中的 高级项目配置) 你好 欢迎观看 《探索Xcode中的 高级项目配置》 我是杰克 此课程将由我 和同事普拉琪共同完成 我会探讨充分利用 Xcode项目构建配置的 策略和技术 我们会探讨三个主题 第一 普拉琪会探讨多平台项目 以及Xcode 13对 多平台框架目标的全新支持 接下来 我将介绍 通过方案、目标设置和依赖项管理 以及构建阶段和规则 对项目进行建模和配置的最佳实践 最后 普拉琪会带你深入了解 构建配置 我们会探讨其架构和操作 项目编辑器UI 配置设置文件以及它们的语法规则 以及更多其他内容 在本次演讲中 我们会使用 名为Fruta的 多平台应用项目 从而展示这些技术 在真实项目中的应用 现在要有请普拉琪 她会讲解多平台框架 谢谢你 杰克 Xcode 13的一个全新特色 就是支持多平台框架 多平台框架让我们可以将 多个框架合并为一个 为我们提供简化的目标管理 一组待管理的构建阶段 以及一组待管理的构建设置 让我们来看看Fruta应用 并更新我们的项目 从而利用这个特色功能 这就是Fruta应用 这是为macOS iOS 和watchOS 打造的多平台应用 它还有三个框架目标 每个平台一个目标 且每个平台包含一组 应用程序使用的共享代码 维护三个独立的框架 会面临一些挑战 例如要使构建设置保持同步 并确保所有源文件能够准确添加 至编译源代码构建阶段中 为了应对这些挑战 我们先将其中一个框架 转化为一个多平台框架 这里我们有三个框架 每个平台对应一个框架 除了仅在macOS 上构建文件的目标 所有其他目标都基本相同 首先 在项目导航器中 找到Build Settings 标签从而完成macOS框架目标 下一步 我们将通过进入 Supported Platforms 构建设置 以及选择Any Platform 配置针对所有平台构建的框架 你还可以看到 允许多平台构建的选项 已自动设置成“是” 这通知了构建系统立即为每个 所支持的平台尽可能地 构建目标一次 既然这是个多平台目标 回想一下初始的macOS框架 有一个额外的文件 那是 构建macOS时所构建的文件 为了设置我们的框架实现上述功能 我们可以添加一个平台筛选条件 来限定这个文件 只针对macOS进行构建 为此 我们首先要进入 Build Phases标签 下一步 展开 Compile Sources的构建阶段 最后 进行 Ingredient+macOS.swift配置 从而仅仅针对macOS进行构建 配置方法为点击 Filters选项 除了“macOS” 以外都不勾选 既然我们现在进行了新的 多平台目标配置 我们可以删除框架中的其他两项变量 因为不再需要它们了 此外 因为我们只有一个框架目标 我们必须配置所有的应用 从而链接并嵌入新目标 我们已经配置好了 macOS应用 因为我们是从macOS 应用开始设置了 多平台目标 我们可以给iOS和watchOS 应用添加新框架 添加方法是进入每个应用目标的 General tab 然后把此框架添加到 Frameworks和Libraries构建阶段中 总结一下 我们采用了 macOS框架目标 并将它启用 从而为 iOS和watchOS构建目标 我们采用平台过滤条件为 macOS源文件定制了框架 最后 我们配置了应用目标 从而链接和嵌入新的单一 多平台框架目标 这就是Xcode中的多平台目标 现在 有请杰克 他会进一步探讨项目配置 谢谢你 普拉琪 我接下来要探讨建模 和配置Xcode项目的最佳实践 并且会展示一些能用来提高 构建性能及正确性的方法 首先 来看看方案的构建选项 我会点击方案选取器 Edit Scheme 然后进入Build部分 我在这里配置的是一些简单的东西 对于Build Order 我们推荐选择Dependency Order 它会在你的项目中生成目标 从而根据依赖项图表 进行并行构建 这会极大提高多核构建性能 可以让您更快地从持续集成中 获得结果 相反 选择Manual Order已经过时 不推荐使用 这个选项会减慢构建速度 当方案所列的目标顺序 和你的项目依赖项不一致时 会导致循环错误 另一个在方案构建选项中的重要设置 是Find Implicit Dependencies 选择这个选项会让Xcode 基于项目中的信息 自动地在目标间添加依赖项 例如在构建设置中的连接器标志 和在构建阶段的链接库名称 当相关目标位于 通常无法添加显式目标依赖项 的不同项目中时 这特别有用 如果由于在不同项目之间 无法添加明确的目标依赖项 而使用手动依赖项命令 从而按具体命令来构建目标 那么启用 Find ImplicitDependencies 并选择Dependency Order 通常是更好的解决方案 现在我要来谈谈脚本阶段和构建规则 我会从项目目标列表中 选择SmoothieKit目标 然后选择Build Phases标签 这里有Process Recipes脚本阶段 它包含一些自定义构建逻辑 其作用之一是从多个 方案文件中生成代码 每个输入都对应一个输出 可按顺序处理 好了 您可能意识到这些运算 完全相互独立的 这提供了一次性能优化的机会 可以通过将它们并行运行来进行优化 构建规则让我们可以这样做 让我们来看看如何在这项工作中 提取一个构建规则 进入框架项目编辑器中的 Build Rules 选项卡 然后点击加号按钮 添加一个新的构建规则 接着输入文件模式*.recipe 它与我想要这个规则执行的 文件类型的文件扩展名相符 下一步 我要把依赖项添加到规则中 我不需要添加任何额外的输入 去构建规则 因为它会自动 抓取它处理的每个输入文件作为输入 然而 我需要去告诉构建系统 规则将为它处理的每个文件 所生成的输出文件的路径 我会点击加号按钮 添加一个新的输出文件 然后输入 $( DERIVEN_ FILE_ DIR)/$ (INPUT_ FILE _BASE) .compiledrecipe 在DERIVED_FILE_DIR下 写入生成的文件是最佳实践 因为它会指向一个由构建系统管理的 恰当的位置 您应该避免在源文件根目录下 生成输出文件 这会干扰源代码控制 并在同时运行 多个构建时会造成冲突 当然 现在必须将 我们的脚本阶段代码 复制到规则中 我会回到脚本阶段 复制我们处理过的每个文件的代码 然后我会回到规则中 把它们粘贴进去 记住 这些规则 对它们处理的每个输入运行一次 所以我会删除for循环 将$RECIPE替换成 $SCRIPT_INPUT_FILE 这与现在处理的输入文件的 绝对文件路径相对应 然后将$DERIVED_FILE_DIR/ $RECIPE.compiledrecipe 替换成$SCRIPT_OUTPUT_FILE_0 这是我在下方输出文件部分中输入的 输出文件路径 不要忘记去引用变量 从而确保正确处理文件路径中 的空格和其他特殊字符 很好 现在还有一样东西要在规则中设置 我说过规则对它们处理的 每个输入运行一次 默认情况下 它们还可以在 每个目标编译的 架构中运行一次 举个例子 在Mac应用目标中的规则 可以在arm64和x86_64各 运行一次 乘以它们每次的输入 所以如果有四个输入乘以两个架构 规则就要被调用八次 当规则的输出依赖于体系架构时 例如目标代码 这非常有用 然而 在这种情况下 我的规则生成 独立于底层CPU体系结构的输出 所以我要取消勾选每个架构运行一次 最后 为了构建系统在构建规则中 传递输入文件 我要将所有的.recipe文件 添加到框架目标的编译源构建阶段中 我会回到Build Phases 展开Compile Sources 使用加号按钮来添加方案文件
现在让我们回到脚本阶段 余下的工作 就是将多个文本文件的内容 合并到一个文件中 我们可以在运行时 更有效地在我们的应用程序中加载 为了获得更好的源代码控制经验 我会让脚本留在项目文件的外部 并且从内嵌的脚本编辑器里调用它 让我们跟随提示到 package.sh中查看代码 构建规则不适用于这种情况 因为我们需要一次性处理所有的输入 从而将它们合并成为一个 因此无法将其分解为可以 并行运行的独立模块 因此让这项工作 留在脚本阶段是有道理的 但这让我们注意到了 其中最重要的地方 脚本没有输入和输出的指定依赖项 这有可能使得构建任务 在错误的指令中运行 并且减慢了构建速度 因为Xcode在 并行运行其它任务时必须要更保守 因为它不知道脚本阶段 会用到什么文件 所以添加输入和输出 依赖项十分重要 从而确保由脚本阶段执行的工作 相对于构建中的其他任务 能够以正确的指令完成 对于这个特别的脚本 我要进行大量的输入 我可以采用xcfilelist 并通过外部文件管理这些输入列表 不需要一个一个地 在项目文件中 输入这些 我继续添加一个文件到项目里 进入文件 新建文件 在其他选项处选择 Build Phase File List
我将在每行粘贴一个此脚本阶段 要处理的输入文件列表 如果你愿意 甚至可以写下备注 用井号开始新的一行备注 这非常适合于添加额外的上下文 现在 我要提一下位于脚本阶段中 的xcfilelist 我会回到脚本阶段 并在输入文件列表中指定 xcfilelist 的路径
最后 我要指定一个输出依赖项 方法是在要写入输出文本的位置 提供一个文件路径 就像我在构建规则中做的一样
还有一点需要提一下 和构建规则相似 脚本阶段会 为您提供一些关键的环境变量 我们回到package.sh 仔细查看 在源代码中 我引用了 SCRIPT_INPUT_FILE_LIST_COUNT 它代表输入文件列表 传递到我们脚本阶段的总数 SCRIPT_INPUT_FILE_LIST_n 代表已解决的绝对文件路径 它位于nth索引中的输入文本列表 还有SCRIPT_OUTPUT_FILE_O 代表已解决的绝对文件路径 它位于第一个 也是这案例中唯一的 输出文件中 这里是由脚本阶段提供的 其中一些重要 环境变量的概述 目标构建设置 也可用于脚本阶段环境 这是针对构建规则概览的 一些环境变量 和一些不那么常见的变量概述 就像脚本阶段 目标的构建设置 也可用于脚本阶段环境 好了 现在 如果我试着去构建项目 我会遇到一个问题
我们仔细看下创建日志 因为SmoothieKit 是一个多平台目标 它要构建两次 一次用于iOS 一次用于watchOS 这就意味着每次构建 都试图在同一路径上生成脚本阶段的输出 这是行不通的 因为构建系统 要求在整个构建中 只有一项任务 能在一个既定路径中生成 有很多方法可用来解决这个问题 一个简单的解决办法是改变脚本阶段 的输出路径 以便它在每次 目标构建时都是唯一的 在这个案例中 我会考虑使用 不同的构建设置 例如DERIVED_FILE_DIR 这是特定于平台的 可使路径足够独特 从而解决冲突 然而如果脚本阶段在执行的实际工作 在同一目标的文本下是相同的 那很容易导致同样的工作完成了两次 对于这种情况 更好的办法是 把脚本阶段移到一个新的集合目标中 它是共享框架目标所依赖的 这就是我要为我的项目做的 首先 我会点击目标列表底部 的加号按钮 选择Other标签 选择Aggregate目标 我会称它为Resources 然后添加一个新的脚本阶段 从框架目标中复制 名称 脚本源 输入和输出
最后 我会从框架目标删除初始的脚本阶段 然后在新的聚合目标上 添加一个目标依赖项
这样 那工作就只需完成一次 不会有输出文件冲突了 iOS和watchOS 框架变量都能 以对应于这个脚本阶段的 正确命令构建 构建成功了 现在 有情普拉琪 她会告诉你与构建设置相关的内容 谢谢你 杰克 那什么是构建设置呢? 这是一个你可以应用 于Xcode目标的属性 从而配置目标构建的各个方面 Xcode提供了配置构建设置的 两个主要机制 第一个是通过构建设置编辑器 第二个是通过配置设置文件 或通过.xcconfig文件 让我们先看看如何用构建设置编辑器 在我们的项目中管理设置吧 为了打开构建设置编辑器 首先 你要在Project Navigator 选择你的项目 下一步 要确保选择你想要配置的目标 最后 点击Build Settings标签 它在标签栏处 在这里 你可以添加新的构建设置 或是调整已有的设置 您还可以通过打开快速帮助检查器 查找所选构建设置的 其他信息 构建设置被分成多个级别定义 你可以把这看成一大堆定义 事实上 这些级别是可显示出来的 只要点击级别筛选器 每一列代表一个不同的级别 可在这里定义一个构建设置 并且它们能被从右到左地取值 从最低的级别开始 这里有预设值 它被限定于当前选择的SDK 项目的级别配置设置文件 来自Xcode项目文件的 项目级别设置 限定在配置设置文件中的目标设置 限定在你的Xcode项目文件中的 目标级别设置 以及最后 构建设置中的已解决值 请注意 如果你看到一个粗体的设置 表示该级别对于构建设置 具有明确的值 另一个Xcode用以 管理构建设置的机制是配置设置文件 或.xcconfig文件 xcconfig文件的优点包括 具有更好的源代码控制管理 跨目标或配置实现共享设置 先进的构建设置构成方式 以及基于开发或测试环境的 包含额外的 xcconfig文件的能力 来看下你可以怎么 在xcconfig文件中 编写构建设置 在最基础的级别 一个构置设置是由名称 赋值运算符和值构成 你可以使用条件语法规则 缩小构建设置的值 用方括号作限定条件性设置 一些支持的条件包括 配置 架构和SDK 就像SDK条件显示的 通配符可被用于匹配目的 也可使用相似的双斜线语法添加 添加备注 构建设置还能通过使用 dollar-parens语法 用来设置另一个构建设置的值 举个例子 将MY_OTHER_BUILD_SETTING 设置成YES MY_BUILD_SETTING_NAME 的值 使用了dollar-parens 语法来对 MY_OTHER_BUILD_SETTING取值 也可以在这里取多个值 如同我们在MORE_SETTINGS 中看到的那样 最后 构建设置的已有值 能和$(inherited)值 一起使用 这让你可以将额外的值 追加到构建设置中 同时保留所有的已有值 这是很方便的形式 因为你还能使用 构建设置的名称 APPEND_TO_EXISTING_SETTINGS 使用另一项构建设置的取值语法 是为了一同编写构建设置 那是从另一组构建设置得来的 首先 我们从一个控制设置开始 IS_BUILD_SETTING_ENABLED 我们会用到这个设置的值作为 其他两个构建设置的后缀 MY_BUILD_SETTING_NO和 MY_BUILD_SETTING_YES 最后 我们要限定 MY_BUILD_SETTING 以取得一个值 那是由 MY_BUILD_SETTING 和IS_BUILD_SETTING_ENABLED 共同组成的 因为构建设置取值是从里到外的 最深处的设置会被取值 然后返回NO 也就是 IS_BUILD_SETTING_ENABLED的值 最终 组合而成的 BUILD_SETTING_NO 会被取为 -use_this_one的值 当在取一个构建设置的值时 你可使用一组运算符 给将取值进行基础转换 这三种运算符是 字符串运算符 路径运算符 和替换运算符 受支持的字符操作分别是 引号 让字符中的字母可被跳过 上下文 可改变字母的大小写 和标识符 在不同的格式中将字符 转换成无效标识符 我们可以提供一组 路径运算符来得到目录 文件名称 基本名称 后缀和标准化路径 每个路径运算符都有 一个可替换的对应操作 这让你能替换部分取值 还有一个默认运算符提供了 替换值 以防构建设置是空的 除此之外 它还用到了 构建设置的已有值 最后要看的一项是 在其他xcconfig文件中 包含xcconfig文件的能力 你能利用的机制有两种 一种是被要求包含的 它要求xcconfig文件 存在于硬盘中 那可能产生一个编译器错误 当找不到文件时 第二种是选择性包含 这要求如果硬盘中显示出了 xcconfig文件就要将其包含 即使文件不存在 它也不会失败 注意 路径与你的 Xcode项目文件位置相关 好了 来看看真实情境中 怎么把所有的信息放在一起 在下面例子中 我们会看到怎么去解决以下问题 在我们的开发机器中 当表达式要花长时间进行类型检查时 编译器会主动对其发出警告 然而 CI机器的反应比它要慢 所以 检查表达式的时间需要增加 为了解决这个问题 这里有三种配置设置文件: debug common 和 ci.xcconfig debug xcconfig 文件用于我们的调试构建 并通过 OTHER_SWIFT_FLAGS 构建设置 将一些额外的标志传递给 Swift 编译器 常规的xcconfig文件 选择性地包含了 ci.xcconfig文件 并且限定了 OTHER_SWIFT_FLAGS设置 来控制表达式警告的类型 它采用$(inherited) 保证包含任何其他的标志设置 例如debug.xcconfig 文件的标志 还有MAX_EXPRESSION_TIME 的构建设置取值 它的默认值是200 ci xcconfig文件 为MAX_EXPRESSION_TIME 限定了一个覆盖值 最后 Xcode 需要被告知 怎么将这些xcconfig文件 应用到其中一个受支持的配置级别 这可通过项目编辑器完成 就是我们在这里看到的 在配置部分 你可以应用项目中的 任何config文件 不管是在项目中还是在目标级别 都可用作任何限定的构建配置 这里 你能看到 应用到项目级别中 的debug.xcconfig文件 作为Fruta的调式配置 另外 common.xcconfig文件 可被设置为项目中的任何目标 总结一下解决方式 使用默认操作符 为MAX_EXPRESSION_TIME 限定一个默认值 ci.xcconfig文件 可选择性地被包含 因为它只存在于CI系统中 一个被覆盖的默认值 是可用在ci.xcconfig文件中 MAX_EXPRESSION_TIME的值 我们的实际示例结束了 好了 有请杰克来回顾 我们讲过的内容 谢谢 普拉琪 来回顾一下 你了解了多平台框架 知道它们如何在多平台项目中 提供更简单的方式 管理构建设置和构建阶段 你了解了如何通过根据依赖命令并行构建目标 从而提高你的项目配置 和构建性能 如何正确地使用构建规则和构建阶段 还有指定依赖项的重要性 最后 你深入研究了构建设置 如何使用配置设置文件 从而更轻松地管理它们 还深入研究了它们的语法 以及它提供的所有结构 我们希望这些课程能给你提供 一系列有用的工具 帮助你充分利用开发经验 感谢观看! ♪
-
-
7:38 - Shell Script - Output Files
$(DERIVED_FILE_DIR)/$(INPUT_FILE_BASE).compiledrecipe
-
8:22 - Build Rule - code
"$SRCROOT/Scripts/gen-code.sh" "$SCRIPT_INPUT_FILE" "$SCRIPT_OUTPUT_FILE_0"
-
10:15 - Shell Script - code
# Package up the recipes. echo "packaging..." for i in $(seq 0 $(expr ${SCRIPT_INPUT_FILE_LIST_COUNT} - 1)) ; do infile_="SCRIPT_INPUT_FILE_LIST_$i" eval infile=\$$infile_ while IFS= read -r file; do cat "$file" >> "$SCRIPT_OUTPUT_FILE_0" done < "$infile" done
-
11:34 - XCFileList
$(SRCROOT)/Recipes/Instructions/berry-blue.md $(SRCROOT)/Recipes/Instructions/carrot-chops.md $(SRCROOT)/Recipes/Instructions/hulking-lemonade.md $(SRCROOT)/Recipes/Instructions/kiwi-cutie.md $(SRCROOT)/Recipes/Instructions/lemonberry.md $(SRCROOT)/Recipes/Instructions/love-you-berry-much.md $(SRCROOT)/Recipes/Instructions/mango-jambo.md $(SRCROOT)/Recipes/Instructions/one-in-a-melon.md $(SRCROOT)/Recipes/Instructions/papas-papaya.md $(SRCROOT)/Recipes/Instructions/pina-y-coco.md
-
11:57 - Shell Script - Input File Lists
$(SRCROOT)/FileList.xcfilelist
-
12:11 - Shell Script - Output Files
$(PROJECT_TEMP_DIR)/instructions.mdarchive
-
12:50 - Environment Variables - Script Phases
// These environment variables are available in script phases: SCRIPT_INPUT_FILE_COUNT // This specifies the number of paths from the Input Files table. SCRIPT_INPUT_FILE_n // This specifies the absolute path of the nth file from the Input Files table, with build settings expanded. SCRIPT_INPUT_FILE_LIST_COUNT // This specifies the number of input file lists. SCRIPT_INPUT_FILE_LIST_n // This specifies the absolute path of the nth "resolved" input file list with contained paths made absolute, build settings expanded, and comments removed. SCRIPT_OUTPUT_FILE_COUNT // This specifies the number of paths from the Output Files table. SCRIPT_OUTPUT_FILE_n // This specifies the absolute path of the nth file from the Output Files table, with build settings expanded. SCRIPT_OUTPUT_FILE_LIST_COUNT // This specifies the number of output file lists. SCRIPT_OUTPUT_FILE_LIST_n // This specifies the absolute path of the nth "resolved" output file list with contained paths made absolute, build settings expanded, and comments removed. * n in the above examples refers to a 0-based index.
-
13:00 - Environment Variables - Build Rules
// These environment variables are available in build rules: SCRIPT_INPUT_FILE // This specifies the absolute path of the main input file being processed by the rule. OTHER_INPUT_FILE_FLAGS // This specifies custom command line flags defined for the input file in the Compile Sources build phase. SCRIPT_INPUT_FILE_COUNT // This specifies the number of paths from the Input Files table. SCRIPT_INPUT_FILE_n // This specifies the absolute path of the nth file from the Input Files table, with build settings expanded. SCRIPT_OUTPUT_FILE_COUNT // This specifies the number of paths from the Output Files table. SCRIPT_OUTPUT_FILE_n // This specifies the absolute path of the nth file from the Output Files table, with build settings expanded. SCRIPT_HEADER_VISIBILITY // This is set to "public" or "private" if the input file being processed is a header file in a Headers build phase, and its Header Visibility is set to one of those values. HEADER_OUTPUT_DIR // This specifies the output directory to which the input file should be copied, if the input file being processed is a header file in a Headers build phase. * n in the above examples refers to a 0-based index.
-
18:17 - Build setting definition
MY_BUILD_SETTING_NAME = "A build setting value"
-
18:30 - Build setting definition with conditions
MY_BUILD_SETTING_NAME = "A build setting value" MY_BUILD_SETTING_NAME[config=Debug] = -debug_flag MY_BUILD_SETTING_NAME[arch=arm64] = -arm64_only MY_BUILD_SETTING_NAME[sdk=iphone*] = -ios_only
-
19:50 - Build setting composition
IS_BUILD_SETTING_ENABLED = NO MY_BUILD_SETTING_NO = -use_this_one MY_BUILD_SETTING_YES = -use_this_instead MY_BUILD_SETTING = $(MY_BUILD_SETTING_$(IS_BUILD_SETTING_ENABLED))
-
21:08 - Build setting evaluation operators (paths)
$(MY_PATH:dir) $(MY_PATH:file) $(MY_PATH:base) $(MY_PATH:suffix) $(MY_PATH:standardizepath)
-
21:21 - Build setting evaluation operators (replacement)
$(MY_PATH:dir=/tmp) $(MY_PATH:file=/better.swift) $(MY_PATH:base=another) $(MY_PATH:suffix=m) $(MY_PATH:default=YES)
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。