diff options
author | Adin Scannell <ascannell@google.com> | 2020-08-25 12:16:31 -0700 |
---|---|---|
committer | gVisor bot <gvisor-bot@google.com> | 2020-08-25 12:18:25 -0700 |
commit | b0c53f8475d14606ef82aeddfb2f742269c1b5b7 (patch) | |
tree | 758f77d07ea16b97cf1b81729fae0577b87c9f08 | |
parent | b83758cd875be8fea521c6bb51b4ba6b41a690a9 (diff) |
Add nogo support to go_binary and go_test targets.
Updates #3374
PiperOrigin-RevId: 328378700
-rw-r--r-- | pkg/cpuid/cpuid_parse_x86_test.go | 12 | ||||
-rw-r--r-- | pkg/seccomp/BUILD | 1 | ||||
-rw-r--r-- | test/benchmarks/database/redis_test.go | 4 | ||||
-rw-r--r-- | test/benchmarks/fs/bazel_test.go | 2 | ||||
-rw-r--r-- | test/benchmarks/network/node_test.go | 4 | ||||
-rw-r--r-- | test/benchmarks/network/ruby_test.go | 4 | ||||
-rw-r--r-- | test/packetimpact/runner/defs.bzl | 1 | ||||
-rw-r--r-- | test/root/crictl_test.go | 2 | ||||
-rw-r--r-- | test/runtimes/proctor/BUILD | 1 | ||||
-rw-r--r-- | tools/bazeldefs/defs.bzl | 9 | ||||
-rw-r--r-- | tools/defs.bzl | 40 | ||||
-rw-r--r-- | tools/issue_reviver/BUILD | 1 | ||||
-rw-r--r-- | tools/issue_reviver/github/BUILD | 1 | ||||
-rw-r--r-- | tools/nogo/check/BUILD | 1 | ||||
-rw-r--r-- | tools/nogo/defs.bzl | 63 |
15 files changed, 118 insertions, 28 deletions
diff --git a/pkg/cpuid/cpuid_parse_x86_test.go b/pkg/cpuid/cpuid_parse_x86_test.go index c9bd40e1b..e4ae0d689 100644 --- a/pkg/cpuid/cpuid_parse_x86_test.go +++ b/pkg/cpuid/cpuid_parse_x86_test.go @@ -32,27 +32,27 @@ func kernelVersion() (int, int, error) { return 0, 0, err } - var r string + var sb strings.Builder for _, b := range u.Release { if b == 0 { break } - r += string(b) + sb.WriteByte(byte(b)) } - s := strings.Split(r, ".") + s := strings.Split(sb.String(), ".") if len(s) < 2 { - return 0, 0, fmt.Errorf("kernel release missing major and minor component: %s", r) + return 0, 0, fmt.Errorf("kernel release missing major and minor component: %s", sb.String()) } major, err := strconv.Atoi(s[0]) if err != nil { - return 0, 0, fmt.Errorf("error parsing major version %q in %q: %v", s[0], r, err) + return 0, 0, fmt.Errorf("error parsing major version %q in %q: %w", s[0], sb.String(), err) } minor, err := strconv.Atoi(s[1]) if err != nil { - return 0, 0, fmt.Errorf("error parsing minor version %q in %q: %v", s[1], r, err) + return 0, 0, fmt.Errorf("error parsing minor version %q in %q: %w", s[1], sb.String(), err) } return major, minor, nil diff --git a/pkg/seccomp/BUILD b/pkg/seccomp/BUILD index 29aeaab8c..bdef7762c 100644 --- a/pkg/seccomp/BUILD +++ b/pkg/seccomp/BUILD @@ -10,6 +10,7 @@ go_binary( "seccomp_test_victim_amd64.go", "seccomp_test_victim_arm64.go", ], + nogo = False, deps = [":seccomp"], ) diff --git a/test/benchmarks/database/redis_test.go b/test/benchmarks/database/redis_test.go index 394fce820..6671a4969 100644 --- a/test/benchmarks/database/redis_test.go +++ b/test/benchmarks/database/redis_test.go @@ -84,12 +84,12 @@ func BenchmarkRedis(b *testing.B) { ip, err := serverMachine.IPAddress() if err != nil { - b.Fatal("failed to get IP from server: %v", err) + b.Fatalf("failed to get IP from server: %v", err) } serverPort, err := server.FindPort(ctx, port) if err != nil { - b.Fatal("failed to get IP from server: %v", err) + b.Fatalf("failed to get IP from server: %v", err) } if err = harness.WaitUntilServing(ctx, clientMachine, ip, serverPort); err != nil { diff --git a/test/benchmarks/fs/bazel_test.go b/test/benchmarks/fs/bazel_test.go index f4236ba37..fdbbfe280 100644 --- a/test/benchmarks/fs/bazel_test.go +++ b/test/benchmarks/fs/bazel_test.go @@ -73,7 +73,7 @@ func runBuildBenchmark(b *testing.B, image, workdir, target string) { if bm.tmpfs { if out, err := container.Exec(ctx, dockerutil.ExecOpts{}, "cp", "-r", workdir, "/tmp/."); err != nil { - b.Fatal("failed to copy directory: %v %s", err, out) + b.Fatalf("failed to copy directory: %v (%s)", err, out) } workdir = "/tmp" + workdir } diff --git a/test/benchmarks/network/node_test.go b/test/benchmarks/network/node_test.go index 52eb794c4..0f4a205b6 100644 --- a/test/benchmarks/network/node_test.go +++ b/test/benchmarks/network/node_test.go @@ -48,14 +48,14 @@ func runNode(b *testing.B, hey *tools.Hey) { // The machine to hold Redis and the Node Server. serverMachine, err := h.GetMachine() if err != nil { - b.Fatal("failed to get machine with: %v", err) + b.Fatalf("failed to get machine with: %v", err) } defer serverMachine.CleanUp() // The machine to run 'hey'. clientMachine, err := h.GetMachine() if err != nil { - b.Fatal("failed to get machine with: %v", err) + b.Fatalf("failed to get machine with: %v", err) } defer clientMachine.CleanUp() diff --git a/test/benchmarks/network/ruby_test.go b/test/benchmarks/network/ruby_test.go index 5e0b2b724..67f63f76a 100644 --- a/test/benchmarks/network/ruby_test.go +++ b/test/benchmarks/network/ruby_test.go @@ -47,14 +47,14 @@ func runRuby(b *testing.B, hey *tools.Hey) { // The machine to hold Redis and the Ruby Server. serverMachine, err := h.GetMachine() if err != nil { - b.Fatal("failed to get machine with: %v", err) + b.Fatalf("failed to get machine with: %v", err) } defer serverMachine.CleanUp() // The machine to run 'hey'. clientMachine, err := h.GetMachine() if err != nil { - b.Fatal("failed to get machine with: %v", err) + b.Fatalf("failed to get machine with: %v", err) } defer clientMachine.CleanUp() ctx := context.Background() diff --git a/test/packetimpact/runner/defs.bzl b/test/packetimpact/runner/defs.bzl index 93a36c6c2..d72c63fe6 100644 --- a/test/packetimpact/runner/defs.bzl +++ b/test/packetimpact/runner/defs.bzl @@ -125,6 +125,7 @@ def packetimpact_go_test(name, size = "small", pure = True, expect_native_failur name = testbench_binary, size = size, pure = pure, + nogo = False, # FIXME(gvisor.dev/issue/3374): Not working with all build systems. tags = [ "local", "manual", diff --git a/test/root/crictl_test.go b/test/root/crictl_test.go index df91fa0fe..11ac5cb52 100644 --- a/test/root/crictl_test.go +++ b/test/root/crictl_test.go @@ -418,7 +418,7 @@ func setup(t *testing.T, version string) (*criutil.Crictl, func(), error) { // care about the docker runtime name. config = v2Template default: - t.Fatalf("unknown version: %d", version) + t.Fatalf("unknown version: %s", version) } t.Logf("Using config: %s", config) diff --git a/test/runtimes/proctor/BUILD b/test/runtimes/proctor/BUILD index f76e2ddc0..d1935cbe8 100644 --- a/test/runtimes/proctor/BUILD +++ b/test/runtimes/proctor/BUILD @@ -21,6 +21,7 @@ go_test( size = "small", srcs = ["proctor_test.go"], library = ":proctor", + nogo = False, # FIXME(gvisor.dev/issue/3374): Not working with all build systems. pure = True, deps = [ "//pkg/test/testutil", diff --git a/tools/bazeldefs/defs.bzl b/tools/bazeldefs/defs.bzl index db7f379b8..4bbcda054 100644 --- a/tools/bazeldefs/defs.bzl +++ b/tools/bazeldefs/defs.bzl @@ -87,13 +87,14 @@ def cc_binary(name, static = False, **kwargs): **kwargs ) -def go_binary(name, static = False, pure = False, **kwargs): +def go_binary(name, static = False, pure = False, x_defs = None, **kwargs): """Build a go binary. Args: name: name of the target. static: build a static binary. pure: build without cgo. + x_defs: additional definitions. **kwargs: rest of the arguments are passed to _go_binary. """ if static: @@ -102,6 +103,7 @@ def go_binary(name, static = False, pure = False, **kwargs): kwargs["pure"] = "on" _go_binary( name = name, + x_defs = x_defs, **kwargs ) @@ -151,6 +153,11 @@ def go_rule(rule, implementation, **kwargs): toolchains = kwargs.get("toolchains", []) + ["@io_bazel_rules_go//go:toolchain"] return rule(implementation, attrs = attrs, toolchains = toolchains, **kwargs) +def go_test_library(target): + if hasattr(target.attr, "embed") and len(target.attr.embed) > 0: + return target.attr.embed[0] + return None + def go_context(ctx): go_ctx = _go_context(ctx) return struct( diff --git a/tools/defs.bzl b/tools/defs.bzl index e71a26cf4..290d564f2 100644 --- a/tools/defs.bzl +++ b/tools/defs.bzl @@ -27,7 +27,6 @@ gbenchmark = _gbenchmark gazelle = _gazelle go_embed_data = _go_embed_data go_path = _go_path -go_test = _go_test gtest = _gtest grpcpp = _grpcpp loopback = _loopback @@ -45,17 +44,35 @@ vdso_linker_option = _vdso_linker_option default_platform = _default_platform platforms = _platforms -def go_binary(name, **kwargs): +def go_binary(name, nogo = True, pure = False, static = False, x_defs = None, **kwargs): """Wraps the standard go_binary. Args: name: the rule name. + nogo: enable nogo analysis. + pure: build a pure Go (no CGo) binary. + static: build a static binary. + x_defs: additional linker definitions. **kwargs: standard go_binary arguments. """ _go_binary( name = name, + pure = pure, + static = static, + x_defs = x_defs, **kwargs ) + if nogo: + # Note that the nogo rule applies only for go_library and go_test + # targets, therefore we construct a library from the binary sources. + _go_library( + name = name + "_nogo_library", + **kwargs + ) + nogo_test( + name = name + "_nogo", + deps = [":" + name + "_nogo_library"], + ) def calculate_sets(srcs): """Calculates special Go sets for templates. @@ -119,6 +136,7 @@ def go_library(name, srcs, deps = [], imports = [], stateify = True, marshal = F stateify: whether statify is enabled (default: true). marshal: whether marshal is enabled (default: false). marshal_debug: whether the gomarshal tools emits debugging output (default: false). + nogo: enable nogo analysis. **kwargs: standard go_library arguments. """ all_srcs = srcs @@ -202,6 +220,24 @@ def go_library(name, srcs, deps = [], imports = [], stateify = True, marshal = F **kwargs ) +def go_test(name, nogo = True, **kwargs): + """Wraps the standard go_test. + + Args: + name: the rule name. + nogo: enable nogo analysis. + **kwargs: standard go_test arguments. + """ + _go_test( + name = name, + **kwargs + ) + if nogo: + nogo_test( + name = name + "_nogo", + deps = [":" + name], + ) + def proto_library(name, srcs, deps = None, has_services = 0, **kwargs): """Wraps the standard proto_library. diff --git a/tools/issue_reviver/BUILD b/tools/issue_reviver/BUILD index 4ef1a3124..35b0111ca 100644 --- a/tools/issue_reviver/BUILD +++ b/tools/issue_reviver/BUILD @@ -5,6 +5,7 @@ package(licenses = ["notice"]) go_binary( name = "issue_reviver", srcs = ["main.go"], + nogo = False, deps = [ "//tools/issue_reviver/github", "//tools/issue_reviver/reviver", diff --git a/tools/issue_reviver/github/BUILD b/tools/issue_reviver/github/BUILD index 0eabc2835..555abd296 100644 --- a/tools/issue_reviver/github/BUILD +++ b/tools/issue_reviver/github/BUILD @@ -21,4 +21,5 @@ go_test( size = "small", srcs = ["github_test.go"], library = ":github", + nogo = False, ) diff --git a/tools/nogo/check/BUILD b/tools/nogo/check/BUILD index e2d76cd5c..21ba2c306 100644 --- a/tools/nogo/check/BUILD +++ b/tools/nogo/check/BUILD @@ -7,6 +7,7 @@ package(licenses = ["notice"]) go_binary( name = "check", srcs = ["main.go"], + nogo = False, visibility = ["//visibility:public"], deps = ["//tools/nogo"], ) diff --git a/tools/nogo/defs.bzl b/tools/nogo/defs.bzl index d399079c5..5377620b0 100644 --- a/tools/nogo/defs.bzl +++ b/tools/nogo/defs.bzl @@ -1,6 +1,6 @@ """Nogo rules.""" -load("//tools/bazeldefs:defs.bzl", "go_context", "go_importpath", "go_rule") +load("//tools/bazeldefs:defs.bzl", "go_context", "go_importpath", "go_rule", "go_test_library") # NogoInfo is the serialized set of package facts for a nogo analysis. # @@ -8,10 +8,13 @@ load("//tools/bazeldefs:defs.bzl", "go_context", "go_importpath", "go_rule") # with the source files as input. Note however, that the individual nogo rules # are simply stubs that enter into the shadow dependency tree (the "aspect"). NogoInfo = provider( + "information for nogo analysis", fields = { "facts": "serialized package facts", "importpath": "package import path", "binaries": "package binary files", + "srcs": "original source files (for go_test support)", + "deps": "original deps (for go_test support)", }, ) @@ -21,16 +24,29 @@ def _nogo_aspect_impl(target, ctx): # All work is done in the shadow properties for go rules. For a proto # library, we simply skip the analysis portion but still need to return a # valid NogoInfo to reference the generated binary. - if ctx.rule.kind == "go_library": + if ctx.rule.kind in ("go_library", "go_binary", "go_test", "go_tool_library"): srcs = ctx.rule.files.srcs - elif ctx.rule.kind == "go_proto_library" or ctx.rule.kind == "go_wrap_cc": + deps = ctx.rule.attr.deps + elif ctx.rule.kind in ("go_proto_library", "go_wrap_cc"): srcs = [] + deps = ctx.rule.attr.deps else: return [NogoInfo()] - go_ctx = go_context(ctx) + # If we're using the "library" attribute, then we need to aggregate the + # original library sources and dependencies into this target to perform + # proper type analysis. + if ctx.rule.kind == "go_test": + library = go_test_library(ctx.rule) + if library != None: + info = library[NogoInfo] + if hasattr(info, "srcs"): + srcs = srcs + info.srcs + if hasattr(info, "deps"): + deps = deps + info.deps # Construct the Go environment from the go_ctx.env dictionary. + go_ctx = go_context(ctx) env_prefix = " ".join(["%s=%s" % (key, value) for (key, value) in go_ctx.env.items()]) # Start with all target files and srcs as input. @@ -41,6 +57,13 @@ def _nogo_aspect_impl(target, ctx): # to cleanly allow us redirect stdout to the actual output file. Perhaps # I'm missing something here, but the intermediate script does work. binaries = target.files.to_list() + objfiles = [f for f in binaries if f.path.endswith(".a")] + if len(objfiles) > 0: + # Prefer the .a files for go_library targets. + target_objfile = objfiles[0] + else: + # Use the raw binary for go_binary and go_test targets. + target_objfile = binaries[0] disasm_file = ctx.actions.declare_file(target.label.name + ".out") dumper = ctx.actions.declare_file("%s-dumper" % ctx.label.name) ctx.actions.write(dumper, "\n".join([ @@ -48,12 +71,12 @@ def _nogo_aspect_impl(target, ctx): "%s %s tool objdump %s > %s\n" % ( env_prefix, go_ctx.go.path, - [f.path for f in binaries if f.path.endswith(".a")][0], + target_objfile.path, disasm_file.path, ), ]), is_executable = True) ctx.actions.run( - inputs = binaries, + inputs = [target_objfile], outputs = [disasm_file], tools = go_ctx.runfiles, mnemonic = "GoObjdump", @@ -63,7 +86,15 @@ def _nogo_aspect_impl(target, ctx): inputs.append(disasm_file) # Extract the importpath for this package. - importpath = go_importpath(target) + if ctx.rule.kind == "go_test": + # If this is a test, then it will not be imported by anything else. + # We can safely set the importapth to just "test". Note that this + # is necessary if the library also imports the core library (in + # addition to including the sources directly), which happens in + # some complex cases (seccomp_victim). + importpath = "test" + else: + importpath = go_importpath(target) # The nogo tool requires a configfile serialized in JSON format to do its # work. This must line up with the nogo.Config fields. @@ -84,7 +115,7 @@ def _nogo_aspect_impl(target, ctx): ) # Collect all info from shadow dependencies. - for dep in ctx.rule.attr.deps: + for dep in deps: # There will be no file attribute set for all transitive dependencies # that are not go_library or go_binary rules, such as a proto rules. # This is handled by the ctx.rule.kind check above. @@ -126,12 +157,18 @@ def _nogo_aspect_impl(target, ctx): facts = facts, importpath = importpath, binaries = binaries, + srcs = srcs, + deps = deps, )] nogo_aspect = go_rule( aspect, implementation = _nogo_aspect_impl, - attr_aspects = ["deps"], + attr_aspects = [ + "deps", + "library", + "embed", + ], attrs = { "_nogo": attr.label( default = "//tools/nogo/check:check", @@ -171,6 +208,10 @@ _nogo_test = rule( test = True, ) -def nogo_test(**kwargs): +def nogo_test(name, **kwargs): tags = kwargs.pop("tags", []) + ["nogo"] - _nogo_test(tags = tags, **kwargs) + _nogo_test( + name = name, + tags = tags, + **kwargs + ) |