如何用 Bazel vendor 模式获取外部依赖本地副本并离线构建?
【免费下载链接】bazela fast, scalable, multi-language and extensible build system项目地址: https://gitcode.com/GitHub_Trending/ba/bazel
Bazel 项目默认从网络拉取外部依赖,构建机一旦断网或需要控制依赖来源,构建就会失败。Bazel 的 vendor 模式可以把外部依赖抓取到工作区内的一个目录,形成可入库的本地副本,之后构建直接使用这份副本,实现离线构建。本文基于 Bazel vendor 模式文档 说明完整的操作路径:开启 vendor 模式、把依赖 vendor 到本地、离线构建,以及如何用VENDOR.bazel管理已 vendor 的仓库。
开启 vendor 模式
vendor 模式通过--vendor_dir标志启用,该参数指定存放外部依赖本地副本的目录。路径可以是相对于工作区根目录的相对路径,也可以是绝对路径。
最方便的做法是把它写进.bazelrc,让所有命令默认生效:
# Enable vendor mode with vendor directory under <workspace>/vendor_src common --vendor_dir=vendor_src这样 vendor 目录位于<workspace>/vendor_src。也可以不在.bazelrc中配置,而是在每条命令后手动追加--vendor_dir=vendor_src。
将目标依赖 vendor 到本地副本
文档推荐的做法是先只 vendor 构建特定目标所需的依赖,而不是把全部依赖都拉下来。
只 vendor 构建目标所需的依赖
bazel vendor --vendor_dir=vendor_src //src/main:hello-world //src/test/...该命令会 vendor 出构建//src/main:hello-world以及//src/test/...下所有目标在当前配置下所需的全部仓库。底层实际上是执行一次bazel build --nobuild来分析这些 target pattern,因此附加在命令上的构建类标志会影响分析结果,进而影响最终 vendor 了哪些仓库。
只 vendor 指定仓库
如果只想把某一个外部仓库 vendor 下来,用--repo标志指定仓库名,它同时接受 canonical repo name 和 apparent repo name 两种写法:
bazel vendor --vendor_dir=vendor_src --repo=@rules_cc或
bazel vendor --vendor_dir=vendor_src --repo=@@rules_cc+两种写法都会把rules_ccvendor 到<workspace root>/vendor_src/rules_cc+下。
可选分支:vendor 全部依赖
bazel vendor --vendor_dir=vendor_src这条命令会 vendor 传递依赖图中的所有仓库,文档明确列出它的几个缺点:
- 拉取所有仓库(包括传递引入的)可能很耗时;
- vendor 目录会变得非常大;
- 某些与当前平台或环境不兼容的仓库可能拉取失败。
因此文档建议优先按目标 vendor,而不是全量 vendor。
离线构建并验证结果
依赖 vendor 完成后,用同样的--vendor_dir构建即可:
bazel build --vendor_dir=vendor_src //src/main:hello-world //src/test/...验证标准来自文档的两条说明:
- 构建应在没有网络访问、也没有 repository cache的干净构建环境中正常工作;
- 由于 vendor 出的源码可以 check in 到代码仓库,你可以在另一台机器上拉取代码后离线构建同样的目标。
换句话说,判断 vendor 是否覆盖完整的方式就是:在无网络、无缓存的环境下执行上面的构建命令,若能成功即说明当前目标所需的依赖都已 vendor 到位。
文档还给出一个需要重新 vendor 的边界条件:如果你修改了待构建的目标、外部依赖、构建配置,或者更换了 Bazel 版本,需要重新执行 vendor 命令,否则离线构建可能失败。
用 VENDOR.bazel 管理已 vendor 的仓库
vendor 目录下的VENDOR.bazel文件控制各个仓库在 vendor 模式下的处理方式。文件中有两个指令,参数都是 canonical repo name 列表:
ignore():让 vendor 模式完全忽略某个仓库;pin():把仓库固定为其当前已 vendor 的源码,效果相当于对该仓库加了一个--override_repository标志。运行 vendor 命令时 Bazel 不会更新被 pin 仓库的源码(除非取消 pin),你可以手工修改并维护这份已 vendor 的源码。
示例配置:
ignore("@@rules_cc+") pin("@@bazel_skylib+")配置后的行为:
- 两个仓库都会从后续的 vendor 命令中排除;
bazel_skylib会被 override 为 vendor 目录下的源码,可以安全地手工修改;- 如果要重新 vendor
bazel_skylib,必须先移除这条 pin 语句。
另外,设置了local或configure为 true 的 repository rule 始终会被排除在 vendor 之外,无法通过 vendor 模式接管。
理解 vendor 模式的内部机制(影响排障)
vendor 的本质是:Bazel 平时把外部依赖放在$(bazel info output_base)/external下,vendor 模式把相关文件和目录移动到 vendor 目录,后续构建直接使用这份副本。被 vendor 的内容包括仓库目录本身和仓库的 marker 文件。
构建时的选择逻辑:如果 vendored marker 文件是最新的,或者仓库在VENDOR.bazel中被 pin,Bazel 会在$(bazel info output_base)/external下创建指向 vendor 源码的符号链接来使用它,而不是重新执行 repository rule;否则 Bazel 打印一条 warning 并回退到抓取该仓库的最新版本。如果你在没有 pin 的情况下手工修改了 vendor 源码,修改会被使用,但一旦 marker 过期并再次 vendor,这份源码会被覆盖。
两个与离线构建直接相关的细节:
- Registry 文件:Bazel 模块解析可能需要访问 registry 文件,vendor 时会把从网络拉取的 registry 文件一并 vendor 到
<vendor_dir>/_registries目录,这是离线构建能走通模块解析的前提。 - 符号链接:Bazel 会自动创建
<vendor_dir>/bazel-external指向$(bazel info output_base)/external,并把 vendor 源码中原先指向 output base 下 external 目录的符号链接改写为<vendor_dir>/bazel-external下的相对路径,因此 vendor 源码移动到别处或 output base 变化后符号链接仍可用。bazel-external由 Bazel 自动生成,文档建议把它加入.gitignore,避免 check in。注意两类边界:指向$(bazel info output_base)/external之外的绝对路径符号链接不会被改写,仍可能破坏跨机器兼容性;Windows 上 vendor 符号链接需要启用--windows_enable_symlinks标志才有效。
离线环境下使用 bazel 子命令的补充项
一些 Bazel 子命令(如bazel mod tidy)有隐式的工具依赖,这些工具无法从用户构建目标到达,因此bazel vendor //...默认不会包含它们。如果计划离线或 hermetic 环境(例如配合--vendor_dir和--nofetch)中运行bazel mod tidy这类命令,需要把@bazel_tools//tools:tools_for_bazel_subcommands这个 filegroup 加入 vendor 调用:
bazel vendor //... @bazel_tools//tools:tools_for_bazel_subcommands小结
一条最短可行路径是:在.bazelrc中加common --vendor_dir=vendor_src,执行bazel vendor --vendor_dir=vendor_src <目标 pattern>,然后用bazel build --vendor_dir=vendor_src <同样的目标>在无网络、无 repository cache 的环境中验证构建通过。需要固定或排除某些仓库时用 vendor 目录下的VENDOR.bazel配置pin()/ignore();目标、依赖、构建配置或 Bazel 版本变化后要重新 vendor。更多背景可参考 外部依赖总览 和 vendor 模式文档。
【免费下载链接】bazela fast, scalable, multi-language and extensible build system项目地址: https://gitcode.com/GitHub_Trending/ba/bazel
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考