Bazel 构建 Go 项目高效,需避开手动写 BUILD、混用 go mod 与 go_repository、忽略 gazelle 更新三大陷阱;正确使用 go_repository 和持续 gazelle update 可使增量构建稳定在 5–10 秒。

Go 项目用 Bazel 构建是否高效,取决于你是否绕开了三个典型陷阱:手动写 BUILD.bazel、混用 go mod 与 go_repository、忽略 gazelle 的更新机制。只要避开这几点,Bazel 在大型 Go 项目中增量构建耗时能稳定压在 5–10 秒内,远优于 Makefile 的全量重编。
go_repository 是唯一推荐的外部依赖声明方式
别再用 new_git_repository 或子模块拉 gRPC、protobuf 这类库——它们无法被 gazelle 自动识别,版本 hash 隐藏在 .gitmodules 里,WORKSPACE 文件失去可审计性。正确做法是全部走 go_repository,由 rules_go 自动解析目标仓库的 go.mod 并生成 BUILD 文件。
-
go_repository支持version+sum(推荐),或commit+replace(调试用) - 所有依赖必须显式出现在 WORKSPACE,不能靠本地
vendor/或GO111MODULE=off绕过 - 若某依赖没发布
go.mod(如极老的库),需加build_file_generation = "on"让 gazelle 尝试推导
gazelle update 不能只跑一次
很多人初始化后执行一次 gazelle update 就以为万事大吉,结果新增包不被识别、依赖缺失、importpath 错乱。gazelle 不是“设好就忘”的工具,它需要持续同步。
- 每次
go mod tidy后,必须立刻跟一个gazelle update - 建议把
gazelle集成进 pre-commit hook,防止漏掉 - 遇到
importpath推断错误(比如把internal/foo误标为example.com/internal/foo),在对应目录下放gazelle.yaml手动 fix:prefix: example.com
go_binary 和 go_library 的 visibility 要按需收紧
大型项目里,随意设 visibility = ["//visibility:public"] 会导致构建图膨胀、缓存失效率升高、甚至意外链接到未声明的内部实现。
立即学习“go语言免费学习笔记(深入)”;
- 对外暴露的库(如 SDK 包)才设 public;内部包一律用
//visibility:private或限定路径如//cmd/... -
go_binary默认 private,不用改;但若要供其他 workspace 引用,应显式声明visibility = ["//some/other:__subpackages__"] - 避免跨
//internal和//pkg边界直接依赖,用 interface +deps显式声明比隐式 import 更可控
最易被忽略的一点:Bazel 的构建性能优势只在依赖图干净时成立。一旦出现循环引用、glob(["**/*.go"])、或大量 alias 嵌套,缓存命中率会断崖下跌——这时候不是 Bazel 慢,是你没让它看清依赖边界。


















