diff --git a/docs/go/core/rules.md b/docs/go/core/rules.md index 13bbfdd84f..719640102e 100644 --- a/docs/go/core/rules.md +++ b/docs/go/core/rules.md @@ -127,7 +127,7 @@ Rules
 go_binary(name, basename, cdeps, cgo, clinkopts, copts, cppopts, cxxopts, data, deps, embed,
           embedsrcs, env, gc_goopts, gc_linkopts, goarch, goos, gotags, importpath, linkmode, msan,
-          out, pgoprofile, pure, race, srcs, static, x_defs)
+          out, pgoprofile, pure, race, srcs, static, x_defs, godebug_default)
 
This builds an executable from a set of source files, @@ -175,6 +175,7 @@ This builds an executable from a set of source files, | srcs | The list of Go source files that are compiled to create the package. Only .go, .s, and .syso files are permitted, unless the cgo attribute is set, in which case, .c .cc .cpp .cxx .h .hh .hpp .hxx .inc .m .mm files are also permitted. Files may be filtered at build time using Go [build constraints]. | List of labels | optional | [] | | static | Controls whether a binary is statically linked. May be one of on, off, or auto. Not available on all platforms or in all modes. It's usually better to control this on the command line with --@io_bazel_rules_go//go/config:static. See [mode attributes], specifically [static]. | String | optional | "auto" | | x_defs | Map of defines to add to the go link command. See [Defines and stamping] for examples of how to use these. | Dictionary: String -> String | optional | {} | +| godebug_default | Map of default GODEBUG keys and values | Dictionary: String -> String | optional | {} | diff --git a/go/private/actions/archive.bzl b/go/private/actions/archive.bzl index 2ac957045f..93d702eaed 100644 --- a/go/private/actions/archive.bzl +++ b/go/private/actions/archive.bzl @@ -208,4 +208,5 @@ def emit_archive(go, source = None, _recompile_suffix = "", recompile_internal_d cgo_deps = depset(transitive = [cgo_deps] + [a.cgo_deps for a in direct]), cgo_exports = cgo_exports, runfiles = runfiles, + godebug_default = getattr(source, "godebug_default", None), ) diff --git a/go/private/actions/link.bzl b/go/private/actions/link.bzl index 18dd1f94ad..7ab3002077 100644 --- a/go/private/actions/link.bzl +++ b/go/private/actions/link.bzl @@ -140,6 +140,11 @@ def emit_link( ])) extldflags.extend(cgo_rpaths) + godebug_default = archive.godebug_default + if godebug_default: + builder_args.add("-X", "runtime.godebugDefault={}".format( + ",".join(["{}={}".format(k, v) for k, v in godebug_default.items()]))) + # Process x_defs, and record whether stamping is used. stamp_x_defs_volatile = False stamp_x_defs_stable = False diff --git a/go/private/context.bzl b/go/private/context.bzl index 7539c2647d..06aa180086 100644 --- a/go/private/context.bzl +++ b/go/private/context.bzl @@ -290,6 +290,7 @@ def _library_to_source(go, attr, library, coverage_instrumented, verify_resolver _merge_embed(source, e) source["deps"] = _dedup_archives(source["deps"]) + source["godebug_default"] = getattr(attr, "godebug_default", None) x_defs = source["x_defs"] for k, v in getattr(attr, "x_defs", {}).items(): diff --git a/go/private/rules/binary.bzl b/go/private/rules/binary.bzl index 686d4f4eae..67390ea5d5 100644 --- a/go/private/rules/binary.bzl +++ b/go/private/rules/binary.bzl @@ -439,6 +439,9 @@ def _go_binary_kwargs(go_cc_aspects = []): """, default = "//go/config:empty", ), + "godebug_default": attr.string_dict( + doc = """Map of default GODEBUG values to add to the go link command.""", + ), "_go_context_data": attr.label(default = "//:go_context_data", cfg = go_transition), "_allowlist_function_transition": attr.label( default = "@bazel_tools//tools/allowlists/function_transition_allowlist", diff --git a/tests/core/go_binary/BUILD.bazel b/tests/core/go_binary/BUILD.bazel index d57f6cfbec..3f5a21327e 100644 --- a/tests/core/go_binary/BUILD.bazel +++ b/tests/core/go_binary/BUILD.bazel @@ -241,7 +241,31 @@ go_binary( go_binary( name = "meaning2", srcs = [ - "//tests/core/go_library:use_syso_srcs", "meaning2.go", + "//tests/core/go_library:use_syso_srcs", ], ) + +go_binary( + name = "godebug_bin_godebug", + srcs = ["godebug_bin.go"], + godebug_default = { + "http2debug": "1", + }, +) + +go_binary( + name = "godebug_bin", + srcs = ["godebug_bin.go"], +) + +go_test( + name = "godebug_test", + srcs = ["godebug_test.go"], + data = [ + ":godebug_bin", + ":godebug_bin_godebug", + ], + rundir = ".", + deps = ["//go/tools/bazel:go_default_library"], +) diff --git a/tests/core/go_binary/README.rst b/tests/core/go_binary/README.rst index a01bdf6032..74feced49e 100644 --- a/tests/core/go_binary/README.rst +++ b/tests/core/go_binary/README.rst @@ -68,3 +68,7 @@ prefix ------ This binary has a name that conflicts with a subdirectory. Its output file name should not have this conflict. Verifies `#2463`_. + +godebug_test +------------ +Test that the ``godebug_default`` sets the default ``GODEBUG`` variables. diff --git a/tests/core/go_binary/godebug_bin.go b/tests/core/go_binary/godebug_bin.go new file mode 100644 index 0000000000..cf92cbad2f --- /dev/null +++ b/tests/core/go_binary/godebug_bin.go @@ -0,0 +1,11 @@ +package main + +import ( + "fmt" + "internal/godebug" +) + +func main() { + http2debug := godebug.New("http2debug") + fmt.Printf("%v\n", http2debug) +} diff --git a/tests/core/go_binary/godebug_test.go b/tests/core/go_binary/godebug_test.go new file mode 100644 index 0000000000..0b9e6affc2 --- /dev/null +++ b/tests/core/go_binary/godebug_test.go @@ -0,0 +1,44 @@ +package godebug + +import ( + "os/exec" + "strings" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel" +) + +func TestGodebugDefaults(t *testing.T) { + tests := map[string]struct { + binary string + want string + }{ + "debug": { + binary: "godebug_bin_godebug", + want: "http2debug=1", + }, + "no_debug": { + binary: "godebug_bin", + want: "http2debug=", + }, + } + + for testName, tc := range tests { + t.Run(testName, func(t *testing.T) { + bin, ok := bazel.FindBinary("tests/core/go_binary", tc.binary) + if !ok { + t.Fatalf("could not find %v binary", tc.binary) + } + + out, err := exec.Command(bin).Output() + if err != nil { + t.Fatal(err) + } + + got := strings.TrimSpace(string(out)) + if got != tc.want { + t.Errorf("got:%v\nwant:%s\n", got, tc.want) + } + }) + } +}