本页介绍了如何使用 Bazel 构建或测试 Xcode 项目。它 介绍了 Xcode 和 Bazel 之间的区别,并提供了将 Xcode 项目转换为 Bazel 项目的步骤 。此外,它还提供了 问题排查解决方案,以解决常见错误。
Xcode 和 Bazel 之间的区别
Bazel 要求您通过构建规则显式指定每个构建目标及其 依赖项,以及相应的构建设置。
Bazel 要求项目所依赖的所有文件都必须位于工作区目录中,或者在
WORKSPACE文件中指定为导入项。使用 Bazel 构建 Xcode 项目时,
BUILD文件将成为 事实来源。如果您在 Xcode 中处理项目,则每次更新BUILD文件时,都必须使用 Tulsi 生成与BUILD文件匹配的新版 Xcode 项目。如果您未使用 Xcode,则bazel build和bazel test命令会提供构建和测试功能,但存在本指南后面所述的某些限制。由于构建配置架构(例如目录布局 或构建标志)存在差异,Xcode 可能无法完全了解构建的“全局情况”,因此某些 Xcode 功能可能无法正常运行。具体而言:
根据您在 Tulsi 中选择进行转换的目标,Xcode 可能 无法正确为项目来源编制索引。这会影响 Xcode 中的代码 补全和导航,因为 Xcode 将无法看到项目的所有 源代码。
静态分析、地址清理程序和线程清理程序可能无法正常运行 ,因为 Bazel 不会生成 Xcode 针对 这些功能所需的输出。
如果您使用 Tulsi 生成 Xcode 项目,并使用该项目从 Xcode 内运行 测试,则 Xcode 将运行测试,而不是 Bazel。如需使用 Bazel 运行测试,请手动运行
bazel test命令。
准备工作
在开始之前,请执行以下操作:
如果您不熟悉 Bazel 及其概念,请完成 iOS 应用教程。您应该了解 Bazel 工作区,包括
WORKSPACE和BUILD文件,以及 目标、构建规则和 Bazel 软件包的概念。分析并了解项目的依赖项。
分析项目依赖项
与 Xcode 不同,Bazel 要求您显式声明所有依赖项,以便
在 BUILD 文件中为每个目标。
如需详细了解外部依赖项,请参阅 使用外部依赖项。
使用 Bazel 构建或测试 Xcode 项目
如需使用 Bazel 构建或测试 Xcode 项目,请执行以下操作:
-
a. 添加应用目标
b. (可选)添加测试目标
c. 添加库目标
第 1 步:创建 WORKSPACE 文件
在新目录中创建 WORKSPACE 文件。此目录将成为 Bazel
工作区根目录。如果项目不使用外部依赖项,则此文件可以为
空。如果项目依赖于不在项目任何目录中的文件或软件包,请在 WORKSPACE
文件中指定这些外部依赖项。
第 2 步:(实验性)集成 CocoaPods 依赖项
如需将 CocoaPods 依赖项集成到 Bazel 工作区中,您必须按照转换 CocoaPods 依赖项中所述将其转换为 Bazel 软件包。
第 3 步:创建 BUILD 文件
定义工作区和外部依赖项后,您需要
创建一个 BUILD 文件,告知 Bazel 项目的结构。在 Bazel 工作区的根目录下创建
BUILD文件,并将其配置为对项目执行
初始构建,如下所示:
提示:如需详细了解软件包和其他 Bazel 概念,请参阅 工作区、软件包和目标。
第 3a 步:添加应用目标
添加 macos_application
或 ios_application
规则目标。此目标分别构建 macOS 或 iOS 应用软件包。
在目标中,至少指定以下内容:
bundle_id- 二进制文件的软件包 ID(反向 DNS 路径,后跟应用名称)。provisioning_profile- Apple 开发者 账号中的配置文件(如果为 iOS 设备构建)。families(仅限 iOS)- 是否为 iPhone、iPad、 或两者构建应用。infoplists- 要合并到最终 Info.plist 文件中的 .plist 文件列表。minimum_os_version- 应用支持的最低 macOS 或 iOS 版本。这可确保 Bazel 使用 正确的 API 级别构建应用。
第 3b 步:(可选)添加测试目标
Bazel 的 Apple 构建规则支持 在 iOS 和 macOS 上运行基于库的单元测试,以及在 macOS 上运行基于应用的 测试。对于 iOS 上基于应用的测试或任一平台上的界面测试,Bazel 将构建测试输出,但测试必须通过使用 Tulsi 生成的项目在 Xcode 中运行。按如下方式添加测试目标:
macos_unit_test,用于在 macOS 上运行基于库和基于应用的单元测试。ios_unit_test,用于在 iOS 上运行基于库的单元测试。对于需要 iOS 模拟器的测试,Bazel 将构建测试输出,但不会运行测试。您必须 使用 Tulsi 生成 Xcode 项目 并从 Xcode 内运行测试。ios_ui_test用于构建使用 Xcode 在 iOS 模拟器中运行界面测试所需的输出 。您必须使用 Tulsi 生成 Xcode 项目 并从 Xcode 内运行测试。Bazel 无法原生运行界面测试。
至少为 minimum_os_version 属性指定一个值。虽然
其他封装属性(例如 bundle_identifier 和 infoplists)
默认使用最常用的值,但请确保这些默认值与项目兼容,
并根据需要进行调整。对于需要 iOS
模拟器的测试,还要将 ios_application 目标名称指定为
test_host 属性的值。
第 3c 步:添加库目标
为应用和/或测试所依赖的每个 Objective C 库添加 objc_library
目标,并为每个 Swift 库添加 swift_library
目标。
按如下方式添加库目标:
将应用库目标作为依赖项添加到应用 目标。
将测试库目标作为依赖项添加到测试目标。
在
srcs属性中列出实现来源。在
hdrs属性中列出标头。
如需详细了解构建规则,请参阅 Bazel 的 Apple 规则。
此时,最好测试一下构建:
bazel build //:<application_target>
第 4 步:(可选)细化构建
如果项目很大,或者随着项目的增长,请考虑将其分块为多个 Bazel 软件包。这种细化程度的提高可提供以下优势:
构建的增量性提高,
构建任务的并行化程度提高,
为未来的用户提供更好的可维护性,
更好地控制目标和软件包之间的源代码可见性。这 可以防止库中包含的实现细节泄露 到公共 API 等问题。
细化项目的提示:
将每个库放在自己的 Bazel 软件包中。从需要最少依赖项的库开始,然后逐步向上遍历依赖项树。
在添加
BUILD文件并指定目标时,将这些新目标添加到依赖于这些目标的deps属性中。glob()函数不会跨越软件包边界,因此随着软件包数量 的增加,glob()匹配的文件数量会减少。向
main目录添加BUILD文件时,还要向 相应的test目录添加BUILD文件。在软件包之间强制执行健康的可见性限制。
在对
BUILD文件进行每次重大更改后构建项目,并在遇到 构建错误时修复这些错误。
第 5 步:运行构建
运行完全迁移的构建,以确保构建完成且没有错误或警告。 单独运行每个应用和测试目标,以便更轻松地找到发生的任何错误的来源 。
例如:
bazel build //:my-target第 6 步:使用 Tulsi 生成 Xcode 项目
使用 Bazel 构建时,WORKSPACE 和 BUILD 文件将成为构建的事实来源
。如需让 Xcode 了解这一点,您必须使用 Tulsi 生成与
Bazel 兼容的 Xcode 项目。
问题排查
当 Bazel 与所选 Xcode 版本不同步时 (例如在应用更新时),可能会出现 Bazel 错误。如果您在使用 Xcode 时遇到错误(例如“必须指定 Xcode 版本才能使用 使用 Apple CROSSTOOL”),请尝试以下操作。
手动运行 Xcode 并接受任何条款及条件。
使用 Xcode select 指明正确的版本,接受许可,并 清除 Bazel 的状态。
sudo xcode-select -s /Applications/Xcode.app/Contents/Developersudo xcodebuild -licensebazel sync --configure
- 如果此方法不起作用,您还可以尝试运行
bazel clean --expunge。