Bazel Lockfile

<ph type="x-smartling-placeholder"></ph> 报告问题 查看来源 敬上 每晚 · 7.3。 · 7.2 条 · 7.1。 · 7.0。 · 6.5

借助 Bazel 中的锁定文件功能,你可以记录特定版本或 项目所需的软件库或软件包的依赖项。它 通过将模块解析和扩展结果 评估。Lockfile 提升可重现的 build,确保一致性 开发环境此外,它还允许 让 Bazel 跳过不受更改影响的解析过程部分 。此外,Lockfile 还可以提高稳定性, 防止外部库中发生意外更新或破坏性更改,从而 从而降低引入错误的风险

生成锁定文件

系统会在工作区根目录下生成名为 MODULE.bazel.lock。它是在构建流程中创建或更新的 特别是在模块解析和扩展评估之后。重要的是 仅包括包含在 build。

当项目中发生会影响其依赖项的更改时,Lockfile 会自动更新以反映新状态。这样可以确保锁定文件 仍然专注于当前所需要的特定依赖项集 准确反映项目的已解析状态 依赖项

锁定文件使用情况

锁文件可以通过 --lockfile_mode至 自定义 Bazel 在项目状态与 lockfile 中找到。可用的模式包括:

  • update(默认):使用锁定文件中显示的信息 跳过下载已知注册表文件并避免重新评估扩展程序 其结果仍是最新的。如果缺少信息, 添加到锁定文件中在此模式下,Bazel 还会避免刷新 未更改依赖项的可变信息(如被拉取的版本), 已更改。
  • refresh:与 update 类似,但可变信息始终会在 切换到此模式,并且在此模式下大约每小时一次。
  • error:与 update 类似,但如果任何信息缺失或已过时, Bazel 将失败并显示错误。此模式绝不会更改锁定文件, 在解决期间执行网络请求。标记了 自己(即 reproducible)或许仍可执行网络请求, 预计始终会产生相同的结果。
  • off:系统不会检查或更新锁定文件。

Lockfile 的优势

锁定文件具有多种优势,可通过多种方式加以利用:

  • 可重现的 build。通过捕获特定版本或依赖项 锁定文件可确保 build 的可重现性 在不同环境中和长期的不同需求。开发者可以 获得一致且可预测的结果。

  • 快速提高分辨率。通过锁定文件,Bazel 下载先前 build 中已使用的注册表文件。 这显著提高了构建效率,尤其是在 可能非常耗时。

  • 稳定性和降低风险。锁定文件有助于保持稳定性 防止外部库中发生意外更新或破坏性更改。修改者 将依赖项锁定为特定版本、引入 bug 的风险 会减少。

锁定文件内容

锁定文件包含所有必要信息,以确定 项目状态已更改。它还包括项目构建结果 这些对象在当前状态下的状态。lockfile 由两个主要部分组成:

  1. 作为模块解析输入的所有远程文件的哈希值。
  2. 对于每个模块扩展,锁定文件都包含影响它的输入, 由 bzlTransitiveDigestusagesDigest 和其他字段表示, 以及运行该扩展程序时的输出(称为 generatedRepoSpecs

下面举例说明了锁定文件的结构 对每个部分的说明:

{
  "lockFileVersion": 10,
  "registryFileHashes": {
    "https://bcr.bazel.build/bazel_registry.json": "8a28e4af...5d5b3497",
    "https://bcr.bazel.build/modules/foo/1.0/MODULE.bazel": "7cd0312e...5c96ace2",
    "https://bcr.bazel.build/modules/foo/2.0/MODULE.bazel": "70390338... 9fc57589",
    "https://bcr.bazel.build/modules/foo/2.0/source.json": "7e3a9adf...170d94ad",
    "https://registry.mycorp.com/modules/foo/1.0/MODULE.bazel": "not found",
    ...
  },
  "selectedYankedVersions": {
    "foo@2.0": "Yanked for demo purposes"
  },
  "moduleExtensions": {
    "//:extension.bzl%lockfile_ext": {
      "general": {
        "bzlTransitiveDigest": "oWDzxG/aLnyY6Ubrfy....+Jp6maQvEPxn0pBM=",
        "usagesDigest": "aLmqbvowmHkkBPve05yyDNGN7oh7QE9kBADr3QIZTZs=",
        ...,
        "generatedRepoSpecs": {
          "hello": {
            "bzlFile": "@@//:extension.bzl",
            ...
          }
        }
      }
    },
    "//:extension.bzl%lockfile_ext2": {
      "os:macos": {
        "bzlTransitiveDigest": "oWDzxG/aLnyY6Ubrfy....+Jp6maQvEPxn0pBM=",
        "usagesDigest": "aLmqbvowmHkkBPve05y....yDNGN7oh7r3QIZTZs=",
        ...,
        "generatedRepoSpecs": {
          "hello": {
            "bzlFile": "@@//:extension.bzl",
            ...
          }
        }
      },
      "os:linux": {
        "bzlTransitiveDigest": "eWDzxG/aLsyY3Ubrto....+Jp4maQvEPxn0pLK=",
        "usagesDigest": "aLmqbvowmHkkBPve05y....yDNGN7oh7r3QIZTZs=",
        ...,
        "generatedRepoSpecs": {
          "hello": {
            "bzlFile": "@@//:extension.bzl",
            ...
          }
        }
      }
    }
  }
}

注册文件哈希

registryFileHashes 部分包含从以下来源开始的所有文件的哈希值: 在模块解析期间访问的远程注册表。由于分辨率 给定相同的输入和所有远程时,算法具有完全确定性 经过哈希处理,这样可确保解析结果完全可重现, 避免锁定文件中出现过多重复的远程信息。请注意, 当特定注册表不包含特定注册表时,这也需要进行记录 但优先级较低的注册表进行了查找(请参阅 )。这种固有可变的信息可通过 bazel mod deps --lockfile_mode=refresh

Bazel 使用锁定文件中的哈希来查找 存储库缓存,这可提高后续 解决方案。

所选退出版本

selectedYankedVersions 部分包含提取的模块版本 根据模块分辨率选择的图片由于这通常会导致错误 当尝试构建时,仅当被提取的版本被提取时,此部分才非空 通过 --allow_yanked_versions 明确允许,或者 BZLMOD_ALLOW_YANKED_VERSIONS

必须使用此字段,因为与模块文件相比,提取的版本信息 本身可变,因此无法通过哈希引用。此信息 可通过 bazel mod deps --lockfile_mode=refresh 进行更新。

模块扩展

moduleExtensions 部分是一个仅包含所使用的扩展程序的地图 在当前调用中或之前调用中,同时排除所有扩展 不再使用的。也就是说,如果扩展程序未被使用 之后就会从 moduleExtensions 中移除 地图。

如果扩展程序独立于操作系统或架构类型, 此部分仅包含一个“常规”条目。否则, 包含的条目(以操作系统和/或架构命名) 这些具体信息与附加信息的评估结果相对应。

扩展程序映射中的每个条目均对应一个已使用的扩展程序, 由其包含的文件和名称标识每个 条目包含与该扩展程序相关的信息:

  1. bzlTransitiveDigest 是扩展程序实现的摘要 以及由它以传递方式加载的 .bzl 文件。
  2. usagesDigest 是扩展程序在 依赖关系图,其中包含所有标记。
  3. 跟踪扩展程序的其他输入的未指定字段, 例如读取的文件或目录的内容 变量。
  4. generatedRepoSpecs 会对由 使用当前输入扩展。
  5. 可选的 moduleExtensionMetadata 字段包含由 例如是否应将扩展程序创建的特定代码库 由根模块通过 use_repo 导入。这些信息为 bazel mod tidy 命令。

模块扩展可以通过设置 使用 reproducible = True 返回元数据。这样做,就意味着 在给定相同的输入时,他们将始终创建相同的仓库。

最佳做法

为了最大限度地发挥 Lockfile 功能的优势,请考虑以下最佳做法 做法:

  • 定期更新锁定文件,以反映项目依赖项中的更改,或 配置。这样可确保后续构建基于 一组最新、准确的依赖项。锁定所有扩展程序 运行 bazel mod deps --lockfile_mode=update

  • 将锁文件包含在版本控制中,以促进协作和 确保所有团队成员都有权访问同一个锁文件,从而提升 在整个项目中保持一致的开发环境。

  • 使用 bazelisk 运行 Bazel,并添加 版本控制中用于指定 Bazel 版本的 .bazelversion 文件 。因为 Bazel 本身是 特定于 Bazel 版本的锁定文件, 甚至在向后兼容 Bazel 版本。使用 bazelisk 可确保所有开发者都使用 与锁定文件匹配的 Bazel 版本

通过遵循这些最佳实践,您可以有效利用 Lockfile 功能,从而提升效率、可靠性和协作性 软件开发工作流。

合并冲突

锁文件格式旨在最大程度地减少合并冲突, 情况。

自动分辨率

Bazel 提供了一种自定义 git merge 驱动程序 以帮助自动解决这些冲突。

将下面这行代码添加到 .gitattributes 文件中的根目录下,以设置该驱动程序 您的 Git 代码库:

# A custom merge driver for the Bazel lockfile.
# https://bazel.build/external/lockfile#automatic-resolution
MODULE.bazel.lock merge=bazel-lockfile-merge

然后,每个想要使用该驱动程序的开发者都必须通过 按以下步骤操作:

  1. 安装 jq(1.5 或更高版本)。
  2. 运行以下命令:
jq_script=$(curl https://raw.githubusercontent.com/bazelbuild/bazel/master/scripts/bazel-lockfile-merge.jq)
printf '%s\n' "${jq_script}" | less # to optionally inspect the jq script
git config --global merge.bazel-lockfile-merge.name   "Merge driver for the Bazel lockfile (MODULE.bazel.lock)"
git config --global merge.bazel-lockfile-merge.driver "jq -s '${jq_script}' -- %O %A %B > %A.jq_tmp && mv %A.jq_tmp %A"

手动解决

registryFileHashesselectedYankedVersions 中的简单合并冲突 通过保留 冲突。

不应手动解决其他类型的合并冲突。相反:

  1. 恢复锁定文件的先前状态 经由git reset MODULE.bazel.lock && git checkout MODULE.bazel.lock
  2. 解决 MODULE.bazel 文件中的所有冲突。
  3. 运行 bazel mod deps 以更新锁定文件。