Bazel Lockfile

Bazel 中的 lockfile 功能可用于记录项目所需的软件库或软件包的特定版本或 依赖项。它 通过存储模块解析和扩展 评估的结果来实现此目的。lockfile 有助于实现可重现的构建,确保开发环境的一致性 。此外,如果项目依赖项没有变化,lockfile 可让 Bazel 跳过解析过程,从而提高构建效率。此外,lockfile 可防止外部库中出现意外更新或重大更改,从而提高稳定性,并降低引入 bug 的风险。

生成 lockfile

lockfile 在工作区根目录下生成,名称为 MODULE.bazel.lock。它在构建过程中创建或更新, 具体来说是在模块解析和扩展评估之后。lockfile 会捕获项目的当前状态,包括 MODULE 文件、标志、 替换和其他相关信息。重要的是,它仅包含 当前构建调用中包含的依赖项。

当项目中发生影响其依赖项的更改时,lockfile 会 自动更新以反映新状态。这可确保 lockfile 始终专注于当前 构建所需的特定依赖项集,从而准确表示项目已解析的 依赖项。

使用 lockfile

您可以使用标志 --lockfile_mode控制 lockfile,以便在项目状态与 lockfile 不同时 自定义 Bazel 的行为。可用模式包括:

  • update(默认):如果项目状态与 lockfile 匹配,则 解析结果会立即从 lockfile 返回。否则, 系统会执行解析,并更新 lockfile 以反映当前 状态。
  • error:如果项目状态与 lockfile 匹配,则解析结果会 从 lockfile 返回。否则,Bazel 会抛出错误,指明项目与 lockfile 之间的 差异。如果您想确保项目的依赖项保持不变,并将任何差异视为错误,则此模式特别 有用。
  • off:系统根本不会检查 lockfile。

lockfile 的优势

lockfile 具有多项优势,并且可以多种方式使用:

  • 可重现的构建。通过捕获软件库的特定版本或依赖项 ,lockfile 可确保构建在不同环境和不同时间段内都是可重现的 。开发者在构建项目时可以依赖 一致且可预测的结果。

  • 高效跳过解析。如果自上次构建以来项目依赖项没有变化,lockfile 可让 Bazel 跳过 解析过程。这可以显著提高构建效率,尤其是在 解析可能非常耗时的情况下。

  • 稳定性和风险降低。lockfile 可防止外部库中出现意外更新或重大更改,从而有助于保持稳定性。通过 将依赖项锁定到特定版本,可以降低因不兼容或未经测试的更新而引入 bug 的风险。

lockfile 内容

lockfile 包含确定 项目状态是否已更改的所有必要信息。它还包含在当前状态下构建项目 的结果。lockfile 由两个主要部分组成:

  1. 模块解析的输入,例如 moduleFileHashflagslocalOverrideHashes,以及解析的输出,即 moduleDepGraph
  2. 对于每个模块扩展,lockfile 都包含影响它的输入, 以 transitiveDigest 表示,以及运行该扩展的输出 称为 generatedRepoSpecs

以下示例展示了 lockfile 的结构,并对每个部分进行了说明:

{
  "lockFileVersion": 1,
  "moduleFileHash": "b0f47b98a67ee15f9.......8dff8721c66b721e370",
  "flags": {
    "cmdRegistries": [
      "https://bcr.bazel.build/"
    ],
    "cmdModuleOverrides": {},
    "allowedYankedVersions": [],
    "envVarAllowedYankedVersions": "",
    "ignoreDevDependency": false,
    "directDependenciesMode": "WARNING",
    "compatibilityMode": "ERROR"
  },
  "localOverrideHashes": {
    "bazel_tools": "b5ae1fa37632140aff8.......15c6fe84a1231d6af9"
  },
  "moduleDepGraph": {
    "<root>": {
      "name": "",
      "version": "",
      "executionPlatformsToRegister": [],
      "toolchainsToRegister": [],
      "extensionUsages": [
        {
          "extensionBzlFile": "extension.bzl",
          "extensionName": "lockfile_ext"
        }
      ],
      ...
    }
  },
  "moduleExtensions": {
    "//:extension.bzl%lockfile_ext": {
      "transitiveDigest": "oWDzxG/aLnyY6Ubrfy....+Jp6maQvEPxn0pBM=",
      "generatedRepoSpecs": {
        "hello": {
          "bzlFile": "@@//:extension.bzl",
          ...
        }
      }
    }
  }
}

模块文件哈希

moduleFileHash 表示 MODULE.bazel 文件内容的哈希值。如果此文件发生任何更改,哈希值也会随之变化。

标志

Flags 对象存储所有可能会影响解析结果的标志。

本地替换哈希

如果根模块包含 local_path_overrides,则此部分会存储本地仓库中 MODULE.bazel 文件的哈希值 。它允许跟踪对此依赖项的更改 。

模块依赖图

moduleDepGraph 表示使用 上述输入进行解析过程的结果。它构成了运行项目所需的所有模块 的依赖图。

模块扩展

moduleExtensions 部分是一个映射,其中仅包含当前调用或之前调用中使用的扩展 ,而不包含任何不再使用的扩展 。换句话说,如果某个扩展在整个依赖图中不再使用 ,则会从 moduleExtensions 映射中移除。

此映射中的每个条目都对应于一个已使用的扩展,并由其 包含文件和名称标识。每个条目的对应值都包含 与该扩展相关的相关信息:

  1. The transitiveDigest:扩展实现及其 传递性 .bzl 文件的摘要。
  2. generatedRepoSpecs:使用 当前输入运行该扩展的结果。

另一个可能会影响扩展结果的因素是其 用法。 虽然用法不会存储在 lockfile 中,但在将扩展的当前状态与 lockfile 中的状态进行比较时,系统会考虑用法。

最佳实践

如需最大限度地发挥 lockfile 功能的优势,请考虑以下最佳 实践:

  • 定期更新 lockfile 以反映项目依赖项或 配置的更改。这可确保后续构建基于最新且最准确的依赖项集。

  • 将 lockfile 纳入版本控制,以方便协作并 确保所有团队成员都可以访问相同的 lockfile,从而在整个项目中实现一致的开发环境。

通过遵循这些最佳实践,您可以有效地利用 Bazel 中的 lockfile 功能,从而实现更高效、更可靠且更协作的 软件开发工作流。