| // Copyright 2023 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package load |
| |
| import ( |
| "cmd/go/internal/modload" |
| "errors" |
| "fmt" |
| "go/build" |
| "internal/godebugs" |
| "sort" |
| "strconv" |
| "strings" |
| ) |
| |
| var ErrNotGoDebug = errors.New("not //go:debug line") |
| |
| func ParseGoDebug(text string) (key, value string, err error) { |
| if !strings.HasPrefix(text, "//go:debug") { |
| return "", "", ErrNotGoDebug |
| } |
| i := strings.IndexAny(text, " \t") |
| if i < 0 { |
| if strings.TrimSpace(text) == "//go:debug" { |
| return "", "", fmt.Errorf("missing key=value") |
| } |
| return "", "", ErrNotGoDebug |
| } |
| k, v, ok := strings.Cut(strings.TrimSpace(text[i:]), "=") |
| if !ok { |
| return "", "", fmt.Errorf("missing key=value") |
| } |
| if strings.ContainsAny(k, " \t") { |
| return "", "", fmt.Errorf("key contains space") |
| } |
| if strings.ContainsAny(v, " \t") { |
| return "", "", fmt.Errorf("value contains space") |
| } |
| if strings.ContainsAny(k, ",") { |
| return "", "", fmt.Errorf("key contains comma") |
| } |
| if strings.ContainsAny(v, ",") { |
| return "", "", fmt.Errorf("value contains comma") |
| } |
| |
| for _, info := range godebugs.All { |
| if k == info.Name { |
| return k, v, nil |
| } |
| } |
| return "", "", fmt.Errorf("unknown //go:debug setting %q", k) |
| } |
| |
| // defaultGODEBUG returns the default GODEBUG setting for the main package p. |
| // When building a test binary, directives, testDirectives, and xtestDirectives |
| // list additional directives from the package under test. |
| func defaultGODEBUG(p *Package, directives, testDirectives, xtestDirectives []build.Directive) string { |
| if p.Name != "main" { |
| return "" |
| } |
| goVersion := modload.MainModules.GoVersion() |
| if modload.RootMode == modload.NoRoot && p.Module != nil { |
| // This is go install pkg@version or go run pkg@version. |
| // Use the Go version from the package. |
| // If there isn't one, then |
| goVersion = p.Module.GoVersion |
| if goVersion == "" { |
| goVersion = "1.20" |
| } |
| } |
| |
| m := godebugForGoVersion(goVersion) |
| for _, list := range [][]build.Directive{p.Internal.Build.Directives, directives, testDirectives, xtestDirectives} { |
| for _, d := range list { |
| k, v, err := ParseGoDebug(d.Text) |
| if err != nil { |
| continue |
| } |
| if m == nil { |
| m = make(map[string]string) |
| } |
| m[k] = v |
| } |
| } |
| var keys []string |
| for k := range m { |
| keys = append(keys, k) |
| } |
| sort.Strings(keys) |
| var b strings.Builder |
| for _, k := range keys { |
| if b.Len() > 0 { |
| b.WriteString(",") |
| } |
| b.WriteString(k) |
| b.WriteString("=") |
| b.WriteString(m[k]) |
| } |
| return b.String() |
| } |
| |
| func godebugForGoVersion(v string) map[string]string { |
| if strings.Count(v, ".") >= 2 { |
| i := strings.Index(v, ".") |
| j := i + 1 + strings.Index(v[i+1:], ".") |
| v = v[:j] |
| } |
| |
| if !strings.HasPrefix(v, "1.") { |
| return nil |
| } |
| n, err := strconv.Atoi(v[len("1."):]) |
| if err != nil { |
| return nil |
| } |
| |
| def := make(map[string]string) |
| for _, info := range godebugs.All { |
| if n < info.Changed { |
| def[info.Name] = info.Old |
| } |
| } |
| return def |
| } |