aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFabian Meumertzheim <fabian@meumertzhe.im>2023-01-25 10:55:26 +0100
committerGitHub <noreply@github.com>2023-01-25 10:55:26 +0100
commitfe6975120bcaf6d43d367e4380d95f724e2cbba8 (patch)
tree348a34ec2213c1969f381a59f66d5da827ea0e91
parentcf78385a58e278b542511d246bb1cef287d528e9 (diff)
downloadbazelbuild-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.yml2
-rw-r--r--README.rst13
-rw-r--r--go/BUILD.bazel7
-rw-r--r--go/private/common.bzl2
-rw-r--r--go/private/rules/go_bin_for_host.bzl35
-rw-r--r--go/tools/BUILD.bazel1
-rw-r--r--go/tools/go_bin_runner/BUILD.bazel44
-rw-r--r--go/tools/go_bin_runner/main.go40
-rw-r--r--go/tools/go_bin_runner/process.go22
-rw-r--r--go/tools/go_bin_runner/process_unix.go11
-rw-r--r--tests/integration/go_bin_runner/BUILD.bazel6
-rw-r--r--tests/integration/go_bin_runner/go_bin_runner_test.go49
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:
diff --git a/README.rst b/README.rst
index 92fc5e0f..062c76c2 100644
--- a/README.rst
+++ b/README.rst
@@ -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"))
+ }
+}