通过 Bazel 中的锁定文件功能,您可以记录项目所需的软件库或软件包的特定版本或依赖项。它通过存储模块解析和扩展评估的结果来实现这一点。Lockfile 促进了可重现的 build,确保了一致的开发环境。此外,它还允许 Bazel 跳过不受项目依赖项更改影响的解析过程部分,从而提高构建效率。此外,锁定文件可防止意外更新或破坏外部库中的破坏性更改,从而提高了稳定性,从而降低引入 bug 的风险。
生成锁定文件
系统会在工作区根目录下生成名为 MODULE.bazel.lock
的锁定文件。它在构建流程中创建或更新,特别是在模块解析和扩展评估之后。重要的是,它仅包含 build 的当前调用中包含的依赖项。
当项目中发生会影响其依赖项的更改时,锁定文件会自动更新以反映新状态。这可确保 lockfile 始终专注于当前 build 所需的特定依赖项集,从而准确表示项目的已解析依赖项。
锁定文件使用情况
锁文件可以通过 --lockfile_mode
标志控制,以便在项目状态与锁定文件不同时自定义 Bazel 的行为。可用的模式包括:
update
(默认):使用锁定文件中显示的信息跳过已知注册表文件的下载,并避免重新评估结果仍为最新结果的扩展程序。如果缺少信息,则会将其添加到锁定文件中。在此模式下,Bazel 还会避免刷新未更改的依赖项的可变信息(例如被拉取的版本)。refresh
:与update
类似,但切换到此模式时始终刷新可变信息,并且在此模式下大约每小时刷新一次。error
:与update
类似,但如果任何信息缺失或过时,Bazel 将失败并报错。此模式从不更改 lockfile 或在解决期间执行网络请求。自行标记为reproducible
的模块扩展或许仍可执行网络请求,但应始终产生相同的结果。off
:系统不会检查或更新锁定文件。
Lockfile 的优势
锁定文件具有多种优势,可通过多种方式加以利用:
可重现的 build。通过捕获软件库的特定版本或依赖项,锁定文件可确保构建在不同环境中和随着时间的推移的可重现性。开发者在构建项目时可以依靠一致且可预测的结果。
快速提高分辨率。通过锁定文件,Bazel 可避免下载已在先前 build 中使用的注册表文件。这样可以显著提高构建效率,尤其是在解析可能非常耗时的情况下。
稳定性和降低风险。锁定文件可防止意外更新或破坏外部库中的破坏性更改,从而帮助保持稳定性。将依赖项锁定在特定版本后,可降低因更新不兼容或未经测试而引入 bug 的风险。
锁定文件内容
锁定文件包含确定项目状态是否已更改所需的所有信息。它还包含在当前状态下构建项目的结果。lockfile 由两个主要部分组成:
- 作为模块解析输入的所有远程文件的哈希值。
- 对于每个模块扩展程序, lockfile 包含影响它的输入(由
bzlTransitiveDigest
、usagesDigest
和其他字段表示)以及运行该扩展程序的输出(称为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
部分包含在模块解析期间访问的远程注册表中所有文件的哈希值。由于在给定相同的输入并且所有远程输入都经过哈希处理时,解析算法具有完全确定性,因此这可确保完全可重现解析结果,同时避免 lockfile 中过多的远程信息重复。请注意,当特定注册表不包含特定模块,但优先级较低的注册表包含特定模块时,这也需要记录(请参阅示例中的“未找到”条目)。这种固有可变的信息可以通过 bazel mod deps --lockfile_mode=refresh
进行更新。
在下载之前,Bazel 会使用锁定文件中的哈希来查找代码库缓存中的注册表文件,从而提高后续解析的速度。
所选退出版本
selectedYankedVersions
部分包含根据模块分辨率选择的模块的拉取版本。由于这通常会导致在尝试构建时发生错误,因此仅当通过 --allow_yanked_versions
或 BZLMOD_ALLOW_YANKED_VERSIONS
明确允许被拉取的版本时,此部分才是非空的。
必须使用此字段,因为与模块文件相比,被提取的版本信息本质上是可变的,因此无法通过哈希引用。您可以通过 bazel mod deps --lockfile_mode=refresh
更新此信息。
模块扩展
moduleExtensions
部分是一个映射,仅包含当前调用中或之前调用的扩展程序,但排除不再使用的扩展程序。换句话说,如果某个扩展不再在整个依赖关系图中使用,则会从 moduleExtensions
映射中移除。
如果扩展程序与操作系统或架构类型无关,则本部分仅包含单个“常规”条目。否则,会包含多个条目(以操作系统和/或架构命名),每个条目对应根据这些具体信息评估扩展程序的结果。
扩展程序映射中的每个条目都对应于一个已使用的扩展程序,并由其包含的文件和名称标识。每个条目的对应值包含与该扩展相关联的相关信息:
bzlTransitiveDigest
是扩展程序实现及其以传递方式加载的 .bzl 文件的摘要。usagesDigest
是依赖关系图中扩展程序的使用情况摘要,其中包含所有标记。- 跟踪扩展程序的其他输入(例如,它读取的文件或目录内容或它使用的环境变量)的其他未指定字段。
generatedRepoSpecs
会使用当前输入对扩展程序创建的代码库进行编码。- 可选的
moduleExtensionMetadata
字段包含扩展程序提供的元数据,例如根模块是否应通过use_repo
导入扩展程序创建的特定代码库。此信息为bazel mod tidy
命令提供支持。
使用 reproducible = True
设置返回元数据,模块扩展可以选择不包含在锁定文件中。这样,他们承诺在给定相同的输入时始终创建相同的代码库。
最佳实践
为了最大限度地发挥 Lockfile 功能的优势,请考虑以下最佳做法:
定期更新锁定文件,以反映项目依赖项或配置中的更改。这可确保后续构建基于最新、最准确的一组依赖项。如需一次锁定所有扩展程序,请运行
bazel mod deps --lockfile_mode=update
。在版本控制中添加 Lockfile 以促进协作,并确保所有团队成员都可以访问同一个锁定文件,从而在整个项目中实现一致的开发环境。
使用
bazelisk
运行 Bazel,并在版本控制中添加.bazelversion
文件,以指定与锁定文件对应的 Bazel 版本。由于 Bazel 本身是构建的依赖项,因此锁定文件特定于 Bazel 版本,甚至在向后兼容的 Bazel 版本之间也会发生变化。使用bazelisk
可确保所有开发者使用的 Bazel 版本与锁定文件相匹配。
通过遵循这些最佳做法,您可以有效利用 Bazel 中的 lockfile 功能,从而打造更高效、更可靠和协作式软件开发工作流。