This document describes how to use rules_go and Gazelle with Bazel's new external dependency subsystem Bzlmod, which is meant to replace WORKSPACE files eventually.
Usages of rules_go and Gazelle in BUILD files are not affected by this; refer to the existing documentation on rules and configuration options for them.
Add the following lines to your MODULE.bazel file:
bazel_dep(name = "rules_go", version = "0.57.0")
bazel_dep(name = "gazelle", version = "0.45.0")The latest versions are always listed on https://registry.bazel.build/.
If you have WORKSPACE dependencies that reference rules_go and/or Gazelle, you can still use the legacy repository names for the two repositories:
bazel_dep(name = "rules_go", version = "0.57.0", repo_name = "io_bazel_rules_go")
bazel_dep(name = "gazelle", version = "0.45.0", repo_name = "bazel_gazelle")rules_go automatically downloads and registers a recent Go SDK, so unless a particular version is required, no manual steps are required.
To register a particular version of the Go SDK, use the go_sdk module extension:
go_sdk = use_extension("@rules_go//go:extensions.bzl", "go_sdk")
# Download an SDK for the host OS & architecture as well as common remote execution
# platforms, using the version given from the `go.mod` file.
go_sdk.from_file(go_mod = "//:go.mod")
# Alternatively, use the version from a `go.work` file.
go_sdk.from_file(go_work = "//:go.work")
# Download an SDK for the host OS & architecture as well as common remote execution
# platforms, with a specific version.
go_sdk.download(version = "1.23.1")
# Alternatively, download an SDK for a fixed OS/architecture.
go_sdk.download(
version = "1.23.1",
goarch = "amd64",
goos = "linux",
)
# Another alternative is to register the Go SDK installed on the host (see the nota bene below).
go_sdk.host()Nota bene: The use of go_sdk.host() may break builds whenever the host Go version is upgraded
(because many OS package managers, such as Debian/Ubuntu's apt, distribute Go into a directory which contains the version, such as /usr/lib/go-1.22/).
As package upgrades happen outside of Bazel's control, this will lead to non-reproducible builds. Due to this, use of go_sdk.host() is discouraged.
When using go_sdk.from_file(), exactly one of go_mod or go_work must be specified.
Version extraction follows the same precedence for both file types: the toolchain directive takes precedence
over the go directive. If neither directive is present, go.mod has an implicit go 1.16 line while
go.work has an implicit go 1.18 line as per Go Toolchains documentation.
You can register multiple Go SDKs and select which one to use on a per-target basis using go_cross_binary.
As long as you specify the version of an SDK, it will be downloaded lazily, that is, only when it is actually needed during a particular build.
The usual rules of toolchain resolution apply, with SDKs registered in the root module taking precedence over those registered in dependencies.
By default, Go SDK repositories are created with mangled names and are not expected to be referenced directly.
For build actions, toolchain resolution is used to select the appropriate SDK for a given target.
go_cross_binary can be used to influence the outcome of the resolution.
The go tool of the SDK registered for the host is available via the @rules_go//go target.
Prefer running it via this target over running go directly to ensure that all developers use the same version.
The @rules_go//go target can be used in scripts executed via bazel run, but cannot be used in build actions.
Note that go command arguments starting with - require the use of the double dash separator with bazel run:
bazel run @rules_go//go -- mod tidy -vIf you really do need direct access to a Go SDK, you can provide the name attribute on the go_sdk.download or go_sdk.host tag and then bring the repository with that name into scope via use_repo.
Note that modules using this attribute cannot be added to registries such as the Bazel Central Registry (BCR).
If you have a use case that would require this, please explain it in an issue.
The nogo tool is a static analyzer for Go code that is run as part of compilation.
It is configured via an instance of the nogo rule, which can then be registered with the go_sdk extension:
go_sdk = use_extension("@rules_go//go:extensions.bzl", "go_sdk")
go_sdk.nogo(nogo = "//:my_nogo")By default, the nogo tool is executed for all Go targets in the main repository, but not any external repositories.
Each module can only provide at most one go_sdk.nogo tag and only the tag of the root module is honored.
It is also possible to include only or exclude particular packages from nogo analysis, using syntax that matches the visibility attribute on rules:
go_sdk = use_extension("@rules_go//go:extensions.bzl", "go_sdk")
go_sdk.nogo(
nogo = "//:my_nogo",
includes = [
"//:__subpackages__",
"@my_own_go_dep//logic:__pkg__",
],
excludes = [
"//third_party:__subpackages__",
],
)go_local_sdk
Add the following to your top-level BUILD file:
load("@gazelle//:def.bzl", "gazelle")
gazelle(name = "gazelle")If there is no go.mod file in the same directory as your top-level BUILD file, also add the following Gazelle directive to that BUILD file to supply Gazelle with your Go module's path:
# gazelle:prefix github.com/example/projectThen, use bazel run //:gazelle to (re-)generate BUILD files.
External Go dependencies are managed by the go_deps module extension provided by Gazelle.
go_deps performs Minimal Version Selection on all transitive Go dependencies of all Bazel modules, so compared to the old WORKSPACE setup, every Bazel module only needs to declare its own Go dependencies.
For every major version of a Go module, there will only ever be a single version in the entire build, just as in regular Go module builds.
Even though this is not a strict requirement, for interoperability with Go tooling that isn't Bazel-aware, it is recommended to manage Go dependencies via go.mod.
The go_deps extension parses this file directly, so external tooling such as gazelle update-repos is no longer needed.
Register the go.mod file with the go_deps extension as follows:
go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps")
go_deps.from_file(go_mod = "//:go.mod")
# All *direct* Go dependencies of the module have to be listed explicitly.
use_repo(
go_deps,
"com_github_gogo_protobuf",
"com_github_golang_mock",
"com_github_golang_protobuf",
"org_golang_x_net",
)When using Bazel 7.1.1 or higher, the @rules_go//go target automatically updates the use_repo call whenever the go.mod file changes, using bazel mod tidy.
With older versions of Bazel, a warning with a fixup command will be emitted during a build if the use_repo call is out of date or missing.
Alternatively, you can specify a module extension tag to add an individual dependency:
go_deps.module(
path = "google.golang.org/grpc",
sum = "h1:fPVVDxY9w++VjTZsYvXWqEf9Rqar/e+9zYfxKK+W+YU=",
version = "v1.50.0",
)The go.work functionality is supported by the go_deps module extension in Gazelle.
go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps")
go_deps.from_file(go_work = "//:go.work")
# All *direct* Go dependencies of all `go.mod` files referenced by the `go.work` file have to be listed explicitly.
use_repo(
go_deps,
"com_github_gogo_protobuf",
"com_github_golang_mock",
"com_github_golang_protobuf",
"org_golang_x_net",
)Limitations:
go.workis supported exclusively in the root module.- Dependencies that are indirect and depend on a go module specified in
go.workwill have that dependency diverge from the one ingo.work. More details can be found here: bazel-contrib/bazel-gazelle#1797.
Go 1.24 introduced the tool directive, allowing you to specify tool dependencies directly in your go.mod like so:
bazel run @rules_go//go -- get -tool golang.org/x/tools/cmd/stringerThis will add a tool section in your go.mod:
tool golang.org/x/tools/cmd/stringer
as well as adding that tool as a dependency.
If you are using Gazelle >=0.47.0, then the tools you have added are exported as a dictionary named GO_TOOLS from @gazelle//:go_tools.bzl. This dictionary is in a suitable format for use by bazel_env.bzl, so you should be able to do the following to get all your repository’s tools into a bazel_env target:
load("@bazel_env.bzl", "bazel_env")
load("@gazelle//:go_tools.bzl", "GO_TOOLS")
bazel_env(
name = "env",
tools = {
// […]
} | GO_TOOLS,
)If you need to depend on Go modules that are only used as tools, you can use the tools.go technique:
-
In a new subdirectory of your repository, create a
tools.gofile that imports the tools' main packages://go:build tools // +build tools package my_tools import ( _ "github.com/the/tool" _ "golang.org/x/tools/cmd/stringer" )
-
Run
bazel run @rules_go//go mod tidyto populate thego.modfile with the dependencies of the tools.
Instead, if you want the tools' dependencies to be resolved independently of the dependencies of your regular code (experimental):
-
Run
bazel run @rules_go//go mod initin the directory containing thetools.gofile to create a newgo.modfile and then runbazel run @rules_go//go mod tidyin that directory. -
Add
common --experimental_isolated_extension_usagesto your.bazelrcfile to enable isolated usages of extensions. -
Add an isolated usage of the
go_depsextension to your module file:go_tool_deps = use_extension("@gazelle//:extensions.bzl", "go_deps", isolate = True) go_tool_deps.from_file(go_mod = "//tools:go.mod")
An initial go.mod file can be created via
bazel run @rules_go//go mod init github.com/example/projectA dependency can be added via
bazel run @rules_go//go get golang.org/x/text@v0.3.2Environment variables (such as GOPROXY and GOPRIVATE) required for fetching Go dependencies can be set as follows:
go_deps.config(
go_env = {
"GOPRIVATE": "...",
},
)Variables set in this way are used by go_deps as well as @rules_go//go, with other variables inheriting their value from the host environment.
go_env does not affect Go build actions.
The root module can override certain aspects of the dependency resolution performed by the go_deps extension.
replace directives in go.mod can be used to replace particular or all versions of dependencies with other versions or entirely different modules.
replace(
golang.org/x/net v1.2.3 => example.com/fork/net v1.4.5
golang.org/x/mod => example.com/my/mod v1.4.5
example.org/hello => ../../../fixtures/hello
)
Some external Go modules may require tweaking how Gazelle generates BUILD files for them via Gazelle directives.
The go_deps extension provides a dedicated go_deps.gazelle_override tag for this purpose:
go_deps.gazelle_override(
directives = [
"gazelle:go_naming_convention go_default_library",
],
path = "github.com/stretchr/testify",
)If you need to use a gazelle_override to get a public Go module to build with Bazel, consider contributing the directives to the public registry for default Gazelle overrides via a PR.
This will allow you to drop the gazelle_override tag and also makes the Go module usable in non-root Bazel modules.
Users can apply custom default directives or extra args to all modules, these can be added via a go_deps.gazelle_default_attributes. These will
disable/overwrite the public registry overrides.
go_deps.gazelle_default_attributes(
build_extra_args = [
"-go_naming_convention_external=go_default_library",
],
build_file_generation = "on",
directives = [
"gazelle:proto disable",
],
)Overrides are applied with precedence decreasing in this order::
- Specific
go_deps.gazelle_overrideoverrides per module go_deps.gazelle_default_attributes, which will overwrite #3 (which now must be applied manually by users).- public registry for default Gazelle overrides
It is recommended to avoid go_deps.gazelle_default_attributes and upstream the overrides to the public registry for default Gazelle overrides.
A go_deps.module_override can be used to apply patches to a Go module:
go_deps.module_override(
patch_strip = 1,
patches = [
"//patches:testify.patch",
],
path = "github.com/stretchr/testify",
)A go_deps.archive_override can be used to replace a Go module with an archive fetched from a URL and is very similar to the archive_override for Bazel modules:
go_deps.archive_override(
urls = [
"https://github.com/bazelbuild/buildtools/archive/ae8e3206e815d086269eb208b01f300639a4b194.tar.gz",
],
patch_strip = 1,
patches = [
"//patches:buildtools.patch",
],
strip_prefix = "buildtools-ae8e3206e815d086269eb208b01f300639a4b194",
path = "github.com/bazelbuild/buildtools",
sha256 = "05d7c3d2bd3cc0b02d15672fefa0d6be48c7aebe459c1c99dced7ac5e598508f",
)- Fetching dependencies from Git repositories
go.modexcludedirectices