go_sdk extension: create `go_host_compatible_sdk` repository (#3543)

See issue 1469 on https://github.com/bazelbuild/bazel-gazelle

Co-authored-by: Fabian Meumertzheim <fabian@meumertzhe.im>
diff --git a/go/private/extensions.bzl b/go/private/extensions.bzl
index dd1166e..03d3fea 100644
--- a/go/private/extensions.bzl
+++ b/go/private/extensions.bzl
@@ -1,6 +1,25 @@
-load("//go/private:sdk.bzl", "go_download_sdk_rule", "go_host_sdk_rule", "go_multiple_toolchains")
+load("//go/private:sdk.bzl", "detect_host_platform", "go_download_sdk_rule", "go_host_sdk_rule", "go_multiple_toolchains")
 load("//go/private:repositories.bzl", "go_rules_dependencies")
 
+def host_compatible_toolchain_impl(ctx):
+    ctx.file("BUILD.bazel")
+    ctx.file("defs.bzl", content = """
+HOST_COMPATIBLE_SDK = Label({})
+""".format(repr(ctx.attr.toolchain)))
+
+host_compatible_toolchain = repository_rule(
+    implementation = host_compatible_toolchain_impl,
+    attrs = {
+        # We cannot use attr.label for the `toolchain` attribute since the module extension cannot
+        # refer to the repositories it creates by their apparent repository names.
+        "toolchain": attr.string(
+            doc = "The apparent label of a `ROOT` file in the repository of a host compatible toolchain created by the `go_sdk` extension",
+            mandatory = True,
+        ),
+    },
+    doc = "An external repository to expose the first host compatible toolchain",
+)
+
 _download_tag = tag_class(
     attrs = {
         "name": attr.string(),
@@ -33,6 +52,11 @@
         else:
             multi_version_module[module.name] = False
 
+    # We remember the first host compatible toolchain declared by the download and host tags.
+    # The order follows bazel's iteration over modules (the toolchains declared by the root module are considered first).
+    # We know that at least `go_default_sdk` (which is declared by the `rules_go` module itself) is host compatible.
+    first_host_compatible_toolchain = None
+    host_detected_goos, host_detected_goarch = detect_host_platform(ctx)
     toolchains = []
     for module in ctx.modules:
         for index, download_tag in enumerate(module.tags.download):
@@ -66,6 +90,9 @@
                 version = download_tag.version,
             )
 
+            if (not download_tag.goos or download_tag.goos == host_detected_goos) and (not download_tag.goarch or download_tag.goarch == host_detected_goarch):
+                first_host_compatible_toolchain = first_host_compatible_toolchain or "@{}//:ROOT".format(name)
+
             toolchains.append(struct(
                 goos = download_tag.goos,
                 goarch = download_tag.goarch,
@@ -99,7 +126,9 @@
                 sdk_type = "host",
                 sdk_version = host_tag.version,
             ))
+            first_host_compatible_toolchain = first_host_compatible_toolchain or "@{}//:ROOT".format(name)
 
+    host_compatible_toolchain(name = "go_host_compatible_sdk_label", toolchain = first_host_compatible_toolchain)
     if len(toolchains) > _MAX_NUM_TOOLCHAINS:
         fail("more than {} go_sdk tags are not supported".format(_MAX_NUM_TOOLCHAINS))
 
diff --git a/go/private/sdk.bzl b/go/private/sdk.bzl
index 5a3ef42..8a9bc64 100644
--- a/go/private/sdk.bzl
+++ b/go/private/sdk.bzl
@@ -59,7 +59,7 @@
 
 def _go_download_sdk_impl(ctx):
     if not ctx.attr.goos and not ctx.attr.goarch:
-        goos, goarch = _detect_host_platform(ctx)
+        goos, goarch = detect_host_platform(ctx)
     else:
         if not ctx.attr.goos:
             fail("goarch set but goos not set")
@@ -173,7 +173,7 @@
 
 def go_toolchains_single_definition(ctx, *, prefix, goos, goarch, sdk_repo, sdk_type, sdk_version):
     if not goos and not goarch:
-        goos, goarch = _detect_host_platform(ctx)
+        goos, goarch = detect_host_platform(ctx)
     else:
         if not goos:
             fail("goarch set but goos not set")
@@ -354,7 +354,7 @@
     if ctx.attr.root_file:
         root_file = ctx.attr.root_file
     else:
-        goos, goarch = _detect_host_platform(ctx)
+        goos, goarch = detect_host_platform(ctx)
         platform = goos + "_" + goarch
         if platform not in ctx.attr.root_files:
             fail("unsupported platform {}".format(platform))
@@ -466,7 +466,7 @@
         content = _define_version_constants(version),
     )
 
-def _detect_host_platform(ctx):
+def detect_host_platform(ctx):
     goos = ctx.os.name
     if goos == "mac os x":
         goos = "darwin"
diff --git a/tests/bcr/MODULE.bazel b/tests/bcr/MODULE.bazel
index 151f522..b774feb 100644
--- a/tests/bcr/MODULE.bazel
+++ b/tests/bcr/MODULE.bazel
@@ -30,6 +30,9 @@
 # Bring the default SDK into scope to verify that it exists.
 use_repo(go_sdk, "go_default_sdk")
 
+# Bring the selected host compatible SDK into scope to verify that it exists.
+use_repo(go_sdk, "go_host_compatible_sdk_label")
+
 go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps")
 go_deps.module(
     path = "google.golang.org/grpc",