gRPC Bazel 构建支持详解:WORKSPACE 依赖接入、版本策略与源码实现剖析
【免费下载链接】grpcC++ based gRPC (C++, Python, Ruby, Objective-C, PHP, C#)项目地址: https://gitcode.com/GitHub_Trending/gr/grpc
gRPC 仓库的主构建系统是 Bazel,C++、Python 与 Objective-C 三类构建规则均以 Bazel 定义为源头,CMake 等其他构建系统的规则也是从 Bazel 定义生成的。本文基于 bazel_support.md 的官方说明,结合 bazel/grpc_deps.bzl、bazel/grpc_extra_deps.bzl 与 MODULE.bazel 等仓库源码,讲解如何在自己的 Bazel 工程(WORKSPACE 或 bzlmod)中引入 gRPC 依赖、当前支持的 Bazel 版本策略(8.7.0),以及grpc_deps/grpc_extra_deps两个仓库规则宏在底层究竟加载了哪些外部仓库。
Bazel 是 gRPC 的主构建系统
doc/bazel_support.md 明确给出 gRPC 对构建系统的定位:
grpc/grpc仓库的主要构建系统是 Bazel,官方为 C++、Python 和 Objective-C 提供了 Bazel 规则;- C++ 虽然同时支持 CMake 等构建系统,但这些规则实际上是从 Bazel 定义生成的;
- 使用 Bazel 构建的项目引入
grpc/grpc,目的不仅是把 gRPC 作为库依赖,还可以用 gRPC 提供的规则直接生成 protobuf 代码、stub 与 servicer 代码。
这一“Bazel 为源、其余构建系统生成”的架构在仓库中有直接证据:仓库根目录的 build_autogenerated.yaml、build_handwritten.yaml 与 tools/buildgen/ 目录共同承担从 Bazel 规则生成各构建系统(CMake、Makefile、Ruby gemspec、CocoaPods 等)所需清单的职责;根 BUILD 文件中定义了核心目标grpc(C 库,见 BUILD)与grpc++(C++ 库,见 BUILD),它们是下游项目最常依赖的两个标签。
因此对下游 Bazel 工程而言,接入 gRPC 的关键只有两步:引入grpc_deps与grpc_extra_deps两个仓库规则,再调用它们。
在 WORKSPACE 中接入 gRPC:grpc_deps 与 grpc_extra_deps
按照 doc/bazel_support.md 的“Basic Usage”一节,下游项目需要在自己的WORKSPACE文件中调用grpc_deps与grpc_extra_deps仓库规则,官方示例如下(文档中以 v1.45.0 版本归档为示例,实际使用时应按需替换为对应的发布 tag、strip_prefix与sha256):
workspace(name = "example_workspace") load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "com_github_grpc_grpc", strip_prefix = "grpc-1.45.0", sha256 = "ec19657a677d49af59aa806ec299c070c882986c9fcc022b1c22c2a3caf01bcd", urls = ["https://github.com/grpc/grpc/archive/refs/tags/v1.45.0.tar.gz"], ) load("@com_github_grpc_grpc//bazel:grpc_deps.bzl", "grpc_deps") grpc_deps() load("@com_github_grpc_grpc//bazel:grpc_extra_deps.bzl", "grpc_extra_deps") grpc_extra_deps()三个步骤的含义:
- 用
http_archive把 gRPC 自身作为外部仓库@com_github_grpc_grpc拉入工作区(仓库内repo_name即com_github_grpc_grpc,见 MODULE.bazel); - 调用
grpc_deps():一次性引入编译与测试 gRPC 所需的全部第三方外部仓库; - 调用
grpc_extra_deps():引入上述外部仓库自身的传递性依赖(如 protobuf、rules_go 等仓库各自声明的依赖)。
深入源码:grpc_deps() 加载了哪些外部仓库
bazel/grpc_deps.bzl 中grpc_deps()的实现是一个幂等的批量http_archive调用序列——每个仓库都先检查"xxx" not in native.existing_rules(),若消费方工作区已声明同名仓库则跳过,从而允许用户自行覆盖版本。所有urls都优先指向 gRPC 维护的镜像storage.googleapis.com/grpc-bazel-mirror/...,并附原始地址作为回退,这是对构建稳定性与可复现性的关键设计(见 bazel/update_mirror.sh 与 bazel/update_mirror_helper.py 的镜像维护逻辑)。
从 bazel/grpc_deps.bzl 的源码结构看,grpc_deps()主要加载以下类别的仓库:
| 类别 | 仓库(仓库名) | 说明 |
|---|---|---|
| 平台与规则集 | platforms、rules_cc、rules_shell、rules_java、rules_proto、bazel_skylib、bazel_features | Bazel 生态基础规则集 |
| 加密与压缩 | boringssl、zlib(打add_custom_build_file.patch补丁)、openssl | 默认 TLS 后端为 BoringSSL;zlib 提供压缩支持 |
| 序列化 | com_google_protobuf(打 protobuf.patch)、grpc_proto | protoc 与 gRPC 服务 proto 定义 |
| 网络与解析 | com_github_cares_cares(c-ares,使用 third_party/cares/cares.BUILD 作为 BUILD)、com_googlesource_code_re2 | 异步 DNS 解析与正则 |
| 通用库 | com_google_absl(abseil-cpp)、com_google_googletest | 核心依赖与测试框架 |
| 可观测性 | io_opencensus_cpp、opencensus_proto、io_opentelemetry_cpp | OpenCensus / OpenTelemetry 遥测 |
| xDS 生态 | com_envoyproxy_protoc_gen_validate、com_github_cncf_xds、dev_cel(cel-spec)、envoy_api | 构建 C++ xDS proto 所需 |
| 语言工具链 | io_bazel_rules_go(打 rules_go.patch)、bazel_gazelle、build_bazel_rules_apple+build_bazel_apple_support(Objective-C)、com_google_fuzztest(fuzz 测试)、com_github_google_benchmark | 分别对应 Go 依赖、Apple 平台构建、fuzz 与基准测试 |
| 其他 | com_google_googleapis、google_cloud_cpp、bazel_toolchains、bazel_compdb | googleapis 导入、RBE 工具链、编译数据库 |
在函数末尾,grpc_deps()还会依次调用grpc_module_deps()(补充google_cloud_cpp仓库)与 bazel/grpc_python_deps.bzl 中的grpc_python_deps(),后者引入 Python 规则集相关依赖,对应文档中“为 Python 提供 Bazel 规则”的能力。
两个容易踩坑的细节值得注意:
- OpenSSL 仅支持 bzlmod。bazel/grpc_deps.bzl 中有一段注释:“Building grpc with openssl is only supported when using bzlmod”,因此 WORKSPACE 模式下
openssl仓库只是被实现为只含空cc_library(ssl、crypto)的 dummy 仓库,保证使用 WORKSPACE 的工程能继续构建; - 测试专用依赖:
grpc_test_only_deps()(bazel/grpc_deps.bzl)额外加载com_google_libprotobuf_mutator与yaml-cpp,仅用于运行 gRPC 自身的测试,源码注释标明其“仅供内部使用,不建议消费方调用”。
深入源码:grpc_extra_deps() 初始化仓库自身的传递依赖
bazel/grpc_extra_deps.bzl 中的grpc_extra_deps(ignore_version_differences = False)负责调用各外部仓库自带的“依赖展开”宏,使其声明的二级依赖就位。从源码调用链看,它依次执行:
rules_shell_dependencies()/rules_shell_toolchains():Shell 规则工具链;rules_java_dependencies()、protobuf_deps()、rules_proto_dependencies()、bazel_features_deps():Java / Protobuf / proto 规则链;go_rules_dependencies()+go_register_toolchains(version = "1.22.5")+gazelle_dependencies()(bazel/grpc_extra_deps.bzl):注册 Go 工具链,供 xDS proto 的 Go 代码生成使用;go_third_party():拉取 protoc-gen-validate 的 Go 第三方依赖(源码注释说明其用于构建 C++ xDS protos);apple_rules_dependencies(ignore_version_differences = ...)与apple_support_dependencies():Objective-C 构建所需的 Apple 规则依赖,参数ignore_version_differences直接透传给前者,用于容忍 Bazel 版本差异告警;switched_rules_by_language(name = "com_google_googleapis_imports", cc = True, grpc = True, python = True)(bazel/grpc_extra_deps.bzl):只为 C++、gRPC 与 Python 三种语言初始化 googleapis 目标,避免无谓地展开其他语言代码生成;google_cloud_cpp_deps()、py_repositories()、googletest_deps():补齐其余仓库的依赖。
源码注释也直接给出了 WORKSPACE 用法模板(grpc_deps()→grpc_test_only_deps()→grpc_extra_deps()的调用顺序),与文档示例一致。
bzlmod:MODULE.bazel 下的模块依赖声明
当前仓库同时提供 bzlmod 支持(Bazel 8 起默认开启的模块化依赖管理),MODULE.bazel 是这一路径的权威声明,也是比 WORKSPACE 方式更推荐使用 gRPC 的方式:
module( name = "grpc", version = "1.84.0-dev", compatibility_level = 1, repo_name = "com_github_grpc_grpc", )MODULE.bazel 声明模块名为grpc、repo_name保持com_github_grpc_grpc(与 WORKSPACE 时代的仓库名兼容)。其后以bazel_dep精确声明了全部常规依赖及版本,例如:
abseil-cpp20250512.1、boringssl0.20260616.0、c-ares1.19.1、protobuf35.1、zlib1.3.1.bcr.5、googletest1.17.0(MODULE.bazel);openssl3.3.1.bcr.1(MODULE.bazel)——印证了“OpenSSL 构建仅在 bzlmod 下支持”的源码注释;- 开发依赖
dev_dependency = True单独归类:google_benchmark、yaml-cpp、fuzztest、google_cloud_cpp、libpfm等(MODULE.bazel); - Python 侧通过
rules_python注册 3.10 至 3.14 多版本工具链(默认 3.11),并用pip.parse(requirements_lock = "//:requirements.bazel.lock")锁定 pip 依赖(MODULE.bazel)。
MODULE.bazel 中还大量使用archive_override/single_version_override覆盖 BCR 默认配置,例如:用f1f503da这个略新于 1.3.1 的 zlib 提交替换 BCR 版本并打补丁(MODULE.bazel);将 re2 声明为 BCR 版本号但实际使用 2022-04-01 快照以保持与 CMake 构建一致(MODULE.bazel);libpfm 因上游原始链接失效而改指 grpc-bazel-mirror 镜像(MODULE.bazel)。从源码结构看,这些覆盖集中体现了“Bazel 侧版本必须与 CMake 等其他构建系统的锁定版本对齐”的维护策略——这正是文档中“Bazel 是源头”主张的具体落地。
当前支持的 Bazel 版本:8.7.0 与版本策略
doc/bazel_support.md 的“Supported Versions”一节给出 gRPC 的 Bazel 版本支持策略:
- 支持Bazel 最新稳定版,以及上一代大版本在转入维护模式后至少 6 个月内的版本;
- 该策略与 Google 基础 C++ 支持策略(Foundational C++ Support Policy)保持一致;
- 个别发布可能有更宽的兼容范围;当前支持版本列表为8.7.0一项。
仓库中有多处与该结论互相印证的证据:
- bazel/supported_versions.txt 的内容仅有一行
8.7.0,是 CI 与工具链脚本读取的机器可读清单; - 仓库根的 .bazelversion 文件同样写入
8.7.0; - tools/bazel 是一个 Bazel wrapper 脚本,利用 Bazel “工作区内
//tools/bazel脚本可劫持 bazel 调用”的机制,确保任何人在该仓库中执行bazel时都使用受支持的版本:- 从
.bazelversion读取版本号(可用环境变量OVERRIDE_BAZEL_VERSION覆盖),再按平台(Linux x86_64 / aarch64、Darwin x86_64 / arm64、Windows MINGW/MSYS)下载对应二进制(见 tools/bazel); - 下载时先尝试
grpc-bazel-mirror镜像、失败再回退原始源(tools/bazel); - 设置
DISABLE_BAZEL_WRAPPER可跳过 wrapper 逻辑直接使用系统 Bazel;OVERRIDE_BAZEL_WRAPPER_DOWNLOAD_DIR可改变二进制下载目录(tools/bazel)。
- 从
对下游工程的含义是:如果你的 Bazel 不是 8.7.0,至少应先确认其落在上述策略区间内再尝试构建;仓库内 .bazelrc 仅做了一件事——从旧位置导入 tools/bazel.rc(import %workspace%/tools/bazel.rc),实际构建参数都集中在后者中,排查 Bazel 行为差异时可以直接查看该文件。
用 gRPC 的 Bazel 规则生成 protobuf / stub / servicer 代码
文档指出,引入 gRPC 仓库的另一个重要用途是生成 protobuf、stub 与 servicer 代码。仓库中对应规则的实现位于 bazel/cc_grpc_library.bzl(cc_grpc_library规则,把cc_proto与 gRPC 代码生成组合起来)、bazel/generate_cc.bzl(底层代码生成 action)以及 bazel/grpc_build_system.bzl。在消费方 BUILD 文件中,典型用法是先对服务 proto 应用cc_proto,再用cc_grpc_library生成 stub/servicer 目标,最后链接@com_github_grpc_grpc//:grpc++目标——具体写法可参考仓库内 examples/cpp/ 下各示例的 BUILD 文件与 doc/bazel_support.md 的说明。
小结
回到 doc/bazel_support.md 的核心结论:gRPC 的 Bazel 支持分为“仓库级”与“规则级”两层——仓库级通过grpc_deps()/grpc_extra_deps()(WORKSPACE)或 MODULE.bazel 的模块依赖(bzlmod)引入 gRPC 及其完整依赖树,版本策略锁定在 Bazel 8.7.0(见 bazel/supported_versions.txt 与 .bazelversion);规则级则提供cc_grpc_library等规则,使下游项目能够直接生成 protobuf/stub/servicer 代码。由于 CMake 等其他构建系统的规则都源自 Bazel 定义,理解上述 Bazel 层结构也是理解 gRPC 全部构建路径的基础。
【免费下载链接】grpcC++ based gRPC (C++, Python, Ruby, Objective-C, PHP, C#)项目地址: https://gitcode.com/GitHub_Trending/gr/grpc
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考