news 2026/9/13 5:36:30

Bazel 模块注册表(Registry)完全指南:格式规范、源码实现与配置实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Bazel 模块注册表(Registry)完全指南:格式规范、源码实现与配置实战

Bazel 模块注册表(Registry)完全指南:格式规范、源码实现与配置实战

【免费下载链接】bazela fast, scalable, multi-language and extensible build system项目地址: https://gitcode.com/GitHub_Trending/ba/bazel

本文聚焦 Bazel 外部依赖解析的核心基础设施——模块注册表(registry)。你将从零理解 Index Registry 的目录格式、bazel_registry.json/metadata.json/source.json三类元数据的字段语义、archive / git_repository / local_path 三种来源类型的底层映射,并学会用--registry接入私有或第三方注册表。阅读后你能独立搭建一个可被 Bazel 直接消费的本地或静态 HTTP 模块注册表,并理解其与http_archivegit_repositorylocal_repository仓库规则的对应关系。

一、什么是 Bazel 注册表

Bazel 通过registry(注册表)发现外部依赖:它是 Bazel 模块(module)的数据库。Bazel 目前只支持一种类型的注册表——Index Registry(索引注册表),即遵循特定格式的本地目录或静态 HTTP 服务器。索引注册表只负责提供模块的"索引信息"(主页、维护者、每个版本的MODULE.bazel、如何获取每个版本的源码),它本身不需要托管源码压缩包——源码归档可以放在任意 URL 上。

这一设计让注册表可以做到"静态、可镜像、可 fork":只要按格式组织文件并放到静态服务器上即可工作。在 Bazel 源码中,负责实现这一行为的是IndexRegistry类,其类注释明确写道:"Represents a Bazel module registry that serves a list of module metadata from a static HTTP server or a local file path."(见 IndexRegistry.java)。

二、Index Registry 的目录格式

一个符合规范的索引注册表必须具有如下结构:

bazel_registry.json # 可选:注册表级元数据 modules/ ├── $MODULE/ # 每个模块一个子目录 │ ├── metadata.json # 模块级元数据(可选) │ └── $VERSION/ # 每个版本一个子目录 │ ├── MODULE.bazel # 该版本的 MODULE.bazel(必填) │ ├── source.json # 如何获取该版本源码(必填) │ ├── patches/ # 可选:补丁文件,仅 archive 类型使用 │ └── overlay/ # 可选:覆盖文件,仅 archive 类型使用 └── ...

各组成部分的要点如下:

  • bazel_registry.json:可选的注册表级元数据文件,见下文第三节。
  • modules/:包含注册表中每个模块的一个子目录。
  • /modules/$MODULE:模块目录,内含每个版本的子目录(以$MODULE命名),以及模块级metadata.json
  • /modules/$MODULE/$VERSION:版本目录,包含以下文件:
    • MODULE.bazel:该版本对外展示的模块文件。注意,这是 Bazel 外部依赖解析期间读取的MODULE.bazel而不是源码归档里的那份(除非使用了非注册表 override)。最佳实践是:用这个文件来声明 release 版本号,而不要在源码归档中的MODULE.bazel里写版本号。关于模块版本化的更多讨论见 external/faq.mdx。
    • source.json:描述如何获取该版本源码的 JSON 文件(必填),详见第五节。
    • patches/:可选目录,存放补丁文件;仅当source.json的 type 为archive时使用。
    • overlay/:可选目录,存放覆盖文件;同样仅当 type 为archive时使用。

从源码看,IndexRegistry严格按此路径约定拼装 URL:模块文件是modules/{name}/{version}/MODULE.bazelsource.jsonmodules/{name}/{version}/source.json,见 IndexRegistry.java 与 IndexRegistry.java。也就是说,目录即路由:静态服务器上只要有这些文件,Bazel 就能按固定路径抓取。

三、bazel_registry.json:注册表级元数据

bazel_registry.json可选文件,声明作用于整个注册表的元数据。它支持以下字段:

字段类型含义
mirrors字符串数组用于源码归档的镜像列表。镜像 URL = 镜像本身 + 模块source.json中源码 URL 去掉协议后的部分。例如源码 URL 为https://foo.com/bar/bazmirrors["https://mirror1.com/", "https://example.com/mirror2/"],则 Bazel 依次尝试https://mirror1.com/foo.com/bar/bazhttps://example.com/mirror2/foo.com/bar/baz,最后回退到原始 URLhttps://foo.com/bar/baz
module_base_path字符串source.json中 type 为local_path时,模块的相对基准路径

源码中的BazelRegistryJson内部类恰好声明了这两个字段(mirrorsmoduleBasePath),Gson 通过LOWER_CASE_WITH_UNDERSCORES命名策略完成下划线字段到驼峰字段的映射,见 IndexRegistry.java。

镜像的拼装逻辑在createArchiveRepoSpec中实现:先把--module_mirrors命令行标志指定的镜像与bazel_registry.json中声明的镜像合并,逐个拼出"镜像 + authority + path"的 URL,最后再追加原始源码 URL,见 IndexRegistry.java。另外值得注意:bazel_registry.json本身被缓存(volatile字段 + 双重检查锁),不会在每个模块解析时重复下载,见 IndexRegistry.java。

四、metadata.json:模块级元数据与 yanked 版本

metadata.json可选的模块级 JSON 文件,位于/modules/$MODULE/metadata.json,包含以下字段:

字段类型含义
versions字符串数组该注册表中此模块可用版本的列表,应与模块目录下的版本子目录保持一致
yanked_versionsJSON 对象该模块被yank(撤回)的版本。键为要撤回的版本号,值为撤回原因的描述(理想情况下附上链接)

示例:

{ "versions": ["1.0.0", "1.1.0"], "yanked_versions": { "1.0.0": "Contains a critical security vulnerability, see https://example.com/advisory/1.0.0" } }

yanked 版本在解析时会导致失败,除非用户通过--allow_yanked_versions显式放行。Bazel 源码中getYankedVersions会读取modules/{name}/metadata.json并解析其中的yankedVersions字段(同样使用LOWER_CASE_WITH_UNDERSCORES映射yanked_versions),见 IndexRegistry.java。metadata.json被视为可变信息,抓取时不带校验和(useChecksum=false),因为注册表维护者可能随时更新 yanked 列表。

关于 yanked 与 lockfile 的联动,tryGetYankedVersionsFromLockfile展示了三条路径(见 IndexRegistry.java):

  • 该版本曾在 lockfile 中被记录为 yanked(用户已放行),则直接复用 lockfile 中的 yanked 信息,避免不必要的网络访问;
  • --lockfile_mode=errormetadata.json无法被抓取(它本身是可变文件),此时假定没有 yanked 版本;
  • lockfile 中记录了该模块的source.json校验和、且不在已选 yanked 列表中,则说明选版时它未被 yanked,为保持构建可复现不再刷新 yanked 信息。

另外,官方Bazel Central Registry(BCR)metadata.json有更严格的要求(需要更多字段),普通私有注册表只需满足上述最小字段。

五、source.json:源码获取方式

source.json必填文件,描述如何获取某个特定版本的源码。其 schema 取决于type字段,type默认为archiveIndexRegistry.getRepoSpec根据 type 分派到三种不同的 RepoSpec 构建路径(见 IndexRegistry.java)。

5.1 type = archive(默认)

该类型的模块版本由http_archive仓库规则支撑:下载指定 URL 的归档并解压。支持以下字段:

字段类型说明
url字符串源码归档的 URL(必填)
mirror_urls字符串列表归档的镜像 URL,按顺序在url之后作为后备尝试
integrity字符串归档的 Subresource Integrity 校验和(必填)
strip_prefix字符串解压时要去掉的目录前缀
overlayJSON 对象叠加在解压后归档之上的覆盖文件。文件位于/modules/$MODULE/$VERSION/overlay/目录下;键为覆盖文件名,值为其 integrity 校验和。overlay 先于 patch 应用
patchesJSON 对象应用于解压后归档的补丁文件。文件位于/modules/$MODULE/$VERSION/patches/目录下;键为补丁文件名,值为其 integrity 校验和。补丁在 overlay 之后、按patches中的顺序应用
patch_strip数字与 Unixpatch--strip参数相同
archive_type字符串下载文件的归档类型(同http_archivetype

示例:

{ "type": "archive", "url": "https://github.com/example/foo/archive/refs/tags/v1.2.3.tar.gz", "integrity": "sha256-...", "strip_prefix": "foo-1.2.3", "patches": { "fix_build.patch": "sha256-...", "add_visibility.patch": "sha256-..." }, "patch_strip": 1 }

源码中的ArchiveSourceJson内部类精确对应了这些字段(见 IndexRegistry.java),而createArchiveRepoSpec则完成了从 JSON 到ArchiveRepoSpecBuilder的转换,包括:拼接镜像 URL、将补丁映射为"<registry>/modules/<module>/<version>/patches/<file>→ integrity"的键值对、把 overlay 文件也解析为带 integrity 的 RemoteFile,最后连同远端MODULE.bazelremote_module_file)一起构建 RepoSpec,见 IndexRegistry.java。

校验与防御细节(同样来自源码,见 IndexRegistry.java 与 IndexRegistryTest.java 中的testGetArchiveRepoSpec_empty*系列用例):

  • url缺失、integrity缺失或为空白,直接报错;
  • 任何补丁或 overlay 文件缺失 integrity,直接报错(对应测试testGetArchiveRepoSpec_emptyPatchIntegrity/emptyOverlayIntegrity/whitespaceIntegrity等)。

5.2 type = git_repository

该类型由git_repository仓库规则支撑,通过克隆 Git 仓库获取。支持以下字段:

字段类型说明
remote字符串Git 远端地址(必填)
commit字符串要检出的 commit 哈希
shallow_since字符串浅克隆的截止时间
tag字符串要检出的 tag
init_submodules布尔是否初始化子模块
verbose布尔是否输出详细日志
strip_prefix字符串检出后去掉的目录前缀
patch_strip数字补丁的 strip 层级
patchesJSON 对象应用于克隆仓库的补丁文件,位于/modules/$MODULE/$VERSION/patches/,按出现顺序应用

示例:

{ "type": "git_repository", "remote": "https://github.com/example/bar.git", "commit": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2", "init_submodules": true, "patches": { "fix_compile.patch": "sha256-..." } }

源码中的GitRepoSourceJson类(见 IndexRegistry.java)与createGitRepoSpec(见 IndexRegistry.java)实现了该类型。注意其中的安全校验:

  • remote只允许https://ssh://git://file://四种 scheme(ALLOWED_GIT_SCHEMES常量,见 IndexRegistry.java);
  • commit必须是 7~64 位十六进制哈希;
  • tag不能以-开头(防止被误解析为命令行选项)。

5.3 type = local_path

该类型由local_repository仓库规则支撑,符号链接到本地磁盘目录。仅支持一个字段:

字段类型说明
path字符串仓库的本地路径,按如下规则解析

path的解析规则(源码实现见 IndexRegistry.java):

  • path为绝对路径时,原样使用;
  • path为相对路径、且bazel_registry.jsonmodule_base_path为绝对路径时,解析为<module_base_path>/<path>
  • pathmodule_base_path均为相对路径时,解析为<registry_path>/<module_base_path>/<path>。此时注册表必须托管在本地、并通过--registry=file://<registry_path>使用,否则 Bazel 会报错。
{ "type": "local_path", "path": "vendor/foo" }

配合bazel_registry.json中的"module_base_path": "third_party",相对路径vendor/foo最终解析为<registry_path>/third_party/vendor/foo

六、Bazel Central Registry(BCR)

Bazel Central Registry(BCR)是官方公共注册表,由 Bazel 社区维护,其内容托管在 GitHub 的bazelbuild/bazel-central-registry仓库,可通过 Web 前端浏览其内容。由于 BCR 只是一个内容更丰富的 Index Registry,你可以直接 fork 它来搭建自己的私有注册表,或学习它的组织方式。

在遵循普通 Index Registry 格式之外,BCR 还要求每个模块版本提供一个presubmit.yml文件/modules/$MODULE/$VERSION/presubmit.yml),它声明几个必要的构建与测试 target,用于校验该模块版本的合法性,同时 BCR 的 CI 流水线也用它保证模块之间的互操作性。如果你的注册表需要类似的质量门槛,可以参考这一约定。

七、选择与配置注册表:--registry标志

可重复的--registry标志用于指定请求模块的注册表列表,使项目可以从第三方或内部注册表拉取依赖:

bazel build //... --registry=https://example.com/registry --registry=https://bcr.bazel.build

关键语义:

  • 顺序即优先级:排在越前面的注册表优先级越高,Bazel 会先在靠前的注册表中查找模块,找不到时才回退到后面的注册表。这一语义与源码中--registry标志的帮助文本一致:"modules will be looked up in earlier registries first, and only fall back to later registries when they're missing from the earlier ones"(见 RepositoryOptions.java)。
  • 推荐写入.bazelrc:为方便使用,可以把一串--registry标志放进项目根目录的.bazelrc文件。
  • GitHub 托管的注册表:如果注册表托管在 GitHub(例如bazelbuild/bazel-central-registry的 fork),--registry值需要使用raw.githubusercontent.com下的裸地址。例如在my-org这个 fork 的main分支上,应设置为--registry=https://raw.githubusercontent.com/my-org/bazel-central-registry/main/
  • 默认行为的改变:使用--registry标志后,Bazel Central Registry不再默认启用;如需保留,需显式加回--registry=https://bcr.bazel.build

注册表 URL 的方案(scheme)也有限制:RegistryFactoryImpl只接受http://https://file://,其他协议会抛出 "Unrecognized registry URL protocol" 错误;同时会根据 scheme 与--lockfile_mode组合推导出对注册表文件的校验和处理模式(KnownFileHashesMode)——file://本地注册表完全忽略校验和,http(s)注册表在error模式下强制要求所有下载带校验和,见 RegistryFactoryImpl.java 与 IndexRegistry.java。

相关辅助标志

  • --module_mirrors:额外指定源码镜像 URL,优先级高于注册表提供的镜像。支持按注册表指定:--module_mirrors=https://bcr.bazel.build=https://mirror.example.com,也支持全局逗号分隔列表;设为空值可禁用注册表默认镜像。所有镜像下载都会用注册表中存储的哈希校验(并被 lockfile 固定),见 RepositoryOptions.java。
  • --allow_yanked_versions:以module@version,module2@version2形式显式放行被 yanked 的版本,否则解析会失败,见 RepositoryOptions.java。

八、实战:搭建一个本地 Index Registry

下面给出一个可直接运行的最小示例,把上文格式串起来。假设项目根目录结构如下:

my-registry/ ├── bazel_registry.json └── modules/ └── my_lib/ ├── metadata.json └── 1.0.0/ ├── MODULE.bazel └── source.json

第 1 步:注册表级元数据bazel_registry.json

{ "mirrors": ["https://mirror.example.com/"], "module_base_path": "" }

第 2 步:模块级元数据modules/my_lib/metadata.json

{ "versions": ["1.0.0"], "yanked_versions": {} }

第 3 步:版本模块文件modules/my_lib/1.0.0/MODULE.bazel

module( name = "my_lib", version = "1.0.0", compatibility_level = 1, )

第 4 步:版本源码信息modules/my_lib/1.0.0/source.json

{ "url": "https://github.com/example/my_lib/archive/refs/tags/v1.0.0.tar.gz", "integrity": "sha256-<base64 校验和>", "strip_prefix": "my_lib-1.0.0" }

第 5 步:在项目中使用

在项目.bazelrc中添加:

common --registry=file:///absolute/path/to/my-registry

或在命令行直接传入:

bazel build //... --registry=file:///absolute/path/to/my-registry --registry=https://bcr.bazel.build

之后在MODULE.bazelbazel_dep(name = "my_lib", version = "1.0.0")即可完成依赖解析。把my-registry目录原样上传到任意静态 Web 服务器,并把file://换成https://地址,就得到了一个对外可用的 HTTP 注册表——因为IndexRegistry对本地目录与静态 HTTP 服务器一视同仁,只是抓取路径从文件系统换成了 URL。

九、验证与进一步阅读

如果你想在源码层面验证本文所述行为,可以从这些位置入手:

  • 核心实现:IndexRegistry.java(目录格式、三类 source、镜像、yanked、lockfile 联动);
  • 注册表工厂与协议校验:RegistryFactoryImpl.java;
  • 标志定义:RepositoryOptions.java(--registry--module_mirrors--allow_yanked_versions);
  • 测试用例:IndexRegistryTest.java(覆盖 HTTP/file 注册表、三种 RepoSpec、yanked 版本、各类 integrity 缺失报错)、IndexRegistryGitRepositoryTest.java、RegistryFactoryTest.java;
  • 关联文档:external/module.mdx(模块与 yanked 版本语义)、external/faq.mdx(模块版本化最佳实践)、external/overview.mdx(Bzlmod 概览)。

理解注册表是掌握 Bazel 外部依赖体系的关键一步:它把"依赖从哪来、源码怎么拿、版本是否可信"这几个问题统一沉淀为一份静态目录结构,让依赖解析既透明又可审计,也为企业内网镜像、离线分发和供应链安全提供了清晰的落地路径。

【免费下载链接】bazela fast, scalable, multi-language and extensible build system项目地址: https://gitcode.com/GitHub_Trending/ba/bazel

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 5:35:58

AI高薪就业路线图:机器学习/NLP/CV/RL/大模型工程实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 5:35:23

6G太赫兹通信技术:原理、优势与应用解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 5:30:52

DeepSeek导出Word的三种技术路径与选型决策

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 5:29:58

PythonRobotics 如何用 Dubins 路径规划生成带航向约束的最短路径

PythonRobotics 如何用 Dubins 路径规划生成带航向约束的最短路径 【免费下载链接】PythonRobotics Python sample codes and textbook for robotics algorithms. 项目地址: https://gitcode.com/GitHub_Trending/py/PythonRobotics 如果你的移动平台&#xff08;小车、…

作者头像 李华