cmd/go: stamp tags and flags in build info
Toolchain flags (like -gcflags), build tags (including race and msan),
and cgo variables (including CGO_ENABLED, CGO_CPPFLAGS and others) are
now stamped into binaries.
For #37475
Change-Id: I9023e682c0618f91805434946c6bc937536b69bb
Reviewed-on: https://go-review.googlesource.com/c/go/+/355493
Trust: Jay Conrod <jayconrod@google.com>
Run-TryBot: Jay Conrod <jayconrod@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Bryan C. Mills <bcmills@google.com>
diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go
index b6ea5a3..d04ba04 100644
--- a/src/cmd/go/alldocs.go
+++ b/src/cmd/go/alldocs.go
@@ -131,6 +131,11 @@
//
// -asmflags '[pattern=]arg list'
// arguments to pass on each go tool asm invocation.
+// -buildinfo
+// Whether to stamp binaries with build flags. By default, the compiler name
+// (gc or gccgo), toolchain flags (like -gcflags), and environment variables
+// containing flags (like CGO_CFLAGS) are stamped into binaries. Use
+// -buildinfo=false to omit build information. See also -buildvcs.
// -buildmode mode
// build mode to use. See 'go help buildmode' for more.
// -buildvcs
@@ -138,7 +143,7 @@
// version control information is stamped into a binary if the main package
// and the main module containing it are in the repository containing the
// current directory (if there is a repository). Use -buildvcs=false to
-// omit version control information.
+// omit version control information. See also -buildinfo.
// -compiler name
// name of compiler to use, as in runtime.Compiler (gccgo or gc).
// -gccgoflags '[pattern=]arg list'
diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go
index 07e9962..d67d01a 100644
--- a/src/cmd/go/go_test.go
+++ b/src/cmd/go/go_test.go
@@ -1380,7 +1380,7 @@
for buf.Len() < sys.ExecArgLengthLimit+1 {
buf.WriteString(testStr)
}
- tg.run("run", "-ldflags", fmt.Sprintf(`-X "main.extern=%s"`, buf.String()), tg.path("main.go"))
+ tg.run("run", "-buildinfo=false", "-ldflags", fmt.Sprintf(`-X "main.extern=%s"`, buf.String()), tg.path("main.go"))
if tg.stderr.String() != buf.String() {
t.Errorf("strings differ")
}
diff --git a/src/cmd/go/internal/cfg/cfg.go b/src/cmd/go/internal/cfg/cfg.go
index e1bf11f..37e9b26 100644
--- a/src/cmd/go/internal/cfg/cfg.go
+++ b/src/cmd/go/internal/cfg/cfg.go
@@ -25,6 +25,7 @@
// These are general "build flags" used by build and other commands.
var (
BuildA bool // -a flag
+ BuildBuildinfo bool // -buildinfo flag
BuildBuildmode string // -buildmode flag
BuildBuildvcs bool // -buildvcs flag
BuildContext = defaultContext()
diff --git a/src/cmd/go/internal/load/flag.go b/src/cmd/go/internal/load/flag.go
index 4e0cb5b..d0d5716 100644
--- a/src/cmd/go/internal/load/flag.go
+++ b/src/cmd/go/internal/load/flag.go
@@ -22,6 +22,7 @@
// that allows specifying different effective flags for different packages.
// See 'go help build' for more details about per-package flags.
type PerPackageFlag struct {
+ raw string
present bool
values []ppfValue
}
@@ -39,6 +40,7 @@
// set is the implementation of Set, taking a cwd (current working directory) for easier testing.
func (f *PerPackageFlag) set(v, cwd string) error {
+ f.raw = v
f.present = true
match := func(p *Package) bool { return p.Internal.CmdlinePkg || p.Internal.CmdlineFiles } // default predicate with no pattern
// For backwards compatibility with earlier flag splitting, ignore spaces around flags.
@@ -72,9 +74,7 @@
return nil
}
-// String is required to implement flag.Value.
-// It is not used, because cmd/go never calls flag.PrintDefaults.
-func (f *PerPackageFlag) String() string { return "<PerPackageFlag>" }
+func (f *PerPackageFlag) String() string { return f.raw }
// Present reports whether the flag appeared on the command line.
func (f *PerPackageFlag) Present() bool {
diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go
index 473fa7a..716994b 100644
--- a/src/cmd/go/internal/load/pkg.go
+++ b/src/cmd/go/internal/load/pkg.go
@@ -2268,6 +2268,35 @@
Main: main,
Deps: deps,
}
+ appendSetting := func(key, value string) {
+ info.Settings = append(info.Settings, debug.BuildSetting{Key: key, Value: value})
+ }
+
+ // Add command-line flags relevant to the build.
+ // This is informational, not an exhaustive list.
+ if cfg.BuildBuildinfo {
+ appendSetting("compiler", cfg.BuildContext.Compiler)
+ if BuildAsmflags.present {
+ appendSetting("asmflags", BuildAsmflags.String())
+ }
+ if BuildGcflags.present && cfg.BuildContext.Compiler == "gc" {
+ appendSetting("gcflags", BuildGcflags.String())
+ }
+ if BuildGccgoflags.present && cfg.BuildContext.Compiler == "gccgo" {
+ appendSetting("gccgoflags", BuildGccgoflags.String())
+ }
+ if BuildLdflags.present {
+ appendSetting("ldflags", BuildLdflags.String())
+ }
+ tags := append(cfg.BuildContext.BuildTags, cfg.BuildContext.ToolTags...)
+ appendSetting("tags", strings.Join(tags, ","))
+ appendSetting("CGO_ENABLED", strconv.FormatBool(cfg.BuildContext.CgoEnabled))
+ if cfg.BuildContext.CgoEnabled {
+ for _, name := range []string{"CGO_CPPFLAGS", "CGO_CFLAGS", "CGO_CXXFLAGS", "CGO_LDFLAGS"} {
+ appendSetting(name, cfg.Getenv(name))
+ }
+ }
+ }
// Add VCS status if all conditions are true:
//
@@ -2328,10 +2357,10 @@
setVCSError(err)
return
}
- info.Settings = []debug.BuildSetting{
+ info.Settings = append(info.Settings, []debug.BuildSetting{
{Key: vcsCmd.Cmd + "revision", Value: st.Revision},
{Key: vcsCmd.Cmd + "uncommitted", Value: strconv.FormatBool(st.Uncommitted)},
- }
+ }...)
}
text, err := info.MarshalText()
diff --git a/src/cmd/go/internal/work/build.go b/src/cmd/go/internal/work/build.go
index 114abab..e10f647 100644
--- a/src/cmd/go/internal/work/build.go
+++ b/src/cmd/go/internal/work/build.go
@@ -85,6 +85,11 @@
-asmflags '[pattern=]arg list'
arguments to pass on each go tool asm invocation.
+ -buildinfo
+ Whether to stamp binaries with build flags. By default, the compiler name
+ (gc or gccgo), toolchain flags (like -gcflags), and environment variables
+ containing flags (like CGO_CFLAGS) are stamped into binaries. Use
+ -buildinfo=false to omit build information. See also -buildvcs.
-buildmode mode
build mode to use. See 'go help buildmode' for more.
-buildvcs
@@ -92,7 +97,7 @@
version control information is stamped into a binary if the main package
and the main module containing it are in the repository containing the
current directory (if there is a repository). Use -buildvcs=false to
- omit version control information.
+ omit version control information. See also -buildinfo.
-compiler name
name of compiler to use, as in runtime.Compiler (gccgo or gc).
-gccgoflags '[pattern=]arg list'
@@ -308,6 +313,7 @@
cmd.Flag.Var((*base.StringsFlag)(&cfg.BuildToolexec), "toolexec", "")
cmd.Flag.BoolVar(&cfg.BuildTrimpath, "trimpath", false, "")
cmd.Flag.BoolVar(&cfg.BuildWork, "work", false, "")
+ cmd.Flag.BoolVar(&cfg.BuildBuildinfo, "buildinfo", true, "")
cmd.Flag.BoolVar(&cfg.BuildBuildvcs, "buildvcs", true, "")
// Undocumented, unstable debugging flags.
diff --git a/src/cmd/go/testdata/script/version_build_settings.txt b/src/cmd/go/testdata/script/version_build_settings.txt
new file mode 100644
index 0000000..7e93643
--- /dev/null
+++ b/src/cmd/go/testdata/script/version_build_settings.txt
@@ -0,0 +1,66 @@
+[short] skip
+
+# Compiler name is always added.
+go build
+go version -m m$GOEXE
+stdout '^\tbuild\tcompiler\tgc$'
+! stdout asmflags|gcflags|ldflags|gccgoflags
+
+# Toolchain flags are added if present.
+# The raw flags are included, with package patterns if specified.
+go build -asmflags=all=-spectre=all
+go version -m m$GOEXE
+stdout '^\tbuild\tasmflags\tall=-spectre=all$'
+
+go build -gcflags=all=-spectre=all
+go version -m m$GOEXE
+stdout '^\tbuild\tgcflags\tall=-spectre=all$'
+
+go build -ldflags=-w
+go version -m m$GOEXE
+stdout '^\tbuild\tldflags\t-w$'
+
+# gccgoflags are not added when gc is used, and vice versa.
+# TODO: test gccgo.
+go build -gccgoflags=all=UNUSED
+go version -m m$GOEXE
+! stdout gccgoflags
+
+# Build and tool tags are added but not release tags.
+# "race" is included with build tags but not "cgo".
+go build -tags=a,b
+go version -m m$GOEXE
+stdout '^\tbuild\ttags\ta,b(,goexperiment\.[a-z0-9]+)*$'
+[race] go build -race
+[race] go version -m m$GOEXE
+[race] stdout '^\tbuild\ttags\t.*race.*$'
+
+# CGO flags are separate settings.
+# CGO_ENABLED is always present.
+# Other flags are added if CGO_ENABLED is true.
+env CGO_ENABLED=0
+go build
+go version -m m$GOEXE
+stdout '^\tbuild\tCGO_ENABLED\tfalse$'
+! stdout CGO_CPPFLAGS|CGO_CFLAGS|CGO_CXXFLAGS|CGO_LDFLAGS
+[cgo] env CGO_ENABLED=1
+[cgo] env CGO_CPPFLAGS=-DFROM_CPPFLAGS=1
+[cgo] env CGO_CFLAGS=-DFROM_CFLAGS=1
+[cgo] env CGO_CXXFLAGS=-DFROM_CXXFLAGS=1
+[cgo] env CGO_LDFLAGS=-L/extra/dir/does/not/exist
+[cgo] go build
+[cgo] go version -m m$GOEXE
+[cgo] stdout '^\tbuild\tCGO_ENABLED\ttrue$'
+[cgo] stdout '^\tbuild\tCGO_CPPFLAGS\t-DFROM_CPPFLAGS=1$'
+[cgo] stdout '^\tbuild\tCGO_CFLAGS\t-DFROM_CFLAGS=1$'
+[cgo] stdout '^\tbuild\tCGO_CXXFLAGS\t-DFROM_CXXFLAGS=1$'
+[cgo] stdout '^\tbuild\tCGO_LDFLAGS\t-L/extra/dir/does/not/exist$'
+
+-- go.mod --
+module example.com/m
+
+go 1.18
+-- m.go --
+package main
+
+func main() {}