// GoPathError is set when GOPATH is not set. it contains an
// explanation why GOPATH is unset.
GoPathError string
+
+ GOEXPERIMENT = envOr("GOEXPERIMENT", buildcfg.DefaultGOEXPERIMENT)
)
func defaultContext() build.Context {
// The experiments flags are based on GOARCH, so they may
// need to change. TODO: This should be cleaned up.
- buildcfg.UpdateExperiments(ctxt.GOOS, ctxt.GOARCH, envOr("GOEXPERIMENT", buildcfg.DefaultGOEXPERIMENT))
+ buildcfg.UpdateExperiments(ctxt.GOOS, ctxt.GOARCH, GOEXPERIMENT)
ctxt.ToolTags = nil
for _, exp := range buildcfg.EnabledExperiments() {
ctxt.ToolTags = append(ctxt.ToolTags, "goexperiment."+exp)
Deps: deps,
}
appendSetting := func(key, value string) {
+ value = strings.ReplaceAll(value, "\n", " ") // make value safe
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.
+ // Please keep the list sorted.
if cfg.BuildBuildinfo && !p.Standard {
- appendSetting("compiler", cfg.BuildContext.Compiler)
- if BuildAsmflags.present {
- appendSetting("asmflags", BuildAsmflags.String())
+ if cfg.BuildASan {
+ appendSetting("-asan", "true")
}
- if BuildGcflags.present && cfg.BuildContext.Compiler == "gc" {
- appendSetting("gcflags", BuildGcflags.String())
+ if BuildAsmflags.present {
+ appendSetting("-asmflags", BuildAsmflags.String())
}
+ appendSetting("-compiler", cfg.BuildContext.Compiler)
if BuildGccgoflags.present && cfg.BuildContext.Compiler == "gccgo" {
- appendSetting("gccgoflags", BuildGccgoflags.String())
+ appendSetting("-gccgoflags", BuildGccgoflags.String())
+ }
+ if BuildGcflags.present && cfg.BuildContext.Compiler == "gc" {
+ appendSetting("-gcflags", BuildGcflags.String())
}
if BuildLdflags.present {
- appendSetting("ldflags", BuildLdflags.String())
+ appendSetting("-ldflags", BuildLdflags.String())
+ }
+ if cfg.BuildMSan {
+ appendSetting("-msan", "true")
}
- tags := append(cfg.BuildContext.BuildTags, cfg.BuildContext.ToolTags...)
- appendSetting("tags", strings.Join(tags, ","))
- appendSetting("CGO_ENABLED", strconv.FormatBool(cfg.BuildContext.CgoEnabled))
+ if cfg.BuildRace {
+ appendSetting("-race", "true")
+ }
+ if tags := cfg.BuildContext.BuildTags; len(tags) > 0 {
+ appendSetting("-tags", strings.Join(tags, ","))
+ }
+ cgo := "0"
+ if cfg.BuildContext.CgoEnabled {
+ cgo = "1"
+ }
+ appendSetting("CGO_ENABLED", cgo)
if cfg.BuildContext.CgoEnabled {
- for _, name := range []string{"CGO_CPPFLAGS", "CGO_CFLAGS", "CGO_CXXFLAGS", "CGO_LDFLAGS"} {
+ for _, name := range []string{"CGO_CFLAGS", "CGO_CPPFLAGS", "CGO_CXXFLAGS", "CGO_LDFLAGS"} {
appendSetting(name, cfg.Getenv(name))
}
}
+ appendSetting("GOARCH", cfg.BuildContext.GOARCH)
+ if cfg.GOEXPERIMENT != "" {
+ appendSetting("GOEXPERIMENT", cfg.GOEXPERIMENT)
+ }
+ appendSetting("GOOS", cfg.BuildContext.GOOS)
+ if key, val := cfg.GetArchEnv(); key != "" && val != "" {
+ appendSetting(key, val)
+ }
}
// Add VCS status if all conditions are true:
}
st := cached.Status
+ appendSetting("vcs", vcsCmd.Cmd)
if st.Revision != "" {
- appendSetting(vcsCmd.Cmd+"revision", st.Revision)
+ appendSetting("vcs.revision", st.Revision)
}
if !st.CommitTime.IsZero() {
stamp := st.CommitTime.UTC().Format(time.RFC3339Nano)
- appendSetting(vcsCmd.Cmd+"committime", stamp)
+ appendSetting("vcs.time", stamp)
}
- appendSetting(vcsCmd.Cmd+"uncommitted", strconv.FormatBool(st.Uncommitted))
+ appendSetting("vcs.modified", strconv.FormatBool(st.Uncommitted))
}
text, err := info.MarshalText()
cfg.BuildContext.InstallSuffix += "_"
}
cfg.BuildContext.InstallSuffix += mode
- cfg.BuildContext.BuildTags = append(cfg.BuildContext.BuildTags, mode)
+ cfg.BuildContext.ToolTags = append(cfg.BuildContext.ToolTags, mode)
}
func buildModeInit() {
# Compiler name is always added.
go build
go version -m m$GOEXE
-stdout '^\tbuild\tcompiler\tgc$'
+stdout '^\tbuild\t-compiler=gc$'
+stdout '^\tbuild\tGOOS='
+stdout '^\tbuild\tGOARCH='
+[amd64] stdout '^\tbuild\tGOAMD64='
! stdout asmflags|gcflags|ldflags|gccgoflags
# Toolchain flags are added if present.
# The raw flags are included, with package patterns if specified.
go build -asmflags=example.com/m=-D=FOO=bar
go version -m m$GOEXE
-stdout '^\tbuild\tasmflags\texample\.com/m=-D=FOO=bar$'
+stdout '^\tbuild\t-asmflags=example\.com/m=-D=FOO=bar$'
go build -gcflags=example.com/m=-N
go version -m m$GOEXE
-stdout '^\tbuild\tgcflags\texample\.com/m=-N$'
+stdout '^\tbuild\t-gcflags=example\.com/m=-N$'
go build -ldflags=example.com/m=-w
go version -m m$GOEXE
-stdout '^\tbuild\tldflags\texample\.com/m=-w$'
+stdout '^\tbuild\t-ldflags=example\.com/m=-w$'
# gccgoflags are not added when gc is used, and vice versa.
# TODO: test gccgo.
# "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]+)*$'
+stdout '^\tbuild\t-tags=a,b$'
[race] go build -race
[race] go version -m m$GOEXE
-[race] stdout '^\tbuild\ttags\t.*race.*$'
+[race] ! stdout '^\tbuild\t-tags='
+[race] stdout '^\tbuild\t-race=true$'
# CGO flags are separate settings.
# CGO_ENABLED is always present.
env CGO_ENABLED=0
go build
go version -m m$GOEXE
-stdout '^\tbuild\tCGO_ENABLED\tfalse$'
+stdout '^\tbuild\tCGO_ENABLED=0$'
! stdout CGO_CPPFLAGS|CGO_CFLAGS|CGO_CXXFLAGS|CGO_LDFLAGS
[cgo] env CGO_ENABLED=1
[cgo] env CGO_CPPFLAGS=-DFROM_CPPFLAGS=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$'
+[cgo] stdout '^\tbuild\tCGO_ENABLED=1$'
+[cgo] stdout '^\tbuild\tCGO_CPPFLAGS=-DFROM_CPPFLAGS=1$'
+[cgo] stdout '^\tbuild\tCGO_CFLAGS=-DFROM_CFLAGS=1$'
+[cgo] stdout '^\tbuild\tCGO_CXXFLAGS=-DFROM_CXXFLAGS=1$'
+[cgo] stdout '^\tbuild\tCGO_LDFLAGS=-L/extra/dir/does/not/exist$'
-- go.mod --
module example.com/m
# If there's no local repository, there's no VCS info.
go install
go version -m $GOBIN/a$GOEXE
-! stdout gitrevision
+! stdout vcs.revision
rm $GOBIN/a$GOEXE
# If there is a repository, but it can't be used for some reason,
cd a
go install
go version -m $GOBIN/a$GOEXE
-! stdout gitrevision
-! stdout gitcommittime
-stdout '^\tbuild\tgituncommitted\ttrue$'
+stdout '^\tbuild\tvcs=git$'
+stdout '^\tbuild\tvcs.modified=true$'
+! stdout vcs.revision
+! stdout vcs.time
rm $GOBIN/a$GOEXE
# Revision and commit time are tagged for repositories with commits.
exec git commit -m 'initial commit'
go install
go version -m $GOBIN/a$GOEXE
-stdout '^\tbuild\tgitrevision\t'
-stdout '^\tbuild\tgitcommittime\t'
-stdout '^\tbuild\tgituncommitted\tfalse$'
+stdout '^\tbuild\tvcs.revision='
+stdout '^\tbuild\tvcs.time='
+stdout '^\tbuild\tvcs.modified=false$'
rm $GOBIN/a$GOEXE
# Building with -buildvcs=false suppresses the info.
go install -buildvcs=false
go version -m $GOBIN/a$GOEXE
-! stdout gitrevision
+! stdout vcs.revision
rm $GOBIN/a$GOEXE
# An untracked file is shown as uncommitted, even if it isn't part of the build.
cp ../../outside/empty.txt .
go install
go version -m $GOBIN/a$GOEXE
-stdout '^\tbuild\tgituncommitted\ttrue$'
+stdout '^\tbuild\tvcs.modified=true$'
rm empty.txt
rm $GOBIN/a$GOEXE
cp ../../outside/empty.txt ../README
go install
go version -m $GOBIN/a$GOEXE
-stdout '^\tbuild\tgituncommitted\ttrue$'
+stdout '^\tbuild\tvcs.modified=true$'
exec git checkout ../README
rm $GOBIN/a$GOEXE
# there should be no VCS info.
go install example.com/cmd/a@v1.0.0
go version -m $GOBIN/a$GOEXE
-! stdout gitrevision
+! stdout vcs.revision
rm $GOBIN/a$GOEXE
go mod edit -require=example.com/c@v0.0.0
go mod edit -replace=example.com/c@v0.0.0=../../outside/c
go install example.com/c
go version -m $GOBIN/c$GOEXE
-! stdout gitrevision
+! stdout vcs.revision
rm $GOBIN/c$GOEXE
exec git checkout go.mod
go mod edit -replace=example.com/d@v0.0.0=../../outside/d
go install example.com/d
go version -m $GOBIN/d$GOEXE
-! stdout gitrevision
+! stdout vcs.revision
exec git checkout go.mod
rm $GOBIN/d$GOEXE
cd a
go install
go version -m $GOBIN/a$GOEXE
-! stdout hgrevision
-! stdout hgcommittime
-stdout '^\tbuild\thguncommitted\ttrue$'
+! stdout vcs.revision
+! stdout vcs.time
+stdout '^\tbuild\tvcs.modified=true$'
cd ..
# Revision and commit time are tagged for repositories with commits.
cd a
go install
go version -m $GOBIN/a$GOEXE
-stdout '^\tbuild\thgrevision\t'
-stdout '^\tbuild\thgcommittime\t'
-stdout '^\tbuild\thguncommitted\tfalse$'
+stdout '^\tbuild\tvcs.revision='
+stdout '^\tbuild\tvcs.time='
+stdout '^\tbuild\tvcs.modified=false$'
rm $GOBIN/a$GOEXE
# Building with -buildvcs=false suppresses the info.
cp ../../outside/empty.txt .
go install
go version -m $GOBIN/a$GOEXE
-stdout '^\tbuild\thguncommitted\ttrue$'
+stdout '^\tbuild\tvcs.modified=true$'
rm empty.txt
rm $GOBIN/a$GOEXE
cp ../../outside/empty.txt ../README
go install
go version -m $GOBIN/a$GOEXE
-stdout '^\tbuild\thguncommitted\ttrue$'
+stdout '^\tbuild\tvcs.modified=true$'
exec hg revert ../README
rm $GOBIN/a$GOEXE
// build lines are included.
got = goVersionRe.ReplaceAllString(got, "go\tGOVERSION\n")
got = buildRe.ReplaceAllStringFunc(got, func(match string) string {
- if strings.HasPrefix(match, "build\tcompiler\t") {
+ if strings.HasPrefix(match, "build\t-compiler=") {
return match
}
return ""
want: "go\tGOVERSION\n" +
"path\texample.com/m\n" +
"mod\texample.com/m\t(devel)\t\n" +
- "build\tcompiler\tgc\n",
+ "build\t-compiler=gc\n",
},
{
name: "invalid_modules",
// BuildSetting describes a setting that may be used to understand how the
// binary was built. For example, VCS commit and dirty status is stored here.
type BuildSetting struct {
- // Key and Value describe the build setting. They must not contain tabs
- // or newlines.
+ // Key and Value describe the build setting.
+ // Key must not contain an equals sign, space, tab, or newline.
+ // Value must not contain newlines ('\n').
Key, Value string
}
formatMod("dep", *dep)
}
for _, s := range bi.Settings {
- if strings.ContainsAny(s.Key, "\n\t") || strings.ContainsAny(s.Value, "\n\t") {
- return nil, fmt.Errorf("build setting %q contains tab or newline", s.Key)
+ if strings.ContainsAny(s.Key, "= \t\n") {
+ return nil, fmt.Errorf("invalid build setting key %q", s.Key)
}
- fmt.Fprintf(buf, "build\t%s\t%s\n", s.Key, s.Value)
+ if strings.Contains(s.Value, "\n") {
+ return nil, fmt.Errorf("invalid build setting value for key %q: contains newline", s.Value)
+ }
+ fmt.Fprintf(buf, "build\t%s=%s\n", s.Key, s.Value)
}
return buf.Bytes(), nil
}
last = nil
case bytes.HasPrefix(line, buildLine):
- elem := bytes.Split(line[len(buildLine):], tab)
- if len(elem) != 2 {
- return fmt.Errorf("expected 2 columns for build setting; got %d", len(elem))
+ key, val, ok := strings.Cut(string(line[len(buildLine):]), "=")
+ if !ok {
+ return fmt.Errorf("invalid build line")
}
- if len(elem[0]) == 0 {
+ if key == "" {
return fmt.Errorf("empty key")
}
- bi.Settings = append(bi.Settings, BuildSetting{Key: string(elem[0]), Value: string(elem[1])})
+ bi.Settings = append(bi.Settings, BuildSetting{Key: key, Value: val})
}
lineNum++
}