diff options
author | Fabian Meumertzheim <fabian@meumertzhe.im> | 2023-01-25 10:55:26 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-01-25 10:55:26 +0100 |
commit | fe6975120bcaf6d43d367e4380d95f724e2cbba8 (patch) | |
tree | 348a34ec2213c1969f381a59f66d5da827ea0e91 | |
parent | cf78385a58e278b542511d246bb1cef287d528e9 (diff) | |
download | bazelbuild-rules_go-fe6975120bcaf6d43d367e4380d95f724e2cbba8.tar.gz |
Make the toolchain's `go` binary available as a target (#3429)
This allows developers to use the `go` command provided by the
registered toolchain to e.g. add dependencies to `go.mod`. This ensures
that everyone uses the same version of Go and does not require a local
installation of Go.
-rw-r--r-- | .bazelci/presubmit.yml | 2 | ||||
-rw-r--r-- | README.rst | 13 | ||||
-rw-r--r-- | go/BUILD.bazel | 7 | ||||
-rw-r--r-- | go/private/common.bzl | 2 | ||||
-rw-r--r-- | go/private/rules/go_bin_for_host.bzl | 35 | ||||
-rw-r--r-- | go/tools/BUILD.bazel | 1 | ||||
-rw-r--r-- | go/tools/go_bin_runner/BUILD.bazel | 44 | ||||
-rw-r--r-- | go/tools/go_bin_runner/main.go | 40 | ||||
-rw-r--r-- | go/tools/go_bin_runner/process.go | 22 | ||||
-rw-r--r-- | go/tools/go_bin_runner/process_unix.go | 11 | ||||
-rw-r--r-- | tests/integration/go_bin_runner/BUILD.bazel | 6 | ||||
-rw-r--r-- | tests/integration/go_bin_runner/go_bin_runner_test.go | 49 |
12 files changed, 229 insertions, 3 deletions
diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index 485f2057..a4e36cf4 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -2,7 +2,7 @@ tasks: ubuntu1804_bazel400: platform: ubuntu1804 - bazel: 5.3.0 # test minimum supported version of bazel + bazel: 5.4.0 # test minimum supported version of bazel shell_commands: - tests/core/cgo/generate_imported_dylib.sh build_targets: @@ -193,7 +193,7 @@ The Go rules are tested and supported on the following host platforms: Users have reported success on several other platforms, but the rules are only tested on those listed above. -Note: Since version v0.38.0, rules_go requires Bazel ≥ 5.3.0 to work. +Note: Since version v0.38.0, rules_go requires Bazel ≥ 5.4.0 to work. The ``master`` branch is only guaranteed to work with the latest version of Bazel. @@ -551,6 +551,17 @@ the ``go`` command (e.g., one package per directory, package paths match directory paths). Tools that aren't compatible with Bazel will still work, and your project can be depended on by non-Bazel projects. +If you need to use the ``go`` command to perform tasks that Bazel doesn't cover +(such as adding a new dependency to ``go.mod``), you can use the following Bazel +invocation to run the ``go`` binary of the Bazel-configured Go SDK: + +.. code:: bash + + bazel run @io_bazel_rules_go//go -- <args> + +Prefer this to running ``go`` directly since it ensures that the version of Go +is identical to the one used by rules_go. + Does this work with Go modules? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/go/BUILD.bazel b/go/BUILD.bazel index 08c119f4..0900ad5d 100644 --- a/go/BUILD.bazel +++ b/go/BUILD.bazel @@ -1,5 +1,12 @@ load("@bazel_skylib//:bzl_library.bzl", "bzl_library") +alias( + name = "go", + actual = "//go/tools/go_bin_runner", + # Meant to be run with `bazel run`, but should not be depended on. + visibility = ["//visibility:private"], +) + filegroup( name = "all_files", testonly = True, diff --git a/go/private/common.bzl b/go/private/common.bzl index a3cd8678..e47ebd18 100644 --- a/go/private/common.bzl +++ b/go/private/common.bzl @@ -169,7 +169,7 @@ def get_versioned_shared_lib_extension(path): # something like 1.2.3, or so.1.2, or dylib.1.2, or foo.1.2 return "" -MINIMUM_BAZEL_VERSION = "5.3.0" +MINIMUM_BAZEL_VERSION = "5.4.0" def as_list(v): """Returns a list, tuple, or depset as a list.""" diff --git a/go/private/rules/go_bin_for_host.bzl b/go/private/rules/go_bin_for_host.bzl new file mode 100644 index 00000000..a6fd9713 --- /dev/null +++ b/go/private/rules/go_bin_for_host.bzl @@ -0,0 +1,35 @@ +# Copyright 2023 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load("@local_config_platform//:constraints.bzl", "HOST_CONSTRAINTS") +load("//go/private:go_toolchain.bzl", "GO_TOOLCHAIN") + +def _go_bin_for_host_impl(ctx): + """Exposes the go binary of the current Go toolchain for the host.""" + sdk = ctx.toolchains[GO_TOOLCHAIN].sdk + sdk_files = ctx.runfiles([sdk.go] + sdk.headers + sdk.libs + sdk.srcs + sdk.tools) + + return [ + DefaultInfo( + files = depset([sdk.go]), + runfiles = sdk_files, + ), + ] + +go_bin_for_host = rule( + implementation = _go_bin_for_host_impl, + toolchains = [GO_TOOLCHAIN], + # Resolve a toolchain that runs on the host platform. + exec_compatible_with = HOST_CONSTRAINTS, +) diff --git a/go/tools/BUILD.bazel b/go/tools/BUILD.bazel index 51867d56..50b87d74 100644 --- a/go/tools/BUILD.bazel +++ b/go/tools/BUILD.bazel @@ -7,6 +7,7 @@ filegroup( "//go/tools/builders:all_files", "//go/tools/bzltestutil:all_files", "//go/tools/coverdata:all_files", + "//go/tools/go_bin_runner:all_files", ], visibility = ["//visibility:public"], ) diff --git a/go/tools/go_bin_runner/BUILD.bazel b/go/tools/go_bin_runner/BUILD.bazel new file mode 100644 index 00000000..0e348208 --- /dev/null +++ b/go/tools/go_bin_runner/BUILD.bazel @@ -0,0 +1,44 @@ +# gazelle:exclude + +load("//go:def.bzl", "go_binary", "go_library") +load("//go/private/rules:go_bin_for_host.bzl", "go_bin_for_host") + +go_bin_for_host( + name = "go_bin_for_host", + visibility = ["//go:__pkg__"], +) + +go_library( + name = "go_bin_runner_lib", + srcs = [ + "main.go", + ] + select({ + "@platforms//os:windows": ["process.go"], + "//conditions:default": ["process_unix.go"], + }), + importpath = "github.com/bazelbuild/rules_go/go/tools/go_bin_runner", + visibility = ["//visibility:private"], + deps = [ + "//go/runfiles", + ] + select({ + "@platforms//os:windows": [], + "//conditions:default": ["@org_golang_x_sys//unix"], + }), +) + +go_binary( + name = "go_bin_runner", + data = [":go_bin_for_host"], + embed = [":go_bin_runner_lib"], + env = { + "GO_BIN_RLOCATIONPATH": "$(rlocationpath :go_bin_for_host)", + }, + visibility = ["//visibility:public"], +) + +filegroup( + name = "all_files", + testonly = True, + srcs = glob(["**"]), + visibility = ["//visibility:public"], +) diff --git a/go/tools/go_bin_runner/main.go b/go/tools/go_bin_runner/main.go new file mode 100644 index 00000000..5fe4fd94 --- /dev/null +++ b/go/tools/go_bin_runner/main.go @@ -0,0 +1,40 @@ +package main + +import ( + "log" + "os" + "path/filepath" + "strings" + + "github.com/bazelbuild/rules_go/go/runfiles" +) + +func main() { + goBinRlocation := os.Getenv("GO_BIN_RLOCATIONPATH") + goBin, err := runfiles.Rlocation(goBinRlocation) + if err != nil { + log.Fatal(err) + } + // The go binary lies at $GOROOT/bin/go. + goRoot, err := filepath.Abs(filepath.Dir(filepath.Dir(goBin))) + if err != nil { + log.Fatal(err) + } + + env := os.Environ() + var filteredEnv []string + for i := 0; i < len(env); i++ { + if !strings.HasPrefix(env[i], "GOROOT=") { + filteredEnv = append(filteredEnv, env[i]) + } + } + filteredEnv = append(filteredEnv, "GOROOT="+goRoot) + + err = os.Chdir(os.Getenv("BUILD_WORKING_DIRECTORY")) + if err != nil { + log.Fatal(err) + } + + args := append([]string{goBin}, os.Args[1:]...) + log.Fatal(ReplaceWithProcess(args, filteredEnv)) +} diff --git a/go/tools/go_bin_runner/process.go b/go/tools/go_bin_runner/process.go new file mode 100644 index 00000000..8b344c96 --- /dev/null +++ b/go/tools/go_bin_runner/process.go @@ -0,0 +1,22 @@ +//go:build !unix + +package main + +import ( + "os" + "os/exec" +) + +func ReplaceWithProcess(args, env []string) error { + cmd := exec.Command(args[0], args[1:]...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Env = env + err := cmd.Run() + if exitErr, ok := err.(*exec.ExitError); ok { + os.Exit(exitErr.ExitCode()) + } else if err == nil { + os.Exit(0) + } + return err +} diff --git a/go/tools/go_bin_runner/process_unix.go b/go/tools/go_bin_runner/process_unix.go new file mode 100644 index 00000000..f59af5d2 --- /dev/null +++ b/go/tools/go_bin_runner/process_unix.go @@ -0,0 +1,11 @@ +//go:build unix + +package main + +import ( + "golang.org/x/sys/unix" +) + +func ReplaceWithProcess(args, env []string) error { + return unix.Exec(args[0], args, env) +} diff --git a/tests/integration/go_bin_runner/BUILD.bazel b/tests/integration/go_bin_runner/BUILD.bazel new file mode 100644 index 00000000..e7db9531 --- /dev/null +++ b/tests/integration/go_bin_runner/BUILD.bazel @@ -0,0 +1,6 @@ +load("//go/tools/bazel_testing:def.bzl", "go_bazel_test") + +go_bazel_test( + name = "go_bin_runner_test", + srcs = ["go_bin_runner_test.go"], +) diff --git a/tests/integration/go_bin_runner/go_bin_runner_test.go b/tests/integration/go_bin_runner/go_bin_runner_test.go new file mode 100644 index 00000000..cb238f7b --- /dev/null +++ b/tests/integration/go_bin_runner/go_bin_runner_test.go @@ -0,0 +1,49 @@ +// Copyright 2023 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package go_bin_runner_test + +import ( + "os" + "path/filepath" + "strings" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{}) +} + +func TestGoEnv(t *testing.T) { + // Set an invalid GOROOT to test that the //go target still finds the expected hermetic GOROOT. + os.Setenv("GOROOT", "invalid") + + bazelInfoOut, err := bazel_testing.BazelOutput("info", "output_base") + if err != nil { + t.Fatal(err) + } + outputBase := strings.TrimSpace(string(bazelInfoOut)) + + goEnvOut, err := bazel_testing.BazelOutput("run", "@io_bazel_rules_go//go", "--", "env", "GOROOT") + if err != nil { + t.Fatal(err) + } + + goRoot := strings.TrimSpace(string(goEnvOut)) + if goRoot != filepath.Join(outputBase, "external", "go_sdk") { + t.Fatalf("GOROOT was not equal to %s", filepath.Join(outputBase, "external", "go_sdk")) + } +} |