From 5d9f106458aafd0ad3b0febaf1c0ef57ee831c87 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Tue, 20 Jul 2021 19:26:03 +0200 Subject: [PATCH 01/24] go/tools/gopackagesdriver: add automatic target detection This commit introduces automatic target detection so that no user input is required after the `GOPACKAGESDRIVER` setup. This effectively deprecates the following environment variables: - `GOPACKAGESDRIVER_BAZEL_TARGETS` - `GOPACKAGESDRIVER_BAZEL_QUERY` - `GOPACKAGESDRIVER_BAZEL_TAG_FILTERS` It works as follows: - for `importpath` queries, it will `bazel query` a matching `go_library` matching it - for `file=` queries, it will try to find the matching `go_library` in the same package - since it supports multiple queries at the same time, it will `union` those queries and query that Once the `go_library` targets are found, it will try to build them directly, which should dramatically speed up compilation times, at the loss of transition support (which wasn't used as much as I thought it would be). I may reintroduce it in the future via a user-defined flag (to only build the part of the graph that needs building). In any case, toolchain or platforms can be switched with the following environment variables it configuration transitions are needed: - `GOPACKAGESDRIVER_BAZEL_FLAGS` which will be passed to `bazel` invocations - `GOPACKAGESDRIVER_BAZEL_QUERY_FLAGS` which will be passed to `bazel query` invocations - `GOPACKAGESDRIVER_BAZEL_BUILD_FLAGS` which will be passed to `bazel build` invocations Finally, the driver will not fail in case of a build failure, and even so uses `--keep_going` and will return whatever packages that did build. Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/BUILD.bazel | 1 + go/tools/gopackagesdriver/aspect.bzl | 56 +++++---- go/tools/gopackagesdriver/bazel.go | 17 ++- .../gopackagesdriver/bazel_json_builder.go | 106 ++++++++++++------ go/tools/gopackagesdriver/main.go | 55 +++++---- go/tools/gopackagesdriver/utils.go | 23 ++++ 6 files changed, 174 insertions(+), 84 deletions(-) create mode 100644 go/tools/gopackagesdriver/utils.go diff --git a/go/tools/gopackagesdriver/BUILD.bazel b/go/tools/gopackagesdriver/BUILD.bazel index 5c7ee3369b..89f7f1408d 100644 --- a/go/tools/gopackagesdriver/BUILD.bazel +++ b/go/tools/gopackagesdriver/BUILD.bazel @@ -10,6 +10,7 @@ go_library( "json_packages_driver.go", "main.go", "packageregistry.go", + "utils.go", ], importpath = "github.com/bazelbuild/rules_go/go/tools/gopackagesdriver", visibility = ["//visibility:private"], diff --git a/go/tools/gopackagesdriver/aspect.bzl b/go/tools/gopackagesdriver/aspect.bzl index 3a15d96b9b..eb67f9925b 100644 --- a/go/tools/gopackagesdriver/aspect.bzl +++ b/go/tools/gopackagesdriver/aspect.bzl @@ -17,18 +17,10 @@ load( "GoArchive", "GoStdLib", ) -load( - "//go/private:context.bzl", - "go_context", -) load( "@bazel_skylib//lib:paths.bzl", "paths", ) -load( - "@bazel_skylib//lib:collections.bzl", - "collections", -) GoPkgInfo = provider() @@ -40,28 +32,43 @@ def _file_path(f): return paths.join("__BAZEL_WORKSPACE__", f.path) return paths.join("__BAZEL_EXECROOT__", f.path) +def _new_pkg_info(archive_data): + return struct( + ID = str(archive_data.label), + 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 + ], + ) + def _go_pkg_info_aspect_impl(target, ctx): # Fetch the stdlib JSON file from the inner most target stdlib_json_file = None deps_transitive_json_file = [] deps_transitive_export_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 + for attr in ["deps", "embed"]: + for dep in getattr(ctx.rule.attr, attr, []): + if GoPkgInfo in dep: + pkg_info = dep[GoPkgInfo] + if attr == "deps": + deps_transitive_json_file.append(pkg_info.transitive_json_file) + deps_transitive_export_file.append(pkg_info.transitive_export_file) + elif attr == "embed": + # 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'. + deps_transitive_json_file.append(pkg_info.deps_transitive_json_file) + deps_transitive_export_file.append(pkg_info.deps_transitive_export_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'. - 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) + # Fetch the stdlib json from the first dependency + if not stdlib_json_file: + stdlib_json_file = pkg_info.stdlib_json_file pkg_json_file = None export_file = None @@ -83,6 +90,7 @@ def _go_pkg_info_aspect_impl(target, ctx): ) 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_file: @@ -113,7 +121,7 @@ def _go_pkg_info_aspect_impl(target, ctx): OutputGroupInfo( 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 []) + 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.go b/go/tools/gopackagesdriver/bazel.go index 772a86c0a2..d3d2e129d7 100644 --- a/go/tools/gopackagesdriver/bazel.go +++ b/go/tools/gopackagesdriver/bazel.go @@ -17,6 +17,7 @@ package main import ( "context" "encoding/json" + "errors" "fmt" "io/ioutil" "net/url" @@ -88,7 +89,13 @@ 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, fmt.Errorf("bazel build failed: %w", err) + // Ignore a regular build failure to get partial data. + // See https://docs.bazel.build/versions/main/guide.html#what-exit-code-will-i-get on + // exit codes. + var exerr *exec.ExitError + if !errors.As(err, &exerr) || (errors.As(err, &exerr) && exerr.ExitCode() != 1) { + return nil, fmt.Errorf("bazel build failed: %w", err) + } } files := make([]string, 0) @@ -120,6 +127,14 @@ func (b *Bazel) Query(ctx context.Context, args ...string) ([]string, error) { return strings.Split(strings.TrimSpace(output), "\n"), nil } +func (b *Bazel) QueryLabels(ctx context.Context, args ...string) ([]string, error) { + output, err := b.run(ctx, "query", args...) + if err != nil { + return nil, fmt.Errorf("bazel query failed: %w", err) + } + return strings.Split(strings.TrimSpace(output), "\n"), nil +} + func (b *Bazel) WorkspaceRoot() string { return b.workspaceRoot } diff --git a/go/tools/gopackagesdriver/bazel_json_builder.go b/go/tools/gopackagesdriver/bazel_json_builder.go index 0950a45f72..2b8889b785 100644 --- a/go/tools/gopackagesdriver/bazel_json_builder.go +++ b/go/tools/gopackagesdriver/bazel_json_builder.go @@ -17,72 +17,104 @@ package main import ( "context" "fmt" + "path/filepath" "strings" ) type BazelJSONBuilder struct { - bazel *Bazel - query string - tagFilters string - targets []string + bazel *Bazel + requests []string } const ( OutputGroupDriverJSONFile = "go_pkg_driver_json_file" OutputGroupStdLibJSONFile = "go_pkg_driver_stdlib_json_file" OutputGroupExportFile = "go_pkg_driver_export_file" + wildcardQuery = `kind("go_library", //...)` ) -func NewBazelJSONBuilder(bazel *Bazel, query, tagFilters string, targets []string) (*BazelJSONBuilder, error) { +func (b *BazelJSONBuilder) fileQuery(filename string) string { + if filepath.IsAbs(filename) { + fp, _ := filepath.Rel(b.bazel.WorkspaceRoot(), filename) + filename = fp + } + return fmt.Sprintf(`some(kind("go_library", same_pkg_direct_rdeps("%s")))`, filename) +} + +func (b *BazelJSONBuilder) packageQuery(importPath string) string { + return fmt.Sprintf(`some(kind("go_library", attr(importpath, "%s", //...)))`, importPath) +} + +func (b *BazelJSONBuilder) queryFromRequests(requests ...string) string { + ret := make([]string, 0, len(requests)) + for _, request := range requests { + if request == "." || request == "./..." { + return wildcardQuery + } else if strings.HasPrefix(request, "file=") { + f := strings.TrimPrefix(request, "file=") + ret = append(ret, b.fileQuery(f)) + } else { + ret = append(ret, b.packageQuery(request)) + } + } + return strings.Join(ret, " union ") +} + +func NewBazelJSONBuilder(bazel *Bazel, requests ...string) (*BazelJSONBuilder, error) { return &BazelJSONBuilder{ - bazel: bazel, - query: query, - tagFilters: tagFilters, - targets: targets, + bazel: bazel, + requests: requests, }, nil } func (b *BazelJSONBuilder) outputGroupsForMode(mode LoadMode) string { og := OutputGroupDriverJSONFile + "," + OutputGroupStdLibJSONFile - if mode&NeedExportsFile != 0 || true { // override for now + if mode&NeedExportsFile != 0 { // override for now og += "," + OutputGroupExportFile } 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=" + b.outputGroupsForMode(mode), - "--keep_going", // Build all possible packages +func (b *BazelJSONBuilder) query(ctx context.Context, query string) ([]string, error) { + queryArgs := concatStringsArrays(bazelFlags, bazelQueryFlags, []string{ + "--ui_event_filters=-info,-stderr", + "--noshow_progress", + // Use Sky Query + "--universe_scope=//...", + "--order_output=no", + "--output=label", + "--nodep_deps", + "--noimplicit_deps", + "--notool_deps", + query, + }) + labels, err := b.bazel.Query(ctx, queryArgs...) + if err != nil { + return nil, fmt.Errorf("unable to query: %w", err) } + return labels, nil +} - if b.tagFilters != "" { - buildsArgs = append(buildsArgs, "--build_tag_filters="+b.tagFilters) +func (b *BazelJSONBuilder) Build(ctx context.Context, mode LoadMode) ([]string, error) { + labels, err := b.query(ctx, b.queryFromRequests(b.requests...)) + if err != nil { + return nil, fmt.Errorf("unable to query: %w", err) } - - if 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) - } - buildsArgs = append(buildsArgs, queryTargets...) + if len(labels) == 0 { + return nil, fmt.Errorf("found no target to build") } - buildsArgs = append(buildsArgs, b.targets...) - - files, err := b.bazel.Build(ctx, buildsArgs...) + buildArgs := concatStringsArrays([]string{ + "--experimental_convenience_symlinks=ignore", + "--ui_event_filters=-info,-stderr", + "--noshow_progress", + "--aspects=" + rulesGoRepositoryName + "//go/tools/gopackagesdriver:aspect.bzl%go_pkg_info_aspect", + "--output_groups=" + b.outputGroupsForMode(mode), + "--keep_going", // Build all possible packages + }, bazelFlags, bazelBuildFlags, labels) + files, err := b.bazel.Build(ctx, buildArgs...) if err != nil { - return nil, fmt.Errorf("unable to bazel build %v: %w", buildsArgs, err) + return nil, fmt.Errorf("unable to bazel build %v: %w", buildArgs, err) } ret := []string{} diff --git a/go/tools/gopackagesdriver/main.go b/go/tools/gopackagesdriver/main.go index a67b3ad54e..ccf19724a8 100644 --- a/go/tools/gopackagesdriver/main.go +++ b/go/tools/gopackagesdriver/main.go @@ -25,6 +25,11 @@ import ( ) type driverResponse struct { + // NotHandled is returned if the request can't be handled by the current + // driver. If an external driver returns a response with NotHandled, the + // rest of the driverResponse is ignored, and go/packages will fallback + // to the next driver. If go/packages is extended in the future to support + // lists of multiple drivers, go/packages will fall back to the next driver. NotHandled bool // Sizes, if not nil, is the types.Sizes to use when type checking. @@ -45,11 +50,20 @@ type driverResponse struct { } var ( - bazelBin = getenvDefault("GOPACKAGESDRIVER_BAZEL", "bazel") - workspaceRoot = os.Getenv("BUILD_WORKSPACE_DIRECTORY") - targets = strings.Fields(os.Getenv("GOPACKAGESDRIVER_BAZEL_TARGETS")) - targetsQueryStr = os.Getenv("GOPACKAGESDRIVER_BAZEL_QUERY") - targetsTagFilters = os.Getenv("GOPACKAGESDRIVER_BAZEL_TAG_FILTERS") + // It seems https://github.com/bazelbuild/bazel/issues/3115 isn't fixed when specifying + // the aspect from the command line. Use this trick in the mean time. + rulesGoRepositoryName = getenvDefault("GOPACKAGESDRIVER_RULES_GO_REPOSITORY_NAME", "@io_bazel_rules_go") + bazelBin = getenvDefault("GOPACKAGESDRIVER_BAZEL", "bazel") + bazelFlags = strings.Fields(os.Getenv("GOPACKAGESDRIVER_BAZEL_FLAGS")) + bazelQueryFlags = strings.Fields(os.Getenv("GOPACKAGESDRIVER_BAZEL_QUERY_FLAGS")) + bazelBuildFlags = strings.Fields(os.Getenv("GOPACKAGESDRIVER_BAZEL_BUILD_FLAGS")) + workspaceRoot = os.Getenv("BUILD_WORKSPACE_DIRECTORY") + emptyResponse = &driverResponse{ + NotHandled: false, + Sizes: types.SizesFor("gc", "amd64").(*types.StdSizes), + Roots: []string{}, + Packages: []*FlatPackage{}, + } ) func getenvDefault(key, defaultValue string) string { @@ -74,48 +88,45 @@ func signalContext(parentCtx context.Context, signals ...os.Signal) (ctx context return ctx, cancel } -func run() error { +func run() (*driverResponse, error) { ctx, cancel := signalContext(context.Background(), os.Interrupt) defer cancel() request, err := ReadDriverRequest(os.Stdin) if err != nil { - return fmt.Errorf("unable to read request: %w", err) + return emptyResponse, fmt.Errorf("unable to read request: %w", err) } bazel, err := NewBazel(ctx, bazelBin, workspaceRoot) if err != nil { - return fmt.Errorf("unable to create bazel instance: %w", err) + return emptyResponse, fmt.Errorf("unable to create bazel instance: %w", err) } - bazelJsonBuilder, err := NewBazelJSONBuilder(bazel, targetsQueryStr, targetsTagFilters, targets) + bazelJsonBuilder, err := NewBazelJSONBuilder(bazel, os.Args[1:]...) if err != nil { - return fmt.Errorf("unable to build JSON files: %w", err) + return emptyResponse, 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) + return emptyResponse, 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) - } - - response := driver.Match(os.Args[1:]...) - // return nil - - if err := json.NewEncoder(os.Stdout).Encode(response); err != nil { - return fmt.Errorf("unable to encode response: %w", err) + return emptyResponse, fmt.Errorf("unable to load JSON files: %w", err) } - return nil + return driver.Match("./..."), nil } func main() { - if err := run(); err != nil { + response, err := run() + if err := json.NewEncoder(os.Stdout).Encode(response); err != nil { + fmt.Fprintf(os.Stderr, "unable to encode response: %v", err) + } + if err != nil { fmt.Fprintf(os.Stderr, "error: %v", err) - os.Exit(1) + os.Exit(0) } } diff --git a/go/tools/gopackagesdriver/utils.go b/go/tools/gopackagesdriver/utils.go new file mode 100644 index 0000000000..47b91e319d --- /dev/null +++ b/go/tools/gopackagesdriver/utils.go @@ -0,0 +1,23 @@ +// 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 + +func concatStringsArrays(values ...[]string) []string { + ret := []string{} + for _, v := range values { + ret = append(ret, v...) + } + return ret +} From 576729d6b58a4b7a7a7d4176ecf1f8f935b0f241 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Wed, 4 Aug 2021 01:40:33 +0200 Subject: [PATCH 02/24] workspace: add vscode configuration This allows to use gopls in the project itself. Signed-off-by: Steeve Morin --- .vscode/extensions.json | 8 ++++++++ .vscode/settings.json | 32 ++++++++++++++++++++++++++++++++ tools/gopackagesdriver.sh | 3 +++ 3 files changed, 43 insertions(+) create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json create mode 100755 tools/gopackagesdriver.sh diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000000..7b30843919 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "bazelbuild.vscode-bazel", + "golang.go", + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..d81fc004ef --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,32 @@ +{ + "editor.formatOnSave": true, + "files.trimTrailingWhitespace": true, + "files.insertFinalNewline": true, + "go.goroot": "${workspaceFolder}/bazel-${workspaceFolderBasename}/external/go_sdk", + "go.toolsEnvVars": { + "GOPACKAGESDRIVER": "${workspaceFolder}/tools/gopackagesdriver.sh" + }, + "go.enableCodeLens": { + "references": false, + "runtest": false + }, + "gopls": { + "formatting.gofumpt": true, + "formatting.local": "github.com/bazelbuild/rules_go", + "ui.completion.usePlaceholders": true, + "ui.semanticTokens": true, + "ui.codelenses": { + "gc_details": false, + "regenerate_cgo": false, + "generate": false, + "test": false, + "tidy": false, + "upgrade_dependency": false, + "vendor": false + }, + }, + "go.useLanguageServer": true, + "go.buildOnSave": "off", + "go.lintOnSave": "off", + "go.vetOnSave": "off", +} diff --git a/tools/gopackagesdriver.sh b/tools/gopackagesdriver.sh new file mode 100755 index 0000000000..b29cc410ef --- /dev/null +++ b/tools/gopackagesdriver.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +export GOPACKAGESDRIVER_RULES_GO_REPOSITORY_NAME= +exec bazel run --tool_tag=gopackagesdriver -- //go/tools/gopackagesdriver "${@}" From a36675c75c0f6ba8fa0b3022334e6355fcd06962 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Thu, 5 Aug 2021 16:14:04 +0200 Subject: [PATCH 03/24] go/tools/gopackagesdriver: don't use //... universe scope for file queries When using `--universe_scope=//...`, the whole `//...` is loaded even though we only use `same_pkg_direct_rdeps`. So remove it and specify the scope in the query, such as `importpath` ones. Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/bazel_json_builder.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/go/tools/gopackagesdriver/bazel_json_builder.go b/go/tools/gopackagesdriver/bazel_json_builder.go index 2b8889b785..c968874bb8 100644 --- a/go/tools/gopackagesdriver/bazel_json_builder.go +++ b/go/tools/gopackagesdriver/bazel_json_builder.go @@ -79,8 +79,6 @@ func (b *BazelJSONBuilder) query(ctx context.Context, query string) ([]string, e queryArgs := concatStringsArrays(bazelFlags, bazelQueryFlags, []string{ "--ui_event_filters=-info,-stderr", "--noshow_progress", - // Use Sky Query - "--universe_scope=//...", "--order_output=no", "--output=label", "--nodep_deps", From baba0dd508f411fc8ab9a2a9b2c3aedbd715c3f6 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Thu, 5 Aug 2021 16:19:42 +0200 Subject: [PATCH 04/24] go/tools/gopackagesdriver: add GOPACKAGESDRIVER_BAZEL_QUERY_SCOPE Add the `GOPACKAGESDRIVER_BAZEL_QUERY_SCOPE` environment variable so that bazel queries are limited the given scope. This should save time when doing `importpath` queries on large monorepos. Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/bazel_json_builder.go | 2 +- go/tools/gopackagesdriver/main.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/go/tools/gopackagesdriver/bazel_json_builder.go b/go/tools/gopackagesdriver/bazel_json_builder.go index c968874bb8..bf5bf55c87 100644 --- a/go/tools/gopackagesdriver/bazel_json_builder.go +++ b/go/tools/gopackagesdriver/bazel_json_builder.go @@ -42,7 +42,7 @@ func (b *BazelJSONBuilder) fileQuery(filename string) string { } func (b *BazelJSONBuilder) packageQuery(importPath string) string { - return fmt.Sprintf(`some(kind("go_library", attr(importpath, "%s", //...)))`, importPath) + return fmt.Sprintf(`some(kind("go_library", attr(importpath, "%s", %s)))`, importPath, bazelQueryScope) } func (b *BazelJSONBuilder) queryFromRequests(requests ...string) string { diff --git a/go/tools/gopackagesdriver/main.go b/go/tools/gopackagesdriver/main.go index ccf19724a8..f7b02cd44c 100644 --- a/go/tools/gopackagesdriver/main.go +++ b/go/tools/gopackagesdriver/main.go @@ -56,6 +56,7 @@ var ( bazelBin = getenvDefault("GOPACKAGESDRIVER_BAZEL", "bazel") bazelFlags = strings.Fields(os.Getenv("GOPACKAGESDRIVER_BAZEL_FLAGS")) bazelQueryFlags = strings.Fields(os.Getenv("GOPACKAGESDRIVER_BAZEL_QUERY_FLAGS")) + bazelQueryScope = getenvDefault("GOPACKAGESDRIVER_BAZEL_QUERY_SCOPE", "//...") bazelBuildFlags = strings.Fields(os.Getenv("GOPACKAGESDRIVER_BAZEL_BUILD_FLAGS")) workspaceRoot = os.Getenv("BUILD_WORKSPACE_DIRECTORY") emptyResponse = &driverResponse{ From b2b33b3b547af756609e66fb36dd86382b8c5f38 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Fri, 6 Aug 2021 22:53:46 +0200 Subject: [PATCH 05/24] Allow querying external dependencies with importpath Co-authored-by: Zhongpeng Lin --- 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 bf5bf55c87..e0fdb63711 100644 --- a/go/tools/gopackagesdriver/bazel_json_builder.go +++ b/go/tools/gopackagesdriver/bazel_json_builder.go @@ -42,7 +42,7 @@ func (b *BazelJSONBuilder) fileQuery(filename string) string { } func (b *BazelJSONBuilder) packageQuery(importPath string) string { - return fmt.Sprintf(`some(kind("go_library", attr(importpath, "%s", %s)))`, importPath, bazelQueryScope) + return fmt.Sprintf(`some(kind("go_library", attr(importpath, "%s", deps(%s))))`, importPath, bazelQueryScope) } func (b *BazelJSONBuilder) queryFromRequests(requests ...string) string { From 646e311b05815c4d4a3239fc74672de69aff73b3 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 8 Aug 2021 19:37:00 +0200 Subject: [PATCH 06/24] go/tools/gopackagesdriver: fetch output_base from bazel info While at it, fetch and parse the whole bazel info data. --- go/tools/gopackagesdriver/aspect.bzl | 9 ++++-- go/tools/gopackagesdriver/bazel.go | 29 +++++++++++++++---- .../gopackagesdriver/bazel_json_builder.go | 1 + 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/go/tools/gopackagesdriver/aspect.bzl b/go/tools/gopackagesdriver/aspect.bzl index eb67f9925b..7552bd0969 100644 --- a/go/tools/gopackagesdriver/aspect.bzl +++ b/go/tools/gopackagesdriver/aspect.bzl @@ -28,9 +28,12 @@ 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) + prefix = "__BAZEL_WORKSPACE__" + if not f.is_source: + prefix = "__BAZEL_EXECROOT__" + elif _is_file_external(f): + prefix = "__BAZEL_OUTPUT_BASE__" + return paths.join(prefix, f.path) def _new_pkg_info(archive_data): return struct( diff --git a/go/tools/gopackagesdriver/bazel.go b/go/tools/gopackagesdriver/bazel.go index d3d2e129d7..e7b269510e 100644 --- a/go/tools/gopackagesdriver/bazel.go +++ b/go/tools/gopackagesdriver/bazel.go @@ -15,6 +15,8 @@ package main import ( + "bufio" + "bytes" "context" "encoding/json" "errors" @@ -35,6 +37,7 @@ type Bazel struct { bazelBin string execRoot string workspaceRoot string + info map[string]string } // Minimal BEP structs to access the build outputs @@ -52,14 +55,26 @@ func NewBazel(ctx context.Context, bazelBin, workspaceRoot string) (*Bazel, erro bazelBin: bazelBin, workspaceRoot: workspaceRoot, } - if execRoot, err := b.run(ctx, "info", "execution_root"); err != nil { - return nil, fmt.Errorf("unable to find execution root: %w", err) - } else { - b.execRoot = strings.TrimSpace(execRoot) + if err := b.fillInfo(ctx); err != nil { + return nil, fmt.Errorf("unable to query bazel info: %w", err) } return b, nil } +func (b *Bazel) fillInfo(ctx context.Context) error { + b.info = map[string]string{} + output, err := b.run(ctx, "info") + if err != nil { + return err + } + scanner := bufio.NewScanner(bytes.NewBufferString(output)) + for scanner.Scan() { + parts := strings.SplitN(strings.TrimSpace(scanner.Text()), ":", 2) + b.info[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1]) + } + return nil +} + func (b *Bazel) run(ctx context.Context, command string, args ...string) (string, error) { cmd := exec.CommandContext(ctx, b.bazelBin, append([]string{ command, @@ -140,5 +155,9 @@ func (b *Bazel) WorkspaceRoot() string { } func (b *Bazel) ExecutionRoot() string { - return b.execRoot + return b.info["execution_root"] +} + +func (b *Bazel) OutputBase() string { + return b.info["output_base"] } diff --git a/go/tools/gopackagesdriver/bazel_json_builder.go b/go/tools/gopackagesdriver/bazel_json_builder.go index e0fdb63711..76a3b30b6f 100644 --- a/go/tools/gopackagesdriver/bazel_json_builder.go +++ b/go/tools/gopackagesdriver/bazel_json_builder.go @@ -130,6 +130,7 @@ 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) + p = strings.Replace(p, "__BAZEL_OUTPUT_BASE__", b.bazel.OutputBase(), 1) return p } } From 3d2175bb9b517e73cbd5e2da211483408b2adcf2 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 8 Aug 2021 19:39:16 +0200 Subject: [PATCH 07/24] go/tools/gopackagesdriver: pull source files and return stdlib by default When fetching the whole graph, default to returning the stdlib packages so that vscode doesn't complain. --- go/tools/gopackagesdriver/aspect.bzl | 22 +++++++++--- .../gopackagesdriver/bazel_json_builder.go | 35 +++++++++++-------- 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/go/tools/gopackagesdriver/aspect.bzl b/go/tools/gopackagesdriver/aspect.bzl index 7552bd0969..bede78915c 100644 --- a/go/tools/gopackagesdriver/aspect.bzl +++ b/go/tools/gopackagesdriver/aspect.bzl @@ -56,6 +56,7 @@ def _go_pkg_info_aspect_impl(target, ctx): deps_transitive_json_file = [] deps_transitive_export_file = [] + deps_transitive_compiled_go_files = [] for attr in ["deps", "embed"]: for dep in getattr(ctx.rule.attr, attr, []): if GoPkgInfo in dep: @@ -63,20 +64,24 @@ def _go_pkg_info_aspect_impl(target, ctx): if attr == "deps": deps_transitive_json_file.append(pkg_info.transitive_json_file) deps_transitive_export_file.append(pkg_info.transitive_export_file) + deps_transitive_compiled_go_files.append(pkg_info.transitive_compiled_go_files) elif attr == "embed": # 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'. deps_transitive_json_file.append(pkg_info.deps_transitive_json_file) deps_transitive_export_file.append(pkg_info.deps_transitive_export_file) + deps_transitive_compiled_go_files.append(pkg_info.deps_transitive_compiled_go_files) # Fetch the stdlib json from the first dependency if not stdlib_json_file: stdlib_json_file = pkg_info.stdlib_json_file pkg_json_file = None + compiled_go_files = [] export_file = None if GoArchive in target: archive = target[GoArchive] + compiled_go_files = archive.data.srcs export_file = archive.data.export_file pkg = struct( ID = str(archive.data.label), @@ -94,10 +99,10 @@ def _go_pkg_info_aspect_impl(target, ctx): 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_file: - stdlib_json_file = ctx.attr._go_stdlib[GoStdLib]._list_json + # 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 pkg_info = GoPkgInfo( json = pkg_json_file, @@ -109,7 +114,13 @@ def _go_pkg_info_aspect_impl(target, ctx): deps_transitive_json_file = depset( transitive = deps_transitive_json_file, ), - export_file = export_file, + transitive_compiled_go_files = depset( + direct = compiled_go_files, + transitive = deps_transitive_compiled_go_files, + ), + deps_transitive_compiled_go_files = depset( + transitive = deps_transitive_compiled_go_files, + ), transitive_export_file = depset( direct = [export_file] if export_file else [], transitive = deps_transitive_export_file, @@ -123,6 +134,7 @@ def _go_pkg_info_aspect_impl(target, ctx): pkg_info, OutputGroupInfo( go_pkg_driver_json_file = pkg_info.transitive_json_file, + go_pkg_driver_srcs = pkg_info.transitive_compiled_go_files, 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 76a3b30b6f..0c9f1a45a1 100644 --- a/go/tools/gopackagesdriver/bazel_json_builder.go +++ b/go/tools/gopackagesdriver/bazel_json_builder.go @@ -27,10 +27,7 @@ type BazelJSONBuilder struct { } const ( - OutputGroupDriverJSONFile = "go_pkg_driver_json_file" - OutputGroupStdLibJSONFile = "go_pkg_driver_stdlib_json_file" - OutputGroupExportFile = "go_pkg_driver_export_file" - wildcardQuery = `kind("go_library", //...)` + RulesGoStdlibLabel = "@io_bazel_rules_go//:stdlib" ) func (b *BazelJSONBuilder) fileQuery(filename string) string { @@ -42,6 +39,9 @@ func (b *BazelJSONBuilder) fileQuery(filename string) string { } func (b *BazelJSONBuilder) packageQuery(importPath string) string { + if strings.HasSuffix(importPath, "/...") { + importPath = strings.TrimSuffix(importPath, "/...") + "/.*" + } return fmt.Sprintf(`some(kind("go_library", attr(importpath, "%s", deps(%s))))`, importPath, bazelQueryScope) } @@ -49,14 +49,23 @@ func (b *BazelJSONBuilder) queryFromRequests(requests ...string) string { ret := make([]string, 0, len(requests)) for _, request := range requests { if request == "." || request == "./..." { - return wildcardQuery + if bazelQueryScope != "" { + ret = append(ret, fmt.Sprintf(`kind("go_library", %s)`, bazelQueryScope)) + } else { + ret = append(ret, fmt.Sprintf(RulesGoStdlibLabel)) + } + } else if request == "builtin" || request == "std" { + ret = append(ret, fmt.Sprintf(RulesGoStdlibLabel)) } else if strings.HasPrefix(request, "file=") { f := strings.TrimPrefix(request, "file=") ret = append(ret, b.fileQuery(f)) - } else { + } else if bazelQueryScope != "" { ret = append(ret, b.packageQuery(request)) } } + if len(ret) == 0 { + return RulesGoStdlibLabel + } return strings.Join(ret, " union ") } @@ -68,9 +77,9 @@ func NewBazelJSONBuilder(bazel *Bazel, requests ...string) (*BazelJSONBuilder, e } func (b *BazelJSONBuilder) outputGroupsForMode(mode LoadMode) string { - og := OutputGroupDriverJSONFile + "," + OutputGroupStdLibJSONFile - if mode&NeedExportsFile != 0 { // override for now - og += "," + OutputGroupExportFile + og := "go_pkg_driver_json_file,go_pkg_driver_stdlib_json_file,go_pkg_driver_srcs" + if mode&NeedExportsFile != 0 { + og += ",go_pkg_driver_export_file" } return og } @@ -98,9 +107,6 @@ func (b *BazelJSONBuilder) Build(ctx context.Context, mode LoadMode) ([]string, if err != nil { return nil, fmt.Errorf("unable to query: %w", err) } - if len(labels) == 0 { - return nil, fmt.Errorf("found no target to build") - } buildArgs := concatStringsArrays([]string{ "--experimental_convenience_symlinks=ignore", @@ -117,10 +123,9 @@ func (b *BazelJSONBuilder) Build(ctx context.Context, mode LoadMode) ([]string, ret := []string{} for _, f := range files { - if !strings.HasSuffix(f, ".pkg.json") { - continue + if strings.HasSuffix(f, ".pkg.json") { + ret = append(ret, f) } - ret = append(ret, f) } return ret, nil From 2071919dce36673f9ba6b686f6068b6aeac2b0a8 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 8 Aug 2021 19:39:31 +0200 Subject: [PATCH 08/24] Fix typo --- go/tools/gopackagesdriver/driver_request.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/tools/gopackagesdriver/driver_request.go b/go/tools/gopackagesdriver/driver_request.go index 8fe2f63e22..1b82ed14e7 100644 --- a/go/tools/gopackagesdriver/driver_request.go +++ b/go/tools/gopackagesdriver/driver_request.go @@ -65,7 +65,7 @@ const ( ) // 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 +// Most fields are disabled since there is no need for them type DriverRequest struct { Mode LoadMode `json:"mode"` // Env specifies the environment the underlying build system should be run in. From 1d4e937026a1ff3ac0e182a06ac6c476bb5a18bc Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 8 Aug 2021 19:39:48 +0200 Subject: [PATCH 09/24] Fix formatting --- go/tools/gopackagesdriver/flatpackage.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/go/tools/gopackagesdriver/flatpackage.go b/go/tools/gopackagesdriver/flatpackage.go index b0fbe0ff36..61aaadcde0 100644 --- a/go/tools/gopackagesdriver/flatpackage.go +++ b/go/tools/gopackagesdriver/flatpackage.go @@ -65,8 +65,10 @@ type FlatPackage struct { Standard bool `json:",omitempty"` } -type PackageFunc func(pkg *FlatPackage) -type PathResolverFunc func(path string) string +type ( + PackageFunc func(pkg *FlatPackage) + PathResolverFunc func(path string) string +) func resolvePathsInPlace(prf PathResolverFunc, paths []string) { for i, path := range paths { From 3c96e2c5d8455390d288fb2e801e3e0335977dd7 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 8 Aug 2021 19:44:03 +0200 Subject: [PATCH 10/24] Enable queries on package registry --- go/tools/gopackagesdriver/main.go | 9 +++-- go/tools/gopackagesdriver/packageregistry.go | 39 +++++++++++--------- go/tools/gopackagesdriver/utils.go | 9 +++++ 3 files changed, 36 insertions(+), 21 deletions(-) diff --git a/go/tools/gopackagesdriver/main.go b/go/tools/gopackagesdriver/main.go index f7b02cd44c..d42058e1bf 100644 --- a/go/tools/gopackagesdriver/main.go +++ b/go/tools/gopackagesdriver/main.go @@ -56,7 +56,7 @@ var ( bazelBin = getenvDefault("GOPACKAGESDRIVER_BAZEL", "bazel") bazelFlags = strings.Fields(os.Getenv("GOPACKAGESDRIVER_BAZEL_FLAGS")) bazelQueryFlags = strings.Fields(os.Getenv("GOPACKAGESDRIVER_BAZEL_QUERY_FLAGS")) - bazelQueryScope = getenvDefault("GOPACKAGESDRIVER_BAZEL_QUERY_SCOPE", "//...") + bazelQueryScope = getenvDefault("GOPACKAGESDRIVER_BAZEL_QUERY_SCOPE", "") bazelBuildFlags = strings.Fields(os.Getenv("GOPACKAGESDRIVER_BAZEL_BUILD_FLAGS")) workspaceRoot = os.Getenv("BUILD_WORKSPACE_DIRECTORY") emptyResponse = &driverResponse{ @@ -93,6 +93,8 @@ func run() (*driverResponse, error) { ctx, cancel := signalContext(context.Background(), os.Interrupt) defer cancel() + queries := os.Args[1:] + request, err := ReadDriverRequest(os.Stdin) if err != nil { return emptyResponse, fmt.Errorf("unable to read request: %w", err) @@ -103,7 +105,7 @@ func run() (*driverResponse, error) { return emptyResponse, fmt.Errorf("unable to create bazel instance: %w", err) } - bazelJsonBuilder, err := NewBazelJSONBuilder(bazel, os.Args[1:]...) + bazelJsonBuilder, err := NewBazelJSONBuilder(bazel, queries...) if err != nil { return emptyResponse, fmt.Errorf("unable to build JSON files: %w", err) } @@ -118,7 +120,8 @@ func run() (*driverResponse, error) { return emptyResponse, fmt.Errorf("unable to load JSON files: %w", err) } - return driver.Match("./..."), nil + // return driver.AllPackagesResponse(), nil + return driver.Match(queries...), nil } func main() { diff --git a/go/tools/gopackagesdriver/packageregistry.go b/go/tools/gopackagesdriver/packageregistry.go index cd979d84d5..c885c18a9b 100644 --- a/go/tools/gopackagesdriver/packageregistry.go +++ b/go/tools/gopackagesdriver/packageregistry.go @@ -84,16 +84,32 @@ func (pr *PackageRegistry) walk(acc map[string]*FlatPackage, root string) { 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 pattern == "." || pattern == "./..." { + for _, pkg := range pr.packagesByImportPath { + if strings.HasPrefix(pkg.ID, "//") { + roots[pkg.ID] = struct{}{} + } + } + } else if strings.HasSuffix(pattern, "/...") { + pkgPrefix := strings.TrimSuffix(pattern, "/...") + for _, pkg := range pr.packagesByImportPath { + if strings.HasPrefix(pkg.PkgPath, pkgPrefix) { + roots[pkg.ID] = struct{}{} + } + } + } else if pattern == "builtin" || pattern == "std" { + for _, pkg := range pr.packagesByImportPath { + if pkg.Standard { + roots[pkg.ID] = struct{}{} + } + } + } else if strings.HasPrefix(pattern, "file=") { + f := ensureAbsolutePathFromWorkspace(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{}{} @@ -101,19 +117,6 @@ func (pr *PackageRegistry) Match(patterns ...string) ([]string, []*FlatPackage) } } - 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) - } - return retRoots, retPkgs - } - walkedPackages := map[string]*FlatPackage{} retRoots := make([]string, 0, len(roots)) for rootPkg := range roots { diff --git a/go/tools/gopackagesdriver/utils.go b/go/tools/gopackagesdriver/utils.go index 47b91e319d..bb96319365 100644 --- a/go/tools/gopackagesdriver/utils.go +++ b/go/tools/gopackagesdriver/utils.go @@ -14,6 +14,8 @@ package main +import "path/filepath" + func concatStringsArrays(values ...[]string) []string { ret := []string{} for _, v := range values { @@ -21,3 +23,10 @@ func concatStringsArrays(values ...[]string) []string { } return ret } + +func ensureAbsolutePathFromWorkspace(path string) string { + if filepath.IsAbs(path) { + return path + } + return filepath.Join(workspaceRoot, path) +} From 6a7a1e6f02fe94d60941de3d54542078096c9b7c Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 8 Aug 2021 22:17:53 +0200 Subject: [PATCH 11/24] Move some utiliy functions to the utils file Easier to put them there --- go/tools/gopackagesdriver/main.go | 24 ------------------------ go/tools/gopackagesdriver/utils.go | 29 ++++++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/go/tools/gopackagesdriver/main.go b/go/tools/gopackagesdriver/main.go index d42058e1bf..5f0b022c31 100644 --- a/go/tools/gopackagesdriver/main.go +++ b/go/tools/gopackagesdriver/main.go @@ -20,7 +20,6 @@ import ( "fmt" "go/types" "os" - "os/signal" "strings" ) @@ -67,28 +66,6 @@ var ( } ) -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) - go func() { - select { - case <-ch: - cancel() - case <-ctx.Done(): - } - }() - signal.Notify(ch, signals...) - - return ctx, cancel -} - func run() (*driverResponse, error) { ctx, cancel := signalContext(context.Background(), os.Interrupt) defer cancel() @@ -120,7 +97,6 @@ func run() (*driverResponse, error) { return emptyResponse, fmt.Errorf("unable to load JSON files: %w", err) } - // return driver.AllPackagesResponse(), nil return driver.Match(queries...), nil } diff --git a/go/tools/gopackagesdriver/utils.go b/go/tools/gopackagesdriver/utils.go index bb96319365..4418a41e6c 100644 --- a/go/tools/gopackagesdriver/utils.go +++ b/go/tools/gopackagesdriver/utils.go @@ -14,7 +14,19 @@ package main -import "path/filepath" +import ( + "context" + "os" + "os/signal" + "path/filepath" +) + +func getenvDefault(key, defaultValue string) string { + if v, ok := os.LookupEnv(key); ok { + return v + } + return defaultValue +} func concatStringsArrays(values ...[]string) []string { ret := []string{} @@ -30,3 +42,18 @@ func ensureAbsolutePathFromWorkspace(path string) string { } return filepath.Join(workspaceRoot, path) } + +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 +} From cd8d55437a7331a93e6ac27d2c17d541a9bf980d Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 8 Aug 2021 22:19:31 +0200 Subject: [PATCH 12/24] Remove unsued bazel struct members --- go/tools/gopackagesdriver/bazel.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/go/tools/gopackagesdriver/bazel.go b/go/tools/gopackagesdriver/bazel.go index e7b269510e..c7d9433ba6 100644 --- a/go/tools/gopackagesdriver/bazel.go +++ b/go/tools/gopackagesdriver/bazel.go @@ -35,7 +35,6 @@ const ( type Bazel struct { bazelBin string - execRoot string workspaceRoot string info map[string]string } @@ -82,7 +81,7 @@ func (b *Bazel) run(ctx context.Context, command string, args ...string) (string "--ui_actions_shown=0", }, args...)...) fmt.Fprintln(os.Stderr, "Running:", cmd.Args) - cmd.Dir = b.workspaceRoot + cmd.Dir = b.WorkspaceRoot() cmd.Stderr = os.Stderr output, err := cmd.Output() return string(output), err From a2f3225cc763dfe79979e1e0e201829de4438a0f Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 8 Aug 2021 22:26:56 +0200 Subject: [PATCH 13/24] Don't match only by string prefix when matching /... Guard against packages with the same prefix by happending `/` to HasPrefix and match the exact name if needed. --- go/tools/gopackagesdriver/packageregistry.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/tools/gopackagesdriver/packageregistry.go b/go/tools/gopackagesdriver/packageregistry.go index c885c18a9b..e5d430614c 100644 --- a/go/tools/gopackagesdriver/packageregistry.go +++ b/go/tools/gopackagesdriver/packageregistry.go @@ -95,7 +95,7 @@ func (pr *PackageRegistry) Match(patterns ...string) ([]string, []*FlatPackage) } else if strings.HasSuffix(pattern, "/...") { pkgPrefix := strings.TrimSuffix(pattern, "/...") for _, pkg := range pr.packagesByImportPath { - if strings.HasPrefix(pkg.PkgPath, pkgPrefix) { + if pkgPrefix == pkg.PkgPath || strings.HasPrefix(pkg.PkgPath, pkgPrefix+"/") { roots[pkg.ID] = struct{}{} } } From d3f0ed0d2a918a86e2fcc19dff320c263b427fe3 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sun, 8 Aug 2021 22:31:31 +0200 Subject: [PATCH 14/24] Stdlib files are relative to the output_base, not execroot While it used to work, this is better. --- 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 90564a2d3e..ec7e3161ae 100644 --- a/go/tools/builders/stdliblist.go +++ b/go/tools/builders/stdliblist.go @@ -111,7 +111,7 @@ func stdlibPackageID(importPath string) string { func execRootPath(execRoot, p string) string { dir, _ := filepath.Rel(execRoot, p) - return filepath.Join("__BAZEL_EXECROOT__", dir) + return filepath.Join("__BAZEL_OUTPUT_BASE__", dir) } func absoluteSourcesPaths(execRoot, pkgDir string, srcs []string) []string { From 503ed0e4d96506ea30c5fd42c1452b88a4f3fbbe Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Tue, 17 Aug 2021 15:56:46 +0200 Subject: [PATCH 15/24] Simpler error checking for bazel errors Co-authored-by: Zhongpeng Lin --- 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 c7d9433ba6..881ccc70b3 100644 --- a/go/tools/gopackagesdriver/bazel.go +++ b/go/tools/gopackagesdriver/bazel.go @@ -107,7 +107,7 @@ func (b *Bazel) Build(ctx context.Context, args ...string) ([]string, error) { // See https://docs.bazel.build/versions/main/guide.html#what-exit-code-will-i-get on // exit codes. var exerr *exec.ExitError - if !errors.As(err, &exerr) || (errors.As(err, &exerr) && exerr.ExitCode() != 1) { + if !errors.As(err, &exerr) || exerr.ExitCode() != 1 { return nil, fmt.Errorf("bazel build failed: %w", err) } } From 2d323ab8c0218b1271e22ae487e58d04ba15f9a5 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Tue, 17 Aug 2021 16:01:06 +0200 Subject: [PATCH 16/24] Don't use some on bazel query for package When there is no such go_library, the some function would lead to errors like ERROR: Evaluation of query failed: argument set is empty, with exit code 7. It is unclear whether the query has a bug or there is no matching package. If we don't have the some function, Bazel query will exit normally with empty result. We can report a better error to gopls for this case. Co-authored-by: Zhongpeng Lin --- go/tools/gopackagesdriver/bazel_json_builder.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go/tools/gopackagesdriver/bazel_json_builder.go b/go/tools/gopackagesdriver/bazel_json_builder.go index 0c9f1a45a1..f4c52260d6 100644 --- a/go/tools/gopackagesdriver/bazel_json_builder.go +++ b/go/tools/gopackagesdriver/bazel_json_builder.go @@ -35,14 +35,14 @@ func (b *BazelJSONBuilder) fileQuery(filename string) string { fp, _ := filepath.Rel(b.bazel.WorkspaceRoot(), filename) filename = fp } - return fmt.Sprintf(`some(kind("go_library", same_pkg_direct_rdeps("%s")))`, filename) + return fmt.Sprintf(`kind("go_library", same_pkg_direct_rdeps("%s"))`, filename) } func (b *BazelJSONBuilder) packageQuery(importPath string) string { if strings.HasSuffix(importPath, "/...") { importPath = strings.TrimSuffix(importPath, "/...") + "/.*" } - return fmt.Sprintf(`some(kind("go_library", attr(importpath, "%s", deps(%s))))`, importPath, bazelQueryScope) + return fmt.Sprintf(`kind("go_library", attr(importpath, "%s", deps(%s)))`, importPath, bazelQueryScope) } func (b *BazelJSONBuilder) queryFromRequests(requests ...string) string { From 42013df439b36c60ca0269981695a7cfa9d2723f Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Tue, 17 Aug 2021 16:04:29 +0200 Subject: [PATCH 17/24] CHeck for empty labels when trying to build a target Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/bazel_json_builder.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/go/tools/gopackagesdriver/bazel_json_builder.go b/go/tools/gopackagesdriver/bazel_json_builder.go index f4c52260d6..f7c4c25481 100644 --- a/go/tools/gopackagesdriver/bazel_json_builder.go +++ b/go/tools/gopackagesdriver/bazel_json_builder.go @@ -105,7 +105,11 @@ func (b *BazelJSONBuilder) query(ctx context.Context, query string) ([]string, e func (b *BazelJSONBuilder) Build(ctx context.Context, mode LoadMode) ([]string, error) { labels, err := b.query(ctx, b.queryFromRequests(b.requests...)) if err != nil { - return nil, fmt.Errorf("unable to query: %w", err) + return nil, fmt.Errorf("query failed: %w", err) + } + + if len(labels) == 0 { + return nil, fmt.Errorf("found no labels matching the requests") } buildArgs := concatStringsArrays([]string{ From ec895a35f338dd3a0be2619c52f711cb384f3963 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Wed, 18 Aug 2021 18:49:53 +0200 Subject: [PATCH 18/24] Honor build tags when listing files This is done using a brand new build context that is configured using the regular environment variables. Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/BUILD.bazel | 1 + go/tools/gopackagesdriver/build_context.go | 38 ++++++++++++++++++++ go/tools/gopackagesdriver/flatpackage.go | 7 ++++ go/tools/gopackagesdriver/packageregistry.go | 4 +++ 4 files changed, 50 insertions(+) create mode 100644 go/tools/gopackagesdriver/build_context.go diff --git a/go/tools/gopackagesdriver/BUILD.bazel b/go/tools/gopackagesdriver/BUILD.bazel index 89f7f1408d..5025cf6379 100644 --- a/go/tools/gopackagesdriver/BUILD.bazel +++ b/go/tools/gopackagesdriver/BUILD.bazel @@ -5,6 +5,7 @@ go_library( srcs = [ "bazel.go", "bazel_json_builder.go", + "build_context.go", "driver_request.go", "flatpackage.go", "json_packages_driver.go", diff --git a/go/tools/gopackagesdriver/build_context.go b/go/tools/gopackagesdriver/build_context.go new file mode 100644 index 0000000000..8074746c9f --- /dev/null +++ b/go/tools/gopackagesdriver/build_context.go @@ -0,0 +1,38 @@ +package main + +import ( + "go/build" + "os" + "path/filepath" + "strings" +) + +var buildContext = makeBuildContext() + +func makeBuildContext() *build.Context { + bctx := &build.Context{ + GOOS: getenvDefault("GOOS", build.Default.GOOS), + GOARCH: getenvDefault("GOARCH", build.Default.GOARCH), + GOROOT: getenvDefault("GOROOT", build.Default.GOROOT), + GOPATH: getenvDefault("GOPATH", build.Default.GOPATH), + BuildTags: strings.Split(getenvDefault("GOTAGS", ""), ","), + ReleaseTags: build.Default.ReleaseTags[:], + } + if v, ok := os.LookupEnv("CGO_ENABLED"); ok { + bctx.CgoEnabled = v == "1" + } else { + bctx.CgoEnabled = build.Default.CgoEnabled + } + return bctx +} + +func filterSourceFilesForTags(files []string) []string { + ret := make([]string, 0, len(files)) + for _, f := range files { + dir, filename := filepath.Split(f) + if match, _ := buildContext.MatchFile(dir, filename); match { + ret = append(ret, f) + } + } + return ret +} diff --git a/go/tools/gopackagesdriver/flatpackage.go b/go/tools/gopackagesdriver/flatpackage.go index 61aaadcde0..885acfd42e 100644 --- a/go/tools/gopackagesdriver/flatpackage.go +++ b/go/tools/gopackagesdriver/flatpackage.go @@ -102,6 +102,13 @@ func (fp *FlatPackage) ResolvePaths(prf PathResolverFunc) error { return nil } +// FilterFilesForBuildTags filters the source files given the current build +// tags. +func (fp *FlatPackage) FilterFilesForBuildTags() { + fp.GoFiles = filterSourceFilesForTags(fp.GoFiles) + fp.CompiledGoFiles = filterSourceFilesForTags(fp.CompiledGoFiles) +} + func (fp *FlatPackage) IsStdlib() bool { return fp.Standard } diff --git a/go/tools/gopackagesdriver/packageregistry.go b/go/tools/gopackagesdriver/packageregistry.go index e5d430614c..8c783abbf6 100644 --- a/go/tools/gopackagesdriver/packageregistry.go +++ b/go/tools/gopackagesdriver/packageregistry.go @@ -56,6 +56,10 @@ func (pr *PackageRegistry) Remove(pkgs ...*FlatPackage) *PackageRegistry { func (pr *PackageRegistry) ResolvePaths(prf PathResolverFunc) error { for _, pkg := range pr.packagesByImportPath { pkg.ResolvePaths(prf) + pkg.FilterFilesForBuildTags() + for _, f := range pkg.CompiledGoFiles { + pr.packagesByFile[f] = pkg + } for _, f := range pkg.CompiledGoFiles { pr.packagesByFile[f] = pkg } From 87c69ed396dd2dc80ba797bef40d91284fa44903 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Wed, 18 Aug 2021 19:17:41 +0200 Subject: [PATCH 19/24] Properly match full and child importpaths Match a/ab and a/ab/c but not a/abc 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 f7c4c25481..024d6cb039 100644 --- a/go/tools/gopackagesdriver/bazel_json_builder.go +++ b/go/tools/gopackagesdriver/bazel_json_builder.go @@ -40,7 +40,7 @@ func (b *BazelJSONBuilder) fileQuery(filename string) string { func (b *BazelJSONBuilder) packageQuery(importPath string) string { if strings.HasSuffix(importPath, "/...") { - importPath = strings.TrimSuffix(importPath, "/...") + "/.*" + importPath = strings.TrimSuffix(importPath, "/...") + "(/.+)?$" } return fmt.Sprintf(`kind("go_library", attr(importpath, "%s", deps(%s)))`, importPath, bazelQueryScope) } From 64ba983e9438afd0f3662d9b3edfda2b11f9235b Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Wed, 18 Aug 2021 19:37:05 +0200 Subject: [PATCH 20/24] Add a comment telling why we exit(0) Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/main.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/go/tools/gopackagesdriver/main.go b/go/tools/gopackagesdriver/main.go index 5f0b022c31..17ee60a470 100644 --- a/go/tools/gopackagesdriver/main.go +++ b/go/tools/gopackagesdriver/main.go @@ -107,6 +107,9 @@ func main() { } if err != nil { fmt.Fprintf(os.Stderr, "error: %v", err) + // gopls will check the packages driver exit code, and if there is an + // error, it will fall back to go list. Obviously we don't want that, + // so force a 0 exit code. os.Exit(0) } } From 0def12df741bff57b23b16ef4603cb6c8dc6ceea Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Wed, 18 Aug 2021 21:09:13 +0200 Subject: [PATCH 21/24] Better regexp for importpath matching Co-authored-by: Zhongpeng Lin --- 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 024d6cb039..a4135571e3 100644 --- a/go/tools/gopackagesdriver/bazel_json_builder.go +++ b/go/tools/gopackagesdriver/bazel_json_builder.go @@ -40,7 +40,7 @@ func (b *BazelJSONBuilder) fileQuery(filename string) string { func (b *BazelJSONBuilder) packageQuery(importPath string) string { if strings.HasSuffix(importPath, "/...") { - importPath = strings.TrimSuffix(importPath, "/...") + "(/.+)?$" + importPath = fmt.Sprintf(`^%s(/.+)?$`, strings.TrimSuffix(importPath, "/...")) } return fmt.Sprintf(`kind("go_library", attr(importpath, "%s", deps(%s)))`, importPath, bazelQueryScope) } From 07037432128249dc54fcb7759326a0a9e056ae92 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Wed, 18 Aug 2021 21:11:45 +0200 Subject: [PATCH 22/24] Don't append empty bazel queries to bazel build Co-authored-by: Zhongpeng Lin --- go/tools/gopackagesdriver/bazel_json_builder.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/go/tools/gopackagesdriver/bazel_json_builder.go b/go/tools/gopackagesdriver/bazel_json_builder.go index a4135571e3..f8c6a7a36d 100644 --- a/go/tools/gopackagesdriver/bazel_json_builder.go +++ b/go/tools/gopackagesdriver/bazel_json_builder.go @@ -48,19 +48,23 @@ func (b *BazelJSONBuilder) packageQuery(importPath string) string { func (b *BazelJSONBuilder) queryFromRequests(requests ...string) string { ret := make([]string, 0, len(requests)) for _, request := range requests { + result := "" if request == "." || request == "./..." { if bazelQueryScope != "" { - ret = append(ret, fmt.Sprintf(`kind("go_library", %s)`, bazelQueryScope)) + result = fmt.Sprintf(`kind("go_library", %s)`, bazelQueryScope) } else { - ret = append(ret, fmt.Sprintf(RulesGoStdlibLabel)) + result = fmt.Sprintf(RulesGoStdlibLabel) } } else if request == "builtin" || request == "std" { - ret = append(ret, fmt.Sprintf(RulesGoStdlibLabel)) + result = fmt.Sprintf(RulesGoStdlibLabel) } else if strings.HasPrefix(request, "file=") { f := strings.TrimPrefix(request, "file=") - ret = append(ret, b.fileQuery(f)) + result = b.fileQuery(f) } else if bazelQueryScope != "" { - ret = append(ret, b.packageQuery(request)) + result = b.packageQuery(request) + } + if result != "" { + ret = append(ret, result) } } if len(ret) == 0 { From ddf8e68762ec17e45a4dc0fc44d24962817d2e2d Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Thu, 19 Aug 2021 01:29:38 +0200 Subject: [PATCH 23/24] Make completion on test files work Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/aspect.bzl | 62 ++++++++++--------- .../gopackagesdriver/bazel_json_builder.go | 2 +- 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/go/tools/gopackagesdriver/aspect.bzl b/go/tools/gopackagesdriver/aspect.bzl index bede78915c..c89e797d99 100644 --- a/go/tools/gopackagesdriver/aspect.bzl +++ b/go/tools/gopackagesdriver/aspect.bzl @@ -35,21 +35,26 @@ def _file_path(f): prefix = "__BAZEL_OUTPUT_BASE__" return paths.join(prefix, f.path) -def _new_pkg_info(archive_data): +def _go_archive_to_pkg(archive): return struct( - ID = str(archive_data.label), - PkgPath = archive_data.importpath, - ExportFile = _file_path(archive_data.export_file), + ID = str(archive.data.label), + PkgPath = archive.data.importpath, + ExportFile = _file_path(archive.data.export_file), GoFiles = [ _file_path(src) - for src in archive_data.orig_srcs + for src in archive.data.orig_srcs ], CompiledGoFiles = [ _file_path(src) - for src in archive_data.srcs + for src in archive.data.srcs ], ) +def _make_pkg_json(ctx, archive, pkg_info): + pkg_json_file = ctx.actions.declare_file(archive.data.name + ".pkg.json") + ctx.actions.write(pkg_json_file, content = pkg_info.to_json()) + return pkg_json_file + def _go_pkg_info_aspect_impl(target, ctx): # Fetch the stdlib JSON file from the inner most target stdlib_json_file = None @@ -57,6 +62,7 @@ def _go_pkg_info_aspect_impl(target, ctx): deps_transitive_json_file = [] deps_transitive_export_file = [] deps_transitive_compiled_go_files = [] + for attr in ["deps", "embed"]: for dep in getattr(ctx.rule.attr, attr, []): if GoPkgInfo in dep: @@ -76,28 +82,29 @@ def _go_pkg_info_aspect_impl(target, ctx): if not stdlib_json_file: stdlib_json_file = pkg_info.stdlib_json_file - pkg_json_file = None + pkg_json_files = [] compiled_go_files = [] - export_file = None + export_files = [] + if GoArchive in target: archive = target[GoArchive] - compiled_go_files = archive.data.srcs - export_file = archive.data.export_file - pkg = struct( - ID = str(archive.data.label), - 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_file = ctx.actions.declare_file(archive.data.name + ".pkg.json") - ctx.actions.write(pkg_json_file, content = pkg.to_json()) + compiled_go_files.extend(archive.source.srcs) + export_files.append(archive.data.export_file) + pkg = _go_archive_to_pkg(archive) + pkg_json_files.append(_make_pkg_json(ctx, archive, pkg)) + + # if the rule is a test, we need to get the embedded go_library with the current + # test's sources. For that, consume the dependency via GoArchive.direct so that + # the test source files are there. Then, create the pkg json file directly. Only + # do that for embedded targets, and use the importpath go find which. + if ctx.rule.kind == "go_test": + embedded_targets = [dep[GoArchive].data.importpath for dep in ctx.rule.attr.embed] + for archive in target[GoArchive].direct: + if archive.data.importpath in embedded_targets: + pkg = _go_archive_to_pkg(archive) + pkg_json_files.append(_make_pkg_json(ctx, archive, pkg)) + compiled_go_files.extend(archive.source.srcs) + export_files.append(archive.data.export_file) # If there was no stdlib json in any dependencies, fetch it from the # current go_ node. @@ -105,10 +112,9 @@ def _go_pkg_info_aspect_impl(target, ctx): stdlib_json_file = ctx.attr._go_stdlib[GoStdLib]._list_json pkg_info = GoPkgInfo( - json = pkg_json_file, stdlib_json_file = stdlib_json_file, transitive_json_file = depset( - direct = [pkg_json_file] if pkg_json_file else [], + direct = pkg_json_files, transitive = deps_transitive_json_file, ), deps_transitive_json_file = depset( @@ -122,7 +128,7 @@ def _go_pkg_info_aspect_impl(target, ctx): transitive = deps_transitive_compiled_go_files, ), transitive_export_file = depset( - direct = [export_file] if export_file else [], + direct = export_files, transitive = deps_transitive_export_file, ), deps_transitive_export_file = depset( diff --git a/go/tools/gopackagesdriver/bazel_json_builder.go b/go/tools/gopackagesdriver/bazel_json_builder.go index f8c6a7a36d..14f001888e 100644 --- a/go/tools/gopackagesdriver/bazel_json_builder.go +++ b/go/tools/gopackagesdriver/bazel_json_builder.go @@ -35,7 +35,7 @@ func (b *BazelJSONBuilder) fileQuery(filename string) string { fp, _ := filepath.Rel(b.bazel.WorkspaceRoot(), filename) filename = fp } - return fmt.Sprintf(`kind("go_library", same_pkg_direct_rdeps("%s"))`, filename) + return fmt.Sprintf(`kind("go_library|go_test", same_pkg_direct_rdeps("%s"))`, filename) } func (b *BazelJSONBuilder) packageQuery(importPath string) string { From 165e959dd161e0f3fbd08f703c69600a0210b1b4 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sat, 21 Aug 2021 02:04:05 +0200 Subject: [PATCH 24/24] Handle tests that don't have embedded libraries Signed-off-by: Steeve Morin --- go/tools/gopackagesdriver/aspect.bzl | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/go/tools/gopackagesdriver/aspect.bzl b/go/tools/gopackagesdriver/aspect.bzl index c89e797d99..de9eacd227 100644 --- a/go/tools/gopackagesdriver/aspect.bzl +++ b/go/tools/gopackagesdriver/aspect.bzl @@ -96,11 +96,16 @@ def _go_pkg_info_aspect_impl(target, ctx): # if the rule is a test, we need to get the embedded go_library with the current # test's sources. For that, consume the dependency via GoArchive.direct so that # the test source files are there. Then, create the pkg json file directly. Only - # do that for embedded targets, and use the importpath go find which. + # do that for direct dependencies that are not defined as deps, and use the + # importpath to find which. if ctx.rule.kind == "go_test": - embedded_targets = [dep[GoArchive].data.importpath for dep in ctx.rule.attr.embed] + deps_targets = [ + dep[GoArchive].data.importpath + for dep in ctx.rule.attr.deps + if GoArchive in dep + ] for archive in target[GoArchive].direct: - if archive.data.importpath in embedded_targets: + if archive.data.importpath not in deps_targets: pkg = _go_archive_to_pkg(archive) pkg_json_files.append(_make_pkg_json(ctx, archive, pkg)) compiled_go_files.extend(archive.source.srcs)