如何处理Bazel中gRPC的传递依赖冲突问题?
Bazel搭建gRPC Python服务器的依赖加载顺序问题
问题现象
在Mac上用Bazel搭建gRPC Python服务器时,出现以下差异:
- 先加载gRPC规则、再加载Proto规则:构建运行完全正常
- 先加载Proto规则、再加载gRPC规则:构建失败,核心错误为找不到
google/protobuf/descriptor.proto
查看grpc_extra_deps发现其内部会自动加载protobuf依赖,推测问题与重复加载Proto依赖导致的冲突有关。
核心困惑
- 后续添加其他规则时,是否必须严格避免重复加载相关规则?
- 若两个不同规则都依赖protobuf,会引发什么问题?
- 调整加载顺序只是临时 workaround,有没有更可靠的解决方法?
我的BUILD文件
load("@com_github_grpc_grpc//bazel:python_rules.bzl", "py_grpc_library", "py_proto_library") proto_library( name = "dummy_pb2", srcs = ["dummy.proto"], ) py_proto_library( name = "dummy_py_pb2", deps = [":dummy_pb2"], ) py_grpc_library( name = "dummy_py_pb2_grpc", srcs = [":dummy_pb2"], deps = [":dummy_py_pb2"], ) py_binary( name = "main", srcs = ["main.py"], deps = [ ":dummy_py_pb2", ":dummy_py_pb2_grpc", ], )
可正常运行的WORKSPACE文件(先加载gRPC规则)
... load("@rules_python//python:repositories.bzl", "py_repositories", "python_register_toolchains") py_repositories() python_register_toolchains( name = "python3_10", python_version = "3.10", ) 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() # Load proto rules after grpc rules: works fine load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains") rules_proto_dependencies() rules_proto_toolchains() load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") protobuf_deps()
构建失败的WORKSPACE文件(先加载Proto规则)
... # Load proto rules before grpc rules: fails load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains") rules_proto_dependencies() rules_proto_toolchains() load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") protobuf_deps() # grpc rules 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()
错误信息
... ERROR: /private/var/tmp/_bazel_chuanye/9a81a05a7dab3dd78d1a247bcd97d00b/external/upb/BUILD:517:28: Executing genrule @upb//:gen_descriptor_upb_proto_stage1 [for tool] failed: (Exit 1): bash failed: error executing command (from target @upb//:gen_descriptor_upb_proto_stage1) /bin/bash -c ... (remaining 1 argument skipped) Use --sandbox_debug to see verbose messages from the sandbox and retain the sandbox build root for debugging src: warning: directory does not exist. Could not make proto path relative: google/protobuf/descriptor.proto: No such file or directory ERROR: /private/var/tmp/_bazel_chuanye/9a81a05a7dab3dd78d1a247bcd97d00b/external/upb/upbc/BUILD:64:28: Executing genrule @upb//upbc:gen_plugin_upb_proto_stage1 [for tool] failed: (Exit 1): bash failed: error executing command (from target @upb//upbc:gen_plugin_upb_proto_stage1) /bin/bash -c ... (remaining 1 argument skipped) ...
已尝试调整Bazel规则的加载顺序。
问题解析与解决方案
根源
这个问题本质是重复仓库定义导致的依赖冲突:
grpc_extra_deps会自动加载gRPC适配的protobuf相关依赖(包括upb——gRPC使用的轻量protobuf实现)- 提前手动加载
rules_proto和protobuf_deps会先初始化一套独立的protobuf仓库,后续gRPC加载的同类型仓库会与前者冲突,导致upb构建时无法找到descriptor.proto的正确路径。
解决方法
1. 最优方案:让gRPC统一管理所有protobuf依赖
完全移除手动加载的rules_proto和protobuf_deps,因为grpc_deps和grpc_extra_deps已经包含了所有必要的protobuf依赖(包括rules_proto和对应的工具链)。修改后的WORKSPACE只需保留:
... load("@rules_python//python:repositories.bzl", "py_repositories", "python_register_toolchains") py_repositories() python_register_toolchains( name = "python3_10", python_version = "3.10", ) 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()
这种方式彻底避免重复定义,让gRPC负责所有依赖的版本对齐和加载顺序。
2. 进阶方案:手动对齐依赖版本
如果必须手动加载部分protobuf依赖,需确保所有依赖的版本完全一致:
- 查看gRPC仓库中使用的protobuf版本(在gRPC的WORKSPACE文件中找到
com_google_protobuf的版本号) - 手动加载
rules_proto和protobuf_deps时指定相同版本,消除版本冲突
3. 长期方案:切换到Bazel模块系统
使用MODULE.bazel替代传统WORKSPACE,Bazel模块系统会自动处理依赖冲突,确保同一依赖只加载一次。示例MODULE.bazel:
module( name = "your_project", version = "0.0.1", ) bazel_dep(name = "rules_python", version = "0.25.0") bazel_dep(name = "grpc", version = "1.59.0") bazel_dep(name = "rules_proto", version = "5.3.0-21.7")
模块系统会自动解析所有依赖的版本兼容性,无需手动调整加载顺序。
对困惑点的解答
是否必须避免重复加载规则?
是的。Bazel中重复加载同一规则仓库(尤其是不同版本)会引发规则定义冲突、路径错误等问题。尽量通过依赖提供方的加载器统一管理依赖,避免手动重复加载。两个规则都依赖protobuf会怎样?
如果依赖的是同一版本的protobuf,Bazel会自动去重;但如果版本不同,会出现仓库冲突,导致构建失败(如找不到文件、规则定义重复等)。比调整顺序更好的方法?
- 优先使用依赖提供方的加载器(如gRPC的
grpc_deps)统一管理依赖 - 切换到Bazel模块系统,让系统自动处理依赖冲突
- 确保所有依赖的版本完全对齐,手动同步版本号
内容的提问来源于stack exchange,提问作者Chuan Ye
相关产品推荐
相关产品推荐

