Skip to content

Commit d54aeee

Browse files
committed
cmd/go: set default GODEBUG for main packages
For #56986, change the go command to compute and set the default GODEBUG settings for each main package, based on the work module's go version and the //go:debug lines in the main package. Change-Id: I2118cf0ae6d981138138661e02120c05af648872 Reviewed-on: https://go-review.googlesource.com/c/go/+/453605 Run-TryBot: Russ Cox <[email protected]> Reviewed-by: Bryan Mills <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent bd8ec78 commit d54aeee

13 files changed

+411
-88
lines changed

src/cmd/go/alldocs.go

+36-35
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/cmd/go/internal/list/list.go

+36-35
Original file line numberDiff line numberDiff line change
@@ -52,44 +52,45 @@ syntax of package template. The default output is equivalent
5252
to -f '{{.ImportPath}}'. The struct being passed to the template is:
5353
5454
type Package struct {
55-
Dir string // directory containing package sources
56-
ImportPath string // import path of package in dir
57-
ImportComment string // path in import comment on package statement
58-
Name string // package name
59-
Doc string // package documentation string
60-
Target string // install path
61-
Shlib string // the shared library that contains this package (only set when -linkshared)
62-
Goroot bool // is this package in the Go root?
63-
Standard bool // is this package part of the standard Go library?
64-
Stale bool // would 'go install' do anything for this package?
65-
StaleReason string // explanation for Stale==true
66-
Root string // Go root or Go path dir containing this package
67-
ConflictDir string // this directory shadows Dir in $GOPATH
68-
BinaryOnly bool // binary-only package (no longer supported)
69-
ForTest string // package is only for use in named test
70-
Export string // file containing export data (when using -export)
71-
BuildID string // build ID of the compiled package (when using -export)
72-
Module *Module // info about package's containing module, if any (can be nil)
73-
Match []string // command-line patterns matching this package
74-
DepOnly bool // package is only a dependency, not explicitly listed
55+
Dir string // directory containing package sources
56+
ImportPath string // import path of package in dir
57+
ImportComment string // path in import comment on package statement
58+
Name string // package name
59+
Doc string // package documentation string
60+
Target string // install path
61+
Shlib string // the shared library that contains this package (only set when -linkshared)
62+
Goroot bool // is this package in the Go root?
63+
Standard bool // is this package part of the standard Go library?
64+
Stale bool // would 'go install' do anything for this package?
65+
StaleReason string // explanation for Stale==true
66+
Root string // Go root or Go path dir containing this package
67+
ConflictDir string // this directory shadows Dir in $GOPATH
68+
BinaryOnly bool // binary-only package (no longer supported)
69+
ForTest string // package is only for use in named test
70+
Export string // file containing export data (when using -export)
71+
BuildID string // build ID of the compiled package (when using -export)
72+
Module *Module // info about package's containing module, if any (can be nil)
73+
Match []string // command-line patterns matching this package
74+
DepOnly bool // package is only a dependency, not explicitly listed
75+
DefaultGODEBUG string // default GODEBUG setting, for main packages
7576
7677
// Source files
77-
GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
78-
CgoFiles []string // .go source files that import "C"
79-
CompiledGoFiles []string // .go files presented to compiler (when using -compiled)
80-
IgnoredGoFiles []string // .go source files ignored due to build constraints
78+
GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
79+
CgoFiles []string // .go source files that import "C"
80+
CompiledGoFiles []string // .go files presented to compiler (when using -compiled)
81+
IgnoredGoFiles []string // .go source files ignored due to build constraints
8182
IgnoredOtherFiles []string // non-.go source files ignored due to build constraints
82-
CFiles []string // .c source files
83-
CXXFiles []string // .cc, .cxx and .cpp source files
84-
MFiles []string // .m source files
85-
HFiles []string // .h, .hh, .hpp and .hxx source files
86-
FFiles []string // .f, .F, .for and .f90 Fortran source files
87-
SFiles []string // .s source files
88-
SwigFiles []string // .swig files
89-
SwigCXXFiles []string // .swigcxx files
90-
SysoFiles []string // .syso object files to add to archive
91-
TestGoFiles []string // _test.go files in package
92-
XTestGoFiles []string // _test.go files outside package
83+
CFiles []string // .c source files
84+
CXXFiles []string // .cc, .cxx and .cpp source files
85+
MFiles []string // .m source files
86+
HFiles []string // .h, .hh, .hpp and .hxx source files
87+
FFiles []string // .f, .F, .for and .f90 Fortran source files
88+
SFiles []string // .s source files
89+
SwigFiles []string // .swig files
90+
SwigCXXFiles []string // .swigcxx files
91+
SysoFiles []string // .syso object files to add to archive
92+
TestGoFiles []string // _test.go files in package
93+
XTestGoFiles []string // _test.go files outside package
9394
9495
// Embedded files
9596
EmbedPatterns []string // //go:embed patterns

src/cmd/go/internal/load/godebug.go

+128
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
// Copyright 2023 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package load
6+
7+
import (
8+
"cmd/go/internal/modload"
9+
"errors"
10+
"fmt"
11+
"go/build"
12+
"sort"
13+
"strconv"
14+
"strings"
15+
)
16+
17+
var ErrNotGoDebug = errors.New("not //go:debug line")
18+
19+
func ParseGoDebug(text string) (key, value string, err error) {
20+
if !strings.HasPrefix(text, "//go:debug") {
21+
return "", "", ErrNotGoDebug
22+
}
23+
i := strings.IndexAny(text, " \t")
24+
if i < 0 {
25+
if strings.TrimSpace(text) == "//go:debug" {
26+
return "", "", fmt.Errorf("missing key=value")
27+
}
28+
return "", "", ErrNotGoDebug
29+
}
30+
k, v, ok := strings.Cut(strings.TrimSpace(text[i:]), "=")
31+
if !ok {
32+
return "", "", fmt.Errorf("missing key=value")
33+
}
34+
if strings.ContainsAny(k, " \t") {
35+
return "", "", fmt.Errorf("key contains space")
36+
}
37+
if strings.ContainsAny(v, " \t") {
38+
return "", "", fmt.Errorf("value contains space")
39+
}
40+
if strings.ContainsAny(k, ",") {
41+
return "", "", fmt.Errorf("key contains comma")
42+
}
43+
if strings.ContainsAny(v, ",") {
44+
return "", "", fmt.Errorf("value contains comma")
45+
}
46+
return k, v, nil
47+
}
48+
49+
// defaultGODEBUG returns the default GODEBUG setting for the main package p.
50+
// When building a test binary, directives, testDirectives, and xtestDirectives
51+
// list additional directives from the package under test.
52+
func defaultGODEBUG(p *Package, directives, testDirectives, xtestDirectives []build.Directive) string {
53+
if p.Name != "main" {
54+
return ""
55+
}
56+
goVersion := modload.MainModules.GoVersion()
57+
if modload.RootMode == modload.NoRoot && p.Module != nil {
58+
// This is go install pkg@version or go run pkg@version.
59+
// Use the Go version from the package.
60+
// If there isn't one, then
61+
goVersion = p.Module.GoVersion
62+
if goVersion == "" {
63+
goVersion = "1.20"
64+
}
65+
}
66+
67+
m := godebugForGoVersion(goVersion)
68+
for _, list := range [][]build.Directive{p.Internal.Build.Directives, directives, testDirectives, xtestDirectives} {
69+
for _, d := range list {
70+
k, v, err := ParseGoDebug(d.Text)
71+
if err != nil {
72+
continue
73+
}
74+
if m == nil {
75+
m = make(map[string]string)
76+
}
77+
m[k] = v
78+
}
79+
}
80+
var keys []string
81+
for k := range m {
82+
keys = append(keys, k)
83+
}
84+
sort.Strings(keys)
85+
var b strings.Builder
86+
for _, k := range keys {
87+
if b.Len() > 0 {
88+
b.WriteString(",")
89+
}
90+
b.WriteString(k)
91+
b.WriteString("=")
92+
b.WriteString(m[k])
93+
}
94+
return b.String()
95+
}
96+
97+
func godebugForGoVersion(v string) map[string]string {
98+
if strings.Count(v, ".") >= 2 {
99+
i := strings.Index(v, ".")
100+
j := i + 1 + strings.Index(v[i+1:], ".")
101+
v = v[:j]
102+
}
103+
104+
if !strings.HasPrefix(v, "1.") {
105+
return nil
106+
}
107+
n, err := strconv.Atoi(v[len("1."):])
108+
if err != nil {
109+
return nil
110+
}
111+
112+
def := make(map[string]string)
113+
for _, d := range defaultGodebugs {
114+
if (d.before != 0 && n < d.before) || (d.first != 0 && n >= d.first) {
115+
def[d.name] = d.value
116+
}
117+
}
118+
return def
119+
}
120+
121+
var defaultGodebugs = []struct {
122+
before int // applies to Go versions up until this one (21 for Go 1.21)
123+
first int // applies to Go versions starting at this one (21 for Go 1.21)
124+
name string
125+
value string
126+
}{
127+
{before: 21, name: "panicnil", value: "1"},
128+
}

src/cmd/go/internal/load/pkg.go

+7-7
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ type Package struct {
5858
type PackagePublic struct {
5959
// Note: These fields are part of the go command's public API.
6060
// See list.go. It is okay to add fields, but not to change or
61-
// remove existing ones. Keep in sync with list.go
61+
// remove existing ones. Keep in sync with ../list/list.go
6262
Dir string `json:",omitempty"` // directory containing package sources
6363
ImportPath string `json:",omitempty"` // import path of package in dir
6464
ImportComment string `json:",omitempty"` // path in import comment on package statement
@@ -79,6 +79,8 @@ type PackagePublic struct {
7979
BinaryOnly bool `json:",omitempty"` // package cannot be recompiled
8080
Incomplete bool `json:",omitempty"` // was there an error loading this package or dependencies?
8181

82+
DefaultGODEBUG string `json:",omitempty"` // default GODEBUG setting (only for Name=="main")
83+
8284
// Stale and StaleReason remain here *only* for the list command.
8385
// They are only initialized in preparation for list execution.
8486
// The regular build determines staleness on the fly during action execution.
@@ -230,9 +232,6 @@ type PackageInternal struct {
230232
TestmainGo *[]byte // content for _testmain.go
231233
Embed map[string][]string // //go:embed comment mapping
232234
OrigImportPath string // original import path before adding '_test' suffix
233-
Directives []build.Directive
234-
TestDirectives []build.Directive
235-
XTestDirectives []build.Directive
236235

237236
Asmflags []string // -asmflags for this package
238237
Gcflags []string // -gcflags for this package
@@ -438,9 +437,6 @@ func (p *Package) copyBuild(opts PackageOpts, pp *build.Package) {
438437
p.TestEmbedPatterns = pp.TestEmbedPatterns
439438
p.XTestEmbedPatterns = pp.XTestEmbedPatterns
440439
p.Internal.OrigImportPath = pp.ImportPath
441-
p.Internal.Directives = pp.Directives
442-
p.Internal.TestDirectives = pp.TestDirectives
443-
p.Internal.XTestDirectives = pp.XTestDirectives
444440
}
445441

446442
// A PackageError describes an error loading information about a package.
@@ -1924,6 +1920,7 @@ func (p *Package) load(ctx context.Context, opts PackageOpts, path string, stk *
19241920
if cfg.ModulesEnabled {
19251921
p.Module = modload.PackageModuleInfo(ctx, pkgPath)
19261922
}
1923+
p.DefaultGODEBUG = defaultGODEBUG(p, nil, nil, nil)
19271924

19281925
p.EmbedFiles, p.Internal.Embed, err = resolveEmbed(p.Dir, p.EmbedPatterns)
19291926
if err != nil {
@@ -2405,6 +2402,9 @@ func (p *Package) setBuildInfo(autoVCS bool) {
24052402
if cfg.BuildTrimpath {
24062403
appendSetting("-trimpath", "true")
24072404
}
2405+
if p.DefaultGODEBUG != "" {
2406+
appendSetting("DefaultGODEBUG", p.DefaultGODEBUG)
2407+
}
24082408
cgo := "0"
24092409
if cfg.BuildContext.CgoEnabled {
24102410
cgo = "1"

src/cmd/go/internal/load/test.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323

2424
"cmd/go/internal/cfg"
2525
"cmd/go/internal/fsys"
26+
"cmd/go/internal/slices"
2627
"cmd/go/internal/str"
2728
"cmd/go/internal/trace"
2829
)
@@ -205,6 +206,7 @@ func TestPackagesAndErrors(ctx context.Context, opts PackageOpts, p *Package, co
205206
ptest.Internal.Embed = testEmbed
206207
ptest.EmbedFiles = str.StringList(p.EmbedFiles, p.TestEmbedFiles)
207208
ptest.Internal.OrigImportPath = p.Internal.OrigImportPath
209+
ptest.Internal.Build.Directives = append(slices.Clip(p.Internal.Build.Directives), p.Internal.Build.TestDirectives...)
208210
ptest.collectDeps()
209211
} else {
210212
ptest = p
@@ -229,7 +231,8 @@ func TestPackagesAndErrors(ctx context.Context, opts PackageOpts, p *Package, co
229231
Internal: PackageInternal{
230232
LocalPrefix: p.Internal.LocalPrefix,
231233
Build: &build.Package{
232-
ImportPos: p.Internal.Build.XTestImportPos,
234+
ImportPos: p.Internal.Build.XTestImportPos,
235+
Directives: p.Internal.Build.XTestDirectives,
233236
},
234237
Imports: ximports,
235238
RawImports: rawXTestImports,
@@ -270,6 +273,9 @@ func TestPackagesAndErrors(ctx context.Context, opts PackageOpts, p *Package, co
270273
},
271274
}
272275

276+
pb := p.Internal.Build
277+
pmain.DefaultGODEBUG = defaultGODEBUG(pmain, pb.Directives, pb.TestDirectives, pb.XTestDirectives)
278+
273279
// The generated main also imports testing, regexp, and os.
274280
// Also the linker introduces implicit dependencies reported by LinkerDeps.
275281
stk.Push("testmain")

0 commit comments

Comments
 (0)