本页介绍如何使用 Bazel 构建或测试 Xcode 项目。其中介绍了 Xcode 与 Bazel 之间的区别,并提供了将 Xcode 项目转换为 Bazel 项目的步骤。以及解决常见错误的问题排查解决方案。
Xcode 和 Bazel 之间的区别
Bazel 要求您通过构建规则明确指定每个构建目标及其依赖项,以及相应的构建设置。
Bazel 要求项目依赖的所有文件都存在于工作区目录中,或在
WORKSPACE
文件中指定为导入文件。使用 Bazel 构建 Xcode 项目时,
BUILD
文件成为可信来源。如果您在 Xcode 中处理项目,则每当更新BUILD
文件时,您都必须使用 Tulsi 生成与BUILD
文件相匹配的新版 Xcode 项目。如果您不使用 Xcode,bazel build
和bazel test
命令会提供构建和测试功能,但会具有本指南后面部分所述的特定限制。由于 build 配置架构的差异(例如目录布局或 build 标志),Xcode 可能无法完全了解 build 的“大局”,因此部分 Xcode 功能可能无法正常运行。即:
根据您在 Tulsi 中选择用于转换的目标,Xcode 可能无法正确地将项目源代码编入索引。这会影响 Xcode 中的代码补全和导航,因为 Xcode 无法查看项目的所有源代码。
静态分析、地址排错程序和线程排错程序可能不起作用,因为 Bazel 不会生成 Xcode 预期这些功能的输出。
如果您使用 Tulsi 生成 Xcode 项目,并使用该项目在 Xcode 中运行测试,那么 Xcode 将运行这些测试,而不是 Bazel。如需使用 Bazel 运行测试,请手动运行
bazel test
命令。
准备工作
在开始之前,请执行以下操作:
安装 Bazel(如果您尚未安装)。
如果您不熟悉 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 规则。
此时,最好对 build 进行测试:
bazel build //:<application_target>
第 4 步:(可选)细化 build
如果项目规模庞大或项目不断扩大,请考虑将其分成多个 Bazel 软件包。这种增加的粒度可提供:
提高了构建增量,
提高了构建任务的并行处理率,
为未来的用户提供更好的可维护性
更好地控制跨目标和软件包的源代码可见性。这可以防止包含实现详情的库泄露到公共 API 等问题。
细化项目的相关提示:
将每个库放在其自己的 Bazel 软件包中。从需要最少依赖项的依赖项开始,一直延伸到依赖项树。
在添加
BUILD
文件并指定目标时,请将这些新目标添加到依赖于它们的目标的deps
属性中。glob()
函数不会跨越软件包边界,因此随着软件包数量的增加,与glob()
匹配的文件会缩小。将
BUILD
文件添加到main
目录时,还应将BUILD
文件添加到相应的test
目录中。对所有软件包强制执行良好的可见性限制。
在每次对
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 选择来指示正确的版本、接受许可并清除 Bazel 的状态。
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
sudo xcodebuild -license
bazel sync --configure
- 如果这样不起作用,您还可以尝试运行
bazel clean --expunge
。