diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/BUILD | 8 | ||||
-rw-r--r-- | tools/bazeldefs/go.bzl | 2 | ||||
-rw-r--r-- | tools/bigquery/BUILD | 1 | ||||
-rw-r--r-- | tools/bigquery/bigquery.go | 8 | ||||
-rw-r--r-- | tools/deps.bzl | 114 | ||||
-rw-r--r-- | tools/go_marshal/defs.bzl | 1 | ||||
-rw-r--r-- | tools/go_marshal/gomarshal/generator.go | 2 | ||||
-rw-r--r-- | tools/go_marshal/gomarshal/generator_interfaces_array_newtype.go | 8 | ||||
-rw-r--r-- | tools/go_marshal/gomarshal/generator_interfaces_primitive_newtype.go | 24 | ||||
-rw-r--r-- | tools/go_marshal/gomarshal/generator_interfaces_struct.go | 33 | ||||
-rw-r--r-- | tools/nogo/analyzers.go | 6 | ||||
-rw-r--r-- | tools/nogo/check/main.go | 17 | ||||
-rw-r--r-- | tools/nogo/defs.bzl | 46 |
13 files changed, 191 insertions, 79 deletions
diff --git a/tools/BUILD b/tools/BUILD index faf310676..3861ff2a5 100644 --- a/tools/BUILD +++ b/tools/BUILD @@ -9,3 +9,11 @@ bzl_library( "//:sandbox", ], ) + +bzl_library( + name = "deps_bzl", + srcs = ["deps.bzl"], + visibility = [ + "//:sandbox", + ], +) diff --git a/tools/bazeldefs/go.bzl b/tools/bazeldefs/go.bzl index bcd8cffe7..d16376032 100644 --- a/tools/bazeldefs/go.bzl +++ b/tools/bazeldefs/go.bzl @@ -132,7 +132,7 @@ def go_context(ctx, goos = None, goarch = None, std = False): runfiles = depset([go_ctx.go] + go_ctx.sdk.srcs + go_ctx.sdk.tools + go_ctx.stdlib.libs), goos = go_ctx.sdk.goos, goarch = go_ctx.sdk.goarch, - tags = go_ctx.tags, + gotags = go_ctx.tags, ) def select_goarch(): diff --git a/tools/bigquery/BUILD b/tools/bigquery/BUILD index 1cea9e1c9..81994f954 100644 --- a/tools/bigquery/BUILD +++ b/tools/bigquery/BUILD @@ -12,5 +12,6 @@ go_library( deps = [ "@com_google_cloud_go_bigquery//:go_default_library", "@org_golang_google_api//option:go_default_library", + "@org_golang_x_oauth2//:go_default_library", ], ) diff --git a/tools/bigquery/bigquery.go b/tools/bigquery/bigquery.go index a4ca93ec2..935154acc 100644 --- a/tools/bigquery/bigquery.go +++ b/tools/bigquery/bigquery.go @@ -119,6 +119,14 @@ func NewBenchmark(name string, iters int) *Benchmark { } } +// NewBenchmarkWithMetric creates a new sending to BigQuery, initialized with a +// single iteration and single metric. +func NewBenchmarkWithMetric(name, metric, unit string, value float64) *Benchmark { + b := NewBenchmark(name, 1) + b.AddMetric(metric, unit, value) + return b +} + // NewSuite initializes a new Suite. func NewSuite(name string, official bool) *Suite { return &Suite{ diff --git a/tools/deps.bzl b/tools/deps.bzl new file mode 100644 index 000000000..ed1135a9e --- /dev/null +++ b/tools/deps.bzl @@ -0,0 +1,114 @@ +"""Rules for dependency checking.""" + +# DepsInfo provides a list of dependencies found when building a target. +DepsInfo = provider( + "lists dependencies encountered while building", + fields = { + "nodes": "a dict from targets to a list of their dependencies", + }, +) + +def _deps_check_impl(target, ctx): + # Check the target's dependencies and add any of our own deps. + deps = [] + for dep in ctx.rule.attr.deps: + deps.append(dep) + nodes = {} + if len(deps) != 0: + nodes[target] = deps + + # Keep and propagate each dep's providers. + for dep in ctx.rule.attr.deps: + nodes.update(dep[DepsInfo].nodes) + + return [DepsInfo(nodes = nodes)] + +_deps_check = aspect( + implementation = _deps_check_impl, + attr_aspects = ["deps"], +) + +def _is_allowed(target, allowlist, prefixes): + # Check for allowed prefixes. + for prefix in prefixes: + workspace, pfx = prefix.split("//", 1) + if len(workspace) > 0 and workspace[0] == "@": + workspace = workspace[1:] + if target.workspace_name == workspace and target.package.startswith(pfx): + return True + + # Check the allowlist. + for allowed in allowlist: + if target == allowed.label: + return True + + return False + +def _deps_test_impl(ctx): + nodes = {} + for target in ctx.attr.targets: + for (node_target, node_deps) in target[DepsInfo].nodes.items(): + # Ignore any disallowed targets. This generates more useful error + # messages. Consider the case where A dependes on B and B depends + # on C, and both B and C are disallowed. Avoid emitting an error + # that B depends on C, when the real issue is that A depends on B. + if not _is_allowed(node_target.label, ctx.attr.allowed, ctx.attr.allowed_prefixes) and node_target.label != target.label: + continue + bad_deps = [] + for dep in node_deps: + if not _is_allowed(dep.label, ctx.attr.allowed, ctx.attr.allowed_prefixes): + bad_deps.append(dep) + if len(bad_deps) > 0: + nodes[node_target] = bad_deps + + # If there aren't any violations, write a passing test. + if len(nodes) == 0: + ctx.actions.write( + output = ctx.outputs.executable, + content = "#!/bin/bash\n\nexit 0\n", + ) + return [] + + # If we're here, we've found at least one violation. + script_lines = [ + "#!/bin/bash", + "echo Invalid dependencies found. If you\\'re sure you want to add dependencies,", + "echo modify this target.", + "echo", + ] + + # List the violations. + for target, deps in nodes.items(): + script_lines.append( + 'echo "{target} depends on:"'.format(target = target.label), + ) + for dep in deps: + script_lines.append('echo "\t{dep}"'.format(dep = dep.label)) + + # The test must fail. + script_lines.append("exit 1\n") + + ctx.actions.write( + output = ctx.outputs.executable, + content = "\n".join(script_lines), + ) + return [] + +# Checks that library and its deps only depends on gVisor and an allowlist of +# other dependencies. +deps_test = rule( + implementation = _deps_test_impl, + attrs = { + "targets": attr.label_list( + doc = "The targets to check the transitive dependencies of.", + aspects = [_deps_check], + ), + "allowed": attr.label_list( + doc = "The allowed dependency targets.", + ), + "allowed_prefixes": attr.string_list( + doc = "Any packages beginning with these prefixes are allowed.", + ), + }, + test = True, +) diff --git a/tools/go_marshal/defs.bzl b/tools/go_marshal/defs.bzl index e23901815..9f620cb76 100644 --- a/tools/go_marshal/defs.bzl +++ b/tools/go_marshal/defs.bzl @@ -57,7 +57,6 @@ go_marshal = rule( # marshal_deps are the dependencies requied by generated code. marshal_deps = [ "//pkg/gohacks", - "//pkg/safecopy", "//pkg/hostarch", "//pkg/marshal", ] diff --git a/tools/go_marshal/gomarshal/generator.go b/tools/go_marshal/gomarshal/generator.go index 0e2d752cb..00961c90d 100644 --- a/tools/go_marshal/gomarshal/generator.go +++ b/tools/go_marshal/gomarshal/generator.go @@ -112,10 +112,8 @@ func NewGenerator(srcs []string, out, outTest, outTestUnconditional, pkg string, g.imports.add("runtime") g.imports.add("unsafe") g.imports.add("gvisor.dev/gvisor/pkg/gohacks") - g.imports.add("gvisor.dev/gvisor/pkg/safecopy") g.imports.add("gvisor.dev/gvisor/pkg/hostarch") g.imports.add("gvisor.dev/gvisor/pkg/marshal") - return &g, nil } diff --git a/tools/go_marshal/gomarshal/generator_interfaces_array_newtype.go b/tools/go_marshal/gomarshal/generator_interfaces_array_newtype.go index 32afece2e..bd7741ae5 100644 --- a/tools/go_marshal/gomarshal/generator_interfaces_array_newtype.go +++ b/tools/go_marshal/gomarshal/generator_interfaces_array_newtype.go @@ -33,13 +33,13 @@ func (g *interfaceGenerator) validateArrayNewtype(n *ast.Ident, a *ast.ArrayType } func (g *interfaceGenerator) emitMarshallableForArrayNewtype(n *ast.Ident, a *ast.ArrayType, elt *ast.Ident) { + g.recordUsedImport("gohacks") + g.recordUsedImport("hostarch") g.recordUsedImport("io") g.recordUsedImport("marshal") g.recordUsedImport("reflect") g.recordUsedImport("runtime") - g.recordUsedImport("safecopy") g.recordUsedImport("unsafe") - g.recordUsedImport("hostarch") lenExpr := g.arrayLenExpr(a) @@ -89,14 +89,14 @@ func (g *interfaceGenerator) emitMarshallableForArrayNewtype(n *ast.Ident, a *as g.emit("// MarshalUnsafe implements marshal.Marshallable.MarshalUnsafe.\n") g.emit("func (%s *%s) MarshalUnsafe(dst []byte) {\n", g.r, g.typeName()) g.inIndent(func() { - g.emit("safecopy.CopyIn(dst, unsafe.Pointer(%s))\n", g.r) + g.emit("gohacks.Memmove(unsafe.Pointer(&dst[0]), unsafe.Pointer(&%s[0]), uintptr(%s.SizeBytes()))\n", g.r, g.r) }) g.emit("}\n\n") g.emit("// UnmarshalUnsafe implements marshal.Marshallable.UnmarshalUnsafe.\n") g.emit("func (%s *%s) UnmarshalUnsafe(src []byte) {\n", g.r, g.typeName()) g.inIndent(func() { - g.emit("safecopy.CopyOut(unsafe.Pointer(%s), src)\n", g.r) + g.emit("gohacks.Memmove(unsafe.Pointer(%s), unsafe.Pointer(&src[0]), uintptr(%s.SizeBytes()))\n", g.r, g.r) }) g.emit("}\n\n") diff --git a/tools/go_marshal/gomarshal/generator_interfaces_primitive_newtype.go b/tools/go_marshal/gomarshal/generator_interfaces_primitive_newtype.go index 05f0e0db4..ba4b7324e 100644 --- a/tools/go_marshal/gomarshal/generator_interfaces_primitive_newtype.go +++ b/tools/go_marshal/gomarshal/generator_interfaces_primitive_newtype.go @@ -95,13 +95,13 @@ func (g *interfaceGenerator) validatePrimitiveNewtype(t *ast.Ident) { // newtypes are always packed, so we can omit the various fallbacks required for // non-packed structs. func (g *interfaceGenerator) emitMarshallableForPrimitiveNewtype(nt *ast.Ident) { + g.recordUsedImport("gohacks") + g.recordUsedImport("hostarch") g.recordUsedImport("io") g.recordUsedImport("marshal") g.recordUsedImport("reflect") g.recordUsedImport("runtime") - g.recordUsedImport("safecopy") g.recordUsedImport("unsafe") - g.recordUsedImport("hostarch") g.emit("// SizeBytes implements marshal.Marshallable.SizeBytes.\n") g.emit("//go:nosplit\n") @@ -141,14 +141,14 @@ func (g *interfaceGenerator) emitMarshallableForPrimitiveNewtype(nt *ast.Ident) g.emit("// MarshalUnsafe implements marshal.Marshallable.MarshalUnsafe.\n") g.emit("func (%s *%s) MarshalUnsafe(dst []byte) {\n", g.r, g.typeName()) g.inIndent(func() { - g.emit("safecopy.CopyIn(dst, unsafe.Pointer(%s))\n", g.r) + g.emit("gohacks.Memmove(unsafe.Pointer(&dst[0]), unsafe.Pointer(%s), uintptr(%s.SizeBytes()))\n", g.r, g.r) }) g.emit("}\n\n") g.emit("// UnmarshalUnsafe implements marshal.Marshallable.UnmarshalUnsafe.\n") g.emit("func (%s *%s) UnmarshalUnsafe(src []byte) {\n", g.r, g.typeName()) g.inIndent(func() { - g.emit("safecopy.CopyOut(unsafe.Pointer(%s), src)\n", g.r) + g.emit("gohacks.Memmove(unsafe.Pointer(%s), unsafe.Pointer(&src[0]), uintptr(%s.SizeBytes()))\n", g.r, g.r) }) g.emit("}\n\n") @@ -260,11 +260,9 @@ func (g *interfaceGenerator) emitMarshallableSliceForPrimitiveNewtype(nt *ast.Id g.emit("}\n") g.emit("size := (*%s)(nil).SizeBytes()\n\n", g.typeName()) - g.emitNoEscapeSliceDataPointer("&src", "val") - - g.emit("length, err := safecopy.CopyIn(dst[:(size*count)], val)\n") - g.emitKeepAlive("src") - g.emit("return length, err\n") + g.emit("dst = dst[:size*count]\n") + g.emit("gohacks.Memmove(unsafe.Pointer(&dst[0]), unsafe.Pointer(&src[0]), uintptr(len(dst)))\n") + g.emit("return size*count, nil\n") }) g.emit("}\n\n") @@ -279,11 +277,9 @@ func (g *interfaceGenerator) emitMarshallableSliceForPrimitiveNewtype(nt *ast.Id g.emit("}\n") g.emit("size := (*%s)(nil).SizeBytes()\n\n", g.typeName()) - g.emitNoEscapeSliceDataPointer("&dst", "val") - - g.emit("length, err := safecopy.CopyOut(val, src[:(size*count)])\n") - g.emitKeepAlive("dst") - g.emit("return length, err\n") + g.emit("src = src[:(size*count)]\n") + g.emit("gohacks.Memmove(unsafe.Pointer(&dst[0]), unsafe.Pointer(&src[0]), uintptr(len(src)))\n") + g.emit("return size*count, nil\n") }) g.emit("}\n\n") } diff --git a/tools/go_marshal/gomarshal/generator_interfaces_struct.go b/tools/go_marshal/gomarshal/generator_interfaces_struct.go index 72df1ab64..4c47218f1 100644 --- a/tools/go_marshal/gomarshal/generator_interfaces_struct.go +++ b/tools/go_marshal/gomarshal/generator_interfaces_struct.go @@ -270,18 +270,18 @@ func (g *interfaceGenerator) emitMarshallableForStruct(st *ast.StructType) { g.emit("%s.MarshalBytes(dst)\n", g.r) } if thisPacked { - g.recordUsedImport("safecopy") + g.recordUsedImport("gohacks") g.recordUsedImport("unsafe") if cond, ok := g.areFieldsPackedExpression(); ok { g.emit("if %s {\n", cond) g.inIndent(func() { - g.emit("safecopy.CopyIn(dst, unsafe.Pointer(%s))\n", g.r) + g.emit("gohacks.Memmove(unsafe.Pointer(&dst[0]), unsafe.Pointer(%s), uintptr(%s.SizeBytes()))\n", g.r, g.r) }) g.emit("} else {\n") g.inIndent(fallback) g.emit("}\n") } else { - g.emit("safecopy.CopyIn(dst, unsafe.Pointer(%s))\n", g.r) + g.emit("gohacks.Memmove(unsafe.Pointer(&dst[0]), unsafe.Pointer(%s), uintptr(%s.SizeBytes()))\n", g.r, g.r) } } else { fallback() @@ -297,25 +297,23 @@ func (g *interfaceGenerator) emitMarshallableForStruct(st *ast.StructType) { g.emit("%s.UnmarshalBytes(src)\n", g.r) } if thisPacked { - g.recordUsedImport("safecopy") - g.recordUsedImport("unsafe") + g.recordUsedImport("gohacks") if cond, ok := g.areFieldsPackedExpression(); ok { g.emit("if %s {\n", cond) g.inIndent(func() { - g.emit("safecopy.CopyOut(unsafe.Pointer(%s), src)\n", g.r) + g.emit("gohacks.Memmove(unsafe.Pointer(%s), unsafe.Pointer(&src[0]), uintptr(%s.SizeBytes()))\n", g.r, g.r) }) g.emit("} else {\n") g.inIndent(fallback) g.emit("}\n") } else { - g.emit("safecopy.CopyOut(unsafe.Pointer(%s), src)\n", g.r) + g.emit("gohacks.Memmove(unsafe.Pointer(%s), unsafe.Pointer(&src[0]), uintptr(%s.SizeBytes()))\n", g.r, g.r) } } else { fallback() } }) g.emit("}\n\n") - g.emit("// CopyOutN implements marshal.Marshallable.CopyOutN.\n") g.emit("//go:nosplit\n") g.recordUsedImport("marshal") @@ -561,16 +559,15 @@ func (g *interfaceGenerator) emitMarshallableSliceForStruct(st *ast.StructType, g.recordUsedImport("reflect") g.recordUsedImport("runtime") g.recordUsedImport("unsafe") + g.recordUsedImport("gohacks") if _, ok := g.areFieldsPackedExpression(); ok { g.emit("if !src[0].Packed() {\n") g.inIndent(fallback) g.emit("}\n\n") } - g.emitNoEscapeSliceDataPointer("&src", "val") - - g.emit("length, err := safecopy.CopyIn(dst[:(size*count)], val)\n") - g.emitKeepAlive("src") - g.emit("return length, err\n") + g.emit("dst = dst[:size*count]\n") + g.emit("gohacks.Memmove(unsafe.Pointer(&dst[0]), unsafe.Pointer(&src[0]), uintptr(len(dst)))\n") + g.emit("return size * count, nil\n") } else { fallback() } @@ -598,19 +595,19 @@ func (g *interfaceGenerator) emitMarshallableSliceForStruct(st *ast.StructType, g.emit("return size * count, nil\n") } if thisPacked { + g.recordUsedImport("gohacks") g.recordUsedImport("reflect") g.recordUsedImport("runtime") - g.recordUsedImport("unsafe") if _, ok := g.areFieldsPackedExpression(); ok { g.emit("if !dst[0].Packed() {\n") g.inIndent(fallback) g.emit("}\n\n") } - g.emitNoEscapeSliceDataPointer("&dst", "val") - g.emit("length, err := safecopy.CopyOut(val, src[:(size*count)])\n") - g.emitKeepAlive("dst") - g.emit("return length, err\n") + g.emit("src = src[:(size*count)]\n") + g.emit("gohacks.Memmove(unsafe.Pointer(&dst[0]), unsafe.Pointer(&src[0]), uintptr(len(src)))\n") + + g.emit("return count*size, nil\n") } else { fallback() } diff --git a/tools/nogo/analyzers.go b/tools/nogo/analyzers.go index 8b4bff3b6..2b3c03fec 100644 --- a/tools/nogo/analyzers.go +++ b/tools/nogo/analyzers.go @@ -83,11 +83,6 @@ var AllAnalyzers = []*analysis.Analyzer{ checklocks.Analyzer, } -// EscapeAnalyzers is a list of escape-related analyzers. -var EscapeAnalyzers = []*analysis.Analyzer{ - checkescape.EscapeAnalyzer, -} - func register(all []*analysis.Analyzer) { // Register all fact types. // @@ -129,5 +124,4 @@ func init() { // Register lists. register(AllAnalyzers) - register(EscapeAnalyzers) } diff --git a/tools/nogo/check/main.go b/tools/nogo/check/main.go index 69bdfe502..4194770be 100644 --- a/tools/nogo/check/main.go +++ b/tools/nogo/check/main.go @@ -31,7 +31,6 @@ var ( stdlibFile = flag.String("stdlib", "", "stdlib configuration file (in JSON format)") findingsOutput = flag.String("findings", "", "output file (or stdout, if not specified)") factsOutput = flag.String("facts", "", "output file for facts (optional)") - escapesOutput = flag.String("escapes", "", "output file for escapes (optional)") ) func loadConfig(file string, config interface{}) interface{} { @@ -66,25 +65,13 @@ func main() { // Run the configuration. if *stdlibFile != "" { - // Perform basic analysis. + // Perform stdlib analysis. c := loadConfig(*stdlibFile, new(nogo.StdlibConfig)).(*nogo.StdlibConfig) findings, factData, err = nogo.CheckStdlib(c, nogo.AllAnalyzers) - } else if *packageFile != "" { - // Perform basic analysis. + // Perform standard analysis. c := loadConfig(*packageFile, new(nogo.PackageConfig)).(*nogo.PackageConfig) findings, factData, err = nogo.CheckPackage(c, nogo.AllAnalyzers, nil) - - // Do we need to do escape analysis? - if *escapesOutput != "" { - escapes, _, err := nogo.CheckPackage(c, nogo.EscapeAnalyzers, nil) - if err != nil { - log.Fatalf("error performing escape analysis: %v", err) - } - if err := nogo.WriteFindingsToFile(escapes, *escapesOutput); err != nil { - log.Fatalf("error writing escapes to %q: %v", *escapesOutput, err) - } - } } else { log.Fatalf("please provide at least one of package or stdlib!") } diff --git a/tools/nogo/defs.bzl b/tools/nogo/defs.bzl index 0c48a7a5a..be8b82f9c 100644 --- a/tools/nogo/defs.bzl +++ b/tools/nogo/defs.bzl @@ -120,7 +120,7 @@ def _nogo_stdlib_impl(ctx): Srcs = [f.path for f in go_ctx.stdlib_srcs], GOOS = go_ctx.goos, GOARCH = go_ctx.goarch, - Tags = go_ctx.tags, + Tags = go_ctx.gotags, ) config_file = ctx.actions.declare_file(ctx.label.name + ".cfg") ctx.actions.write(config_file, config.to_json()) @@ -174,7 +174,6 @@ NogoInfo = provider( fields = { "facts": "serialized package facts", "raw_findings": "raw package findings (if relevant)", - "escapes": "escape-only findings (if relevant)", "importpath": "package import path", "binaries": "package binary files", "srcs": "srcs (for go_test support)", @@ -281,14 +280,13 @@ def _nogo_aspect_impl(target, ctx): go_ctx = go_context(ctx, goos = nogo_target_info.goos, goarch = nogo_target_info.goarch) facts = ctx.actions.declare_file(target.label.name + ".facts") raw_findings = ctx.actions.declare_file(target.label.name + ".raw_findings") - escapes = ctx.actions.declare_file(target.label.name + ".escapes") config = struct( ImportPath = importpath, GoFiles = [src.path for src in srcs if src.path.endswith(".go")], NonGoFiles = [src.path for src in srcs if not src.path.endswith(".go")], GOOS = go_ctx.goos, GOARCH = go_ctx.goarch, - Tags = go_ctx.tags, + Tags = go_ctx.gotags, FactMap = fact_map, ImportMap = import_map, StdlibFacts = stdlib_facts.path, @@ -298,7 +296,7 @@ def _nogo_aspect_impl(target, ctx): inputs.append(config_file) ctx.actions.run( inputs = inputs, - outputs = [facts, raw_findings, escapes], + outputs = [facts, raw_findings], tools = depset(go_ctx.runfiles.to_list() + ctx.files._nogo_objdump_tool), executable = ctx.files._nogo_check[0], mnemonic = "NogoAnalysis", @@ -309,7 +307,6 @@ def _nogo_aspect_impl(target, ctx): "-package=%s" % config_file.path, "-findings=%s" % raw_findings.path, "-facts=%s" % facts.path, - "-escapes=%s" % escapes.path, ], ) @@ -322,15 +319,16 @@ def _nogo_aspect_impl(target, ctx): all_raw_findings = [stdlib_info.raw_findings] + depset(all_raw_findings).to_list() + [raw_findings] # Return the package facts as output. - return [NogoInfo( - facts = facts, - raw_findings = all_raw_findings, - escapes = escapes, - importpath = importpath, - binaries = binaries, - srcs = srcs, - deps = deps, - )] + return [ + NogoInfo( + facts = facts, + raw_findings = all_raw_findings, + importpath = importpath, + binaries = binaries, + srcs = srcs, + deps = deps, + ), + ] nogo_aspect = go_rule( aspect, @@ -367,7 +365,6 @@ def _nogo_test_impl(ctx): if len(ctx.attr.deps) != 1: fail("nogo_test requires exactly one dep.") raw_findings = ctx.attr.deps[0][NogoInfo].raw_findings - escapes = ctx.attr.deps[0][NogoInfo].escapes # Build a step that applies the configuration. config_srcs = ctx.attr.config[NogoConfigInfo].srcs @@ -409,8 +406,6 @@ def _nogo_test_impl(ctx): # pays attention to the mnemoic above, so this must be # what is expected by the tooling. nogo_findings = depset([findings]), - # Expose all escape analysis findings (see above). - nogo_escapes = depset([escapes]), )] nogo_test = rule( @@ -432,3 +427,18 @@ nogo_test = rule( }, test = True, ) + +def _nogo_aspect_tricorder_impl(target, ctx): + if ctx.rule.kind != "nogo_test" or OutputGroupInfo not in target: + return [] + if not hasattr(target[OutputGroupInfo], "nogo_findings"): + return [] + return [ + OutputGroupInfo(tricorder = target[OutputGroupInfo].nogo_findings), + ] + +# Trivial aspect that forwards the findings from a nogo_test rule to +# go/tricorder, which reads from the `tricorder` output group. +nogo_aspect_tricorder = aspect( + implementation = _nogo_aspect_tricorder_impl, +) |