From 4b7e1d9d4237b612c68365631eb0b75503f40d30 Mon Sep 17 00:00:00 2001 From: James Bartlett Date: Thu, 23 Jun 2022 16:37:37 -0700 Subject: [PATCH] Add support for --@io_bazel_rules_go//go/toolchain:sdk_version flag. - 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 --- go/private/BUILD.sdk.bazel | 51 +++++++++++++++++++ go/private/go_toolchain.bzl | 3 +- go/private/sdk.bzl | 36 +++++++++++-- go/toolchain/BUILD.bazel | 6 +++ go/toolchains.rst | 10 ++++ .../go_download_sdk/go_download_sdk_test.go | 47 +++++++++++++++-- 6 files changed, 142 insertions(+), 11 deletions(-) diff --git a/go/private/BUILD.sdk.bazel b/go/private/BUILD.sdk.bazel index 50d9127df1..4d2676913b 100644 --- a/go/private/BUILD.sdk.bazel +++ b/go/private/BUILD.sdk.bazel @@ -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"]) @@ -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( diff --git a/go/private/go_toolchain.bzl b/go/private/go_toolchain.bzl index 9a364815a0..142b413710 100644 --- a/go/private/go_toolchain.bzl +++ b/go/private/go_toolchain.bzl @@ -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 @@ -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, ) diff --git a/go/private/sdk.bzl b/go/private/sdk.bzl index ee53cb0b8a..1e105e40d3 100644 --- a/go/private/sdk.bzl +++ b/go/private/sdk.bzl @@ -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( @@ -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 @@ -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. @@ -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( @@ -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( @@ -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"), @@ -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, }, ) @@ -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. diff --git a/go/toolchain/BUILD.bazel b/go/toolchain/BUILD.bazel index b5e33a6998..ba47e90500 100644 --- a/go/toolchain/BUILD.bazel +++ b/go/toolchain/BUILD.bazel @@ -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", @@ -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"]), diff --git a/go/toolchains.rst b/go/toolchains.rst index 2358249bcb..30bf5b2d02 100644 --- a/go/toolchains.rst +++ b/go/toolchains.rst @@ -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 ~~~~~~~~~~~~~ diff --git a/tests/core/go_download_sdk/go_download_sdk_test.go b/tests/core/go_download_sdk/go_download_sdk_test.go index 250d054e8c..51dd5c0ae4 100644 --- a/tests/core/go_download_sdk/go_download_sdk_test.go +++ b/tests/core/go_download_sdk/go_download_sdk_test.go @@ -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", @@ -68,7 +69,7 @@ go_download_sdk( ) `, - wantVersion: "go1.16", + optToWantVersion: map[string]string{"": "go1.16"}, }, { desc: "custom_archives", rule: ` @@ -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) { @@ -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) + } } }) }