You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何处理Bazel中gRPC的传递依赖冲突问题?

Bazel搭建gRPC Python服务器的依赖加载顺序问题

问题现象

在Mac上用Bazel搭建gRPC Python服务器时,出现以下差异:

  • 先加载gRPC规则、再加载Proto规则:构建运行完全正常
  • 先加载Proto规则、再加载gRPC规则:构建失败,核心错误为找不到google/protobuf/descriptor.proto

查看grpc_extra_deps发现其内部会自动加载protobuf依赖,推测问题与重复加载Proto依赖导致的冲突有关。

核心困惑

  1. 后续添加其他规则时,是否必须严格避免重复加载相关规则?
  2. 若两个不同规则都依赖protobuf,会引发什么问题?
  3. 调整加载顺序只是临时 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")

模块系统会自动解析所有依赖的版本兼容性,无需手动调整加载顺序。

对困惑点的解答

  1. 是否必须避免重复加载规则?
    是的。Bazel中重复加载同一规则仓库(尤其是不同版本)会引发规则定义冲突、路径错误等问题。尽量通过依赖提供方的加载器统一管理依赖,避免手动重复加载。

  2. 两个规则都依赖protobuf会怎样?
    如果依赖的是同一版本的protobuf,Bazel会自动去重;但如果版本不同,会出现仓库冲突,导致构建失败(如找不到文件、规则定义重复等)。

  3. 比调整顺序更好的方法?

  • 优先使用依赖提供方的加载器(如gRPC的grpc_deps)统一管理依赖
  • 切换到Bazel模块系统,让系统自动处理依赖冲突
  • 确保所有依赖的版本完全对齐,手动同步版本号

内容的提问来源于stack exchange,提问作者Chuan Ye

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 22:48:13