diff options
author | Jamy Timmermans <jamy@uber.com> | 2023-11-09 08:57:25 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-11-09 16:57:25 +0000 |
commit | d8bf98b2289d8ab431b5be242c16f4b01019a6a9 (patch) | |
tree | e38680b1d81f75f121ebfdc329eab1b27bd4038f | |
parent | 5f6203e13d81edd08b0392ab4b67be1d172ed2ff (diff) | |
download | bazelbuild-rules_go-d8bf98b2289d8ab431b5be242c16f4b01019a6a9.tar.gz |
feat(gopackagesdriver): add base test case for go packages driver (#3743)
* feat(tests): add base test case for go packages driver
* fixup! debug log
* fixup! build.bazel
* tmp debug
* typos and returns
* testing
* fixup! windows fix
* fix canonical labels
* fixup! rulesgoname
* feat(gpd): use bazel version to enable prefixing of packages (backwards compat)
* fixup! makecompatible with pre-bazel 6
* fixup! use bowi
-rw-r--r-- | go/tools/BUILD.bazel | 1 | ||||
-rw-r--r-- | go/tools/bazel_testing/bazel_testing.go | 12 | ||||
-rw-r--r-- | go/tools/gopackagesdriver/BUILD.bazel | 12 | ||||
-rw-r--r-- | go/tools/gopackagesdriver/bazel.go | 6 | ||||
-rw-r--r-- | go/tools/gopackagesdriver/bazel_json_builder.go | 12 | ||||
-rw-r--r-- | go/tools/gopackagesdriver/json_packages_driver.go | 4 | ||||
-rw-r--r-- | go/tools/gopackagesdriver/main.go | 2 | ||||
-rw-r--r-- | go/tools/gopackagesdriver/packageregistry.go | 26 | ||||
-rw-r--r-- | tests/integration/gopackagesdriver/BUILD.bazel | 13 | ||||
-rw-r--r-- | tests/integration/gopackagesdriver/README.rst | 8 | ||||
-rw-r--r-- | tests/integration/gopackagesdriver/gopackagesdriver_test.go | 126 |
11 files changed, 214 insertions, 8 deletions
diff --git a/go/tools/BUILD.bazel b/go/tools/BUILD.bazel index 50b87d74..503171d4 100644 --- a/go/tools/BUILD.bazel +++ b/go/tools/BUILD.bazel @@ -8,6 +8,7 @@ filegroup( "//go/tools/bzltestutil:all_files", "//go/tools/coverdata:all_files", "//go/tools/go_bin_runner:all_files", + "//go/tools/gopackagesdriver:all_files", ], visibility = ["//visibility:public"], ) diff --git a/go/tools/bazel_testing/bazel_testing.go b/go/tools/bazel_testing/bazel_testing.go index 45431405..36ba1f6c 100644 --- a/go/tools/bazel_testing/bazel_testing.go +++ b/go/tools/bazel_testing/bazel_testing.go @@ -206,11 +206,23 @@ func RunBazel(args ...string) error { // If the command starts but exits with a non-zero status, a *StderrExitError // will be returned which wraps the original *exec.ExitError. func BazelOutput(args ...string) ([]byte, error) { + return BazelOutputWithInput(nil, args...) +} + +// BazelOutputWithInput invokes a bazel command with a list of arguments and +// an input stream and returns the content of stdout. +// +// If the command starts but exits with a non-zero status, a *StderrExitError +// will be returned which wraps the original *exec.ExitError. +func BazelOutputWithInput(stdin io.Reader, args ...string) ([]byte, error) { cmd := BazelCmd(args...) stdout := &bytes.Buffer{} stderr := &bytes.Buffer{} cmd.Stdout = stdout cmd.Stderr = stderr + if stdin != nil { + cmd.Stdin = stdin + } err := cmd.Run() if eErr, ok := err.(*exec.ExitError); ok { eErr.Stderr = stderr.Bytes() diff --git a/go/tools/gopackagesdriver/BUILD.bazel b/go/tools/gopackagesdriver/BUILD.bazel index 6b6e36c1..86ad484d 100644 --- a/go/tools/gopackagesdriver/BUILD.bazel +++ b/go/tools/gopackagesdriver/BUILD.bazel @@ -1,5 +1,4 @@ load("//go:def.bzl", "go_binary", "go_library") -load(":aspect.bzl", "bazel_supports_canonical_label_literals") load("//go/private:common.bzl", "RULES_GO_REPO_NAME") go_library( @@ -16,7 +15,9 @@ go_library( "utils.go", ], importpath = "github.com/bazelbuild/rules_go/go/tools/gopackagesdriver", - visibility = ["//visibility:private"], + visibility = [ + "//tests/integration/gopackagesdriver:__pkg__", + ], ) go_binary( @@ -29,3 +30,10 @@ go_binary( "rulesGoRepositoryName": RULES_GO_REPO_NAME, }, ) + +filegroup( + name = "all_files", + testonly = True, + srcs = glob(["**"]), + visibility = ["//visibility:public"], +) diff --git a/go/tools/gopackagesdriver/bazel.go b/go/tools/gopackagesdriver/bazel.go index 08da745d..979faa6d 100644 --- a/go/tools/gopackagesdriver/bazel.go +++ b/go/tools/gopackagesdriver/bazel.go @@ -38,6 +38,7 @@ type Bazel struct { workspaceRoot string bazelStartupFlags []string info map[string]string + version string } // Minimal BEP structs to access the build outputs @@ -55,6 +56,7 @@ func NewBazel(ctx context.Context, bazelBin, workspaceRoot string, bazelStartupF bazelBin: bazelBin, workspaceRoot: workspaceRoot, bazelStartupFlags: bazelStartupFlags, + version: "6", } if err := b.fillInfo(ctx); err != nil { return nil, fmt.Errorf("unable to query bazel info: %w", err) @@ -73,6 +75,10 @@ func (b *Bazel) fillInfo(ctx context.Context) error { parts := strings.SplitN(strings.TrimSpace(scanner.Text()), ":", 2) b.info[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1]) } + release := strings.Split(b.info["release"], " ") + if len(release) == 2 { + b.version = release[1] + } return nil } diff --git a/go/tools/gopackagesdriver/bazel_json_builder.go b/go/tools/gopackagesdriver/bazel_json_builder.go index 32924749..a100fefc 100644 --- a/go/tools/gopackagesdriver/bazel_json_builder.go +++ b/go/tools/gopackagesdriver/bazel_json_builder.go @@ -24,6 +24,7 @@ import ( "path" "path/filepath" "regexp" + "runtime" "strings" ) @@ -238,7 +239,7 @@ func (b *BazelJSONBuilder) Build(ctx context.Context, labels []string, mode Load ret := []string{} for _, f := range files { if strings.HasSuffix(f, ".pkg.json") { - ret = append(ret, f) + ret = append(ret, cleanPath(f)) } } @@ -253,3 +254,12 @@ func (b *BazelJSONBuilder) PathResolver() PathResolverFunc { return p } } + +func cleanPath(p string) string { + // On Windows the paths may contain a starting `\`, this would make them not resolve + if runtime.GOOS == "windows" && p[0] == '\\' { + return p[1:] + } + + return p +} diff --git a/go/tools/gopackagesdriver/json_packages_driver.go b/go/tools/gopackagesdriver/json_packages_driver.go index d91f2ed1..00b55b3c 100644 --- a/go/tools/gopackagesdriver/json_packages_driver.go +++ b/go/tools/gopackagesdriver/json_packages_driver.go @@ -23,9 +23,9 @@ type JSONPackagesDriver struct { registry *PackageRegistry } -func NewJSONPackagesDriver(jsonFiles []string, prf PathResolverFunc) (*JSONPackagesDriver, error) { +func NewJSONPackagesDriver(jsonFiles []string, prf PathResolverFunc, bazelVersion string) (*JSONPackagesDriver, error) { jpd := &JSONPackagesDriver{ - registry: NewPackageRegistry(), + registry: NewPackageRegistry(bazelVersion), } for _, f := range jsonFiles { diff --git a/go/tools/gopackagesdriver/main.go b/go/tools/gopackagesdriver/main.go index 8cc904d3..0213f6f8 100644 --- a/go/tools/gopackagesdriver/main.go +++ b/go/tools/gopackagesdriver/main.go @@ -103,7 +103,7 @@ func run() (*driverResponse, error) { return emptyResponse, fmt.Errorf("unable to build JSON files: %w", err) } - driver, err := NewJSONPackagesDriver(jsonFiles, bazelJsonBuilder.PathResolver()) + driver, err := NewJSONPackagesDriver(jsonFiles, bazelJsonBuilder.PathResolver(), bazel.version) if err != nil { return emptyResponse, fmt.Errorf("unable to load JSON files: %w", err) } diff --git a/go/tools/gopackagesdriver/packageregistry.go b/go/tools/gopackagesdriver/packageregistry.go index 92f9eac7..1afad7ce 100644 --- a/go/tools/gopackagesdriver/packageregistry.go +++ b/go/tools/gopackagesdriver/packageregistry.go @@ -16,18 +16,21 @@ package main import ( "fmt" + "strconv" "strings" ) type PackageRegistry struct { packagesByID map[string]*FlatPackage stdlib map[string]string + bazelVersion []int } -func NewPackageRegistry(pkgs ...*FlatPackage) *PackageRegistry { +func NewPackageRegistry(bazelVersion string, pkgs ...*FlatPackage) *PackageRegistry { pr := &PackageRegistry{ packagesByID: map[string]*FlatPackage{}, stdlib: map[string]string{}, + bazelVersion: parseVersion(bazelVersion), } pr.Add(pkgs...) return pr @@ -88,7 +91,10 @@ func (pr *PackageRegistry) Match(labels []string) ([]string, []*FlatPackage) { roots := map[string]struct{}{} for _, label := range labels { - if !strings.HasPrefix(label, "@") { + // When packagesdriver is ran from rules go, rulesGoRepositoryName will just be @ + if pr.bazelVersion[0] >= 6 && + !strings.HasPrefix(label, "@") { + // Canonical labels is only since Bazel 6.0.0 label = fmt.Sprintf("@%s", label) } @@ -119,3 +125,19 @@ func (pr *PackageRegistry) Match(labels []string) ([]string, []*FlatPackage) { return retRoots, retPkgs } + +func parseVersion(v string) []int { + parts := strings.Split(v, ".") + version := make([]int, len(parts)) + + var err error + for i, p := range parts { + version[i], err = strconv.Atoi(p) + if err != nil { + // Failsafe default + return []int{6, 0, 0} + } + } + + return version +} diff --git a/tests/integration/gopackagesdriver/BUILD.bazel b/tests/integration/gopackagesdriver/BUILD.bazel new file mode 100644 index 00000000..088663ed --- /dev/null +++ b/tests/integration/gopackagesdriver/BUILD.bazel @@ -0,0 +1,13 @@ +load("//go/tools/bazel_testing:def.bzl", "go_bazel_test") + +go_bazel_test( + name = "gopackagesdriver_test", + size = "enormous", + srcs = ["gopackagesdriver_test.go"], + rule_files = [ + "//:all_files", + ], + deps = [ + "@io_bazel_rules_go//go/tools/gopackagesdriver:gopackagesdriver_lib", + ] +) diff --git a/tests/integration/gopackagesdriver/README.rst b/tests/integration/gopackagesdriver/README.rst new file mode 100644 index 00000000..89d2d9ce --- /dev/null +++ b/tests/integration/gopackagesdriver/README.rst @@ -0,0 +1,8 @@ +Go Packages Driver + +gopackagesdriver_test +-------------------- +Verifies that the output of the go packages driver includes the correct output. + +Go x/tools is very sensitive to inaccuracies in the package output, so we should +validate each added feature against what is expected by x/tools. diff --git a/tests/integration/gopackagesdriver/gopackagesdriver_test.go b/tests/integration/gopackagesdriver/gopackagesdriver_test.go new file mode 100644 index 00000000..1d2e0d4a --- /dev/null +++ b/tests/integration/gopackagesdriver/gopackagesdriver_test.go @@ -0,0 +1,126 @@ +package gopackagesdriver_test + +import ( + "encoding/json" + "path" + "strings" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" + gpd "github.com/bazelbuild/rules_go/go/tools/gopackagesdriver" +) + +type response struct { + Roots []string `json:",omitempty"` + Packages []*gpd.FlatPackage +} + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "hello", + srcs = ["hello.go"], + importpath = "example.com/hello", + visibility = ["//visibility:public"], +) + +-- hello.go -- +package hello + +import "os" + +func main() { + fmt.Fprintln(os.Stderr, "Hello World!") +} + `, + }) +} + +const ( + osPkgID = "@io_bazel_rules_go//stdlib:os" +) + +func TestBaseFileLookup(t *testing.T) { + reader := strings.NewReader("{}") + out, err := bazel_testing.BazelOutputWithInput(reader, "run", "@io_bazel_rules_go//go/tools/gopackagesdriver", "--", "file=hello.go") + if err != nil { + t.Errorf("Unexpected error: %w", err.Error()) + return + } + var resp response + err = json.Unmarshal(out, &resp) + if err != nil { + t.Errorf("Failed to unmarshal packages driver response: %w\n%w", err.Error(), out) + return + } + + 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], "//:hello") { + t.Errorf("Unexpected package id: %q", resp.Roots[0]) + return + } + }) + + t.Run("package", func(t *testing.T) { + var pkg *gpd.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]) != "hello.go" || path.Base(pkg.CompiledGoFiles[0]) != "hello.go" { + t.Errorf("Expected to find 1 file (hello.go) in (Compiled)GoFiles:\n%+v", pkg) + return + } + + if pkg.Standard { + t.Errorf("Expected package to not be Standard:\n%+v", pkg) + return + } + + if len(pkg.Imports) != 1 { + t.Errorf("Expected one import:\n%+v", pkg) + return + } + + if pkg.Imports["os"] != osPkgID { + t.Errorf("Expected os import to map to %q:\n%+v", osPkgID, pkg) + return + } + }) + + t.Run("dependency", func(t *testing.T) { + var osPkg *gpd.FlatPackage + for _, p := range resp.Packages { + if p.ID == osPkgID { + osPkg = p + } + } + + if osPkg == nil { + t.Errorf("Expected os package to be included:\n%+v", osPkg) + return + } + + if !osPkg.Standard { + t.Errorf("Expected os import to be standard:\n%+v", osPkg) + return + } + }) +} |