Skip to content

Commit

Permalink
Add support for --@io_bazel_rules_go//go/toolchain:sdk_version flag.
Browse files Browse the repository at this point in the history
- Each go_sdk now has config_settings that specify different values of
  sdk_version. Eg. a go1.17.3 SDK would declare a config_setting for
  sdk_version=1.17, sdk_version=1.17.3, and sdk_version="".
- Then each toolchain that is registered for an SDK will have a
  config_setting_group constraint that combines the above
  config_settings. In the example given above, the go1.17.3 SDK would
  have a toolchain that only gets selected if sdk_version is 1.17,
  1.17.3 or the empty string (default).
- If sdk_version flag is not specified, then all SDKs can be selected
  because of the inclusion of the empty string config_setting in the
  config_setting_group.

Signed-off-by: James Bartlett <[email protected]>
  • Loading branch information
JamesMBartlett committed Aug 3, 2022
1 parent 28e7e33 commit 4b7e1d9
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 11 deletions.
51 changes: 51 additions & 0 deletions go/private/BUILD.sdk.bazel
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
load("@{rules_go_repo_name}//go/private/rules:binary.bzl", "go_tool_binary")
load("@{rules_go_repo_name}//go/private/rules:sdk.bzl", "package_list")
load("@{rules_go_repo_name}//go:def.bzl", "declare_toolchains", "go_sdk")
load("@bazel_skylib//lib:selects.bzl", "selects")

package(default_visibility = ["//visibility:public"])

Expand Down Expand Up @@ -58,10 +59,60 @@ package_list(
root_file = "ROOT",
)

sdk_version_label = "@{rules_go_repo_name}//go/toolchain:sdk_version"

config_setting(
name = "match_all_versions",
flag_values = {
sdk_version_label: "",
},
)

config_setting(
name = "match_major_version",
flag_values = {
sdk_version_label: "{major_version}",
},
)

config_setting(
name = "match_major_minor_version",
flag_values = {
sdk_version_label: "{major_version}.{minor_version}",
},
)

config_setting(
name = "match_patch_version",
flag_values = {
sdk_version_label: "{major_version}.{minor_version}.{patch_version}",
},
)

# If prerelease version is "", this will be the same as ":match_patch_version", but that's fine since we use match_any in config_setting_group.
config_setting(
name = "match_prerelease_version",
flag_values = {
sdk_version_label: "{major_version}.{minor_version}.{patch_version}{prerelease_suffix}",
},
)

selects.config_setting_group(
name = "sdk_version_setting",
match_any = [
":match_all_versions",
":match_major_version",
":match_major_minor_version",
":match_patch_version",
":match_prerelease_version",
],
)

declare_toolchains(
builder = ":builder",
host = "{goos}_{goarch}",
sdk = ":go_sdk",
sdk_version_setting = ":sdk_version_setting",
)

filegroup(
Expand Down
3 changes: 2 additions & 1 deletion go/private/go_toolchain.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ go_toolchain = rule(
provides = [platform_common.ToolchainInfo],
)

def declare_toolchains(host, sdk, builder):
def declare_toolchains(host, sdk, builder, sdk_version_setting):
"""Declares go_toolchain and toolchain targets for each platform."""

# keep in sync with generate_toolchain_names
Expand Down Expand Up @@ -139,5 +139,6 @@ def declare_toolchains(host, sdk, builder):
"@io_bazel_rules_go//go/toolchain:" + host_goarch,
],
target_compatible_with = constraints,
target_settings = [sdk_version_setting],
toolchain = ":" + impl_name,
)
36 changes: 31 additions & 5 deletions go/private/sdk.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ MIN_SUPPORTED_VERSION = (1, 14, 0)
def _go_host_sdk_impl(ctx):
goroot = _detect_host_sdk(ctx)
platform = _detect_sdk_platform(ctx, goroot)
_sdk_build_file(ctx, platform)
version = _detect_sdk_version(ctx, goroot)
_sdk_build_file(ctx, platform, version)
_local_sdk(ctx, goroot)

_go_host_sdk = repository_rule(
Expand All @@ -53,7 +54,6 @@ def _go_download_sdk_impl(ctx):
fail("goos set but goarch not set")
goos, goarch = ctx.attr.goos, ctx.attr.goarch
platform = goos + "_" + goarch
_sdk_build_file(ctx, platform)

version = ctx.attr.version
sdks = ctx.attr.sdks
Expand Down Expand Up @@ -100,6 +100,9 @@ def _go_download_sdk_impl(ctx):
filename, sha256 = sdks[platform]
_remote_sdk(ctx, [url.format(filename) for url in ctx.attr.urls], ctx.attr.strip_prefix, sha256)

detected_version = _detect_sdk_version(ctx, ".")
_sdk_build_file(ctx, platform, detected_version)

if not ctx.attr.sdks and not ctx.attr.version:
# Returning this makes Bazel print a message that 'version' must be
# specified for a reproducible build.
Expand Down Expand Up @@ -134,7 +137,8 @@ def go_download_sdk(name, register_toolchains = True, **kwargs):
def _go_local_sdk_impl(ctx):
goroot = ctx.attr.path
platform = _detect_sdk_platform(ctx, goroot)
_sdk_build_file(ctx, platform)
version = _detect_sdk_version(ctx, goroot)
_sdk_build_file(ctx, platform, version)
_local_sdk(ctx, goroot)

_go_local_sdk = repository_rule(
Expand Down Expand Up @@ -164,7 +168,8 @@ def _go_wrap_sdk_impl(ctx):
root_file = Label(ctx.attr.root_files[platform])
goroot = str(ctx.path(root_file).dirname)
platform = _detect_sdk_platform(ctx, goroot)
_sdk_build_file(ctx, platform)
version = _detect_sdk_version(ctx, goroot)
_sdk_build_file(ctx, platform, version)
_local_sdk(ctx, goroot)

_go_wrap_sdk = repository_rule(
Expand Down Expand Up @@ -226,9 +231,16 @@ def _local_sdk(ctx, path):
for entry in ["src", "pkg", "bin", "lib"]:
ctx.symlink(path + "/" + entry, entry)

def _sdk_build_file(ctx, platform):
def _sdk_build_file(ctx, platform, version):
ctx.file("ROOT")
goos, _, goarch = platform.partition("_")

pv = _parse_version(version)
if pv == None or len(pv) < 3:
fail("error parsing sdk version: " + version)
major, minor, patch = pv[0], pv[1], pv[2]
prerelease = pv[3] if len(pv) > 3 else ""

ctx.template(
"BUILD.bazel",
Label("//go/private:BUILD.sdk.bazel"),
Expand All @@ -238,6 +250,10 @@ def _sdk_build_file(ctx, platform):
"{goarch}": goarch,
"{exe}": ".exe" if goos == "windows" else "",
"{rules_go_repo_name}": "io_bazel_rules_go",
"{major_version}": str(major),
"{minor_version}": str(minor),
"{patch_version}": str(patch),
"{prerelease_suffix}": prerelease,
},
)

Expand Down Expand Up @@ -321,6 +337,16 @@ def _detect_sdk_platform(ctx, goroot):
fail("Could not detect SDK platform: found multiple platforms %s in %s" % (platforms, path))
return platforms[0]

def _detect_sdk_version(ctx, goroot):
path = goroot + "/VERSION"
version_contents = ctx.read(path)

# VERSION file has version prefixed by go, eg. go1.18.3
version = version_contents[2:]
if _parse_version(version) == None:
fail("Could not parse SDK version from version file (%s): %s" % (path, version_contents))
return version

def _parse_versions_json(data):
"""Parses version metadata returned by golang.org.
Expand Down
6 changes: 6 additions & 0 deletions go/toolchain/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
load(
":toolchains.bzl",
"declare_constraints",
Expand All @@ -8,6 +9,11 @@ package(default_visibility = ["//visibility:public"])

declare_constraints()

string_flag(
name = "sdk_version",
build_setting_default = "",
)

filegroup(
name = "all_rules",
srcs = glob(["*.bzl"]),
Expand Down
10 changes: 10 additions & 0 deletions go/toolchains.rst
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@ SDKs are specific to a host platform (e.g., ``linux_amd64``) and a version of
Go. They may target all platforms that Go supports. The Go SDK is naturally
cross compiling.

By default, all ``go_binary``, ``go_test``, etc. rules will use the first declared
Go SDK. If you would like to build a target using a specific Go SDK version, first
ensure that you have declared a Go SDK of that version using one of the above rules
(`go_download_sdk`_, `go_host_sdk`_, `go_local_sdk`_, `go_wrap_sdk`_). Then you
can specify the sdk version to build with when running a ``bazel build`` by passing
the flag ``--@io_bazel_rules_go//go/toolchain:sdk_version="version"`` where
``"version"`` is the SDK version you would like to build with, eg. ``"1.18.3"``.
The SDK version can omit the patch, or include a prerelease part, eg. ``"1"``,
``"1.18"``, ``"1.18.0"``, and ``"1.19beta1"`` are all valid values for ``sdk_version``.

The toolchain
~~~~~~~~~~~~~

Expand Down
47 changes: 42 additions & 5 deletions tests/core/go_download_sdk/go_download_sdk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ func Test(t *testing.T) {

func Test(t *testing.T) {
for _, test := range []struct {
desc, rule, wantVersion string
desc, rule string
optToWantVersion map[string]string
}{
{
desc: "version",
Expand All @@ -68,7 +69,7 @@ go_download_sdk(
)
`,
wantVersion: "go1.16",
optToWantVersion: map[string]string{"": "go1.16"},
}, {
desc: "custom_archives",
rule: `
Expand All @@ -84,7 +85,33 @@ go_download_sdk(
},
)
`,
wantVersion: "go1.16",
optToWantVersion: map[string]string{"": "go1.16"},
},
{
desc: "multiple_sdks",
rule: `
load("@io_bazel_rules_go//go:deps.bzl", "go_download_sdk")
go_download_sdk(
name = "go_sdk",
version = "1.16",
)
go_download_sdk(
name = "go_sdk_1_17",
version = "1.17",
)
go_download_sdk(
name = "go_sdk_1_17_1",
version = "1.17.1",
)
`,
optToWantVersion: map[string]string{
"": "go1.16",
"--@io_bazel_rules_go//go/toolchain:sdk_version=1": "go1.16",
"--@io_bazel_rules_go//go/toolchain:sdk_version=1.17": "go1.17",
"--@io_bazel_rules_go//go/toolchain:sdk_version=1.17.0": "go1.17",
"--@io_bazel_rules_go//go/toolchain:sdk_version=1.17.1": "go1.17.1",
},
},
} {
t.Run(test.desc, func(t *testing.T) {
Expand Down Expand Up @@ -115,8 +142,18 @@ go_register_toolchains()
}
}()

if err := bazel_testing.RunBazel("test", "//:version_test", "--test_arg=-version="+test.wantVersion); err != nil {
t.Fatal(err)
for opt, wantVersion := range test.optToWantVersion {
args := []string{
"test",
"//:version_test",
"--test_arg=-version=" + wantVersion,
}
if opt != "" {
args = append(args, opt)
}
if err := bazel_testing.RunBazel(args...); err != nil {
t.Fatal(err)
}
}
})
}
Expand Down

0 comments on commit 4b7e1d9

Please sign in to comment.