From 4feb7b8fe2a047e23c6327659896a2637cd0ca38 Mon Sep 17 00:00:00 2001
From: Marat Reymers <16486128+maratori@users.noreply.github.com>
Date: Sun, 18 Aug 2024 02:41:46 +0200
Subject: [PATCH 1/7] gomock: support uber's `-typed` flag (#4017)
**What type of PR is this?**
Feature
**What does this PR do? Why is it needed?**
The PR adds support for `-typed` flag in Uber's
[gomock](https://github.com/uber-go/mock?tab=readme-ov-file#flags).
**Which issues(s) does this PR fix?**
Fixes #4012
**Other notes for review**
I can add tests, but it requires adding a new dependency.
---
docs/go/extras/extras.md | 3 ++-
extras/gomock.bzl | 17 +++++++++++++++--
2 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/docs/go/extras/extras.md b/docs/go/extras/extras.md
index b3eb5be1df..a0a76c823d 100644
--- a/docs/go/extras/extras.md
+++ b/docs/go/extras/extras.md
@@ -34,7 +34,7 @@ This rule has moved. See [gazelle rule] in the Gazelle repository.
gomock(name, out, library, source_importpath, source, interfaces, package, self_package, aux_files,
- mockgen_tool, imports, copyright_file, mock_names, kwargs)
+ mockgen_tool, imports, copyright_file, mock_names, typed, kwargs)
Calls [mockgen](https://github.com/golang/mock) to generates a Go file containing mocks from the given library.
@@ -60,6 +60,7 @@ If `source` is given, the mocks are generated in source mode; otherwise in refle
| imports | dictionary of name-path pairs of explicit imports to use. See [mockgen's -imports](https://github.com/golang/mock#flags) for more information. | {}
|
| copyright_file | optional file containing copyright to prepend to the generated contents. See [mockgen's -copyright_file](https://github.com/golang/mock#flags) for more information. | None
|
| mock_names | dictionary of interface name to mock name pairs to change the output names of the mock objects. Mock names default to 'Mock' prepended to the name of the interface. See [mockgen's -mock_names](https://github.com/golang/mock#flags) for more information. | {}
|
+| typed | generate type-safe 'Return', 'Do', 'DoAndReturn' functions. See [mockgen's -typed](https://github.com/uber-go/mock#flags) for more information. | False
|
| kwargs | -
| none |
diff --git a/extras/gomock.bzl b/extras/gomock.bzl
index 2711ccb479..54e962a180 100644
--- a/extras/gomock.bzl
+++ b/extras/gomock.bzl
@@ -152,6 +152,10 @@ _gomock_source = rule(
allow_single_file = True,
mandatory = False,
),
+ "typed": attr.bool(
+ doc = "Generate type-safe 'Return', 'Do', 'DoAndReturn' functions.",
+ mandatory = False,
+ ),
"mockgen_tool": attr.label(
doc = "The mockgen tool to run",
default = _MOCKGEN_TOOL,
@@ -167,7 +171,7 @@ _gomock_source = rule(
toolchains = [GO_TOOLCHAIN],
)
-def gomock(name, out, library = None, source_importpath = "", source = None, interfaces = [], package = "", self_package = "", aux_files = {}, mockgen_tool = _MOCKGEN_TOOL, imports = {}, copyright_file = None, mock_names = {}, **kwargs):
+def gomock(name, out, library = None, source_importpath = "", source = None, interfaces = [], package = "", self_package = "", aux_files = {}, mockgen_tool = _MOCKGEN_TOOL, imports = {}, copyright_file = None, mock_names = {}, typed = False, **kwargs):
"""Calls [mockgen](https://github.com/golang/mock) to generates a Go file containing mocks from the given library.
If `source` is given, the mocks are generated in source mode; otherwise in reflective mode.
@@ -186,7 +190,8 @@ def gomock(name, out, library = None, source_importpath = "", source = None, int
imports: dictionary of name-path pairs of explicit imports to use. See [mockgen's -imports](https://github.com/golang/mock#flags) for more information.
copyright_file: optional file containing copyright to prepend to the generated contents. See [mockgen's -copyright_file](https://github.com/golang/mock#flags) for more information.
mock_names: dictionary of interface name to mock name pairs to change the output names of the mock objects. Mock names default to 'Mock' prepended to the name of the interface. See [mockgen's -mock_names](https://github.com/golang/mock#flags) for more information.
- kwargs: common attributes](https://bazel.build/reference/be/common-definitions#common-attributes) to all Bazel rules.
+ typed: generate type-safe 'Return', 'Do', 'DoAndReturn' functions. See [mockgen's -typed](https://github.com/uber-go/mock#flags) for more information.
+ kwargs: [common attributes](https://bazel.build/reference/be/common-definitions#common-attributes) to all Bazel rules.
"""
if source:
_gomock_source(
@@ -202,6 +207,7 @@ def gomock(name, out, library = None, source_importpath = "", source = None, int
imports = imports,
copyright_file = copyright_file,
mock_names = mock_names,
+ typed = typed,
**kwargs
)
else:
@@ -216,6 +222,7 @@ def gomock(name, out, library = None, source_importpath = "", source = None, int
imports = imports,
copyright_file = copyright_file,
mock_names = mock_names,
+ typed = typed,
**kwargs
)
@@ -360,6 +367,10 @@ _gomock_prog_exec = rule(
allow_single_file = True,
mandatory = False,
),
+ "typed": attr.bool(
+ doc = "Generate type-safe 'Return', 'Do', 'DoAndReturn' functions.",
+ mandatory = False,
+ ),
"prog_bin": attr.label(
doc = "The program binary generated by mockgen's -prog_only and compiled by bazel.",
allow_single_file = True,
@@ -398,5 +409,7 @@ def _handle_shared_args(ctx, args):
if len(ctx.attr.mock_names) > 0:
mock_names = ",".join(["{0}={1}".format(name, pkg) for name, pkg in ctx.attr.mock_names.items()])
args += ["-mock_names", mock_names]
+ if ctx.attr.typed:
+ args += ["-typed"]
return args, needed_files
From 552592ddf495cece93530b1f1e79b4beed675ecc Mon Sep 17 00:00:00 2001
From: Walter Cacau
Date: Sun, 18 Aug 2024 08:23:07 -0700
Subject: [PATCH 2/7] Making gopackagesdriver correctly handle relative queries
made from a subdirectory. (#4002)
**What type of PR is this?**
Bug fix
**What does this PR do? Why is it needed?**
It allows gopackagesdriver to be called within a subdirectory of a
workspace and properly resolve queries from gopls from IDEs like VSCode
when they include relative references. For example, if you have the
following repo/workspace structure:
```
WORKSPACE
project1/BUILD.bazel
project1/main.go
project2/BUILD.bazel
project2/main.go
...
```
If you run gopackagesdriver using bazel run from project1 directory and
receive the following queries:
```
file=./main.go
./...
```
The existing code will try to resolve those queries against the root of
the workspace, when instead we would expect it to resolve relative to
the project1 directory.
**Which issues(s) does this PR fix?**
Fixes #4001
**Other notes for review**
---
go/tools/gopackagesdriver/bazel.go | 24 ++--
.../gopackagesdriver/bazel_json_builder.go | 37 +++---
.../gopackagesdriver/gopackagesdriver_test.go | 107 +++++++++++++++++-
go/tools/gopackagesdriver/main.go | 3 +-
4 files changed, 140 insertions(+), 31 deletions(-)
diff --git a/go/tools/gopackagesdriver/bazel.go b/go/tools/gopackagesdriver/bazel.go
index 554c2af596..a88a60dcea 100644
--- a/go/tools/gopackagesdriver/bazel.go
+++ b/go/tools/gopackagesdriver/bazel.go
@@ -35,11 +35,12 @@ const (
)
type Bazel struct {
- bazelBin string
- workspaceRoot string
- bazelStartupFlags []string
- info map[string]string
- version bazelVersion
+ bazelBin string
+ workspaceRoot string
+ buildWorkingDirectory string
+ bazelStartupFlags []string
+ info map[string]string
+ version bazelVersion
}
// Minimal BEP structs to access the build outputs
@@ -52,11 +53,12 @@ type BEPNamedSet struct {
} `json:"namedSetOfFiles"`
}
-func NewBazel(ctx context.Context, bazelBin, workspaceRoot string, bazelStartupFlags []string) (*Bazel, error) {
+func NewBazel(ctx context.Context, bazelBin, workspaceRoot string, buildWorkingDirectory string, bazelStartupFlags []string) (*Bazel, error) {
b := &Bazel{
- bazelBin: bazelBin,
- workspaceRoot: workspaceRoot,
- bazelStartupFlags: bazelStartupFlags,
+ bazelBin: bazelBin,
+ workspaceRoot: workspaceRoot,
+ buildWorkingDirectory: buildWorkingDirectory,
+ bazelStartupFlags: bazelStartupFlags,
}
if err := b.fillInfo(ctx); err != nil {
return nil, fmt.Errorf("unable to query bazel info: %w", err)
@@ -163,6 +165,10 @@ func (b *Bazel) WorkspaceRoot() string {
return b.workspaceRoot
}
+func (b *Bazel) BuildWorkingDirectory() string {
+ return b.buildWorkingDirectory
+}
+
func (b *Bazel) ExecutionRoot() string {
return b.info["execution_root"]
}
diff --git a/go/tools/gopackagesdriver/bazel_json_builder.go b/go/tools/gopackagesdriver/bazel_json_builder.go
index f36108ce4b..a86c396011 100644
--- a/go/tools/gopackagesdriver/bazel_json_builder.go
+++ b/go/tools/gopackagesdriver/bazel_json_builder.go
@@ -21,7 +21,6 @@ import (
"fmt"
"io/ioutil"
"os"
- "path"
"path/filepath"
"regexp"
"runtime"
@@ -39,14 +38,9 @@ var _defaultKinds = []string{"go_library", "go_test", "go_binary"}
var externalRe = regexp.MustCompile(".*\\/external\\/([^\\/]+)(\\/(.*))?\\/([^\\/]+.go)")
-func (b *BazelJSONBuilder) fileQuery(filename string) string {
- label := filename
-
- if filepath.IsAbs(filename) {
- label, _ = filepath.Rel(b.bazel.WorkspaceRoot(), filename)
- } else if strings.HasPrefix(filename, "./") {
- label = strings.TrimPrefix(filename, "./")
- }
+func (b *BazelJSONBuilder) fileQuery(label string) string {
+ label = b.adjustToRelativePathIfPossible(label)
+ filename := filepath.FromSlash(label)
if matches := externalRe.FindStringSubmatch(filename); len(matches) == 5 {
// if filepath is for a third party lib, we need to know, what external
@@ -56,7 +50,7 @@ func (b *BazelJSONBuilder) fileQuery(filename string) string {
}
relToBin, err := filepath.Rel(b.bazel.info["output_path"], filename)
- if err == nil && !strings.HasPrefix(relToBin, "../") {
+ if err == nil && !strings.HasPrefix(relToBin, filepath.FromSlash("../")) {
parts := strings.SplitN(relToBin, string(filepath.Separator), 3)
relToBin = parts[2]
// We've effectively converted filename from bazel-bin/some/path.go to some/path.go;
@@ -93,12 +87,7 @@ func (b *BazelJSONBuilder) getKind() string {
}
func (b *BazelJSONBuilder) localQuery(request string) string {
- request = path.Clean(request)
- if filepath.IsAbs(request) {
- if relPath, err := filepath.Rel(workspaceRoot, request); err == nil {
- request = relPath
- }
- }
+ request = b.adjustToRelativePathIfPossible(request)
if !strings.HasSuffix(request, "...") {
request = fmt.Sprintf("%s:*", request)
@@ -107,6 +96,22 @@ func (b *BazelJSONBuilder) localQuery(request string) string {
return fmt.Sprintf(`kind("^(%s) rule$", %s)`, b.getKind(), request)
}
+func (b *BazelJSONBuilder) adjustToRelativePathIfPossible(request string) string {
+ // If request is a relative path and gopackagesdriver is ran within a subdirectory of the
+ // workspace, we must first resolve the absolute path for it.
+ // Note: Using FromSlash/ToSlash for handling windows
+ absRequest := filepath.FromSlash(request)
+ if !filepath.IsAbs(absRequest) {
+ absRequest = filepath.Join(b.bazel.BuildWorkingDirectory(), absRequest)
+ }
+ if relPath, err := filepath.Rel(workspaceRoot, absRequest); err == nil {
+ request = filepath.ToSlash(relPath)
+ } else {
+ fmt.Fprintf(os.Stderr, "error adjusting path to be relative to the workspace root from request %s: %v\n", request, err)
+ }
+ return request
+}
+
func (b *BazelJSONBuilder) packageQuery(importPath string) string {
if strings.HasSuffix(importPath, "/...") {
importPath = fmt.Sprintf(`^%s(/.+)?$`, strings.TrimSuffix(importPath, "/..."))
diff --git a/go/tools/gopackagesdriver/gopackagesdriver_test.go b/go/tools/gopackagesdriver/gopackagesdriver_test.go
index 3d61ab96ae..47d2ce6ce7 100644
--- a/go/tools/gopackagesdriver/gopackagesdriver_test.go
+++ b/go/tools/gopackagesdriver/gopackagesdriver_test.go
@@ -6,6 +6,7 @@ import (
"encoding/json"
"os"
"path"
+ "path/filepath"
"strings"
"testing"
@@ -61,6 +62,25 @@ package hello_test
import "testing"
func TestHelloExternal(t *testing.T) {}
+
+-- subhello/BUILD.bazel --
+load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
+
+go_library(
+ name = "subhello",
+ srcs = ["subhello.go"],
+ importpath = "example.com/hello/subhello",
+ visibility = ["//visibility:public"],
+)
+
+-- subhello/subhello.go --
+package subhello
+
+import "os"
+
+func main() {
+ fmt.Fprintln(os.Stderr, "Subdirectory Hello World!")
+}
`,
})
}
@@ -71,7 +91,7 @@ const (
)
func TestBaseFileLookup(t *testing.T) {
- resp := runForTest(t, "file=hello.go")
+ resp := runForTest(t, ".", "file=hello.go")
t.Run("roots", func(t *testing.T) {
if len(resp.Roots) != 1 {
@@ -140,8 +160,80 @@ func TestBaseFileLookup(t *testing.T) {
})
}
+func TestRelativeFileLookup(t *testing.T) {
+ resp := runForTest(t, "subhello", "file=./subhello.go")
+
+ t.Run("roots", func(t *testing.T) {
+ if len(resp.Roots) != 1 {
+ t.Errorf("Expected 1 package root: %+v", resp.Roots)
+ return
+ }
+
+ if !strings.HasSuffix(resp.Roots[0], "//subhello:subhello") {
+ t.Errorf("Unexpected package id: %q", resp.Roots[0])
+ return
+ }
+ })
+
+ t.Run("package", func(t *testing.T) {
+ var pkg *FlatPackage
+ for _, p := range resp.Packages {
+ if p.ID == resp.Roots[0] {
+ pkg = p
+ }
+ }
+
+ if pkg == nil {
+ t.Errorf("Expected to find %q in resp.Packages", resp.Roots[0])
+ return
+ }
+
+ if len(pkg.CompiledGoFiles) != 1 || len(pkg.GoFiles) != 1 ||
+ path.Base(pkg.GoFiles[0]) != "subhello.go" || path.Base(pkg.CompiledGoFiles[0]) != "subhello.go" {
+ t.Errorf("Expected to find 1 file (subhello.go) in (Compiled)GoFiles:\n%+v", pkg)
+ return
+ }
+ })
+}
+
+func TestRelativePatternWildcardLookup(t *testing.T) {
+ resp := runForTest(t, "subhello", "./...")
+
+ t.Run("roots", func(t *testing.T) {
+ if len(resp.Roots) != 1 {
+ t.Errorf("Expected 1 package root: %+v", resp.Roots)
+ return
+ }
+
+ if !strings.HasSuffix(resp.Roots[0], "//subhello:subhello") {
+ t.Errorf("Unexpected package id: %q", resp.Roots[0])
+ return
+ }
+ })
+
+ t.Run("package", func(t *testing.T) {
+ var pkg *FlatPackage
+ for _, p := range resp.Packages {
+ if p.ID == resp.Roots[0] {
+ pkg = p
+ }
+ }
+
+ if pkg == nil {
+ t.Errorf("Expected to find %q in resp.Packages", resp.Roots[0])
+ return
+ }
+
+ if len(pkg.CompiledGoFiles) != 1 || len(pkg.GoFiles) != 1 ||
+ path.Base(pkg.GoFiles[0]) != "subhello.go" || path.Base(pkg.CompiledGoFiles[0]) != "subhello.go" {
+ t.Errorf("Expected to find 1 file (subhello.go) in (Compiled)GoFiles:\n%+v", pkg)
+ return
+ }
+ })
+}
+
func TestExternalTests(t *testing.T) {
- resp := runForTest(t, "file=hello_external_test.go")
+ resp := runForTest(t, ".", "file=hello_external_test.go")
if len(resp.Roots) != 2 {
t.Errorf("Expected exactly two roots for package: %+v", resp.Roots)
}
@@ -167,7 +259,7 @@ func TestExternalTests(t *testing.T) {
}
}
-func runForTest(t *testing.T, args ...string) driverResponse {
+func runForTest(t *testing.T, relativeWorkingDir string, args ...string) driverResponse {
t.Helper()
// Remove most environment variables, other than those on an allowlist.
@@ -210,7 +302,7 @@ func runForTest(t *testing.T, args ...string) driverResponse {
}
}()
- // Set workspaceRoot global variable.
+ // Set workspaceRoot and buildWorkingDirectory global variable.
// It's initialized to the BUILD_WORKSPACE_DIRECTORY environment variable
// before this point.
wd, err := os.Getwd()
@@ -218,8 +310,13 @@ func runForTest(t *testing.T, args ...string) driverResponse {
t.Fatal(err)
}
oldWorkspaceRoot := workspaceRoot
+ oldBuildWorkingDirectory := buildWorkingDirectory
workspaceRoot = wd
- defer func() { workspaceRoot = oldWorkspaceRoot }()
+ buildWorkingDirectory = filepath.Join(wd, relativeWorkingDir)
+ defer func() {
+ workspaceRoot = oldWorkspaceRoot
+ buildWorkingDirectory = oldBuildWorkingDirectory
+ }()
in := strings.NewReader("{}")
out := &bytes.Buffer{}
diff --git a/go/tools/gopackagesdriver/main.go b/go/tools/gopackagesdriver/main.go
index 0613a3be79..ea591f6f7c 100644
--- a/go/tools/gopackagesdriver/main.go
+++ b/go/tools/gopackagesdriver/main.go
@@ -62,6 +62,7 @@ var (
bazelQueryScope = getenvDefault("GOPACKAGESDRIVER_BAZEL_QUERY_SCOPE", "")
bazelBuildFlags = strings.Fields(os.Getenv("GOPACKAGESDRIVER_BAZEL_BUILD_FLAGS"))
workspaceRoot = os.Getenv("BUILD_WORKSPACE_DIRECTORY")
+ buildWorkingDirectory = os.Getenv("BUILD_WORKING_DIRECTORY")
additionalAspects = strings.Fields(os.Getenv("GOPACKAGESDRIVER_BAZEL_ADDTL_ASPECTS"))
additionalKinds = strings.Fields(os.Getenv("GOPACKAGESDRIVER_BAZEL_KINDS"))
emptyResponse = &driverResponse{
@@ -81,7 +82,7 @@ func run(ctx context.Context, in io.Reader, out io.Writer, args []string) error
return fmt.Errorf("unable to read request: %w", err)
}
- bazel, err := NewBazel(ctx, bazelBin, workspaceRoot, bazelStartupFlags)
+ bazel, err := NewBazel(ctx, bazelBin, workspaceRoot, buildWorkingDirectory, bazelStartupFlags)
if err != nil {
return fmt.Errorf("unable to create bazel instance: %w", err)
}
From 8e662663a818e35da17cb8d47d3da1ad558fb51c Mon Sep 17 00:00:00 2001
From: David Zbarsky
Date: Sun, 18 Aug 2024 16:31:27 -0400
Subject: [PATCH 3/7] Cleanup GoArchiveData usage of as_tuple (#4032)
**What type of PR is this?**
Starlark cleanup
**What does this PR do? Why is it needed?**
`as_tuple` was showing up in profiles, we can just call `tuple`
directly. This exposed that we were flattening a depset which isn't
necessary.
**Which issues(s) does this PR fix?**
Fixes #
**Other notes for review**
---
go/private/actions/archive.bzl | 22 +++++++++-------------
go/private/common.bzl | 10 ----------
go/private/rules/test.bzl | 2 +-
3 files changed, 10 insertions(+), 24 deletions(-)
diff --git a/go/private/actions/archive.bzl b/go/private/actions/archive.bzl
index 40e9d5d87e..8ee3e84392 100644
--- a/go/private/actions/archive.bzl
+++ b/go/private/actions/archive.bzl
@@ -12,10 +12,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-load(
- "//go/private:common.bzl",
- "as_tuple",
-)
load(
"//go/private:context.bzl",
"get_nogo",
@@ -170,17 +166,17 @@ def emit_archive(go, source = None, _recompile_suffix = "", recompile_internal_d
pathtype = source.library.pathtype,
# GoSource fields
- srcs = as_tuple(source.srcs),
+ srcs = tuple(source.srcs),
_cover = source.cover,
- _embedsrcs = as_tuple(source.embedsrcs),
+ _embedsrcs = tuple(source.embedsrcs),
_x_defs = tuple(source.x_defs.items()),
- _gc_goopts = as_tuple(source.gc_goopts),
+ _gc_goopts = tuple(source.gc_goopts),
_cgo = source.cgo,
- _cdeps = as_tuple(source.cdeps),
- _cppopts = as_tuple(source.cppopts),
- _copts = as_tuple(source.copts),
- _cxxopts = as_tuple(source.cxxopts),
- _clinkopts = as_tuple(source.clinkopts),
+ _cdeps = tuple(source.cdeps),
+ _cppopts = tuple(source.cppopts),
+ _copts = tuple(source.copts),
+ _cxxopts = tuple(source.cxxopts),
+ _clinkopts = tuple(source.clinkopts),
# Information on dependencies
_dep_labels = tuple([d.data.label for d in direct]),
@@ -192,7 +188,7 @@ def emit_archive(go, source = None, _recompile_suffix = "", recompile_internal_d
facts_file = out_facts,
runfiles = source.runfiles,
_validation_output = out_nogo_validation,
- _cgo_deps = as_tuple(cgo_deps),
+ _cgo_deps = cgo_deps,
)
x_defs = dict(source.x_defs)
for a in direct:
diff --git a/go/private/common.bzl b/go/private/common.bzl
index 2d5d016b4f..27378b375b 100644
--- a/go/private/common.bzl
+++ b/go/private/common.bzl
@@ -162,16 +162,6 @@ def as_iterable(v):
return v.to_list()
fail("as_iterator failed on {}".format(v))
-def as_tuple(v):
- """Returns a list, tuple, or depset as a tuple."""
- if type(v) == "tuple":
- return v
- if type(v) == "list":
- return tuple(v)
- if type(v) == "depset":
- return tuple(v.to_list())
- fail("as_tuple failed on {}".format(v))
-
def count_group_matches(v, prefix, suffix):
"""Counts reluctant substring matches between prefix and suffix.
diff --git a/go/private/rules/test.bzl b/go/private/rules/test.bzl
index 6b29bb922b..6b970f4ef3 100644
--- a/go/private/rules/test.bzl
+++ b/go/private/rules/test.bzl
@@ -699,7 +699,7 @@ def _recompile_external_deps(go, external_source, internal_archive, library_labe
libs = depset(direct = [arc_data.file], transitive = [a.libs for a in deps]),
transitive = depset(direct = [arc_data], transitive = [a.transitive for a in deps]),
x_defs = source.x_defs,
- cgo_deps = depset(direct = arc_data._cgo_deps, transitive = [a.cgo_deps for a in deps]),
+ cgo_deps = depset(transitive = [arc_data._cgo_deps] + [a.cgo_deps for a in deps]),
cgo_exports = depset(transitive = [a.cgo_exports for a in deps]),
runfiles = source.runfiles,
mode = go.mode,
From 5418bcb89cc501f2d7f3e968c50f4ef178c88902 Mon Sep 17 00:00:00 2001
From: David Zbarsky
Date: Sun, 18 Aug 2024 16:56:24 -0400
Subject: [PATCH 4/7] Cleanup get_mode (#4055)
**What type of PR is this?**
Starlark cleanup
**What does this PR do? Why is it needed?**
This PR simplifies and optimizes `get_mode`. It's now around 2-3x
faster, which is a 100ms win on the buildbuddy repo.
**Which issues(s) does this PR fix?**
Fixes #
**Other notes for review**
---
go/private/context.bzl | 10 +++-
go/private/mode.bzl | 116 +++++++++++++++++++----------------------
2 files changed, 63 insertions(+), 63 deletions(-)
diff --git a/go/private/context.bzl b/go/private/context.bzl
index dcb75128de..ba4e26f220 100644
--- a/go/private/context.bzl
+++ b/go/private/context.bzl
@@ -855,6 +855,14 @@ cgo_context_data_proxy = rule(
)
def _go_config_impl(ctx):
+ pgo_profiles = ctx.attr.pgoprofile.files.to_list()
+ if len(pgo_profiles) > 2:
+ fail("providing more than one pprof file to pgoprofile is not supported")
+ if len(pgo_profiles) == 1:
+ pgoprofile = pgo_profiles[0]
+ else:
+ pgoprofile = None
+
return [GoConfigInfo(
static = ctx.attr.static[BuildSettingInfo].value,
race = ctx.attr.race[BuildSettingInfo].value,
@@ -870,7 +878,7 @@ def _go_config_impl(ctx):
gc_goopts = ctx.attr.gc_goopts[BuildSettingInfo].value,
amd64 = ctx.attr.amd64,
arm = ctx.attr.arm,
- pgoprofile = ctx.attr.pgoprofile,
+ pgoprofile = pgoprofile,
)]
go_config = rule(
diff --git a/go/private/mode.bzl b/go/private/mode.bzl
index 0f6291d17f..173934aaa0 100644
--- a/go/private/mode.bzl
+++ b/go/private/mode.bzl
@@ -12,6 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+load(":providers.bzl", "GoConfigInfo")
+
# Modes are documented in go/modes.rst#compilation-modes
LINKMODE_NORMAL = "normal"
@@ -58,89 +60,79 @@ def mode_string(mode):
result.extend(mode.gc_goopts)
return "_".join(result)
-def _ternary(*values):
- for v in values:
- if v == None:
- continue
- if type(v) == "bool":
- return v
- if type(v) != "string":
- fail("Invalid value type {}".format(type(v)))
- v = v.lower()
- if v == "on":
- return True
- if v == "off":
- return False
- if v == "auto":
- continue
- fail("Invalid value {}".format(v))
- fail("_ternary failed to produce a final result from {}".format(values))
+default_go_config_info = GoConfigInfo(
+ static = False,
+ race = False,
+ msan = False,
+ pure = False,
+ strip = False,
+ debug = False,
+ linkmode = LINKMODE_NORMAL,
+ gc_linkopts = [],
+ tags = [],
+ stamp = False,
+ cover_format = None,
+ gc_goopts = [],
+ amd64 = None,
+ arm = None,
+ pgoprofile = None,
+)
def get_mode(ctx, go_toolchain, cgo_context_info, go_config_info):
- static = _ternary(go_config_info.static if go_config_info else "off")
- if getattr(ctx.attr, "pure", None) == "off" and not cgo_context_info:
- fail("{} has pure explicitly set to off, but no C++ toolchain could be found for its platform".format(ctx.label))
- pure = _ternary(
- "on" if not cgo_context_info else "auto",
- go_config_info.pure if go_config_info else "off",
- )
- race = _ternary(go_config_info.race if go_config_info else "off")
- msan = _ternary(go_config_info.msan if go_config_info else "off")
- strip = go_config_info.strip if go_config_info else False
- stamp = go_config_info.stamp if go_config_info else False
- debug = go_config_info.debug if go_config_info else False
- linkmode = go_config_info.linkmode if go_config_info else LINKMODE_NORMAL
- cover_format = go_config_info and go_config_info.cover_format
- amd64 = go_config_info.amd64 if go_config_info else None
- arm = go_config_info.arm if go_config_info else None
+ if go_config_info == None:
+ go_config_info = default_go_config_info
+
+ if not cgo_context_info:
+ if getattr(ctx.attr, "pure", None) == "off":
+ fail("{} has pure explicitly set to off, but no C++ toolchain could be found for its platform".format(ctx.label))
+ pure = True
+ else:
+ pure = go_config_info.pure
+
+ race = go_config_info.race
+ msan = go_config_info.msan
+ linkmode = go_config_info.linkmode
goos = go_toolchain.default_goos if getattr(ctx.attr, "goos", "auto") == "auto" else ctx.attr.goos
goarch = go_toolchain.default_goarch if getattr(ctx.attr, "goarch", "auto") == "auto" else ctx.attr.goarch
- gc_goopts = go_config_info.gc_goopts if go_config_info else []
- pgoprofile = None
- if go_config_info:
- if len(go_config_info.pgoprofile.files.to_list()) > 2:
- fail("providing more than one pprof file to pgoprofile is not supported")
- elif len(go_config_info.pgoprofile.files.to_list()) == 1:
- pgoprofile = go_config_info.pgoprofile.files.to_list()[0]
# TODO(jayconrod): check for more invalid and contradictory settings.
- if pure and race:
- fail("race instrumentation can't be enabled when cgo is disabled. Check that pure is not set to \"off\" and a C/C++ toolchain is configured.")
- if pure and msan:
- fail("msan instrumentation can't be enabled when cgo is disabled. Check that pure is not set to \"off\" and a C/C++ toolchain is configured.")
- if pure and linkmode in LINKMODES_REQUIRING_EXTERNAL_LINKING:
- fail(("linkmode '{}' can't be used when cgo is disabled. Check that pure is not set to \"off\" and that a C/C++ toolchain is configured for " +
- "your current platform. If you defined a custom platform, make sure that it has the @io_bazel_rules_go//go/toolchain:cgo_on constraint value.").format(linkmode))
-
- gc_linkopts = list(go_config_info.gc_linkopts) if go_config_info else []
- tags = list(go_config_info.tags) if go_config_info else []
+ if pure:
+ if race:
+ fail("race instrumentation can't be enabled when cgo is disabled. Check that pure is not set to \"off\" and a C/C++ toolchain is configured.")
+ if msan:
+ fail("msan instrumentation can't be enabled when cgo is disabled. Check that pure is not set to \"off\" and a C/C++ toolchain is configured.")
+ if linkmode in LINKMODES_REQUIRING_EXTERNAL_LINKING:
+ fail(("linkmode '{}' can't be used when cgo is disabled. Check that pure is not set to \"off\" and that a C/C++ toolchain is configured for " +
+ "your current platform. If you defined a custom platform, make sure that it has the @io_bazel_rules_go//go/toolchain:cgo_on constraint value.").format(linkmode))
+
+ tags = list(go_config_info.tags)
if "gotags" in ctx.var:
- tags.extend(ctx.var["gotags"].split(","))
+ tags += ctx.var["gotags"].split(",")
if cgo_context_info:
- tags.extend(cgo_context_info.tags)
+ tags += cgo_context_info.tags
if race:
tags.append("race")
if msan:
tags.append("msan")
return struct(
- static = static,
+ static = go_config_info.static,
race = race,
msan = msan,
pure = pure,
link = linkmode,
- gc_linkopts = gc_linkopts,
- strip = strip,
- stamp = stamp,
- debug = debug,
+ gc_linkopts = go_config_info.gc_linkopts,
+ strip = go_config_info.strip,
+ stamp = go_config_info.stamp,
+ debug = go_config_info.debug,
goos = goos,
goarch = goarch,
tags = tags,
- cover_format = cover_format,
- amd64 = amd64,
- arm = arm,
- gc_goopts = gc_goopts,
- pgoprofile = pgoprofile,
+ cover_format = go_config_info.cover_format,
+ amd64 = go_config_info.amd64,
+ arm = go_config_info.arm,
+ gc_goopts = go_config_info.gc_goopts,
+ pgoprofile = go_config_info.pgoprofile,
)
def installsuffix(mode):
From 2cbd80609e113efed4877b6b8ffd5255960ee8aa Mon Sep 17 00:00:00 2001
From: David Zbarsky
Date: Mon, 19 Aug 2024 03:38:35 -0400
Subject: [PATCH 5/7] Cleanup _merge_embed (#4048)
**What type of PR is this?**
Starlark cleanup
**What does this PR do? Why is it needed?**
Since only one the lib or one of the embeds can specify cgo, we can
simplify this a bit
**Which issues(s) does this PR fix?**
Fixes #
**Other notes for review**
---
go/private/context.bzl | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/go/private/context.bzl b/go/private/context.bzl
index ba4e26f220..ad0270fe35 100644
--- a/go/private/context.bzl
+++ b/go/private/context.bzl
@@ -222,14 +222,16 @@ def _merge_embed(source, embed):
source["x_defs"].update(s.x_defs)
source["gc_goopts"] = source["gc_goopts"] + s.gc_goopts
source["runfiles"] = source["runfiles"].merge(s.runfiles)
- if s.cgo and source["cgo"]:
- fail("multiple libraries with cgo enabled")
- source["cgo"] = source["cgo"] or s.cgo
- source["cdeps"] = source["cdeps"] or s.cdeps
- source["cppopts"] = source["cppopts"] or s.cppopts
- source["copts"] = source["copts"] or s.copts
- source["cxxopts"] = source["cxxopts"] or s.cxxopts
- source["clinkopts"] = source["clinkopts"] or s.clinkopts
+
+ if s.cgo:
+ if source["cgo"]:
+ fail("multiple libraries with cgo enabled")
+ source["cgo"] = s.cgo
+ source["cdeps"] = s.cdeps
+ source["cppopts"] = s.cppopts
+ source["copts"] = s.copts
+ source["cxxopts"] = s.cxxopts
+ source["clinkopts"] = s.clinkopts
def _dedup_archives(archives):
"""Returns a list of archives without duplicate import paths.
@@ -258,9 +260,7 @@ def _library_to_source(go, attr, library, coverage_instrumented):
srcs = attr_srcs + generated_srcs
embedsrcs = [f for t in getattr(attr, "embedsrcs", []) for f in as_iterable(t.files)]
attr_deps = getattr(attr, "deps", [])
- generated_deps = getattr(library, "deps", [])
- deps = attr_deps + generated_deps
- deps = [get_archive(dep) for dep in deps]
+ deps = [get_archive(dep) for dep in attr_deps]
source = {
"library": library,
"mode": go.mode,
From 756d39ca69a3bf902227907c9ff8818818369408 Mon Sep 17 00:00:00 2001
From: David Zbarsky
Date: Mon, 19 Aug 2024 18:15:56 -0400
Subject: [PATCH 6/7] Remove mode_tags_equivalent (#4057)
**What type of PR is this?**
starlark cleanup
**What does this PR do? Why is it needed?**
It's unused
**Which issues(s) does this PR fix?**
Fixes #
**Other notes for review**
---
go/private/mode.bzl | 8 --------
1 file changed, 8 deletions(-)
diff --git a/go/private/mode.bzl b/go/private/mode.bzl
index 173934aaa0..66cef3a5bc 100644
--- a/go/private/mode.bzl
+++ b/go/private/mode.bzl
@@ -143,14 +143,6 @@ def installsuffix(mode):
s += "_msan"
return s
-def mode_tags_equivalent(l, r):
- # Returns whether two modes are equivalent for Go build tags. For example,
- # goos and goarch must match, but static doesn't matter.
- return (l.goos == r.goos and
- l.goarch == r.goarch and
- l.race == r.race and
- l.msan == r.msan)
-
# Ported from https://github.com/golang/go/blob/master/src/cmd/go/internal/work/init.go#L76
_LINK_C_ARCHIVE_PLATFORMS = {
"darwin/arm64": None,
From f3029e2a89432e06f9e4c8d304f114f54b5436eb Mon Sep 17 00:00:00 2001
From: David Zbarsky
Date: Tue, 20 Aug 2024 03:13:43 -0400
Subject: [PATCH 7/7] Remove more hasattr/getattr calls in go_context (#4054)
**What type of PR is this?**
starlark cleanup
**What does this PR do? Why is it needed?**
The current logic to grab all the providers is confusing; it runs the
fallback logics even in the case when `go_context_data` is found. Easier
to follow when we pass the data in explicitly, and a bit faster as well.
I also considered making a `go_context_v2` api that doesn't have all the
fallbacks and using that to power `go_context`, but didn't do it for
now.
**Which issues(s) does this PR fix?**
Fixes #
**Other notes for review**
---
go/private/context.bzl | 42 +++++++++++++++++++++---------------
go/private/rules/binary.bzl | 8 ++++++-
go/private/rules/library.bzl | 19 +++++++++++++++-
go/private/rules/test.bzl | 8 ++++++-
4 files changed, 57 insertions(+), 20 deletions(-)
diff --git a/go/private/context.bzl b/go/private/context.bzl
index ad0270fe35..03fe09887e 100644
--- a/go/private/context.bzl
+++ b/go/private/context.bzl
@@ -381,7 +381,8 @@ def go_context(
importpath = None,
importmap = None,
embed = None,
- importpath_aliases = None):
+ importpath_aliases = None,
+ go_context_data = None):
"""Returns an API used to build Go code.
See /go/toolchains.rst#go-context
@@ -390,26 +391,33 @@ def go_context(
attr = ctx.attr
toolchain = ctx.toolchains[GO_TOOLCHAIN]
cgo_context_info = None
+ go_context_info = None
go_config_info = None
stdlib = None
- coverdata = None
- nogo = None
- if hasattr(attr, "_go_context_data"):
- go_context_data = _flatten_possibly_transitioned_attr(attr._go_context_data)
+
+ if go_context_data == None:
+ if hasattr(attr, "_go_context_data"):
+ go_context_data = _flatten_possibly_transitioned_attr(attr._go_context_data)
+ if CgoContextInfo in go_context_data:
+ cgo_context_info = go_context_data[CgoContextInfo]
+ go_config_info = go_context_data[GoConfigInfo]
+ stdlib = go_context_data[GoStdLib]
+ go_context_info = go_context_data[GoContextInfo]
+ if getattr(attr, "_cgo_context_data", None) and CgoContextInfo in attr._cgo_context_data:
+ cgo_context_info = attr._cgo_context_data[CgoContextInfo]
+ if getattr(attr, "cgo_context_data", None) and CgoContextInfo in attr.cgo_context_data:
+ cgo_context_info = attr.cgo_context_data[CgoContextInfo]
+ if hasattr(attr, "_go_config"):
+ go_config_info = attr._go_config[GoConfigInfo]
+ if hasattr(attr, "_stdlib"):
+ stdlib = _flatten_possibly_transitioned_attr(attr._stdlib)[GoStdLib]
+ else:
+ go_context_data = _flatten_possibly_transitioned_attr(go_context_data)
if CgoContextInfo in go_context_data:
cgo_context_info = go_context_data[CgoContextInfo]
go_config_info = go_context_data[GoConfigInfo]
stdlib = go_context_data[GoStdLib]
- coverdata = go_context_data[GoContextInfo].coverdata
- nogo = go_context_data[GoContextInfo].nogo
- if getattr(attr, "_cgo_context_data", None) and CgoContextInfo in attr._cgo_context_data:
- cgo_context_info = attr._cgo_context_data[CgoContextInfo]
- if getattr(attr, "cgo_context_data", None) and CgoContextInfo in attr.cgo_context_data:
- cgo_context_info = attr.cgo_context_data[CgoContextInfo]
- if hasattr(attr, "_go_config"):
- go_config_info = attr._go_config[GoConfigInfo]
- if hasattr(attr, "_stdlib"):
- stdlib = _flatten_possibly_transitioned_attr(attr._stdlib)[GoStdLib]
+ go_context_info = go_context_data[GoContextInfo]
mode = get_mode(ctx, toolchain, cgo_context_info, go_config_info)
@@ -515,8 +523,8 @@ def go_context(
importpath_aliases = importpath_aliases,
pathtype = pathtype,
cgo_tools = cgo_tools,
- nogo = nogo,
- coverdata = coverdata,
+ nogo = go_context_info.nogo if go_context_info else None,
+ coverdata = go_context_info.coverdata if go_context_info else None,
coverage_enabled = ctx.configuration.coverage_enabled,
coverage_instrumented = ctx.coverage_instrumented(),
env = env,
diff --git a/go/private/rules/binary.bzl b/go/private/rules/binary.bzl
index 193fd3741a..cd863af97f 100644
--- a/go/private/rules/binary.bzl
+++ b/go/private/rules/binary.bzl
@@ -115,7 +115,13 @@ _go_cc_aspect = aspect(
def _go_binary_impl(ctx):
"""go_binary_impl emits actions for compiling and linking a go executable."""
- go = go_context(ctx, include_deprecated_properties = False)
+ go = go_context(
+ ctx,
+ include_deprecated_properties = False,
+ importpath = ctx.attr.importpath,
+ embed = ctx.attr.embed,
+ go_context_data = ctx.attr._go_context_data,
+ )
is_main = go.mode.link not in (LINKMODE_SHARED, LINKMODE_PLUGIN)
library = go.new_library(go, importable = False, is_main = is_main)
diff --git a/go/private/rules/library.bzl b/go/private/rules/library.bzl
index 23476bc56a..d830409e4f 100644
--- a/go/private/rules/library.bzl
+++ b/go/private/rules/library.bzl
@@ -39,8 +39,11 @@ def _go_library_impl(ctx):
include_deprecated_properties = False,
importpath = ctx.attr.importpath,
importmap = ctx.attr.importmap,
+ importpath_aliases = ctx.attr.importpath_aliases,
embed = ctx.attr.embed,
+ go_context_data = ctx.attr._go_context_data,
)
+
library = go.new_library(go)
source = go.library_to_source(go, ctx.attr, library, ctx.coverage_instrumented())
archive = go.archive(go, source)
@@ -204,8 +207,22 @@ go_library = rule(
# See docs/go/core/rules.md#go_library for full documentation.
+def _go_tool_library_impl(ctx):
+ """Implements the go_tool_library() rule."""
+ go = go_context(ctx, include_deprecated_properties = False)
+
+ library = go.new_library(go)
+ source = go.library_to_source(go, ctx.attr, library, ctx.coverage_instrumented())
+ archive = go.archive(go, source)
+
+ return [
+ library,
+ source,
+ archive,
+ ]
+
go_tool_library = rule(
- _go_library_impl,
+ _go_tool_library_impl,
attrs = {
"data": attr.label_list(allow_files = True),
"srcs": attr.label_list(allow_files = True),
diff --git a/go/private/rules/test.bzl b/go/private/rules/test.bzl
index 6b970f4ef3..3c9b4b02d1 100644
--- a/go/private/rules/test.bzl
+++ b/go/private/rules/test.bzl
@@ -56,7 +56,13 @@ def _go_test_impl(ctx):
It emits an action to run the test generator, and then compiles the
test into a binary."""
- go = go_context(ctx, include_deprecated_properties = False)
+ go = go_context(
+ ctx,
+ include_deprecated_properties = False,
+ importpath = ctx.attr.importpath,
+ embed = ctx.attr.embed,
+ go_context_data = ctx.attr._go_context_data,
+ )
# Compile the library to test with internal white box tests
internal_library = go.new_library(go, testfilter = "exclude")