Skip to content

Commit

Permalink
Automatically register SDKs for common execution platforms
Browse files Browse the repository at this point in the history
The `go_sdk` module extension now registers unnamed SDKs for all major
execution platforms in addition to the host platform. This ensures that
Go builds should "just work" if the exec platform is on this list, even
if it differs from the host platform.
  • Loading branch information
fmeum committed Dec 11, 2023
1 parent 43e644e commit 4c5db8f
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 8 deletions.
5 changes: 3 additions & 2 deletions docs/go/core/bzlmod.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ To register a particular version of the Go SDK, use the `go_sdk` module extensio
```starlark
go_sdk = use_extension("@rules_go//go:extensions.bzl", "go_sdk")

# Download an SDK for the host OS & architecture.
# Download an SDK for the host OS & architecture as well as common remote execution platforms.
go_sdk.download(version = "1.20.3")

# Alternately, download an SDK for a fixed OS/architecture, e.g. for remote execution.
# Alternately, download an SDK for a fixed OS/architecture.
go_sdk.download(
version = "1.20.3",
goarch = "amd64",
Expand All @@ -45,6 +45,7 @@ go_sdk.host()
```

You can register multiple Go SDKs and select which one to use on a per-target basis using [`go_cross_binary`](rules.md#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](https://bazel.build/extending/toolchains#toolchain-resolution) apply, with SDKs registered in the root module taking precedence over those registered in dependencies.

### Using a Go SDK
Expand Down
60 changes: 54 additions & 6 deletions go/private/extensions.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,18 @@ _host_tag = tag_class(
},
)

# A list of (goos, goarch) pairs that are commonly used for remote executors in cross-platform
# builds (where host != exec platform). By default, we register toolchains for all of these
# platforms in addition to the host platform.
_COMMON_EXEC_PLATFORMS = [
("darwin", "amd64"),
("darwin", "arm64"),
("linux", "amd64"),
("linux", "arm64"),
("windows", "amd64"),
("windows", "arm64"),
]

# This limit can be increased essentially arbitrarily, but doing so will cause a rebuild of all
# targets using any of these toolchains due to the changed repository name.
_MAX_NUM_TOOLCHAINS = 9999
Expand Down Expand Up @@ -96,10 +108,9 @@ def _go_sdk_impl(ctx):

# SDKs with an explicit name are at risk of colliding with those from other modules.
# This is acceptable if brought in by the root module as the user is responsible for any
# conflicts that arise. rules_go itself provides "go_default_sdk", which is used by
# Gazelle to bootstrap itself.
# TODO(https://github.com/bazelbuild/bazel-gazelle/issues/1469): Investigate whether
# Gazelle can use the first user-defined SDK instead to prevent unnecessary downloads.
# conflicts that arise. rules_go itself provides "go_default_sdk".
# TODO: Now that Gazelle relies on the go_host_compatible_sdk_label repo, remove the
# special case for "go_default_sdk". Users should migrate to @rules_go//go.
if (not module.is_root and not module.name == "rules_go") and download_tag.name:
fail("go_sdk.download: name must not be specified in non-root module " + module.name)

Expand Down Expand Up @@ -133,6 +144,42 @@ def _go_sdk_impl(ctx):
sdk_version = download_tag.version,
))

# Additionally register SDKs for all common execution platforms, but only if the user
# specified a version to prevent eager fetches.
if download_tag.version and not download_tag.goos and not download_tag.goarch:
for goos, goarch in _COMMON_EXEC_PLATFORMS:
if goos == host_detected_goos and goarch == host_detected_goarch:
# We already added the host-compatible toolchain above.
continue

if download_tag.sdks and not "{}_{}".format(goos, goarch) in download_tag.sdks:
# The user supplied custom download links, but not for this tuple.
continue

default_name = _default_go_sdk_name(
module = module,
multi_version = multi_version_module[module.name],
tag_type = "download",
index = index,
suffix = "_{}_{}".format(goos, goarch),
)
go_download_sdk_rule(
name = default_name,
goos = download_tag.goos,
goarch = download_tag.goarch,
sdks = download_tag.sdks,
urls = download_tag.urls,
version = download_tag.version,
)

toolchains.append(struct(
goos = goos,
goarch = goarch,
sdk_repo = default_name,
sdk_type = "remote",
sdk_version = download_tag.version,
))

for index, host_tag in enumerate(module.tags.host):
# Dependencies can rely on rules_go providing a default remote SDK. They can also
# configure a specific version of the SDK to use. However, they should not add a
Expand Down Expand Up @@ -183,15 +230,16 @@ def _go_sdk_impl(ctx):
sdk_versions = [toolchain.sdk_version for toolchain in toolchains],
)

def _default_go_sdk_name(*, module, multi_version, tag_type, index):
def _default_go_sdk_name(*, module, multi_version, tag_type, index, suffix = ""):
# Keep the version out of the repository name if possible to prevent unnecessary rebuilds when
# it changes.
return "{name}_{version}_{tag_type}_{index}".format(
return "{name}_{version}_{tag_type}_{index}{suffix}".format(
# "main_" is not a valid module name and thus can't collide.
name = module.name or "main_",
version = module.version if multi_version else "",
tag_type = tag_type,
index = index,
suffix = suffix,
)

def _toolchain_prefix(index, name):
Expand Down

0 comments on commit 4c5db8f

Please sign in to comment.