summaryrefslogtreecommitdiffhomepage
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/bazeldefs/defs.bzl28
-rw-r--r--tools/defs.bzl4
-rw-r--r--tools/nogo/BUILD11
-rw-r--r--tools/nogo/defs.bzl88
-rw-r--r--tools/nogo/nogo.go16
5 files changed, 124 insertions, 23 deletions
diff --git a/tools/bazeldefs/defs.bzl b/tools/bazeldefs/defs.bzl
index cf5b1dc0d..4aa55e8e5 100644
--- a/tools/bazeldefs/defs.bzl
+++ b/tools/bazeldefs/defs.bzl
@@ -158,11 +158,31 @@ def go_test_library(target):
return target.attr.embed[0]
return None
-def go_context(ctx, std = False):
+def go_context(ctx, goos = None, goarch = None, std = False):
+ """Extracts a standard Go context struct.
+
+ Args:
+ ctx: the starlark context (required).
+ goos: the GOOS value.
+ goarch: the GOARCH value.
+ std: ignored.
+
+ Returns:
+ A context Go struct with pointers to Go toolchain components.
+ """
+
# We don't change anything for the standard library analysis. All Go files
# are available in all instances. Note that this includes the standard
# library sources, which are analyzed by nogo.
go_ctx = _go_context(ctx)
+ if goos == None:
+ goos = go_ctx.sdk.goos
+ elif goos != go_ctx.sdk.goos:
+ fail("Internal GOOS (%s) doesn't match GoSdk GOOS (%s)." % (goos, go_ctx.sdk.goos))
+ if goarch == None:
+ goarch = go_ctx.sdk.goarch
+ elif goarch != go_ctx.sdk.goarch:
+ fail("Internal GOARCH (%s) doesn't match GoSdk GOARCH (%s)." % (goarch, go_ctx.sdk.goarch))
return struct(
go = go_ctx.go,
env = go_ctx.env,
@@ -186,6 +206,12 @@ def select_arch(amd64 = "amd64", arm64 = "arm64", default = None, **kwargs):
def select_system(linux = ["__linux__"], **kwargs):
return linux # Only Linux supported.
+def select_goarch():
+ return select_arch(arm64 = "arm64", amd64 = "amd64")
+
+def select_goos():
+ return select_system(linux = "linux")
+
def default_installer():
return None
diff --git a/tools/defs.bzl b/tools/defs.bzl
index e2c72a1f6..9a90f2554 100644
--- a/tools/defs.bzl
+++ b/tools/defs.bzl
@@ -7,7 +7,7 @@ change for Google-internal and bazel-compatible rules.
load("//tools/go_stateify:defs.bzl", "go_stateify")
load("//tools/go_marshal:defs.bzl", "go_marshal", "marshal_deps", "marshal_test_deps")
-load("//tools/bazeldefs:defs.bzl", _build_test = "build_test", _bzl_library = "bzl_library", _cc_binary = "cc_binary", _cc_flags_supplier = "cc_flags_supplier", _cc_grpc_library = "cc_grpc_library", _cc_library = "cc_library", _cc_proto_library = "cc_proto_library", _cc_test = "cc_test", _cc_toolchain = "cc_toolchain", _coreutil = "coreutil", _default_installer = "default_installer", _default_net_util = "default_net_util", _gazelle = "gazelle", _gbenchmark = "gbenchmark", _go_binary = "go_binary", _go_embed_data = "go_embed_data", _go_grpc_and_proto_libraries = "go_grpc_and_proto_libraries", _go_library = "go_library", _go_path = "go_path", _go_proto_library = "go_proto_library", _go_test = "go_test", _grpcpp = "grpcpp", _gtest = "gtest", _loopback = "loopback", _pkg_deb = "pkg_deb", _pkg_tar = "pkg_tar", _proto_library = "proto_library", _py_binary = "py_binary", _rbe_platform = "rbe_platform", _rbe_toolchain = "rbe_toolchain", _select_arch = "select_arch", _select_system = "select_system", _short_path = "short_path", _vdso_linker_option = "vdso_linker_option")
+load("//tools/bazeldefs:defs.bzl", _build_test = "build_test", _bzl_library = "bzl_library", _cc_binary = "cc_binary", _cc_flags_supplier = "cc_flags_supplier", _cc_grpc_library = "cc_grpc_library", _cc_library = "cc_library", _cc_proto_library = "cc_proto_library", _cc_test = "cc_test", _cc_toolchain = "cc_toolchain", _coreutil = "coreutil", _default_installer = "default_installer", _default_net_util = "default_net_util", _gazelle = "gazelle", _gbenchmark = "gbenchmark", _go_binary = "go_binary", _go_embed_data = "go_embed_data", _go_grpc_and_proto_libraries = "go_grpc_and_proto_libraries", _go_library = "go_library", _go_path = "go_path", _go_proto_library = "go_proto_library", _go_test = "go_test", _grpcpp = "grpcpp", _gtest = "gtest", _loopback = "loopback", _pkg_deb = "pkg_deb", _pkg_tar = "pkg_tar", _proto_library = "proto_library", _py_binary = "py_binary", _rbe_platform = "rbe_platform", _rbe_toolchain = "rbe_toolchain", _select_arch = "select_arch", _select_goarch = "select_goarch", _select_goos = "select_goos", _select_system = "select_system", _short_path = "short_path", _vdso_linker_option = "vdso_linker_option")
load("//tools/bazeldefs:platforms.bzl", _default_platform = "default_platform", _platforms = "platforms")
load("//tools/bazeldefs:tags.bzl", "go_suffixes")
load("//tools/nogo:defs.bzl", "nogo_test")
@@ -35,6 +35,8 @@ pkg_tar = _pkg_tar
py_binary = _py_binary
select_arch = _select_arch
select_system = _select_system
+select_goos = _select_goos
+select_goarch = _select_goarch
short_path = _short_path
rbe_platform = _rbe_platform
rbe_toolchain = _rbe_toolchain
diff --git a/tools/nogo/BUILD b/tools/nogo/BUILD
index dd4b46f58..3c6be3339 100644
--- a/tools/nogo/BUILD
+++ b/tools/nogo/BUILD
@@ -1,8 +1,15 @@
-load("//tools:defs.bzl", "bzl_library", "go_library")
-load("//tools/nogo:defs.bzl", "nogo_objdump_tool", "nogo_stdlib")
+load("//tools:defs.bzl", "bzl_library", "go_library", "select_goarch", "select_goos")
+load("//tools/nogo:defs.bzl", "nogo_objdump_tool", "nogo_stdlib", "nogo_target")
package(licenses = ["notice"])
+nogo_target(
+ name = "target",
+ goarch = select_goarch(),
+ goos = select_goos(),
+ visibility = ["//visibility:public"],
+)
+
nogo_objdump_tool(
name = "objdump_tool",
visibility = ["//visibility:public"],
diff --git a/tools/nogo/defs.bzl b/tools/nogo/defs.bzl
index c6fcfd402..8096087dd 100644
--- a/tools/nogo/defs.bzl
+++ b/tools/nogo/defs.bzl
@@ -2,9 +2,33 @@
load("//tools/bazeldefs:defs.bzl", "go_context", "go_importpath", "go_rule", "go_test_library")
-def _nogo_objdump_tool_impl(ctx):
- go_ctx = go_context(ctx)
+NogoTargetInfo = provider(
+ "information about the Go target",
+ fields = {
+ "goarch": "the build architecture (GOARCH)",
+ "goos": "the build OS target (GOOS)",
+ },
+)
+
+def _nogo_target_impl(ctx):
+ return [NogoTargetInfo(
+ goarch = ctx.attr.goarch,
+ goos = ctx.attr.goos,
+ )]
+nogo_target = go_rule(
+ rule,
+ implementation = _nogo_target_impl,
+ attrs = {
+ # goarch is the build architecture. This will normally be provided by a
+ # select statement, but this information is propagated to other rules.
+ "goarch": attr.string(mandatory = True),
+ # goos is similarly the build operating system target.
+ "goos": attr.string(mandatory = True),
+ },
+)
+
+def _nogo_objdump_tool_impl(ctx):
# Construct the magic dump command.
#
# Note that in some cases, the input is being fed into the tool via stdin.
@@ -12,6 +36,8 @@ def _nogo_objdump_tool_impl(ctx):
# we need the tool to handle this case by creating a temporary file.
#
# [1] https://github.com/golang/go/issues/41051
+ nogo_target_info = ctx.attr._nogo_target[NogoTargetInfo]
+ go_ctx = go_context(ctx, goos = nogo_target_info.goos, goarch = nogo_target_info.goarch)
env_prefix = " ".join(["%s=%s" % (key, value) for (key, value) in go_ctx.env.items()])
dumper = ctx.actions.declare_file(ctx.label.name)
ctx.actions.write(dumper, "\n".join([
@@ -42,6 +68,12 @@ def _nogo_objdump_tool_impl(ctx):
nogo_objdump_tool = go_rule(
rule,
implementation = _nogo_objdump_tool_impl,
+ attrs = {
+ "_nogo_target": attr.label(
+ default = "//tools/nogo:target",
+ cfg = "target",
+ ),
+ },
)
# NogoStdlibInfo is the set of standard library facts.
@@ -54,9 +86,9 @@ NogoStdlibInfo = provider(
)
def _nogo_stdlib_impl(ctx):
- go_ctx = go_context(ctx)
-
# Build the standard library facts.
+ nogo_target_info = ctx.attr._nogo_target[NogoTargetInfo]
+ go_ctx = go_context(ctx, goos = nogo_target_info.goos, goarch = nogo_target_info.goarch)
facts = ctx.actions.declare_file(ctx.label.name + ".facts")
findings = ctx.actions.declare_file(ctx.label.name + ".findings")
config = struct(
@@ -70,12 +102,12 @@ def _nogo_stdlib_impl(ctx):
ctx.actions.run(
inputs = [config_file] + go_ctx.stdlib_srcs,
outputs = [facts, findings],
- tools = depset(go_ctx.runfiles.to_list() + ctx.files._objdump_tool),
- executable = ctx.files._nogo[0],
+ tools = depset(go_ctx.runfiles.to_list() + ctx.files._nogo_objdump_tool),
+ executable = ctx.files._nogo_check[0],
mnemonic = "GoStandardLibraryAnalysis",
progress_message = "Analyzing Go Standard Library",
arguments = go_ctx.nogo_args + [
- "-objdump_tool=%s" % ctx.files._objdump_tool[0].path,
+ "-objdump_tool=%s" % ctx.files._nogo_objdump_tool[0].path,
"-stdlib=%s" % config_file.path,
"-findings=%s" % findings.path,
"-facts=%s" % facts.path,
@@ -92,11 +124,17 @@ nogo_stdlib = go_rule(
rule,
implementation = _nogo_stdlib_impl,
attrs = {
- "_nogo": attr.label(
+ "_nogo_check": attr.label(
default = "//tools/nogo/check:check",
+ cfg = "host",
),
- "_objdump_tool": attr.label(
+ "_nogo_objdump_tool": attr.label(
default = "//tools/nogo:objdump_tool",
+ cfg = "host",
+ ),
+ "_nogo_target": attr.label(
+ default = "//tools/nogo:target",
+ cfg = "target",
),
},
)
@@ -119,8 +157,6 @@ NogoInfo = provider(
)
def _nogo_aspect_impl(target, ctx):
- go_ctx = go_context(ctx)
-
# If this is a nogo rule itself (and not the shadow of a go_library or
# go_binary rule created by such a rule), then we simply return nothing.
# All work is done in the shadow properties for go rules. For a proto
@@ -200,10 +236,13 @@ def _nogo_aspect_impl(target, ctx):
inputs += info.binaries
# Add the standard library facts.
- stdlib_facts = ctx.attr._nogo_stdlib[NogoStdlibInfo].facts
+ stdlib_info = ctx.attr._nogo_stdlib[NogoStdlibInfo]
+ stdlib_facts = stdlib_info.facts
inputs.append(stdlib_facts)
# The nogo tool operates on a configuration serialized in JSON format.
+ nogo_target_info = ctx.attr._nogo_target[NogoTargetInfo]
+ go_ctx = go_context(ctx, goos = nogo_target_info.goos, goarch = nogo_target_info.goarch)
facts = ctx.actions.declare_file(target.label.name + ".facts")
findings = ctx.actions.declare_file(target.label.name + ".findings")
escapes = ctx.actions.declare_file(target.label.name + ".escapes")
@@ -224,13 +263,13 @@ def _nogo_aspect_impl(target, ctx):
ctx.actions.run(
inputs = inputs,
outputs = [facts, findings, escapes],
- tools = depset(go_ctx.runfiles.to_list() + ctx.files._objdump_tool),
- executable = ctx.files._nogo[0],
+ tools = depset(go_ctx.runfiles.to_list() + ctx.files._nogo_objdump_tool),
+ executable = ctx.files._nogo_check[0],
mnemonic = "GoStaticAnalysis",
progress_message = "Analyzing %s" % target.label,
arguments = go_ctx.nogo_args + [
"-binary=%s" % target_objfile.path,
- "-objdump_tool=%s" % ctx.files._objdump_tool[0].path,
+ "-objdump_tool=%s" % ctx.files._nogo_objdump_tool[0].path,
"-package=%s" % config_file.path,
"-findings=%s" % findings.path,
"-facts=%s" % facts.path,
@@ -266,9 +305,22 @@ nogo_aspect = go_rule(
"embed",
],
attrs = {
- "_nogo": attr.label(default = "//tools/nogo/check:check"),
- "_nogo_stdlib": attr.label(default = "//tools/nogo:stdlib"),
- "_objdump_tool": attr.label(default = "//tools/nogo:objdump_tool"),
+ "_nogo_check": attr.label(
+ default = "//tools/nogo/check:check",
+ cfg = "host",
+ ),
+ "_nogo_stdlib": attr.label(
+ default = "//tools/nogo:stdlib",
+ cfg = "host",
+ ),
+ "_nogo_objdump_tool": attr.label(
+ default = "//tools/nogo:objdump_tool",
+ cfg = "host",
+ ),
+ "_nogo_target": attr.label(
+ default = "//tools/nogo:target",
+ cfg = "target",
+ ),
},
)
diff --git a/tools/nogo/nogo.go b/tools/nogo/nogo.go
index 120fdcff5..b178f63ab 100644
--- a/tools/nogo/nogo.go
+++ b/tools/nogo/nogo.go
@@ -264,12 +264,17 @@ func checkStdlib(config *stdlibConfig, ac map[*analysis.Analyzer]matcher) ([]str
// Closure to check a single package.
allFindings := make([]string, 0)
stdlibFacts := make(map[string][]byte)
+ stdlibErrs := make(map[string]error)
var checkOne func(pkg string) error // Recursive.
checkOne = func(pkg string) error {
// Is this already done?
if _, ok := stdlibFacts[pkg]; ok {
return nil
}
+ // Did this fail previously?
+ if _, ok := stdlibErrs[pkg]; ok {
+ return nil
+ }
// Lookup the configuration.
config, ok := packages[pkg]
@@ -283,6 +288,7 @@ func checkStdlib(config *stdlibConfig, ac map[*analysis.Analyzer]matcher) ([]str
// If there's no binary for this package, it is likely
// not built with the distribution. That's fine, we can
// just skip analysis.
+ stdlibErrs[pkg] = err
return nil
}
@@ -299,6 +305,7 @@ func checkStdlib(config *stdlibConfig, ac map[*analysis.Analyzer]matcher) ([]str
if err != nil {
// If we can't analyze a package from the standard library,
// then we skip it. It will simply not have any findings.
+ stdlibErrs[pkg] = err
return nil
}
stdlibFacts[pkg] = factData
@@ -312,7 +319,9 @@ func checkStdlib(config *stdlibConfig, ac map[*analysis.Analyzer]matcher) ([]str
// to evaluate in the order provided here. We do ensure however, that
// all packages are evaluated.
for pkg := range packages {
- checkOne(pkg)
+ if err := checkOne(pkg); err != nil {
+ return nil, nil, err
+ }
}
// Sanity check.
@@ -326,6 +335,11 @@ func checkStdlib(config *stdlibConfig, ac map[*analysis.Analyzer]matcher) ([]str
return nil, nil, fmt.Errorf("error saving stdlib facts: %w", err)
}
+ // Write out all errors.
+ for pkg, err := range stdlibErrs {
+ log.Printf("WARNING: error while processing %v: %v", pkg, err)
+ }
+
// Return all findings.
return allFindings, factData, nil
}