From a67afe06879898f338c03caab7425ecf1ffab85c Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 28 Mar 2021 21:04:43 +0200 Subject: [PATCH 01/41] go/tools: add gopackagesdriver This commit introduces the GOPACKAGESDRIVER for rules_go Signed-off-by: Steeve Morin --- go/private/actions/stdlib.bzl | 17 ++ go/tools/builders/BUILD.bazel | 1 + go/tools/builders/builder.go | 2 + go/tools/builders/stdliblist.go | 201 ++++++++++++++++++ go/tools/gopackagesdriver/BUILD.bazel | 23 ++ go/tools/gopackagesdriver/aspect.bzl | 113 ++++++++++ go/tools/gopackagesdriver/bazel.go | 107 ++++++++++ .../gopackagesdriver/bazel_json_builder.go | 75 +++++++ go/tools/gopackagesdriver/driver_request.go | 29 +++ go/tools/gopackagesdriver/flatpackage.go | 121 +++++++++++ .../gopackagesdriver/json_packages_driver.go | 36 ++++ go/tools/gopackagesdriver/main.go | 104 +++++++++ go/tools/gopackagesdriver/packageregistry.go | 75 +++++++ 13 files changed, 904 insertions(+) create mode 100644 go/tools/builders/stdliblist.go create mode 100644 go/tools/gopackagesdriver/BUILD.bazel create mode 100644 go/tools/gopackagesdriver/aspect.bzl create mode 100644 go/tools/gopackagesdriver/bazel.go create mode 100644 go/tools/gopackagesdriver/bazel_json_builder.go create mode 100644 go/tools/gopackagesdriver/driver_request.go create mode 100644 go/tools/gopackagesdriver/flatpackage.go create mode 100644 go/tools/gopackagesdriver/json_packages_driver.go create mode 100644 go/tools/gopackagesdriver/main.go create mode 100644 go/tools/gopackagesdriver/packageregistry.go diff --git a/go/private/actions/stdlib.bzl b/go/private/actions/stdlib.bzl index 24f8e5f74f..013e0ecbb0 100644 --- a/go/private/actions/stdlib.bzl +++ b/go/private/actions/stdlib.bzl @@ -51,9 +51,25 @@ def _should_use_sdk_stdlib(go): not go.mode.pure and go.mode.link == LINKMODE_NORMAL) +def _build_stdlib_list_json(go): + out = go.declare_file(go, "stdlib.pkg.json") + args = go.builder_args(go, "stdliblist") + args.add("-out", out) + go.actions.run( + inputs = go.sdk_files, + outputs = [out], + mnemonic = "GoStdlibList", + executable = go.toolchain._builder, + arguments = [args], + env = go.env, + ) + return out + + def _sdk_stdlib(go): return GoStdLib( root_file = go.sdk.root_file, + list_json = _build_stdlib_list_json(go), libs = go.sdk.libs, ) @@ -100,5 +116,6 @@ def _build_stdlib(go): ) return GoStdLib( root_file = root_file, + list_json = _build_stdlib_list_json(go), libs = [pkg], ) diff --git a/go/tools/builders/BUILD.bazel b/go/tools/builders/BUILD.bazel index 4635dc4087..41e54fdb1c 100644 --- a/go/tools/builders/BUILD.bazel +++ b/go/tools/builders/BUILD.bazel @@ -35,6 +35,7 @@ filegroup( "read.go", "replicate.go", "stdlib.go", + "stdliblist.go", ] + select({ "@bazel_tools//src/conditions:windows": ["path_windows.go"], "//conditions:default": ["path.go"], diff --git a/go/tools/builders/builder.go b/go/tools/builders/builder.go index 905c04e697..8ccd746efb 100644 --- a/go/tools/builders/builder.go +++ b/go/tools/builders/builder.go @@ -59,6 +59,8 @@ func main() { action = pack case "stdlib": action = stdlib + case "stdliblist": + action = stdliblist default: log.Fatalf("unknown action: %s", verb) } diff --git a/go/tools/builders/stdliblist.go b/go/tools/builders/stdliblist.go new file mode 100644 index 0000000000..a399625b22 --- /dev/null +++ b/go/tools/builders/stdliblist.go @@ -0,0 +1,201 @@ +// Copyright 2018 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 main + +import ( + "bytes" + "encoding/json" + "flag" + "go/build" + "os" + "path/filepath" + "strings" +) + +// Copy and pasted from golang.org/x/tools/go/packages +type flatPackagesError struct { + Pos string // "file:line:col" or "file:line" or "" or "-" + Msg string + Kind flatPackagesErrorKind +} + +type flatPackagesErrorKind int + +const ( + UnknownError flatPackagesErrorKind = iota + ListError + ParseError + TypeError +) + +func (err flatPackagesError) Error() string { + pos := err.Pos + if pos == "" { + pos = "-" // like token.Position{}.String() + } + return pos + ": " + err.Msg +} + +// flatPackage is the JSON form of Package +// It drops all the type and syntax fields, and transforms the Imports +type flatPackage struct { + ID string + Name string `json:",omitempty"` + PkgPath string `json:",omitempty"` + Standard bool `json:",omitempty"` + Errors []flatPackagesError `json:",omitempty"` + GoFiles []string `json:",omitempty"` + CompiledGoFiles []string `json:",omitempty"` + OtherFiles []string `json:",omitempty"` + ExportFile string `json:",omitempty"` + Imports map[string]string `json:",omitempty"` +} + +type goListPackage struct { + Dir string // directory containing package sources + ImportPath string // import path of package in dir + Name string // package name + Target string // install path + Goroot bool // is this package in the Go root? + Standard bool // is this package part of the standard Go library? + Root string // Go root or Go path dir containing this package + Export string // file containing export data (when using -export) + // Source files + GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) + CgoFiles []string // .go source files that import "C" + CompiledGoFiles []string // .go files presented to compiler (when using -compiled) + IgnoredGoFiles []string // .go source files ignored due to build constraints + IgnoredOtherFiles []string // non-.go source files ignored due to build constraints + CFiles []string // .c source files + CXXFiles []string // .cc, .cxx and .cpp source files + MFiles []string // .m source files + HFiles []string // .h, .hh, .hpp and .hxx source files + FFiles []string // .f, .F, .for and .f90 Fortran source files + SFiles []string // .s source files + SwigFiles []string // .swig files + SwigCXXFiles []string // .swigcxx files + SysoFiles []string // .syso object files to add to archive + TestGoFiles []string // _test.go files in package + XTestGoFiles []string // _test.go files outside package + // Embedded files + EmbedPatterns []string // //go:embed patterns + EmbedFiles []string // files matched by EmbedPatterns + TestEmbedPatterns []string // //go:embed patterns in TestGoFiles + TestEmbedFiles []string // files matched by TestEmbedPatterns + XTestEmbedPatterns []string // //go:embed patterns in XTestGoFiles + XTestEmbedFiles []string // files matched by XTestEmbedPatterns + // Dependency information + Imports []string // import paths used by this package + ImportMap map[string]string // map from source import to ImportPath (identity entries omitted) + // Error information + Incomplete bool // this package or a dependency has an error + Error *flatPackagesError // error loading package + DepsErrors []*flatPackagesError // errors loading dependencies +} + +func stdlibPackageID(importPath string) string { + return "@io_bazel_rules_go//stdlib:" + importPath +} + +func execRootPath(execRoot, p string) string { + dir, _ := filepath.Rel(execRoot, p) + return filepath.Join("__BAZEL_EXECROOT__", dir) +} + +func absoluteSourcesPaths(execRoot, pkgDir string, srcs []string) []string { + ret := []string{} + pkgDir = execRootPath(execRoot, pkgDir) + for _, src := range srcs { + ret = append(ret, filepath.Join(pkgDir, src)) + } + return ret +} + +func packageToPackage(execRoot string, pkg *goListPackage) *flatPackage { + newPkg := &flatPackage{ + ID: stdlibPackageID(pkg.ImportPath), + Name: pkg.Name, + PkgPath: pkg.ImportPath, + ExportFile: execRootPath(execRoot, pkg.Target), + Imports: map[string]string{}, + Standard: pkg.Standard, + GoFiles: absoluteSourcesPaths(execRoot, pkg.Dir, pkg.GoFiles), + CompiledGoFiles: absoluteSourcesPaths(execRoot, pkg.Dir, pkg.CompiledGoFiles), + } + for _, imp := range pkg.Imports { + newPkg.Imports[imp] = stdlibPackageID(imp) + } + return newPkg +} + +// stdlib builds the standard library in the appropriate mode into a new goroot. +func stdliblist(args []string) error { + // process the args + flags := flag.NewFlagSet("stdliblist", flag.ExitOnError) + goenv := envFlags(flags) + out := flags.String("out", "", "Path to output go list json") + if err := flags.Parse(args); err != nil { + return err + } + if err := goenv.checkFlags(); err != nil { + return err + } + + // Ensure paths are absolute. + absPaths := []string{} + for _, path := range filepath.SplitList(os.Getenv("PATH")) { + absPaths = append(absPaths, abs(path)) + } + os.Setenv("PATH", strings.Join(absPaths, string(os.PathListSeparator))) + os.Setenv("GOROOT", abs(os.Getenv("GOROOT"))) + + execRoot := abs(".") + + cachePath := abs(*out + ".gocache") + defer os.RemoveAll(cachePath) + os.Setenv("GOCACHE", cachePath) + os.Setenv("GOPATH", cachePath) + + listArgs := goenv.goCmd("list") + if len(build.Default.BuildTags) > 0 { + listArgs = append(listArgs, "-tags", strings.Join(build.Default.BuildTags, " ")) + } + listArgs = append(listArgs, "-json", "-compiled", "builtin", "std", "runtime/cgo") + + jsonFile, err := os.Create(*out) + if err != nil { + return err + } + defer jsonFile.Close() + + jsonData := &bytes.Buffer{} + if err := goenv.runCommandToFile(jsonData, listArgs); err != nil { + return err + } + + encoder := json.NewEncoder(jsonFile) + decoder := json.NewDecoder(jsonData) + for decoder.More() { + var pkg *goListPackage + if err := decoder.Decode(&pkg); err != nil { + return err + } + if err := encoder.Encode(packageToPackage(execRoot, pkg)); err != nil { + return err + } + } + + return nil +} diff --git a/go/tools/gopackagesdriver/BUILD.bazel b/go/tools/gopackagesdriver/BUILD.bazel new file mode 100644 index 0000000000..131e4641a3 --- /dev/null +++ b/go/tools/gopackagesdriver/BUILD.bazel @@ -0,0 +1,23 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") + +go_library( + name = "go_default_library", + srcs = [ + "bazel_json_builder.go", + "bazel.go", + "driver_request.go", + "flatpackage.go", + "json_packages_driver.go", + "packageregistry.go", + "main.go", + ], + importpath = "github.com/bazelbuild/rules_go/go/tools/gopackagesdriver", + visibility = ["//visibility:private"], + deps = ["@org_golang_x_tools//go/packages:go_default_library"], +) + +go_binary( + name = "gopackagesdriver", + embed = [":go_default_library"], + visibility = ["//visibility:public"], +) diff --git a/go/tools/gopackagesdriver/aspect.bzl b/go/tools/gopackagesdriver/aspect.bzl new file mode 100644 index 0000000000..ed634111a6 --- /dev/null +++ b/go/tools/gopackagesdriver/aspect.bzl @@ -0,0 +1,113 @@ +load("//go:def.bzl", "GoArchive") +load( + "//go/private:context.bzl", + "go_context", +) +load( + "@bazel_skylib//lib:paths.bzl", + "paths", +) +load( + "@bazel_skylib//lib:collections.bzl", + "collections", +) + +GoPkgInfo = provider() + +def _is_file_external(f): + return f.owner.workspace_root != "" + +def _file_path(f): + if f.is_source and not _is_file_external(f): + return paths.join("__BAZEL_WORKSPACE__", f.path) + return paths.join("__BAZEL_EXECROOT__", f.path) + +def _go_pkg_info_aspect_impl(target, ctx): + pkg_json = None + x = None + if GoArchive in target: + archive = target[GoArchive] + x = archive.data.export_file + pkg = struct( + ID = str(archive.data.label), + Name = "main" if archive.source.library.is_main else paths.basename(archive.data.importpath), + PkgPath = archive.data.importpath, + ExportFile = _file_path(archive.data.export_file), + GoFiles = [ + _file_path(src) + for src in archive.data.orig_srcs + ], + CompiledGoFiles = [ + _file_path(src) + for src in archive.data.srcs + ], + ) + pkg_json = ctx.actions.declare_file(archive.data.name + ".pkg.json") + ctx.actions.write(pkg_json, content = pkg.to_json()) + + deps_transitive_json = [] + deps_transitive_x = [] + if hasattr(ctx.rule.attr, "deps"): + for dep in ctx.rule.attr.deps: + if GoPkgInfo in dep: + pkg_info = dep[GoPkgInfo] + deps_transitive_json.append(pkg_info.transitive_json) + deps_transitive_x.append(pkg_info.transitive_x) + # If deps are embedded, no not gather their json or x since they are + # included in the current target, but do gather their deps'. + if hasattr(ctx.rule.attr, "embed"): + for dep in ctx.rule.attr.embed: + if GoPkgInfo in dep: + pkg_info = dep[GoPkgInfo] + deps_transitive_json.append(pkg_info.deps_transitive_json) + deps_transitive_x.append(pkg_info.deps_transitive_x) + + pkg_info = GoPkgInfo( + json = pkg_json, + transitive_json = depset( + direct = [pkg_json] if pkg_json else None, + transitive = deps_transitive_json, + ), + deps_transitive_json = depset( + transitive = deps_transitive_json, + ), + x = x, + transitive_x = depset( + direct = [x] if x else None, + transitive = deps_transitive_x, + ), + deps_transitive_x = depset( + transitive = deps_transitive_x, + ), + ) + + return [ + pkg_info, + OutputGroupInfo( + go_pkg_driver_json = pkg_info.transitive_json, + go_pkg_driver_x = pkg_info.transitive_x, + ) + ] + +go_pkg_info_aspect = aspect( + implementation = _go_pkg_info_aspect_impl, + attr_aspects = ["embed", "deps"], +) + +def _go_std_pkg_info_aspect_impl(target, ctx): + go = go_context(ctx, attr = ctx.rule.attr) + return [ + OutputGroupInfo( + go_pkg_driver_stdlib_json = [go.stdlib.list_json], + ), + ] + +go_std_pkg_info_aspect = aspect( + implementation = _go_std_pkg_info_aspect_impl, + attrs = { + "_go_context_data": attr.label( + default = "@io_bazel_rules_go//:go_context_data", + ), + }, + toolchains = ["@io_bazel_rules_go//go:toolchain"], +) diff --git a/go/tools/gopackagesdriver/bazel.go b/go/tools/gopackagesdriver/bazel.go new file mode 100644 index 0000000000..a93c06ee05 --- /dev/null +++ b/go/tools/gopackagesdriver/bazel.go @@ -0,0 +1,107 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "os" + "os/exec" + "strings" +) + +const ( + toolTag = "gopackagesdriver" +) + +type Bazel struct { + bazelBin string + execRoot string + workspaceRoot string +} + +// Minimal BEP structs to access the build outputs +type BEPNamedSet struct { + NamedSetOfFiles *struct { + Files []struct { + Name string `json:"name"` + URI string `json:"uri"` + } `json:"files"` + } `json:"namedSetOfFiles"` +} + +func NewBazel(ctx context.Context, bazelBin, workspaceRoot string) (*Bazel, error) { + b := &Bazel{ + bazelBin: bazelBin, + workspaceRoot: workspaceRoot, + } + if execRoot, err := b.run(ctx, "info", "execution_root"); err != nil { + return nil, err + } else { + b.execRoot = strings.TrimSpace(execRoot) + } + return b, nil +} + +func (b *Bazel) run(ctx context.Context, command string, args ...string) (string, error) { + cmd := exec.CommandContext(ctx, b.bazelBin, append([]string{ + command, + "--tool_tag=" + toolTag, + }, args...)...) + fmt.Fprintln(os.Stderr, "Running:", cmd.Args) + cmd.Dir = b.workspaceRoot + cmd.Stderr = os.Stderr + output, err := cmd.Output() + return string(output), err +} + +func (b *Bazel) Build(ctx context.Context, args ...string) ([]string, error) { + jsonTmp, _ := os.CreateTemp("", "bep_") + jsonTmp.Close() + defer os.RemoveAll(jsonTmp.Name()) + + args = append([]string{ + "--build_event_json_file=" + jsonTmp.Name(), + "--build_event_json_file_path_conversion=no", + }, args...) + if _, err := b.run(ctx, "build", args...); err != nil { + return nil, err + } + + jsonFile, err := os.Open(jsonTmp.Name()) + if err != nil { + return nil, err + } + defer jsonFile.Close() + + files := make([]string, 0) + decoder := json.NewDecoder(jsonFile) + for decoder.More() { + var namedSet BEPNamedSet + if err := decoder.Decode(&namedSet); err != nil { + panic(err) + } + if namedSet.NamedSetOfFiles != nil { + for _, f := range namedSet.NamedSetOfFiles.Files { + files = append(files, strings.TrimPrefix(f.URI, "file://")) + } + } + } + + return files, nil +} + +func (b *Bazel) Query(ctx context.Context, args ...string) ([]string, error) { + output, err := b.run(ctx, "query", args...) + if err != nil { + return nil, err + } + return strings.Split(strings.TrimSpace(output), "\n"), nil +} + +func (b *Bazel) WorkspaceRoot() string { + return b.workspaceRoot +} + +func (b *Bazel) ExecutionRoot() string { + return b.execRoot +} diff --git a/go/tools/gopackagesdriver/bazel_json_builder.go b/go/tools/gopackagesdriver/bazel_json_builder.go new file mode 100644 index 0000000000..83f065b26f --- /dev/null +++ b/go/tools/gopackagesdriver/bazel_json_builder.go @@ -0,0 +1,75 @@ +package main + +import ( + "context" + "fmt" + "strings" +) + +type BazelJSONBuilder struct { + bazel *Bazel + query string + tagFilters string + targets []string +} + +func NewBazelJSONBuilder(bazel *Bazel, query, tagFilters string, targets []string) (*BazelJSONBuilder, error) { + return &BazelJSONBuilder{ + bazel: bazel, + query: query, + tagFilters: tagFilters, + targets: targets, + }, nil +} + +func (b *BazelJSONBuilder) Build(ctx context.Context, needExports bool) ([]string, error) { + output_groups := "go_pkg_driver_json,go_pkg_driver_stdlib_json" + + // Override for now + needExports = true + if needExports { + output_groups += ",go_pkg_driver_x" + } + buildsArgs := []string{ + "--aspects=@io_bazel_rules_go//go/tools/gopackagesdriver:aspect.bzl%go_pkg_info_aspect", + "--aspects=@io_bazel_rules_go//go/tools/gopackagesdriver:aspect.bzl%go_std_pkg_info_aspect", + "--output_groups=" + output_groups, + } + + if b.tagFilters != "" { + buildsArgs = append(buildsArgs, "--build_tag_filters="+b.tagFilters) + } + + if b.query != "" { + queryTargets, err := b.bazel.Query(ctx, b.query) + if err != nil { + return nil, fmt.Errorf("unable to query %v: %w", b.query, err) + } + buildsArgs = append(buildsArgs, queryTargets...) + } + + buildsArgs = append(buildsArgs, b.targets...) + + files, err := b.bazel.Build(ctx, buildsArgs...) + if err != nil { + return nil, fmt.Errorf("unable to bazel build %v: %w", buildsArgs, err) + } + + ret := []string{} + for _, f := range files { + if strings.HasSuffix(f, ".pkg.json") == false { + continue + } + ret = append(ret, f) + } + + return ret, nil +} + +func (b *BazelJSONBuilder) PathResolver() PathResolverFunc { + return func(p string) string { + p = strings.Replace(p, "__BAZEL_EXECROOT__", b.bazel.ExecutionRoot(), 1) + p = strings.Replace(p, "__BAZEL_WORKSPACE__", b.bazel.WorkspaceRoot(), 1) + return p + } +} diff --git a/go/tools/gopackagesdriver/driver_request.go b/go/tools/gopackagesdriver/driver_request.go new file mode 100644 index 0000000000..92e7aaac3f --- /dev/null +++ b/go/tools/gopackagesdriver/driver_request.go @@ -0,0 +1,29 @@ +package main + +import ( + "encoding/json" + "io" + + "golang.org/x/tools/go/packages" +) + +type DriverRequest struct { + Mode packages.LoadMode `json:"mode"` + // Env specifies the environment the underlying build system should be run in. + Env []string `json:"env"` + // BuildFlags are flags that should be passed to the underlying build system. + BuildFlags []string `json:"build_flags"` + // Tests specifies whether the patterns should also return test packages. + Tests bool `json:"tests"` + // Overlay maps file paths (relative to the driver's working directory) to the byte contents + // of overlay files. + Overlay map[string][]byte `json:"overlay"` +} + +func ReadDriverRequest(r io.Reader) (*DriverRequest, error) { + req := &DriverRequest{} + if err := json.NewDecoder(r).Decode(&req); err != nil { + return nil, err + } + return req, nil +} diff --git a/go/tools/gopackagesdriver/flatpackage.go b/go/tools/gopackagesdriver/flatpackage.go new file mode 100644 index 0000000000..2dea72eeec --- /dev/null +++ b/go/tools/gopackagesdriver/flatpackage.go @@ -0,0 +1,121 @@ +package main + +import ( + "encoding/json" + "go/parser" + "go/token" + "os" + "strings" +) + +type ResolvePkgFunc func(importPath string) *FlatPackage + +// Copy and pasted from golang.org/x/tools/go/packages +type FlatPackagesError struct { + Pos string // "file:line:col" or "file:line" or "" or "-" + Msg string + Kind FlatPackagesErrorKind +} + +type FlatPackagesErrorKind int + +const ( + UnknownError FlatPackagesErrorKind = iota + ListError + ParseError + TypeError +) + +func (err FlatPackagesError) Error() string { + pos := err.Pos + if pos == "" { + pos = "-" // like token.Position{}.String() + } + return pos + ": " + err.Msg +} + +// FlatPackage is the JSON form of Package +// It drops all the type and syntax fields, and transforms the Imports +type FlatPackage struct { + ID string + Name string `json:",omitempty"` + PkgPath string `json:",omitempty"` + Errors []FlatPackagesError `json:",omitempty"` + GoFiles []string `json:",omitempty"` + CompiledGoFiles []string `json:",omitempty"` + OtherFiles []string `json:",omitempty"` + ExportFile string `json:",omitempty"` + Imports map[string]string `json:",omitempty"` + Standard bool `json:",omitempty"` +} + +type PackageFunc func(pkg *FlatPackage) +type PathResolverFunc func(path string) string + +func resolvePathsInPlace(prf PathResolverFunc, paths []string) { + for i, path := range paths { + paths[i] = prf(path) + } +} + +func WalkFlatPackagesFromJSON(jsonFile string, onPkg PackageFunc) error { + f, err := os.Open(jsonFile) + if err != nil { + return err + } + defer f.Close() + + decoder := json.NewDecoder(f) + for decoder.More() { + pkg := &FlatPackage{} + if err := decoder.Decode(&pkg); err != nil { + return err + } + onPkg(pkg) + } + return nil +} + +func (fp *FlatPackage) ResolvePaths(prf PathResolverFunc) error { + resolvePathsInPlace(prf, fp.CompiledGoFiles) + resolvePathsInPlace(prf, fp.GoFiles) + resolvePathsInPlace(prf, fp.OtherFiles) + fp.ExportFile = prf(fp.ExportFile) + return nil +} + +func (fp *FlatPackage) IsStdlib() bool { + return fp.Standard +} + +func (fp *FlatPackage) ResolveImports(resolve ResolvePkgFunc) { + // Stdlib packages are already complete import wise + if fp.IsStdlib() { + return + } + + fset := token.NewFileSet() + + for _, file := range fp.CompiledGoFiles { + f, err := parser.ParseFile(fset, file, nil, parser.ImportsOnly) + if err != nil { + continue + } + for _, rawImport := range f.Imports { + imp := strings.Trim(rawImport.Path.Value, "\"") + if _, ok := fp.Imports[imp]; ok { + continue + } + if pkg := resolve(imp); pkg != nil { + if fp.Imports == nil { + fp.Imports = map[string]string{} + } + fp.Imports[imp] = pkg.ID + } + } + } +} + +func (fp *FlatPackage) IsRoot() bool { + return strings.HasPrefix(fp.ID, "//") +} diff --git a/go/tools/gopackagesdriver/json_packages_driver.go b/go/tools/gopackagesdriver/json_packages_driver.go new file mode 100644 index 0000000000..61e286b03a --- /dev/null +++ b/go/tools/gopackagesdriver/json_packages_driver.go @@ -0,0 +1,36 @@ +package main + +import "fmt" + +type JSONPackagesDriver struct { + registry *PackageRegistry +} + +func NewJSONPackagesDriver(jsonFiles []string, prf PathResolverFunc) (*JSONPackagesDriver, error) { + jpd := &JSONPackagesDriver{ + registry: NewPackageRegistry(), + } + + for _, f := range jsonFiles { + if err := WalkFlatPackagesFromJSON(f, func(pkg *FlatPackage) { + jpd.registry.Add(pkg) + }); err != nil { + return nil, fmt.Errorf("unable to walk json: %w", err) + } + } + + if err := jpd.registry.ResolvePaths(prf); err != nil { + return nil, err + } + + if err := jpd.registry.ResolveImports(); err != nil { + return nil, err + } + + return jpd, nil +} + +// Match matches packages based on pattern +func (b *JSONPackagesDriver) Packages() []*FlatPackage { + return b.registry.ToList() +} diff --git a/go/tools/gopackagesdriver/main.go b/go/tools/gopackagesdriver/main.go new file mode 100644 index 0000000000..afac47bcef --- /dev/null +++ b/go/tools/gopackagesdriver/main.go @@ -0,0 +1,104 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "go/types" + "os" + "os/signal" + "strings" + + "golang.org/x/tools/go/packages" +) + +type driverResponse struct { + NotHandled bool + + // Sizes, if not nil, is the types.Sizes to use when type checking. + Sizes *types.StdSizes + + // Roots is the set of package IDs that make up the root packages. + // We have to encode this separately because when we encode a single package + // we cannot know if it is one of the roots as that requires knowledge of the + // graph it is part of. + Roots []string `json:",omitempty"` + + // Packages is the full set of packages in the graph. + // The packages are not connected into a graph. + // The Imports if populated will be stubs that only have their ID set. + // Imports will be connected and then type and syntax information added in a + // later pass (see refine). + Packages []*FlatPackage +} + +const ( + defaultBazelBin = "bazel" +) + +var ( + bazelBin = os.Getenv("GOPACKAGESDRIVER_BAZEL") + workspaceRoot = os.Getenv("GOPACKAGESDRIVER_BAZEL_WORKSPACE") + targetsStr = os.Getenv("GOPACKAGESDRIVER_BAZEL_TARGETS") + targetsQueryStr = os.Getenv("GOPACKAGESDRIVER_BAZEL_QUERY") + targetsTagFilters = os.Getenv("GOPACKAGESDRIVER_BAZEL_TAG_FILTERS") +) + +func run() error { + ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt) + defer cancel() + + request, err := ReadDriverRequest(os.Stdin) + if err != nil { + return fmt.Errorf("unable to read request: %w", err) + } + + if bazelBin == "" { + bazelBin = defaultBazelBin + } + bazel, err := NewBazel(ctx, bazelBin, workspaceRoot) + if err != nil { + return fmt.Errorf("unable to create bazel instance: %w", err) + } + + targets := []string{} + if targetsStr != "" { + targets = strings.Split(targetsStr, " ") + } + bazelJsonBuilder, err := NewBazelJSONBuilder(bazel, targetsQueryStr, targetsTagFilters, targets) + jsonFiles, err := bazelJsonBuilder.Build(ctx, request.Mode&packages.NeedExportsFile != 0) + if err != nil { + return fmt.Errorf("unable to build JSON files: %w", err) + } + + driver, err := NewJSONPackagesDriver(jsonFiles, bazelJsonBuilder.PathResolver()) + if err != nil { + return fmt.Errorf("unable to load JSON files: %w", err) + } + + pkgs := driver.Packages() + roots := []string{} + for _, pkg := range pkgs { + if pkg.IsRoot() { + roots = append(roots, pkg.ID) + } + } + + response := &driverResponse{ + NotHandled: false, + Sizes: types.SizesFor("gc", "amd64").(*types.StdSizes), + Roots: roots, + Packages: pkgs, + } + if err := json.NewEncoder(os.Stdout).Encode(response); err != nil { + return fmt.Errorf("unable to encode response: %w", err) + } + + return nil +} + +func main() { + if err := run(); err != nil { + panic(err) + } +} diff --git a/go/tools/gopackagesdriver/packageregistry.go b/go/tools/gopackagesdriver/packageregistry.go new file mode 100644 index 0000000000..53c7f7dea9 --- /dev/null +++ b/go/tools/gopackagesdriver/packageregistry.go @@ -0,0 +1,75 @@ +package main + +type PackageRegistry struct { + packagesByID map[string]*FlatPackage + packagesByImportPath map[string]*FlatPackage + packagesByFile map[string]*FlatPackage +} + +func NewPackageRegistry(pkgs ...*FlatPackage) *PackageRegistry { + pr := &PackageRegistry{ + packagesByID: map[string]*FlatPackage{}, + packagesByImportPath: map[string]*FlatPackage{}, + packagesByFile: map[string]*FlatPackage{}, + } + pr.Add(pkgs...) + return pr +} + +func (pr *PackageRegistry) Add(pkgs ...*FlatPackage) *PackageRegistry { + for _, pkg := range pkgs { + pr.packagesByID[pkg.ID] = pkg + pr.packagesByImportPath[pkg.PkgPath] = pkg + for _, f := range pkg.GoFiles { + pr.packagesByFile[f] = pkg + } + } + return pr +} + +func (pr *PackageRegistry) FromPkgID(pkgPath string) *FlatPackage { + return pr.packagesByImportPath[pkgPath] +} + +func (pr *PackageRegistry) FromPkgPath(pkgPath string) *FlatPackage { + return pr.packagesByImportPath[pkgPath] +} + +func (pr *PackageRegistry) FromFile(filePath string) *FlatPackage { + return pr.packagesByFile[filePath] +} + +func (pr *PackageRegistry) Remove(pkgs ...*FlatPackage) *PackageRegistry { + for _, pkg := range pkgs { + delete(pr.packagesByID, pkg.ID) + delete(pr.packagesByImportPath, pkg.PkgPath) + for _, f := range pkg.GoFiles { + delete(pr.packagesByFile, f) + } + } + return pr +} + +func (pr *PackageRegistry) ToList() []*FlatPackage { + pkgs := make([]*FlatPackage, 0, len(pr.packagesByID)) + for _, pkg := range pr.packagesByID { + pkgs = append(pkgs, pkg) + } + return pkgs +} + +func (pr *PackageRegistry) ResolvePaths(prf PathResolverFunc) error { + for _, pkg := range pr.packagesByID { + pkg.ResolvePaths(prf) + } + return nil +} + +func (pr *PackageRegistry) ResolveImports() error { + for _, pkg := range pr.packagesByID { + pkg.ResolveImports(func(importPath string) *FlatPackage { + return pr.FromPkgPath(importPath) + }) + } + return nil +} From bdd0556dc3f3720d56bfd4205e44b8741ac1ca2f Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 4 Apr 2021 00:05:51 +0200 Subject: [PATCH 02/41] go/tools/gopackagesdriver: Go <= 1.16 compatibility Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/bazel.go | 3 ++- go/tools/gopackagesdriver/main.go | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/go/tools/gopackagesdriver/bazel.go b/go/tools/gopackagesdriver/bazel.go index a93c06ee05..c1896041a2 100644 --- a/go/tools/gopackagesdriver/bazel.go +++ b/go/tools/gopackagesdriver/bazel.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "io/ioutil" "os" "os/exec" "strings" @@ -55,7 +56,7 @@ func (b *Bazel) run(ctx context.Context, command string, args ...string) (string } func (b *Bazel) Build(ctx context.Context, args ...string) ([]string, error) { - jsonTmp, _ := os.CreateTemp("", "bep_") + jsonTmp, _ := ioutil.TempFile("", "bep_") jsonTmp.Close() defer os.RemoveAll(jsonTmp.Name()) diff --git a/go/tools/gopackagesdriver/main.go b/go/tools/gopackagesdriver/main.go index afac47bcef..270d8ffcff 100644 --- a/go/tools/gopackagesdriver/main.go +++ b/go/tools/gopackagesdriver/main.go @@ -44,8 +44,23 @@ var ( targetsTagFilters = os.Getenv("GOPACKAGESDRIVER_BAZEL_TAG_FILTERS") ) +func signalContext(parentCtx context.Context, signals ...os.Signal) (ctx context.Context, stop context.CancelFunc) { + ctx, cancel := context.WithCancel(parentCtx) + ch := make(chan os.Signal, 1) + go func() { + select { + case <-ch: + cancel() + case <-ctx.Done(): + } + }() + signal.Notify(ch, signals...) + + return ctx, cancel +} + func run() error { - ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt) + ctx, cancel := signalContext(context.Background(), os.Interrupt) defer cancel() request, err := ReadDriverRequest(os.Stdin) From 76e675325c090ebfa589d1d59ae7e4841ad369cf Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 4 Apr 2021 02:25:38 +0200 Subject: [PATCH 03/41] go/tools/gopackagesdriver: don't use compiled Go files Those files are generated and will end up in the temporary mod cache, which isn't available later on. Signed-off-by: Steeve Morin --- go/tools/builders/stdliblist.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/go/tools/builders/stdliblist.go b/go/tools/builders/stdliblist.go index a399625b22..3389bd4310 100644 --- a/go/tools/builders/stdliblist.go +++ b/go/tools/builders/stdliblist.go @@ -124,6 +124,9 @@ func absoluteSourcesPaths(execRoot, pkgDir string, srcs []string) []string { } func packageToPackage(execRoot string, pkg *goListPackage) *flatPackage { + // Don't use generated files from the stdlib + goFiles := absoluteSourcesPaths(execRoot, pkg.Dir, pkg.GoFiles) + newPkg := &flatPackage{ ID: stdlibPackageID(pkg.ImportPath), Name: pkg.Name, @@ -131,8 +134,8 @@ func packageToPackage(execRoot string, pkg *goListPackage) *flatPackage { ExportFile: execRootPath(execRoot, pkg.Target), Imports: map[string]string{}, Standard: pkg.Standard, - GoFiles: absoluteSourcesPaths(execRoot, pkg.Dir, pkg.GoFiles), - CompiledGoFiles: absoluteSourcesPaths(execRoot, pkg.Dir, pkg.CompiledGoFiles), + GoFiles: goFiles, + CompiledGoFiles: goFiles, } for _, imp := range pkg.Imports { newPkg.Imports[imp] = stdlibPackageID(imp) @@ -172,7 +175,7 @@ func stdliblist(args []string) error { if len(build.Default.BuildTags) > 0 { listArgs = append(listArgs, "-tags", strings.Join(build.Default.BuildTags, " ")) } - listArgs = append(listArgs, "-json", "-compiled", "builtin", "std", "runtime/cgo") + listArgs = append(listArgs, "-json", "builtin", "std", "runtime/cgo") jsonFile, err := os.Create(*out) if err != nil { From 5a53ae22d35fc47a3c14d6ad5b9aaf63f21b0af4 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 4 Apr 2021 02:26:54 +0200 Subject: [PATCH 04/41] go/tools/gopackagesdriver: explicitely ignore the C package Signed-off-by: Steeve Morin --- go/tools/builders/stdliblist.go | 2 ++ go/tools/gopackagesdriver/flatpackage.go | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/go/tools/builders/stdliblist.go b/go/tools/builders/stdliblist.go index 3389bd4310..7ee967828e 100644 --- a/go/tools/builders/stdliblist.go +++ b/go/tools/builders/stdliblist.go @@ -140,6 +140,8 @@ func packageToPackage(execRoot string, pkg *goListPackage) *flatPackage { for _, imp := range pkg.Imports { newPkg.Imports[imp] = stdlibPackageID(imp) } + // We don't support CGo for now + delete(newPkg.Imports, "C") return newPkg } diff --git a/go/tools/gopackagesdriver/flatpackage.go b/go/tools/gopackagesdriver/flatpackage.go index 2dea72eeec..9d5827d9ce 100644 --- a/go/tools/gopackagesdriver/flatpackage.go +++ b/go/tools/gopackagesdriver/flatpackage.go @@ -103,6 +103,10 @@ func (fp *FlatPackage) ResolveImports(resolve ResolvePkgFunc) { } for _, rawImport := range f.Imports { imp := strings.Trim(rawImport.Path.Value, "\"") + // We don't handle CGo for now + if imp == "C" { + continue + } if _, ok := fp.Imports[imp]; ok { continue } From d2b527abaec1c0b85ba71b34e3a70c62b522acca Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 4 Apr 2021 02:28:16 +0200 Subject: [PATCH 05/41] go/tools/packagesdriver: fetch package name from the sources This handles some edges cases in which the import path last part is not the package name. Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/aspect.bzl | 1 - go/tools/gopackagesdriver/flatpackage.go | 4 ++++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/go/tools/gopackagesdriver/aspect.bzl b/go/tools/gopackagesdriver/aspect.bzl index ed634111a6..7e127e29cb 100644 --- a/go/tools/gopackagesdriver/aspect.bzl +++ b/go/tools/gopackagesdriver/aspect.bzl @@ -30,7 +30,6 @@ def _go_pkg_info_aspect_impl(target, ctx): x = archive.data.export_file pkg = struct( ID = str(archive.data.label), - Name = "main" if archive.source.library.is_main else paths.basename(archive.data.importpath), PkgPath = archive.data.importpath, ExportFile = _file_path(archive.data.export_file), GoFiles = [ diff --git a/go/tools/gopackagesdriver/flatpackage.go b/go/tools/gopackagesdriver/flatpackage.go index 9d5827d9ce..c5566b0c6a 100644 --- a/go/tools/gopackagesdriver/flatpackage.go +++ b/go/tools/gopackagesdriver/flatpackage.go @@ -101,6 +101,10 @@ func (fp *FlatPackage) ResolveImports(resolve ResolvePkgFunc) { if err != nil { continue } + // If the name is not provided, fetch it from the sources + if fp.Name == "" { + fp.Name = f.Name.Name + } for _, rawImport := range f.Imports { imp := strings.Trim(rawImport.Path.Value, "\"") // We don't handle CGo for now From ce618824371eee15c038385beb543143022fb9e1 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 4 Apr 2021 13:54:05 +0200 Subject: [PATCH 06/41] go/tools/gopackagesdriver: ensure bazel doesn't print out output files It's no use, and it can be a significant amount of files, so disable it. Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/bazel_json_builder.go | 1 + 1 file changed, 1 insertion(+) diff --git a/go/tools/gopackagesdriver/bazel_json_builder.go b/go/tools/gopackagesdriver/bazel_json_builder.go index 83f065b26f..ba8831bb28 100644 --- a/go/tools/gopackagesdriver/bazel_json_builder.go +++ b/go/tools/gopackagesdriver/bazel_json_builder.go @@ -34,6 +34,7 @@ func (b *BazelJSONBuilder) Build(ctx context.Context, needExports bool) ([]strin "--aspects=@io_bazel_rules_go//go/tools/gopackagesdriver:aspect.bzl%go_pkg_info_aspect", "--aspects=@io_bazel_rules_go//go/tools/gopackagesdriver:aspect.bzl%go_std_pkg_info_aspect", "--output_groups=" + output_groups, + "--show_result=0", } if b.tagFilters != "" { From b08da87a1c1919cdb28e0f6775d58469cf49a377 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 4 Apr 2021 14:01:44 +0200 Subject: [PATCH 07/41] go/tools/gopackagesdriver: don't index by package ID or files The indexing by ID was never used except for iterating on all packages. The file index isn't used either since the whole graph is dumped anyway. Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/packageregistry.go | 28 +++----------------- 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/go/tools/gopackagesdriver/packageregistry.go b/go/tools/gopackagesdriver/packageregistry.go index 53c7f7dea9..348cade5d8 100644 --- a/go/tools/gopackagesdriver/packageregistry.go +++ b/go/tools/gopackagesdriver/packageregistry.go @@ -1,16 +1,12 @@ package main type PackageRegistry struct { - packagesByID map[string]*FlatPackage packagesByImportPath map[string]*FlatPackage - packagesByFile map[string]*FlatPackage } func NewPackageRegistry(pkgs ...*FlatPackage) *PackageRegistry { pr := &PackageRegistry{ - packagesByID: map[string]*FlatPackage{}, packagesByImportPath: map[string]*FlatPackage{}, - packagesByFile: map[string]*FlatPackage{}, } pr.Add(pkgs...) return pr @@ -18,55 +14,39 @@ func NewPackageRegistry(pkgs ...*FlatPackage) *PackageRegistry { func (pr *PackageRegistry) Add(pkgs ...*FlatPackage) *PackageRegistry { for _, pkg := range pkgs { - pr.packagesByID[pkg.ID] = pkg pr.packagesByImportPath[pkg.PkgPath] = pkg - for _, f := range pkg.GoFiles { - pr.packagesByFile[f] = pkg - } } return pr } -func (pr *PackageRegistry) FromPkgID(pkgPath string) *FlatPackage { - return pr.packagesByImportPath[pkgPath] -} - func (pr *PackageRegistry) FromPkgPath(pkgPath string) *FlatPackage { return pr.packagesByImportPath[pkgPath] } -func (pr *PackageRegistry) FromFile(filePath string) *FlatPackage { - return pr.packagesByFile[filePath] -} - func (pr *PackageRegistry) Remove(pkgs ...*FlatPackage) *PackageRegistry { for _, pkg := range pkgs { - delete(pr.packagesByID, pkg.ID) delete(pr.packagesByImportPath, pkg.PkgPath) - for _, f := range pkg.GoFiles { - delete(pr.packagesByFile, f) - } } return pr } func (pr *PackageRegistry) ToList() []*FlatPackage { - pkgs := make([]*FlatPackage, 0, len(pr.packagesByID)) - for _, pkg := range pr.packagesByID { + pkgs := make([]*FlatPackage, 0, len(pr.packagesByImportPath)) + for _, pkg := range pr.packagesByImportPath { pkgs = append(pkgs, pkg) } return pkgs } func (pr *PackageRegistry) ResolvePaths(prf PathResolverFunc) error { - for _, pkg := range pr.packagesByID { + for _, pkg := range pr.packagesByImportPath { pkg.ResolvePaths(prf) } return nil } func (pr *PackageRegistry) ResolveImports() error { - for _, pkg := range pr.packagesByID { + for _, pkg := range pr.packagesByImportPath { pkg.ResolveImports(func(importPath string) *FlatPackage { return pr.FromPkgPath(importPath) }) From 5dd1e0e4f41fd437b14b1da3014c0ceb943fb0bf Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 4 Apr 2021 23:03:31 +0200 Subject: [PATCH 08/41] go/tools/builders/stdliblist: ensure CC is absolute Needed when in a CGo environment for go list to work Signed-off-by: Steeve Morin --- go/tools/builders/stdliblist.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/go/tools/builders/stdliblist.go b/go/tools/builders/stdliblist.go index 7ee967828e..62192728ec 100644 --- a/go/tools/builders/stdliblist.go +++ b/go/tools/builders/stdliblist.go @@ -165,6 +165,9 @@ func stdliblist(args []string) error { } os.Setenv("PATH", strings.Join(absPaths, string(os.PathListSeparator))) os.Setenv("GOROOT", abs(os.Getenv("GOROOT"))) + // Make sure we have an absolute path to the C compiler. + // TODO(#1357): also take absolute paths of includes and other paths in flags. + os.Setenv("CC", abs(os.Getenv("CC"))) execRoot := abs(".") From 168e90d517836081126d48ec53286ff50a3e8031 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 4 Apr 2021 23:41:02 +0200 Subject: [PATCH 09/41] go/tools/gopackagedriver: fetch stdlib info from inner most target Fetch the stdlib JSON from the deepest target, and cascade it upward so that transitions are applied properly. Also, this enables applying the aspect to a target that depends on a go_binary, such as a cc_binary with proper transition applied. Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/aspect.bzl | 72 ++++++++++--------- .../gopackagesdriver/bazel_json_builder.go | 1 - 2 files changed, 38 insertions(+), 35 deletions(-) diff --git a/go/tools/gopackagesdriver/aspect.bzl b/go/tools/gopackagesdriver/aspect.bzl index 7e127e29cb..8544570183 100644 --- a/go/tools/gopackagesdriver/aspect.bzl +++ b/go/tools/gopackagesdriver/aspect.bzl @@ -1,4 +1,8 @@ -load("//go:def.bzl", "GoArchive") +load( + "//go/private:providers.bzl", + "GoArchive", + "GoStdLib", +) load( "//go/private:context.bzl", "go_context", @@ -23,6 +27,30 @@ def _file_path(f): return paths.join("__BAZEL_EXECROOT__", f.path) def _go_pkg_info_aspect_impl(target, ctx): + # Fetch the stdlib JSON file from thte inner most target + stdlib_json = None + + deps_transitive_json = [] + deps_transitive_x = [] + if hasattr(ctx.rule.attr, "deps"): + for dep in ctx.rule.attr.deps: + if GoPkgInfo in dep: + pkg_info = dep[GoPkgInfo] + deps_transitive_json.append(pkg_info.transitive_json) + deps_transitive_x.append(pkg_info.transitive_x) + # Fetch the stdlib json from the first dependency + if not stdlib_json: + stdlib_json = pkg_info.stdlib_json + + # If deps are embedded, no not gather their json or x since they are + # included in the current target, but do gather their deps'. + if hasattr(ctx.rule.attr, "embed"): + for dep in ctx.rule.attr.embed: + if GoPkgInfo in dep: + pkg_info = dep[GoPkgInfo] + deps_transitive_json.append(pkg_info.deps_transitive_json) + deps_transitive_x.append(pkg_info.deps_transitive_x) + pkg_json = None x = None if GoArchive in target: @@ -43,28 +71,16 @@ def _go_pkg_info_aspect_impl(target, ctx): ) pkg_json = ctx.actions.declare_file(archive.data.name + ".pkg.json") ctx.actions.write(pkg_json, content = pkg.to_json()) - - deps_transitive_json = [] - deps_transitive_x = [] - if hasattr(ctx.rule.attr, "deps"): - for dep in ctx.rule.attr.deps: - if GoPkgInfo in dep: - pkg_info = dep[GoPkgInfo] - deps_transitive_json.append(pkg_info.transitive_json) - deps_transitive_x.append(pkg_info.transitive_x) - # If deps are embedded, no not gather their json or x since they are - # included in the current target, but do gather their deps'. - if hasattr(ctx.rule.attr, "embed"): - for dep in ctx.rule.attr.embed: - if GoPkgInfo in dep: - pkg_info = dep[GoPkgInfo] - deps_transitive_json.append(pkg_info.deps_transitive_json) - deps_transitive_x.append(pkg_info.deps_transitive_x) + # If there was no stdlib json in any dependencies, fetch it from the + # current go_ node. + if not stdlib_json: + stdlib_json = ctx.attr._go_stdlib[GoStdLib].list_json pkg_info = GoPkgInfo( json = pkg_json, + stdlib_json = stdlib_json, transitive_json = depset( - direct = [pkg_json] if pkg_json else None, + direct = [pkg_json] if pkg_json else [], transitive = deps_transitive_json, ), deps_transitive_json = depset( @@ -84,6 +100,7 @@ def _go_pkg_info_aspect_impl(target, ctx): pkg_info, OutputGroupInfo( go_pkg_driver_json = pkg_info.transitive_json, + go_pkg_driver_stdlib_json = depset([pkg_info.stdlib_json]), go_pkg_driver_x = pkg_info.transitive_x, ) ] @@ -91,22 +108,9 @@ def _go_pkg_info_aspect_impl(target, ctx): go_pkg_info_aspect = aspect( implementation = _go_pkg_info_aspect_impl, attr_aspects = ["embed", "deps"], -) - -def _go_std_pkg_info_aspect_impl(target, ctx): - go = go_context(ctx, attr = ctx.rule.attr) - return [ - OutputGroupInfo( - go_pkg_driver_stdlib_json = [go.stdlib.list_json], - ), - ] - -go_std_pkg_info_aspect = aspect( - implementation = _go_std_pkg_info_aspect_impl, attrs = { - "_go_context_data": attr.label( - default = "@io_bazel_rules_go//:go_context_data", + "_go_stdlib": attr.label( + default = "@io_bazel_rules_go//:stdlib", ), }, - toolchains = ["@io_bazel_rules_go//go:toolchain"], ) diff --git a/go/tools/gopackagesdriver/bazel_json_builder.go b/go/tools/gopackagesdriver/bazel_json_builder.go index ba8831bb28..7a9fbfaddd 100644 --- a/go/tools/gopackagesdriver/bazel_json_builder.go +++ b/go/tools/gopackagesdriver/bazel_json_builder.go @@ -32,7 +32,6 @@ func (b *BazelJSONBuilder) Build(ctx context.Context, needExports bool) ([]strin } buildsArgs := []string{ "--aspects=@io_bazel_rules_go//go/tools/gopackagesdriver:aspect.bzl%go_pkg_info_aspect", - "--aspects=@io_bazel_rules_go//go/tools/gopackagesdriver:aspect.bzl%go_std_pkg_info_aspect", "--output_groups=" + output_groups, "--show_result=0", } From 331470a74d75be04fc18e45fc012dfa828076d1d Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 4 Apr 2021 23:44:34 +0200 Subject: [PATCH 10/41] go/tools/gopackagesdriver: move bazel UI related flags to the bazel type This is more correct. Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/bazel.go | 2 ++ go/tools/gopackagesdriver/bazel_json_builder.go | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/go/tools/gopackagesdriver/bazel.go b/go/tools/gopackagesdriver/bazel.go index c1896041a2..32b19cc233 100644 --- a/go/tools/gopackagesdriver/bazel.go +++ b/go/tools/gopackagesdriver/bazel.go @@ -47,6 +47,8 @@ func (b *Bazel) run(ctx context.Context, command string, args ...string) (string cmd := exec.CommandContext(ctx, b.bazelBin, append([]string{ command, "--tool_tag=" + toolTag, + "--show_result=0", + "--ui_actions_shown=0", }, args...)...) fmt.Fprintln(os.Stderr, "Running:", cmd.Args) cmd.Dir = b.workspaceRoot diff --git a/go/tools/gopackagesdriver/bazel_json_builder.go b/go/tools/gopackagesdriver/bazel_json_builder.go index 7a9fbfaddd..c7c9542eab 100644 --- a/go/tools/gopackagesdriver/bazel_json_builder.go +++ b/go/tools/gopackagesdriver/bazel_json_builder.go @@ -33,7 +33,6 @@ func (b *BazelJSONBuilder) Build(ctx context.Context, needExports bool) ([]strin buildsArgs := []string{ "--aspects=@io_bazel_rules_go//go/tools/gopackagesdriver:aspect.bzl%go_pkg_info_aspect", "--output_groups=" + output_groups, - "--show_result=0", } if b.tagFilters != "" { From 29d0b78736555502ea7bb9aa923991abce0e037a Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Mon, 5 Apr 2021 09:52:40 +0200 Subject: [PATCH 11/41] go/tools/gopackagesdriver: add keep_going when building This ensures that all possible packages are built Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/bazel_json_builder.go | 1 + 1 file changed, 1 insertion(+) diff --git a/go/tools/gopackagesdriver/bazel_json_builder.go b/go/tools/gopackagesdriver/bazel_json_builder.go index c7c9542eab..8424d9cb87 100644 --- a/go/tools/gopackagesdriver/bazel_json_builder.go +++ b/go/tools/gopackagesdriver/bazel_json_builder.go @@ -33,6 +33,7 @@ func (b *BazelJSONBuilder) Build(ctx context.Context, needExports bool) ([]strin buildsArgs := []string{ "--aspects=@io_bazel_rules_go//go/tools/gopackagesdriver:aspect.bzl%go_pkg_info_aspect", "--output_groups=" + output_groups, + "--keep_going", // Build all possible packages } if b.tagFilters != "" { From 529b5ab416d0c351f13b21f447ae7ba9d6801939 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Mon, 5 Apr 2021 10:10:47 +0200 Subject: [PATCH 12/41] go/tools/gopackagesdriver: remove dependecy on x/tools/packages The key types have to be copied and paster so might has well remove the dependency altogether. In the process, rework how the LoadMode is passed. Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/BUILD.bazel | 1 - .../gopackagesdriver/bazel_json_builder.go | 23 +++++--- go/tools/gopackagesdriver/driver_request.go | 56 +++++++++++++++++-- go/tools/gopackagesdriver/main.go | 4 +- 4 files changed, 66 insertions(+), 18 deletions(-) diff --git a/go/tools/gopackagesdriver/BUILD.bazel b/go/tools/gopackagesdriver/BUILD.bazel index 131e4641a3..6caf91a30f 100644 --- a/go/tools/gopackagesdriver/BUILD.bazel +++ b/go/tools/gopackagesdriver/BUILD.bazel @@ -13,7 +13,6 @@ go_library( ], importpath = "github.com/bazelbuild/rules_go/go/tools/gopackagesdriver", visibility = ["//visibility:private"], - deps = ["@org_golang_x_tools//go/packages:go_default_library"], ) go_binary( diff --git a/go/tools/gopackagesdriver/bazel_json_builder.go b/go/tools/gopackagesdriver/bazel_json_builder.go index 8424d9cb87..5b6611eef5 100644 --- a/go/tools/gopackagesdriver/bazel_json_builder.go +++ b/go/tools/gopackagesdriver/bazel_json_builder.go @@ -13,6 +13,12 @@ type BazelJSONBuilder struct { targets []string } +const ( + OutputGroupDriverJSON = "go_pkg_driver_json" + OutputGroupStdLibJSON = "go_pkg_driver_stdlib_json" + OutputGroupExportFiles = "go_pkg_driver_x" +) + func NewBazelJSONBuilder(bazel *Bazel, query, tagFilters string, targets []string) (*BazelJSONBuilder, error) { return &BazelJSONBuilder{ bazel: bazel, @@ -22,17 +28,18 @@ func NewBazelJSONBuilder(bazel *Bazel, query, tagFilters string, targets []strin }, nil } -func (b *BazelJSONBuilder) Build(ctx context.Context, needExports bool) ([]string, error) { - output_groups := "go_pkg_driver_json,go_pkg_driver_stdlib_json" - - // Override for now - needExports = true - if needExports { - output_groups += ",go_pkg_driver_x" +func (b *BazelJSONBuilder) outputGroupsForMode(mode LoadMode) string { + og := OutputGroupDriverJSON + "," + OutputGroupStdLibJSON + if mode&NeedExportsFile != 0 || true { // override for now + og += "," + OutputGroupExportFiles } + return og +} + +func (b *BazelJSONBuilder) Build(ctx context.Context, mode LoadMode) ([]string, error) { buildsArgs := []string{ "--aspects=@io_bazel_rules_go//go/tools/gopackagesdriver:aspect.bzl%go_pkg_info_aspect", - "--output_groups=" + output_groups, + "--output_groups=" + b.outputGroupsForMode(mode), "--keep_going", // Build all possible packages } diff --git a/go/tools/gopackagesdriver/driver_request.go b/go/tools/gopackagesdriver/driver_request.go index 92e7aaac3f..f07f11cdcd 100644 --- a/go/tools/gopackagesdriver/driver_request.go +++ b/go/tools/gopackagesdriver/driver_request.go @@ -3,21 +3,65 @@ package main import ( "encoding/json" "io" +) + +// From https://pkg.go.dev/golang.org/x/tools/go/packages#LoadMode +type LoadMode int + +// Only NeedExportsFile is needed in our case +const ( + // NeedName adds Name and PkgPath. + NeedName LoadMode = 1 << iota + + // NeedFiles adds GoFiles and OtherFiles. + NeedFiles + + // NeedCompiledGoFiles adds CompiledGoFiles. + NeedCompiledGoFiles + + // NeedImports adds Imports. If NeedDeps is not set, the Imports field will contain + // "placeholder" Packages with only the ID set. + NeedImports + + // NeedDeps adds the fields requested by the LoadMode in the packages in Imports. + NeedDeps + + // NeedExportsFile adds ExportFile. + NeedExportsFile + + // NeedTypes adds Types, Fset, and IllTyped. + NeedTypes + + // NeedSyntax adds Syntax. + NeedSyntax + + // NeedTypesInfo adds TypesInfo. + NeedTypesInfo + + // NeedTypesSizes adds TypesSizes. + NeedTypesSizes + + // typecheckCgo enables full support for type checking cgo. Requires Go 1.15+. + // Modifies CompiledGoFiles and Types, and has no effect on its own. + typecheckCgo - "golang.org/x/tools/go/packages" + // NeedModule adds Module. + NeedModule ) +// From https://github.com/golang/tools/blob/v0.1.0/go/packages/external.go#L32 +// Most fields are disabled since there are no needs for them type DriverRequest struct { - Mode packages.LoadMode `json:"mode"` + Mode LoadMode `json:"mode"` // Env specifies the environment the underlying build system should be run in. - Env []string `json:"env"` + // Env []string `json:"env"` // BuildFlags are flags that should be passed to the underlying build system. - BuildFlags []string `json:"build_flags"` + // BuildFlags []string `json:"build_flags"` // Tests specifies whether the patterns should also return test packages. - Tests bool `json:"tests"` + // Tests bool `json:"tests"` // Overlay maps file paths (relative to the driver's working directory) to the byte contents // of overlay files. - Overlay map[string][]byte `json:"overlay"` + // Overlay map[string][]byte `json:"overlay"` } func ReadDriverRequest(r io.Reader) (*DriverRequest, error) { diff --git a/go/tools/gopackagesdriver/main.go b/go/tools/gopackagesdriver/main.go index 270d8ffcff..9186cc59c0 100644 --- a/go/tools/gopackagesdriver/main.go +++ b/go/tools/gopackagesdriver/main.go @@ -8,8 +8,6 @@ import ( "os" "os/signal" "strings" - - "golang.org/x/tools/go/packages" ) type driverResponse struct { @@ -81,7 +79,7 @@ func run() error { targets = strings.Split(targetsStr, " ") } bazelJsonBuilder, err := NewBazelJSONBuilder(bazel, targetsQueryStr, targetsTagFilters, targets) - jsonFiles, err := bazelJsonBuilder.Build(ctx, request.Mode&packages.NeedExportsFile != 0) + jsonFiles, err := bazelJsonBuilder.Build(ctx, request.Mode) if err != nil { return fmt.Errorf("unable to build JSON files: %w", err) } From a0eeeec16fa2c7b63e135f1d5d59f734f4537bf6 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Mon, 5 Apr 2021 10:14:47 +0200 Subject: [PATCH 13/41] Buildifier pass Signed-off-by: Steeve Morin --- go/private/actions/stdlib.bzl | 1 - go/tools/gopackagesdriver/BUILD.bazel | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/go/private/actions/stdlib.bzl b/go/private/actions/stdlib.bzl index 013e0ecbb0..f59c269c37 100644 --- a/go/private/actions/stdlib.bzl +++ b/go/private/actions/stdlib.bzl @@ -65,7 +65,6 @@ def _build_stdlib_list_json(go): ) return out - def _sdk_stdlib(go): return GoStdLib( root_file = go.sdk.root_file, diff --git a/go/tools/gopackagesdriver/BUILD.bazel b/go/tools/gopackagesdriver/BUILD.bazel index 6caf91a30f..47de628c34 100644 --- a/go/tools/gopackagesdriver/BUILD.bazel +++ b/go/tools/gopackagesdriver/BUILD.bazel @@ -3,13 +3,13 @@ load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") go_library( name = "go_default_library", srcs = [ - "bazel_json_builder.go", "bazel.go", + "bazel_json_builder.go", "driver_request.go", "flatpackage.go", "json_packages_driver.go", - "packageregistry.go", "main.go", + "packageregistry.go", ], importpath = "github.com/bazelbuild/rules_go/go/tools/gopackagesdriver", visibility = ["//visibility:private"], From 79f0cb89295898c1b3285fb2a0508923633d94ac Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Mon, 5 Apr 2021 11:49:41 +0200 Subject: [PATCH 14/41] go/tools/gopackagesdriver: use BUILD_WORKSPACE_DIRECTORY for workspace Since the packages driver is meant to be run via `bazel run`, it's simpler to leverage BUILD_WORKSPACE_DIRECTORY. It's one less parameter. Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/tools/gopackagesdriver/main.go b/go/tools/gopackagesdriver/main.go index 9186cc59c0..fa2b2597e4 100644 --- a/go/tools/gopackagesdriver/main.go +++ b/go/tools/gopackagesdriver/main.go @@ -36,7 +36,7 @@ const ( var ( bazelBin = os.Getenv("GOPACKAGESDRIVER_BAZEL") - workspaceRoot = os.Getenv("GOPACKAGESDRIVER_BAZEL_WORKSPACE") + workspaceRoot = os.Getenv("BUILD_WORKSPACE_DIRECTORY") targetsStr = os.Getenv("GOPACKAGESDRIVER_BAZEL_TARGETS") targetsQueryStr = os.Getenv("GOPACKAGESDRIVER_BAZEL_QUERY") targetsTagFilters = os.Getenv("GOPACKAGESDRIVER_BAZEL_TAG_FILTERS") From 8f76121055608530dd367691cd772df97fdc819e Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Mon, 5 Apr 2021 11:54:59 +0200 Subject: [PATCH 15/41] go/tools/gopackagesdriver: simplify bazel bin lookup Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/main.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/go/tools/gopackagesdriver/main.go b/go/tools/gopackagesdriver/main.go index fa2b2597e4..7867a10d9d 100644 --- a/go/tools/gopackagesdriver/main.go +++ b/go/tools/gopackagesdriver/main.go @@ -30,18 +30,21 @@ type driverResponse struct { Packages []*FlatPackage } -const ( - defaultBazelBin = "bazel" -) - var ( - bazelBin = os.Getenv("GOPACKAGESDRIVER_BAZEL") + bazelBin = getenvDefault("GOPACKAGESDRIVER_BAZEL", "bazel") workspaceRoot = os.Getenv("BUILD_WORKSPACE_DIRECTORY") targetsStr = os.Getenv("GOPACKAGESDRIVER_BAZEL_TARGETS") targetsQueryStr = os.Getenv("GOPACKAGESDRIVER_BAZEL_QUERY") targetsTagFilters = os.Getenv("GOPACKAGESDRIVER_BAZEL_TAG_FILTERS") ) +func getenvDefault(key, defaultValue string) string { + if v, ok := os.LookupEnv(key); ok { + return v + } + return defaultValue +} + func signalContext(parentCtx context.Context, signals ...os.Signal) (ctx context.Context, stop context.CancelFunc) { ctx, cancel := context.WithCancel(parentCtx) ch := make(chan os.Signal, 1) @@ -66,9 +69,6 @@ func run() error { return fmt.Errorf("unable to read request: %w", err) } - if bazelBin == "" { - bazelBin = defaultBazelBin - } bazel, err := NewBazel(ctx, bazelBin, workspaceRoot) if err != nil { return fmt.Errorf("unable to create bazel instance: %w", err) From 1203bea0b396ae2b04e8c088027b15ebbf5513d8 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Mon, 5 Apr 2021 12:19:32 +0200 Subject: [PATCH 16/41] go/tools/gopackagesdriver: simplify BEP JSON file handling Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/bazel.go | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/go/tools/gopackagesdriver/bazel.go b/go/tools/gopackagesdriver/bazel.go index 32b19cc233..74b84dc87c 100644 --- a/go/tools/gopackagesdriver/bazel.go +++ b/go/tools/gopackagesdriver/bazel.go @@ -58,24 +58,23 @@ func (b *Bazel) run(ctx context.Context, command string, args ...string) (string } func (b *Bazel) Build(ctx context.Context, args ...string) ([]string, error) { - jsonTmp, _ := ioutil.TempFile("", "bep_") - jsonTmp.Close() - defer os.RemoveAll(jsonTmp.Name()) + jsonFile, err := ioutil.TempFile("", "gopackagesdriver_bep_") + if err != nil { + return nil, fmt.Errorf("unable to create BEP JSON file: %w", err) + } + defer func() { + jsonFile.Close() + os.Remove(jsonFile.Name()) + }() args = append([]string{ - "--build_event_json_file=" + jsonTmp.Name(), + "--build_event_json_file=" + jsonFile.Name(), "--build_event_json_file_path_conversion=no", }, args...) if _, err := b.run(ctx, "build", args...); err != nil { return nil, err } - jsonFile, err := os.Open(jsonTmp.Name()) - if err != nil { - return nil, err - } - defer jsonFile.Close() - files := make([]string, 0) decoder := json.NewDecoder(jsonFile) for decoder.More() { From 2657063585efaee4428a2c7cc847d3e26fc4b92a Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Mon, 5 Apr 2021 12:27:38 +0200 Subject: [PATCH 17/41] go/tools/gopackagesdriver: explicit errors Will be easier when debugging. Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/bazel.go | 8 ++++---- go/tools/gopackagesdriver/driver_request.go | 3 ++- go/tools/gopackagesdriver/flatpackage.go | 5 +++-- go/tools/gopackagesdriver/json_packages_driver.go | 4 ++-- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/go/tools/gopackagesdriver/bazel.go b/go/tools/gopackagesdriver/bazel.go index 74b84dc87c..7be5b42f85 100644 --- a/go/tools/gopackagesdriver/bazel.go +++ b/go/tools/gopackagesdriver/bazel.go @@ -36,7 +36,7 @@ func NewBazel(ctx context.Context, bazelBin, workspaceRoot string) (*Bazel, erro workspaceRoot: workspaceRoot, } if execRoot, err := b.run(ctx, "info", "execution_root"); err != nil { - return nil, err + return nil, fmt.Errorf("unable to find execution root: %w", err) } else { b.execRoot = strings.TrimSpace(execRoot) } @@ -72,7 +72,7 @@ func (b *Bazel) Build(ctx context.Context, args ...string) ([]string, error) { "--build_event_json_file_path_conversion=no", }, args...) if _, err := b.run(ctx, "build", args...); err != nil { - return nil, err + return nil, fmt.Errorf("bazel build failed: %w", err) } files := make([]string, 0) @@ -80,7 +80,7 @@ func (b *Bazel) Build(ctx context.Context, args ...string) ([]string, error) { for decoder.More() { var namedSet BEPNamedSet if err := decoder.Decode(&namedSet); err != nil { - panic(err) + return nil, fmt.Errorf("unable to decode %s: %w", jsonFile, err) } if namedSet.NamedSetOfFiles != nil { for _, f := range namedSet.NamedSetOfFiles.Files { @@ -95,7 +95,7 @@ func (b *Bazel) Build(ctx context.Context, args ...string) ([]string, error) { func (b *Bazel) Query(ctx context.Context, args ...string) ([]string, error) { output, err := b.run(ctx, "query", args...) if err != nil { - return nil, err + return nil, fmt.Errorf("bazel query failed: %w", err) } return strings.Split(strings.TrimSpace(output), "\n"), nil } diff --git a/go/tools/gopackagesdriver/driver_request.go b/go/tools/gopackagesdriver/driver_request.go index f07f11cdcd..8519541ac5 100644 --- a/go/tools/gopackagesdriver/driver_request.go +++ b/go/tools/gopackagesdriver/driver_request.go @@ -2,6 +2,7 @@ package main import ( "encoding/json" + "fmt" "io" ) @@ -67,7 +68,7 @@ type DriverRequest struct { func ReadDriverRequest(r io.Reader) (*DriverRequest, error) { req := &DriverRequest{} if err := json.NewDecoder(r).Decode(&req); err != nil { - return nil, err + return nil, fmt.Errorf("unable to decode driver request: %w", err) } return req, nil } diff --git a/go/tools/gopackagesdriver/flatpackage.go b/go/tools/gopackagesdriver/flatpackage.go index c5566b0c6a..085892980e 100644 --- a/go/tools/gopackagesdriver/flatpackage.go +++ b/go/tools/gopackagesdriver/flatpackage.go @@ -2,6 +2,7 @@ package main import ( "encoding/json" + "fmt" "go/parser" "go/token" "os" @@ -61,7 +62,7 @@ func resolvePathsInPlace(prf PathResolverFunc, paths []string) { func WalkFlatPackagesFromJSON(jsonFile string, onPkg PackageFunc) error { f, err := os.Open(jsonFile) if err != nil { - return err + return fmt.Errorf("unable to open package JSON file: %w", err) } defer f.Close() @@ -69,7 +70,7 @@ func WalkFlatPackagesFromJSON(jsonFile string, onPkg PackageFunc) error { for decoder.More() { pkg := &FlatPackage{} if err := decoder.Decode(&pkg); err != nil { - return err + return fmt.Errorf("unable to decode package in %s: %w", f.Name(), err) } onPkg(pkg) } diff --git a/go/tools/gopackagesdriver/json_packages_driver.go b/go/tools/gopackagesdriver/json_packages_driver.go index 61e286b03a..71cf3f5b3c 100644 --- a/go/tools/gopackagesdriver/json_packages_driver.go +++ b/go/tools/gopackagesdriver/json_packages_driver.go @@ -20,11 +20,11 @@ func NewJSONPackagesDriver(jsonFiles []string, prf PathResolverFunc) (*JSONPacka } if err := jpd.registry.ResolvePaths(prf); err != nil { - return nil, err + return nil, fmt.Errorf("unable to resolve paths: %w", err) } if err := jpd.registry.ResolveImports(); err != nil { - return nil, err + return nil, fmt.Errorf("unable to resolve paths: %w", err) } return jpd, nil From 859a3b1abca6518b9502aef1cbd90bfd31b66deb Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Mon, 5 Apr 2021 21:53:46 +0200 Subject: [PATCH 18/41] go/tools/gopackagesdriver: check for stdlibjson before returning it Sometimes if the aspects explores nodes that won't have go_ rules, there is no inner stdlib_json to fetch. Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/aspect.bzl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go/tools/gopackagesdriver/aspect.bzl b/go/tools/gopackagesdriver/aspect.bzl index 8544570183..87ff72032b 100644 --- a/go/tools/gopackagesdriver/aspect.bzl +++ b/go/tools/gopackagesdriver/aspect.bzl @@ -100,9 +100,9 @@ def _go_pkg_info_aspect_impl(target, ctx): pkg_info, OutputGroupInfo( go_pkg_driver_json = pkg_info.transitive_json, - go_pkg_driver_stdlib_json = depset([pkg_info.stdlib_json]), go_pkg_driver_x = pkg_info.transitive_x, - ) + go_pkg_driver_stdlib_json = depset([pkg_info.stdlib_json] if pkg_info.stdlib_json else []) + ), ] go_pkg_info_aspect = aspect( From 2190ab60546fee9eb5293bf43dfd77680224d2af Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Tue, 6 Apr 2021 00:19:56 +0200 Subject: [PATCH 19/41] go/tools/gopackagesdriver: fix wrong error format Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/bazel.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/tools/gopackagesdriver/bazel.go b/go/tools/gopackagesdriver/bazel.go index 7be5b42f85..83757cda65 100644 --- a/go/tools/gopackagesdriver/bazel.go +++ b/go/tools/gopackagesdriver/bazel.go @@ -80,7 +80,7 @@ func (b *Bazel) Build(ctx context.Context, args ...string) ([]string, error) { for decoder.More() { var namedSet BEPNamedSet if err := decoder.Decode(&namedSet); err != nil { - return nil, fmt.Errorf("unable to decode %s: %w", jsonFile, err) + return nil, fmt.Errorf("unable to decode %s: %w", jsonFile.Name(), err) } if namedSet.NamedSetOfFiles != nil { for _, f := range namedSet.NamedSetOfFiles.Files { From d69fad238e1a1b204aab189699fc0f84c982d22c Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Tue, 6 Apr 2021 15:24:52 +0200 Subject: [PATCH 20/41] go/tools/gopackagesdriver: try to speed up bazel queries Use special flags in order to speed up the bazel query Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/bazel_json_builder.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/go/tools/gopackagesdriver/bazel_json_builder.go b/go/tools/gopackagesdriver/bazel_json_builder.go index 5b6611eef5..afd738363e 100644 --- a/go/tools/gopackagesdriver/bazel_json_builder.go +++ b/go/tools/gopackagesdriver/bazel_json_builder.go @@ -48,7 +48,16 @@ func (b *BazelJSONBuilder) Build(ctx context.Context, mode LoadMode) ([]string, } if b.query != "" { - queryTargets, err := b.bazel.Query(ctx, b.query) + queryTargets, err := b.bazel.Query( + ctx, + "--order_output=no", + "--output=label", + "--experimental_graphless_query", + "--nodep_deps", + "--noimplicit_deps", + "--notool_deps", + b.query, + ) if err != nil { return nil, fmt.Errorf("unable to query %v: %w", b.query, err) } From 4719e91314654a1ab710c676770870ccaedd68fd Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Wed, 7 Apr 2021 00:49:10 +0200 Subject: [PATCH 21/41] go/tools/gopackagesdriver: add missing error check Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/main.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/go/tools/gopackagesdriver/main.go b/go/tools/gopackagesdriver/main.go index 7867a10d9d..636f249c14 100644 --- a/go/tools/gopackagesdriver/main.go +++ b/go/tools/gopackagesdriver/main.go @@ -79,6 +79,10 @@ func run() error { targets = strings.Split(targetsStr, " ") } bazelJsonBuilder, err := NewBazelJSONBuilder(bazel, targetsQueryStr, targetsTagFilters, targets) + if err != nil { + return fmt.Errorf("unable to build JSON files: %w", err) + } + jsonFiles, err := bazelJsonBuilder.Build(ctx, request.Mode) if err != nil { return fmt.Errorf("unable to build JSON files: %w", err) From 07ad71106ff5206e16bfe76b0e0321df7b02c255 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Wed, 7 Apr 2021 22:15:51 +0200 Subject: [PATCH 22/41] go/tools/gopackagesdriver: fix typos in comments Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/aspect.bzl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go/tools/gopackagesdriver/aspect.bzl b/go/tools/gopackagesdriver/aspect.bzl index 87ff72032b..88498b99f8 100644 --- a/go/tools/gopackagesdriver/aspect.bzl +++ b/go/tools/gopackagesdriver/aspect.bzl @@ -27,7 +27,7 @@ def _file_path(f): return paths.join("__BAZEL_EXECROOT__", f.path) def _go_pkg_info_aspect_impl(target, ctx): - # Fetch the stdlib JSON file from thte inner most target + # Fetch the stdlib JSON file from the inner most target stdlib_json = None deps_transitive_json = [] @@ -42,7 +42,7 @@ def _go_pkg_info_aspect_impl(target, ctx): if not stdlib_json: stdlib_json = pkg_info.stdlib_json - # If deps are embedded, no not gather their json or x since they are + # If deps are embedded, do not gather their json or x since they are # included in the current target, but do gather their deps'. if hasattr(ctx.rule.attr, "embed"): for dep in ctx.rule.attr.embed: From b3af1b13b856fbee53f41c1166d00d11f6150b53 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Wed, 7 Apr 2021 22:16:21 +0200 Subject: [PATCH 23/41] go/tools/gopackagesdriver: unquote imports with strconv.Unquote Cleaner than triming. Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/flatpackage.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/go/tools/gopackagesdriver/flatpackage.go b/go/tools/gopackagesdriver/flatpackage.go index 085892980e..1b1ba70472 100644 --- a/go/tools/gopackagesdriver/flatpackage.go +++ b/go/tools/gopackagesdriver/flatpackage.go @@ -6,6 +6,7 @@ import ( "go/parser" "go/token" "os" + "strconv" "strings" ) @@ -107,7 +108,10 @@ func (fp *FlatPackage) ResolveImports(resolve ResolvePkgFunc) { fp.Name = f.Name.Name } for _, rawImport := range f.Imports { - imp := strings.Trim(rawImport.Path.Value, "\"") + imp, err := strconv.Unquote(rawImport.Path.Value) + if err != nil { + continue + } // We don't handle CGo for now if imp == "C" { continue From 079bb2afe15d7c3bbb6f6b6379fbdd31b6fdeeb8 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Thu, 8 Apr 2021 18:55:05 +0200 Subject: [PATCH 24/41] go/tools/gopackagesdriver: pass show_result only to a build command Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/bazel.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/tools/gopackagesdriver/bazel.go b/go/tools/gopackagesdriver/bazel.go index 83757cda65..ea55f84aac 100644 --- a/go/tools/gopackagesdriver/bazel.go +++ b/go/tools/gopackagesdriver/bazel.go @@ -47,7 +47,6 @@ func (b *Bazel) run(ctx context.Context, command string, args ...string) (string cmd := exec.CommandContext(ctx, b.bazelBin, append([]string{ command, "--tool_tag=" + toolTag, - "--show_result=0", "--ui_actions_shown=0", }, args...)...) fmt.Fprintln(os.Stderr, "Running:", cmd.Args) @@ -68,6 +67,7 @@ func (b *Bazel) Build(ctx context.Context, args ...string) ([]string, error) { }() args = append([]string{ + "--show_result=0", "--build_event_json_file=" + jsonFile.Name(), "--build_event_json_file_path_conversion=no", }, args...) From 070b4c3fc482440d46483582b251b673c7a5103f Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Thu, 8 Apr 2021 19:12:14 +0200 Subject: [PATCH 25/41] go/tools/gopackagesdriver: remove unrelated comment Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/json_packages_driver.go | 1 - 1 file changed, 1 deletion(-) diff --git a/go/tools/gopackagesdriver/json_packages_driver.go b/go/tools/gopackagesdriver/json_packages_driver.go index 71cf3f5b3c..a358469d17 100644 --- a/go/tools/gopackagesdriver/json_packages_driver.go +++ b/go/tools/gopackagesdriver/json_packages_driver.go @@ -30,7 +30,6 @@ func NewJSONPackagesDriver(jsonFiles []string, prf PathResolverFunc) (*JSONPacka return jpd, nil } -// Match matches packages based on pattern func (b *JSONPackagesDriver) Packages() []*FlatPackage { return b.registry.ToList() } From 69fef40eb021402e51207e4a9dfd80e4a4c8c796 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Thu, 8 Apr 2021 22:23:29 +0200 Subject: [PATCH 26/41] go/tools/packagesdriver: split targets with fields Empty string becomes handled, and multiple spaces too. Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/main.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/go/tools/gopackagesdriver/main.go b/go/tools/gopackagesdriver/main.go index 636f249c14..45ac7d0a8b 100644 --- a/go/tools/gopackagesdriver/main.go +++ b/go/tools/gopackagesdriver/main.go @@ -33,7 +33,7 @@ type driverResponse struct { var ( bazelBin = getenvDefault("GOPACKAGESDRIVER_BAZEL", "bazel") workspaceRoot = os.Getenv("BUILD_WORKSPACE_DIRECTORY") - targetsStr = os.Getenv("GOPACKAGESDRIVER_BAZEL_TARGETS") + targets = strings.Fields(os.Getenv("GOPACKAGESDRIVER_BAZEL_TARGETS")) targetsQueryStr = os.Getenv("GOPACKAGESDRIVER_BAZEL_QUERY") targetsTagFilters = os.Getenv("GOPACKAGESDRIVER_BAZEL_TAG_FILTERS") ) @@ -74,10 +74,6 @@ func run() error { return fmt.Errorf("unable to create bazel instance: %w", err) } - targets := []string{} - if targetsStr != "" { - targets = strings.Split(targetsStr, " ") - } bazelJsonBuilder, err := NewBazelJSONBuilder(bazel, targetsQueryStr, targetsTagFilters, targets) if err != nil { return fmt.Errorf("unable to build JSON files: %w", err) From fb2df6805155480cf32fed7630ce26200e7c0f70 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 18 Apr 2021 00:20:27 +0200 Subject: [PATCH 27/41] go/tools/gopackagesdriver: rename x as export_file Clearer that way Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/aspect.bzl | 68 +++++++++---------- .../gopackagesdriver/bazel_json_builder.go | 10 +-- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/go/tools/gopackagesdriver/aspect.bzl b/go/tools/gopackagesdriver/aspect.bzl index 88498b99f8..84b6c900e5 100644 --- a/go/tools/gopackagesdriver/aspect.bzl +++ b/go/tools/gopackagesdriver/aspect.bzl @@ -28,34 +28,34 @@ def _file_path(f): def _go_pkg_info_aspect_impl(target, ctx): # Fetch the stdlib JSON file from the inner most target - stdlib_json = None + stdlib_json_file = None - deps_transitive_json = [] - deps_transitive_x = [] + deps_transitive_json_file = [] + deps_transitive_export_file = [] if hasattr(ctx.rule.attr, "deps"): for dep in ctx.rule.attr.deps: if GoPkgInfo in dep: pkg_info = dep[GoPkgInfo] - deps_transitive_json.append(pkg_info.transitive_json) - deps_transitive_x.append(pkg_info.transitive_x) + deps_transitive_json_file.append(pkg_info.transitive_json_file) + deps_transitive_export_file.append(pkg_info.transitive_export_file) # Fetch the stdlib json from the first dependency - if not stdlib_json: - stdlib_json = pkg_info.stdlib_json + if not stdlib_json_file: + stdlib_json_file = pkg_info.stdlib_json_file - # If deps are embedded, do not gather their json or x since they are - # included in the current target, but do gather their deps'. + # If deps are embedded, do not gather their json or export_file since they + # are included in the current target, but do gather their deps'. if hasattr(ctx.rule.attr, "embed"): for dep in ctx.rule.attr.embed: if GoPkgInfo in dep: pkg_info = dep[GoPkgInfo] - deps_transitive_json.append(pkg_info.deps_transitive_json) - deps_transitive_x.append(pkg_info.deps_transitive_x) + deps_transitive_json_file.append(pkg_info.deps_transitive_json_file) + deps_transitive_export_file.append(pkg_info.deps_transitive_export_file) - pkg_json = None - x = None + pkg_json_file = None + export_file = None if GoArchive in target: archive = target[GoArchive] - x = archive.data.export_file + export_file = archive.data.export_file pkg = struct( ID = str(archive.data.label), PkgPath = archive.data.importpath, @@ -69,39 +69,39 @@ def _go_pkg_info_aspect_impl(target, ctx): for src in archive.data.srcs ], ) - pkg_json = ctx.actions.declare_file(archive.data.name + ".pkg.json") - ctx.actions.write(pkg_json, content = pkg.to_json()) + pkg_json_file = ctx.actions.declare_file(archive.data.name + ".pkg.json") + ctx.actions.write(pkg_json_file, content = pkg.to_json()) # If there was no stdlib json in any dependencies, fetch it from the # current go_ node. - if not stdlib_json: - stdlib_json = ctx.attr._go_stdlib[GoStdLib].list_json + if not stdlib_json_file: + stdlib_json_file = ctx.attr._go_stdlib[GoStdLib].list_json pkg_info = GoPkgInfo( - json = pkg_json, - stdlib_json = stdlib_json, - transitive_json = depset( - direct = [pkg_json] if pkg_json else [], - transitive = deps_transitive_json, + json = pkg_json_file, + stdlib_json_file = stdlib_json_file, + transitive_json_file = depset( + direct = [pkg_json_file] if pkg_json_file else [], + transitive = deps_transitive_json_file, ), - deps_transitive_json = depset( - transitive = deps_transitive_json, + deps_transitive_json_file = depset( + transitive = deps_transitive_json_file, ), - x = x, - transitive_x = depset( - direct = [x] if x else None, - transitive = deps_transitive_x, + export_file = export_file, + transitive_export_file = depset( + direct = [export_file] if export_file else [], + transitive = deps_transitive_export_file, ), - deps_transitive_x = depset( - transitive = deps_transitive_x, + deps_transitive_export_file = depset( + transitive = deps_transitive_export_file, ), ) return [ pkg_info, OutputGroupInfo( - go_pkg_driver_json = pkg_info.transitive_json, - go_pkg_driver_x = pkg_info.transitive_x, - go_pkg_driver_stdlib_json = depset([pkg_info.stdlib_json] if pkg_info.stdlib_json else []) + go_pkg_driver_json_file = pkg_info.transitive_json_file, + go_pkg_driver_export_file = pkg_info.transitive_export_file, + go_pkg_driver_stdlib_json_file = depset([pkg_info.stdlib_json_file] if pkg_info.stdlib_json_file else []) ), ] diff --git a/go/tools/gopackagesdriver/bazel_json_builder.go b/go/tools/gopackagesdriver/bazel_json_builder.go index afd738363e..d04b46bfcf 100644 --- a/go/tools/gopackagesdriver/bazel_json_builder.go +++ b/go/tools/gopackagesdriver/bazel_json_builder.go @@ -14,9 +14,9 @@ type BazelJSONBuilder struct { } const ( - OutputGroupDriverJSON = "go_pkg_driver_json" - OutputGroupStdLibJSON = "go_pkg_driver_stdlib_json" - OutputGroupExportFiles = "go_pkg_driver_x" + OutputGroupDriverJSONFile = "go_pkg_driver_json_file" + OutputGroupStdLibJSONFile = "go_pkg_driver_stdlib_json_file" + OutputGroupExportFile = "go_pkg_driver_export_file" ) func NewBazelJSONBuilder(bazel *Bazel, query, tagFilters string, targets []string) (*BazelJSONBuilder, error) { @@ -29,9 +29,9 @@ func NewBazelJSONBuilder(bazel *Bazel, query, tagFilters string, targets []strin } func (b *BazelJSONBuilder) outputGroupsForMode(mode LoadMode) string { - og := OutputGroupDriverJSON + "," + OutputGroupStdLibJSON + og := OutputGroupDriverJSONFile + "," + OutputGroupStdLibJSONFile if mode&NeedExportsFile != 0 || true { // override for now - og += "," + OutputGroupExportFiles + og += "," + OutputGroupExportFile } return og } From 0d01dc39414c7b071d92719e4fd1ee479beb86a8 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 18 Apr 2021 00:21:44 +0200 Subject: [PATCH 28/41] go/tools/gopackagesdriver: make GoStdLib._list_json private Signed-off-by: Steeve Morin --- go/private/actions/stdlib.bzl | 8 ++++---- go/tools/gopackagesdriver/aspect.bzl | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/go/private/actions/stdlib.bzl b/go/private/actions/stdlib.bzl index f59c269c37..80f4011fa5 100644 --- a/go/private/actions/stdlib.bzl +++ b/go/private/actions/stdlib.bzl @@ -67,9 +67,9 @@ def _build_stdlib_list_json(go): def _sdk_stdlib(go): return GoStdLib( - root_file = go.sdk.root_file, - list_json = _build_stdlib_list_json(go), + _list_json = _build_stdlib_list_json(go), libs = go.sdk.libs, + root_file = go.sdk.root_file, ) def _build_stdlib(go): @@ -114,7 +114,7 @@ def _build_stdlib(go): env = env, ) return GoStdLib( - root_file = root_file, - list_json = _build_stdlib_list_json(go), + _list_json = _build_stdlib_list_json(go), libs = [pkg], + root_file = root_file, ) diff --git a/go/tools/gopackagesdriver/aspect.bzl b/go/tools/gopackagesdriver/aspect.bzl index 84b6c900e5..e7ce89a528 100644 --- a/go/tools/gopackagesdriver/aspect.bzl +++ b/go/tools/gopackagesdriver/aspect.bzl @@ -74,7 +74,7 @@ def _go_pkg_info_aspect_impl(target, ctx): # If there was no stdlib json in any dependencies, fetch it from the # current go_ node. if not stdlib_json_file: - stdlib_json_file = ctx.attr._go_stdlib[GoStdLib].list_json + stdlib_json_file = ctx.attr._go_stdlib[GoStdLib]._list_json pkg_info = GoPkgInfo( json = pkg_json_file, From c36a5eb62507a0d274288a1d9fa1e110914c898e Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 18 Apr 2021 00:27:11 +0200 Subject: [PATCH 29/41] go/tools/gopackagesdriver: don't reallocate absolute paths slice Signed-off-by: Steeve Morin --- go/tools/builders/stdliblist.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/tools/builders/stdliblist.go b/go/tools/builders/stdliblist.go index 62192728ec..14bd8d2692 100644 --- a/go/tools/builders/stdliblist.go +++ b/go/tools/builders/stdliblist.go @@ -115,7 +115,7 @@ func execRootPath(execRoot, p string) string { } func absoluteSourcesPaths(execRoot, pkgDir string, srcs []string) []string { - ret := []string{} + ret := make([]string, 0, len(srcs)) pkgDir = execRootPath(execRoot, pkgDir) for _, src := range srcs { ret = append(ret, filepath.Join(pkgDir, src)) From 2773277ff06716147af0fbb5bbf8526df41d4a4e Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 18 Apr 2021 00:30:54 +0200 Subject: [PATCH 30/41] go/tools/gopackagesdriver: packageToPackage -> flatPackageForStd Signed-off-by: Steeve Morin --- go/tools/builders/stdliblist.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go/tools/builders/stdliblist.go b/go/tools/builders/stdliblist.go index 14bd8d2692..a490ee280d 100644 --- a/go/tools/builders/stdliblist.go +++ b/go/tools/builders/stdliblist.go @@ -123,7 +123,7 @@ func absoluteSourcesPaths(execRoot, pkgDir string, srcs []string) []string { return ret } -func packageToPackage(execRoot string, pkg *goListPackage) *flatPackage { +func flatPackageForStd(execRoot string, pkg *goListPackage) *flatPackage { // Don't use generated files from the stdlib goFiles := absoluteSourcesPaths(execRoot, pkg.Dir, pkg.GoFiles) @@ -200,7 +200,7 @@ func stdliblist(args []string) error { if err := decoder.Decode(&pkg); err != nil { return err } - if err := encoder.Encode(packageToPackage(execRoot, pkg)); err != nil { + if err := encoder.Encode(flatPackageForStd(execRoot, pkg)); err != nil { return err } } From 5923382ca46aca03004ef7b663e90c5c7ef38401 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 18 Apr 2021 00:33:02 +0200 Subject: [PATCH 31/41] go/tools/builders: fix stdliblist comment Signed-off-by: Steeve Morin --- go/tools/builders/stdliblist.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/tools/builders/stdliblist.go b/go/tools/builders/stdliblist.go index a490ee280d..2fe00e241d 100644 --- a/go/tools/builders/stdliblist.go +++ b/go/tools/builders/stdliblist.go @@ -145,7 +145,7 @@ func flatPackageForStd(execRoot string, pkg *goListPackage) *flatPackage { return newPkg } -// stdlib builds the standard library in the appropriate mode into a new goroot. +// stdliblist runs `go list -json` on the standard library and saves it to a file. func stdliblist(args []string) error { // process the args flags := flag.NewFlagSet("stdliblist", flag.ExitOnError) From b962df02164412fe7db36aacd0a9afa34a6cbdad Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 18 Apr 2021 00:35:29 +0200 Subject: [PATCH 32/41] go/tools/builder: add GOMODCACHE to stdliblist builder Signed-off-by: Steeve Morin --- go/tools/builders/stdliblist.go | 1 + 1 file changed, 1 insertion(+) diff --git a/go/tools/builders/stdliblist.go b/go/tools/builders/stdliblist.go index 2fe00e241d..1aa8e6e2a8 100644 --- a/go/tools/builders/stdliblist.go +++ b/go/tools/builders/stdliblist.go @@ -174,6 +174,7 @@ func stdliblist(args []string) error { cachePath := abs(*out + ".gocache") defer os.RemoveAll(cachePath) os.Setenv("GOCACHE", cachePath) + os.Setenv("GOMODCACHE", cachePath) os.Setenv("GOPATH", cachePath) listArgs := goenv.goCmd("list") From 98338cc304b5c6ddf2bd732962ac211e99ef6385 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 18 Apr 2021 00:37:55 +0200 Subject: [PATCH 33/41] go/tools/builder: stdlib and stdliblist tags should be comma separated Signed-off-by: Steeve Morin --- go/tools/builders/stdlib.go | 2 +- go/tools/builders/stdliblist.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/go/tools/builders/stdlib.go b/go/tools/builders/stdlib.go index 6daa6883de..bd020095e8 100644 --- a/go/tools/builders/stdlib.go +++ b/go/tools/builders/stdlib.go @@ -112,7 +112,7 @@ You may need to use the flags --cpu=x64_windows --compiler=mingw-gcc.`) // we strip the build ids, since they won't be used after this. installArgs := goenv.goCmd("install", "-toolexec", abs(os.Args[0])+" filterbuildid") if len(build.Default.BuildTags) > 0 { - installArgs = append(installArgs, "-tags", strings.Join(build.Default.BuildTags, " ")) + installArgs = append(installArgs, "-tags", strings.Join(build.Default.BuildTags, ",")) } gcflags := []string{} diff --git a/go/tools/builders/stdliblist.go b/go/tools/builders/stdliblist.go index 1aa8e6e2a8..b4a2583011 100644 --- a/go/tools/builders/stdliblist.go +++ b/go/tools/builders/stdliblist.go @@ -179,7 +179,7 @@ func stdliblist(args []string) error { listArgs := goenv.goCmd("list") if len(build.Default.BuildTags) > 0 { - listArgs = append(listArgs, "-tags", strings.Join(build.Default.BuildTags, " ")) + listArgs = append(listArgs, "-tags", strings.Join(build.Default.BuildTags, ",")) } listArgs = append(listArgs, "-json", "builtin", "std", "runtime/cgo") From bcd9713630428deb2705b6e0d1e0a6a0056d8f8c Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 18 Apr 2021 00:40:24 +0200 Subject: [PATCH 34/41] go/tools/gopackagesdriver: rename to new gazelle convention Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/BUILD.bazel | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go/tools/gopackagesdriver/BUILD.bazel b/go/tools/gopackagesdriver/BUILD.bazel index 47de628c34..5c7ee3369b 100644 --- a/go/tools/gopackagesdriver/BUILD.bazel +++ b/go/tools/gopackagesdriver/BUILD.bazel @@ -1,7 +1,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") go_library( - name = "go_default_library", + name = "gopackagesdriver_lib", srcs = [ "bazel.go", "bazel_json_builder.go", @@ -17,6 +17,6 @@ go_library( go_binary( name = "gopackagesdriver", - embed = [":go_default_library"], + embed = [":gopackagesdriver_lib"], visibility = ["//visibility:public"], ) From b81ac13c41d3d3031bd13b021b53e0b577bd8173 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 18 Apr 2021 00:43:03 +0200 Subject: [PATCH 35/41] go/tools/gopackagesdriver: add file headers Signed-off-by: Steeve Morin --- go/tools/builders/stdliblist.go | 2 +- go/tools/gopackagesdriver/aspect.bzl | 14 ++++++++++++++ go/tools/gopackagesdriver/bazel.go | 14 ++++++++++++++ go/tools/gopackagesdriver/bazel_json_builder.go | 14 ++++++++++++++ go/tools/gopackagesdriver/driver_request.go | 14 ++++++++++++++ go/tools/gopackagesdriver/flatpackage.go | 14 ++++++++++++++ go/tools/gopackagesdriver/json_packages_driver.go | 14 ++++++++++++++ go/tools/gopackagesdriver/main.go | 14 ++++++++++++++ go/tools/gopackagesdriver/packageregistry.go | 14 ++++++++++++++ 9 files changed, 113 insertions(+), 1 deletion(-) diff --git a/go/tools/builders/stdliblist.go b/go/tools/builders/stdliblist.go index b4a2583011..90564a2d3e 100644 --- a/go/tools/builders/stdliblist.go +++ b/go/tools/builders/stdliblist.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Bazel Authors. All rights reserved. +// Copyright 2021 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. diff --git a/go/tools/gopackagesdriver/aspect.bzl b/go/tools/gopackagesdriver/aspect.bzl index e7ce89a528..09de266120 100644 --- a/go/tools/gopackagesdriver/aspect.bzl +++ b/go/tools/gopackagesdriver/aspect.bzl @@ -1,3 +1,17 @@ +# Copyright 2021 The Bazel Go Rules 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( "//go/private:providers.bzl", "GoArchive", diff --git a/go/tools/gopackagesdriver/bazel.go b/go/tools/gopackagesdriver/bazel.go index ea55f84aac..f6456a214b 100644 --- a/go/tools/gopackagesdriver/bazel.go +++ b/go/tools/gopackagesdriver/bazel.go @@ -1,3 +1,17 @@ +// Copyright 2021 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 main import ( diff --git a/go/tools/gopackagesdriver/bazel_json_builder.go b/go/tools/gopackagesdriver/bazel_json_builder.go index d04b46bfcf..cdc25dc435 100644 --- a/go/tools/gopackagesdriver/bazel_json_builder.go +++ b/go/tools/gopackagesdriver/bazel_json_builder.go @@ -1,3 +1,17 @@ +// Copyright 2021 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 main import ( diff --git a/go/tools/gopackagesdriver/driver_request.go b/go/tools/gopackagesdriver/driver_request.go index 8519541ac5..8fe2f63e22 100644 --- a/go/tools/gopackagesdriver/driver_request.go +++ b/go/tools/gopackagesdriver/driver_request.go @@ -1,3 +1,17 @@ +// Copyright 2021 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 main import ( diff --git a/go/tools/gopackagesdriver/flatpackage.go b/go/tools/gopackagesdriver/flatpackage.go index 1b1ba70472..b0fbe0ff36 100644 --- a/go/tools/gopackagesdriver/flatpackage.go +++ b/go/tools/gopackagesdriver/flatpackage.go @@ -1,3 +1,17 @@ +// Copyright 2021 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 main import ( diff --git a/go/tools/gopackagesdriver/json_packages_driver.go b/go/tools/gopackagesdriver/json_packages_driver.go index a358469d17..50e3dbe3f7 100644 --- a/go/tools/gopackagesdriver/json_packages_driver.go +++ b/go/tools/gopackagesdriver/json_packages_driver.go @@ -1,3 +1,17 @@ +// Copyright 2021 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 main import "fmt" diff --git a/go/tools/gopackagesdriver/main.go b/go/tools/gopackagesdriver/main.go index 45ac7d0a8b..29a2699b47 100644 --- a/go/tools/gopackagesdriver/main.go +++ b/go/tools/gopackagesdriver/main.go @@ -1,3 +1,17 @@ +// Copyright 2021 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 main import ( diff --git a/go/tools/gopackagesdriver/packageregistry.go b/go/tools/gopackagesdriver/packageregistry.go index 348cade5d8..1ab3c049f8 100644 --- a/go/tools/gopackagesdriver/packageregistry.go +++ b/go/tools/gopackagesdriver/packageregistry.go @@ -1,3 +1,17 @@ +// Copyright 2021 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 main type PackageRegistry struct { From ae8788fa05f733b72e0f038f4cd9f5d7ad2a38ae Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 18 Apr 2021 00:45:03 +0200 Subject: [PATCH 36/41] go/tools/gopackagesdriver: cleaner iteration on optional fields Cleaner than hasattr Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/aspect.bzl | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/go/tools/gopackagesdriver/aspect.bzl b/go/tools/gopackagesdriver/aspect.bzl index 09de266120..3a15d96b9b 100644 --- a/go/tools/gopackagesdriver/aspect.bzl +++ b/go/tools/gopackagesdriver/aspect.bzl @@ -46,24 +46,22 @@ def _go_pkg_info_aspect_impl(target, ctx): deps_transitive_json_file = [] deps_transitive_export_file = [] - if hasattr(ctx.rule.attr, "deps"): - for dep in ctx.rule.attr.deps: - if GoPkgInfo in dep: - pkg_info = dep[GoPkgInfo] - deps_transitive_json_file.append(pkg_info.transitive_json_file) - deps_transitive_export_file.append(pkg_info.transitive_export_file) - # Fetch the stdlib json from the first dependency - if not stdlib_json_file: - stdlib_json_file = pkg_info.stdlib_json_file + for dep in getattr(ctx.rule.attr, "deps", []): + if GoPkgInfo in dep: + pkg_info = dep[GoPkgInfo] + deps_transitive_json_file.append(pkg_info.transitive_json_file) + deps_transitive_export_file.append(pkg_info.transitive_export_file) + # Fetch the stdlib json from the first dependency + if not stdlib_json_file: + stdlib_json_file = pkg_info.stdlib_json_file # If deps are embedded, do not gather their json or export_file since they # are included in the current target, but do gather their deps'. - if hasattr(ctx.rule.attr, "embed"): - for dep in ctx.rule.attr.embed: - if GoPkgInfo in dep: - pkg_info = dep[GoPkgInfo] - deps_transitive_json_file.append(pkg_info.deps_transitive_json_file) - deps_transitive_export_file.append(pkg_info.deps_transitive_export_file) + for dep in getattr(ctx.rule.attr, "embed", []): + if GoPkgInfo in dep: + pkg_info = dep[GoPkgInfo] + deps_transitive_json_file.append(pkg_info.deps_transitive_json_file) + deps_transitive_export_file.append(pkg_info.deps_transitive_export_file) pkg_json_file = None export_file = None From 5b80ccdb5a6112b816e65abcd2de268028e48c1c Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 18 Apr 2021 00:49:05 +0200 Subject: [PATCH 37/41] go/tools/gopackagesdriver: ensure BEP file paths work on windows too Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/bazel.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/go/tools/gopackagesdriver/bazel.go b/go/tools/gopackagesdriver/bazel.go index f6456a214b..772a86c0a2 100644 --- a/go/tools/gopackagesdriver/bazel.go +++ b/go/tools/gopackagesdriver/bazel.go @@ -19,8 +19,10 @@ import ( "encoding/json" "fmt" "io/ioutil" + "net/url" "os" "os/exec" + "path/filepath" "strings" ) @@ -98,7 +100,11 @@ func (b *Bazel) Build(ctx context.Context, args ...string) ([]string, error) { } if namedSet.NamedSetOfFiles != nil { for _, f := range namedSet.NamedSetOfFiles.Files { - files = append(files, strings.TrimPrefix(f.URI, "file://")) + fileUrl, err := url.Parse(f.URI) + if err != nil { + return nil, fmt.Errorf("unable to parse file URI: %w", err) + } + files = append(files, filepath.FromSlash(fileUrl.Path)) } } } From 34ee2be379b65a75b9f80e98144e7c11be50a96e Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 18 Apr 2021 00:49:57 +0200 Subject: [PATCH 38/41] go/tools/gopackagesdriver: remove == false expressions Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/bazel_json_builder.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/tools/gopackagesdriver/bazel_json_builder.go b/go/tools/gopackagesdriver/bazel_json_builder.go index cdc25dc435..0950a45f72 100644 --- a/go/tools/gopackagesdriver/bazel_json_builder.go +++ b/go/tools/gopackagesdriver/bazel_json_builder.go @@ -87,7 +87,7 @@ func (b *BazelJSONBuilder) Build(ctx context.Context, mode LoadMode) ([]string, ret := []string{} for _, f := range files { - if strings.HasSuffix(f, ".pkg.json") == false { + if !strings.HasSuffix(f, ".pkg.json") { continue } ret = append(ret, f) From dfe668a5cfa02bfa133491d6d22eb69a5466c57b Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 18 Apr 2021 00:51:50 +0200 Subject: [PATCH 39/41] go/tools/gopackagesdriver: don't panic when an error is returned simply print it and exit(1) Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/main.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/go/tools/gopackagesdriver/main.go b/go/tools/gopackagesdriver/main.go index 29a2699b47..556353dc39 100644 --- a/go/tools/gopackagesdriver/main.go +++ b/go/tools/gopackagesdriver/main.go @@ -126,6 +126,7 @@ func run() error { func main() { if err := run(); err != nil { - panic(err) + fmt.Fprintf(os.Stderr, "error: %w", err) + os.Exit(1) } } From 81166a845c32327fd4cafb96d54326f185d524e6 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 18 Apr 2021 01:32:19 +0200 Subject: [PATCH 40/41] go/tools/gopackagesdriver: properly compute roots using a pattern matcher Signed-off-by: Steeve Morin --- .../gopackagesdriver/json_packages_driver.go | 16 ++++-- go/tools/gopackagesdriver/main.go | 15 +----- go/tools/gopackagesdriver/packageregistry.go | 52 ++++++++++++++++--- 3 files changed, 59 insertions(+), 24 deletions(-) diff --git a/go/tools/gopackagesdriver/json_packages_driver.go b/go/tools/gopackagesdriver/json_packages_driver.go index 50e3dbe3f7..a2cd0dcece 100644 --- a/go/tools/gopackagesdriver/json_packages_driver.go +++ b/go/tools/gopackagesdriver/json_packages_driver.go @@ -14,7 +14,10 @@ package main -import "fmt" +import ( + "fmt" + "go/types" +) type JSONPackagesDriver struct { registry *PackageRegistry @@ -44,6 +47,13 @@ func NewJSONPackagesDriver(jsonFiles []string, prf PathResolverFunc) (*JSONPacka return jpd, nil } -func (b *JSONPackagesDriver) Packages() []*FlatPackage { - return b.registry.ToList() +func (b *JSONPackagesDriver) Match(pattern ...string) *driverResponse { + rootPkgs, packages := b.registry.Match(pattern...) + + return &driverResponse{ + NotHandled: false, + Sizes: types.SizesFor("gc", "amd64").(*types.StdSizes), + Roots: rootPkgs, + Packages: packages, + } } diff --git a/go/tools/gopackagesdriver/main.go b/go/tools/gopackagesdriver/main.go index 556353dc39..954c084dab 100644 --- a/go/tools/gopackagesdriver/main.go +++ b/go/tools/gopackagesdriver/main.go @@ -103,20 +103,9 @@ func run() error { return fmt.Errorf("unable to load JSON files: %w", err) } - pkgs := driver.Packages() - roots := []string{} - for _, pkg := range pkgs { - if pkg.IsRoot() { - roots = append(roots, pkg.ID) - } - } + response := driver.Match(os.Args[1:]...) + // return nil - response := &driverResponse{ - NotHandled: false, - Sizes: types.SizesFor("gc", "amd64").(*types.StdSizes), - Roots: roots, - Packages: pkgs, - } if err := json.NewEncoder(os.Stdout).Encode(response); err != nil { return fmt.Errorf("unable to encode response: %w", err) } diff --git a/go/tools/gopackagesdriver/packageregistry.go b/go/tools/gopackagesdriver/packageregistry.go index 1ab3c049f8..1d44e606cc 100644 --- a/go/tools/gopackagesdriver/packageregistry.go +++ b/go/tools/gopackagesdriver/packageregistry.go @@ -14,13 +14,19 @@ package main +import ( + "strings" +) + type PackageRegistry struct { packagesByImportPath map[string]*FlatPackage + packagesByFile map[string]*FlatPackage } func NewPackageRegistry(pkgs ...*FlatPackage) *PackageRegistry { pr := &PackageRegistry{ packagesByImportPath: map[string]*FlatPackage{}, + packagesByFile: map[string]*FlatPackage{}, } pr.Add(pkgs...) return pr @@ -44,17 +50,12 @@ func (pr *PackageRegistry) Remove(pkgs ...*FlatPackage) *PackageRegistry { return pr } -func (pr *PackageRegistry) ToList() []*FlatPackage { - pkgs := make([]*FlatPackage, 0, len(pr.packagesByImportPath)) - for _, pkg := range pr.packagesByImportPath { - pkgs = append(pkgs, pkg) - } - return pkgs -} - func (pr *PackageRegistry) ResolvePaths(prf PathResolverFunc) error { for _, pkg := range pr.packagesByImportPath { pkg.ResolvePaths(prf) + for _, f := range pkg.CompiledGoFiles { + pr.packagesByFile[f] = pkg + } } return nil } @@ -67,3 +68,38 @@ func (pr *PackageRegistry) ResolveImports() error { } return nil } + +func (pr *PackageRegistry) Match(patterns ...string) ([]string, []*FlatPackage) { + roots := map[string]struct{}{} + wildcard := false + + for _, pattern := range patterns { + if strings.HasPrefix(pattern, "file=") { + f := strings.TrimPrefix(pattern, "file=") + if pkg, ok := pr.packagesByFile[f]; ok { + roots[pkg.ID] = struct{}{} + } + } else if pattern == "." || pattern == "./..." { + wildcard = true + } else { + if pkg, ok := pr.packagesByImportPath[pattern]; ok { + roots[pkg.ID] = struct{}{} + } + } + } + + retPkgs := make([]*FlatPackage, 0, len(pr.packagesByImportPath)) + for _, pkg := range pr.packagesByImportPath { + if wildcard && strings.HasPrefix(pkg.ID, "//") { + roots[pkg.ID] = struct{}{} + } + retPkgs = append(retPkgs, pkg) + } + + retRoots := make([]string, 0, len(roots)) + for pkg := range roots { + retRoots = append(retRoots, pkg) + } + + return retRoots, retPkgs +} From 32aba6126128e425e3bc1d6eb6bcf142d9475462 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 18 Apr 2021 01:50:11 +0200 Subject: [PATCH 41/41] go/tools/gopackagesdriver: walk packages from root Since root packages are now properly computed, walk the package graph from them. This saves on JSON payload size a bit. Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/packageregistry.go | 39 ++++++++++++++++---- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/go/tools/gopackagesdriver/packageregistry.go b/go/tools/gopackagesdriver/packageregistry.go index 1d44e606cc..cd979d84d5 100644 --- a/go/tools/gopackagesdriver/packageregistry.go +++ b/go/tools/gopackagesdriver/packageregistry.go @@ -19,12 +19,14 @@ import ( ) type PackageRegistry struct { + packagesByID map[string]*FlatPackage packagesByImportPath map[string]*FlatPackage packagesByFile map[string]*FlatPackage } func NewPackageRegistry(pkgs ...*FlatPackage) *PackageRegistry { pr := &PackageRegistry{ + packagesByID: map[string]*FlatPackage{}, packagesByImportPath: map[string]*FlatPackage{}, packagesByFile: map[string]*FlatPackage{}, } @@ -34,6 +36,7 @@ func NewPackageRegistry(pkgs ...*FlatPackage) *PackageRegistry { func (pr *PackageRegistry) Add(pkgs ...*FlatPackage) *PackageRegistry { for _, pkg := range pkgs { + pr.packagesByID[pkg.ID] = pkg pr.packagesByImportPath[pkg.PkgPath] = pkg } return pr @@ -69,6 +72,16 @@ func (pr *PackageRegistry) ResolveImports() error { return nil } +func (pr *PackageRegistry) walk(acc map[string]*FlatPackage, root string) { + pkg := pr.packagesByID[root] + acc[pkg.ID] = pkg + for _, pkgID := range pkg.Imports { + if _, ok := acc[pkgID]; !ok { + pr.walk(acc, pkgID) + } + } +} + func (pr *PackageRegistry) Match(patterns ...string) ([]string, []*FlatPackage) { roots := map[string]struct{}{} wildcard := false @@ -88,17 +101,29 @@ func (pr *PackageRegistry) Match(patterns ...string) ([]string, []*FlatPackage) } } - retPkgs := make([]*FlatPackage, 0, len(pr.packagesByImportPath)) - for _, pkg := range pr.packagesByImportPath { - if wildcard && strings.HasPrefix(pkg.ID, "//") { - roots[pkg.ID] = struct{}{} + if wildcard { + retPkgs := make([]*FlatPackage, 0, len(pr.packagesByImportPath)) + retRoots := make([]string, 0, len(pr.packagesByImportPath)) + for _, pkg := range pr.packagesByImportPath { + if strings.HasPrefix(pkg.ID, "//") { + retRoots = append(retRoots, pkg.ID) + roots[pkg.ID] = struct{}{} + } + retPkgs = append(retPkgs, pkg) } - retPkgs = append(retPkgs, pkg) + return retRoots, retPkgs } + walkedPackages := map[string]*FlatPackage{} retRoots := make([]string, 0, len(roots)) - for pkg := range roots { - retRoots = append(retRoots, pkg) + for rootPkg := range roots { + retRoots = append(retRoots, rootPkg) + pr.walk(walkedPackages, rootPkg) + } + + retPkgs := make([]*FlatPackage, 0, len(walkedPackages)) + for _, pkg := range walkedPackages { + retPkgs = append(retPkgs, pkg) } return retRoots, retPkgs