跳转到内容

模块缓存

::: warning 要求

  • 一个生成的项目
  • 一个Tuist 账户和项目

:::

Tuist 模块缓存提供了一种通过将模块缓存为二进制文件(.xcframework)并在不同环境之间共享来优化构建时间的强大方法。此功能使你能够利用之前生成的二进制文件,减少重复编译的需求并加快开发过程。

Tuist 高效地利用哈希值来检测依赖图中每个目标的变化。利用这些数据,它为源自这些目标的二进制文件构建并分配唯一标识符。在图生成时,Tuist 无缝地将原始目标替换为它们对应的二进制版本。

这个被称为*“预热”*的操作会生成用于本地使用或通过 Tuist 与队友和 CI 环境共享的二进制文件。预热缓存的过程很简单,可以用以下命令启动:

Terminal window
tuist cache

该命令会重用二进制文件以加快这个过程。

默认情况下,当 Tuist 命令需要生成项目时,如果缓存中有可用的依赖项,它们会自动将依赖项替换为它们的二进制版本。此外,如果你指定了要聚焦的目标列表,Tuist 还将用它们的缓存二进制文件替换任何依赖目标(如果可用)。对于喜欢不同方法的人,可以使用特定标志选择完全退出此行为:

::: code-group

Terminal window
tuist generate # 仅依赖项
tuist generate Search # 依赖项 + Search 的依赖项
tuist generate Search Settings # 依赖项,以及 Search 和 Settings 的依赖项
tuist generate --cache-profile none # 完全不使用缓存
Terminal window
tuist test

:::

::: warning

二进制缓存是为开发工作流设计的,例如在模拟器或设备上运行应用,或运行测试。它不适用于发布构建。归档应用时,使用 --cache-profile none 生成包含源码的项目。

:::

Tuist 支持缓存配置文件来控制生成项目时目标被缓存二进制替换的积极程度。

  • 内置配置:
    • only-external:仅替换外部依赖(系统默认)
    • all-possible:替换尽可能多的目标(包括内部目标)
    • none:永远不用缓存二进制替换

使用 tuist generate 上的 --cache-profile 选择配置:

Terminal window
# 内置配置文件
tuist generate --cache-profile all-possible
# 自定义配置文件(在 Tuist 配置中定义)
tuist generate --cache-profile development
# 使用配置默认值(无标志)
tuist generate
# 聚焦特定目标(意味着 all-possible)
tuist generate MyModule AnotherTarget
# 完全禁用二进制替换
tuist generate --cache-profile none

::: info 已弃用的标志

--no-binary-cache 标志已被弃用。请改用 --cache-profile none。为了向后兼容,弃用的标志仍然有效。

:::

解析有效行为时的优先级(从高到低):

  1. --cache-profile none
  2. 目标聚焦(将目标传递给 generate)→ 配置 all-possible
  3. --cache-profile <value>
  4. 配置默认值(如果设置)
  5. 系统默认 (only-external)

只有以下目标产品可以被 Tuist 缓存:

  • 不依赖于 XCTest 的框架(静态和动态)
  • Bundle
  • Swift 宏

我们正在努力支持依赖于 XCTest 的库和目标。

::: info 上游依赖

当一个目标不可缓存时,它也会使上游目标不可缓存。例如,如果你有依赖图 A > B,其中 A 依赖于 B,如果 B 不可缓存,A也将不可缓存。

:::

二进制缓存能达到的效率水平很大程度上取决于图结构。为了获得最佳结果,我们建议:

  1. 避免过于嵌套的依赖图。图越浅越好。
  2. 使用协议/接口目标而不是实现目标来定义依赖项,并从最顶层目标依赖注入实现。
  3. 将经常修改的目标拆分为更小的、变更可能性更低的目标。

上述建议是模块化架构的一部分,我们建议将其作为构建项目的方式,以最大化二进制缓存以及 Xcode 功能的好处。

我们建议有一个 CI 作业在主分支的每个提交上运行来预热缓存。这将确保缓存始终包含 main 中更改的二进制文件,以便本地和 CI 分支可以在此基础上增量构建。

::: tip 保持缓存预热隔离

在专门的 CI 步骤中运行 tuist cache,而不依赖于生成的工作空间的后续步骤。由于 tuist cache 修改工作空间以进行缓存构建,任何需要工作空间的 CI 步骤应首先运行 tuist generate 以获取新的可用工作空间。

:::

::: tip 缓存预热使用二进制

tuist cache 命令也利用二进制缓存来加速预热。

:::

以下是一些常见工作流程的示例:

开发者开始开发新功能 {#a-developer-starts-to-work-on-a-new-feature}

Section titled “开发者开始开发新功能 {#a-developer-starts-to-work-on-a-new-feature}”
  1. 他们从 main 创建新分支。
  2. 他们运行 tuist generate
  3. Tuist 从 main 中提取最新的二进制文件,并用它们生成项目。

开发者向上游推送更改 {#a-developer-pushes-changes-upstream}

Section titled “开发者向上游推送更改 {#a-developer-pushes-changes-upstream}”
  1. CI 流水线将运行 xcodebuild buildtuist test 来构建或测试项目。
  2. 工作流程将从 main 中提取最新的二进制文件,并用它们生成项目。
  3. 然后它将增量构建或测试项目。

缓存并发限制 {#cache-concurrency-limit}

Section titled “缓存并发限制 {#cache-concurrency-limit}”

默认情况下,Tuist 下载和上传缓存产物没有任何并发限制,以最大化吞吐量。你可以使用 TUIST_CACHE_CONCURRENCY_LIMIT 环境变量来控制此行为:

Terminal window
# 设置特定的并发限制
export TUIST_CACHE_CONCURRENCY_LIMIT=10
tuist generate
# 使用 "none" 表示无限制(默认行为)
export TUIST_CACHE_CONCURRENCY_LIMIT=none
tuist generate

这在网络带宽受限的环境或减少缓存操作期间的系统负载时可能很有用。

它没有为我的目标使用二进制 {#it-doesnt-use-binaries-for-my-targets}

Section titled “它没有为我的目标使用二进制 {#it-doesnt-use-binaries-for-my-targets}”

确保哈希值在环境和运行之间是确定性的。这可能发生在项目引用环境的地方,例如通过绝对路径。你可以使用 diff 命令比较两次 tuist generate 调用或跨环境或运行生成的项目。

还要确保目标不直接或间接依赖于不可缓存的目标

使用源码时,Xcode 的构建系统通过 Derived Data 可以解析未显式声明的依赖。但是,当你依赖二进制缓存时,依赖必须显式声明;否则当你找不到符号时可能会看到编译错误。为了调试这个问题,我们建议使用tuist inspect dependencies --only implicit命令并在 CI 中设置它以防止隐式链接的回归。