Skip to content

Commit

Permalink
modfile: add API for godebug lines
Browse files Browse the repository at this point in the history
For golang/go#65573

Change-Id: I5c1be8833f70b0b5a7257bd5216fa6a89bd2665f
Reviewed-on: https://go-review.googlesource.com/c/mod/+/584300
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Russ Cox <rsc@golang.org>
Reviewed-by: Michael Matloob <matloob@golang.org>
Reviewed-by: Sam Thanawalla <samthanawalla@google.com>
  • Loading branch information
rsc authored and gopherbot committed May 14, 2024
1 parent 6686f41 commit c0bdc7b
Show file tree
Hide file tree
Showing 4 changed files with 339 additions and 4 deletions.
106 changes: 103 additions & 3 deletions modfile/rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ type File struct {
Module *Module
Go *Go
Toolchain *Toolchain
Godebug []*Godebug
Require []*Require
Exclude []*Exclude
Replace []*Replace
Expand Down Expand Up @@ -65,6 +66,13 @@ type Toolchain struct {
Syntax *Line
}

// A Godebug is a single godebug key=value statement.
type Godebug struct {
Key string
Value string
Syntax *Line
}

// An Exclude is a single exclude statement.
type Exclude struct {
Mod module.Version
Expand Down Expand Up @@ -289,7 +297,7 @@ func parseToFile(file string, data []byte, fix VersionFixer, strict bool) (parse
})
}
continue
case "module", "require", "exclude", "replace", "retract":
case "module", "godebug", "require", "exclude", "replace", "retract":
for _, l := range x.Line {
f.add(&errs, x, l, x.Token[0], l.Token, fix, strict)
}
Expand All @@ -308,7 +316,9 @@ var laxGoVersionRE = lazyregexp.New(`^v?(([1-9][0-9]*)\.(0|[1-9][0-9]*))([^0-9].

// Toolchains must be named beginning with `go1`,
// like "go1.20.3" or "go1.20.3-gccgo". As a special case, "default" is also permitted.
// TODO(samthanawalla): Replace regex with https://pkg.go.dev/go/version#IsValid in 1.23+
// Note that this regexp is a much looser condition than go/version.IsValid,
// for forward compatibility.
// (This code has to be work to identify new toolchains even if we tweak the syntax in the future.)
var ToolchainRE = lazyregexp.New(`^default$|^go1($|\.)`)

func (f *File) add(errs *ErrorList, block *LineBlock, line *Line, verb string, args []string, fix VersionFixer, strict bool) {
Expand Down Expand Up @@ -384,7 +394,7 @@ func (f *File) add(errs *ErrorList, block *LineBlock, line *Line, verb string, a
if len(args) != 1 {
errorf("toolchain directive expects exactly one argument")
return
} else if strict && !ToolchainRE.MatchString(args[0]) {
} else if !ToolchainRE.MatchString(args[0]) {
errorf("invalid toolchain version '%s': must match format go1.23.0 or default", args[0])
return
}
Expand Down Expand Up @@ -412,6 +422,22 @@ func (f *File) add(errs *ErrorList, block *LineBlock, line *Line, verb string, a
}
f.Module.Mod = module.Version{Path: s}

case "godebug":
if len(args) != 1 || strings.ContainsAny(args[0], "\"`',") {
errorf("usage: godebug key=value")
return
}
key, value, ok := strings.Cut(args[0], "=")
if !ok {
errorf("usage: godebug key=value")
return
}
f.Godebug = append(f.Godebug, &Godebug{
Key: key,
Value: value,
Syntax: line,
})

case "require", "exclude":
if len(args) != 2 {
errorf("usage: %s module/path v1.2.3", verb)
Expand Down Expand Up @@ -654,6 +680,22 @@ func (f *WorkFile) add(errs *ErrorList, line *Line, verb string, args []string,
f.Toolchain = &Toolchain{Syntax: line}
f.Toolchain.Name = args[0]

case "godebug":
if len(args) != 1 || strings.ContainsAny(args[0], "\"`',") {
errorf("usage: godebug key=value")
return
}
key, value, ok := strings.Cut(args[0], "=")
if !ok {
errorf("usage: godebug key=value")
return
}
f.Godebug = append(f.Godebug, &Godebug{
Key: key,
Value: value,
Syntax: line,
})

case "use":
if len(args) != 1 {
errorf("usage: %s local/dir", verb)
Expand Down Expand Up @@ -929,6 +971,15 @@ func (f *File) Format() ([]byte, error) {
// Cleanup cleans out all the cleared entries.
func (f *File) Cleanup() {
w := 0
for _, g := range f.Godebug {
if g.Key != "" {
f.Godebug[w] = g
w++
}
}
f.Godebug = f.Godebug[:w]

w = 0
for _, r := range f.Require {
if r.Mod.Path != "" {
f.Require[w] = r
Expand Down Expand Up @@ -1027,6 +1078,45 @@ func (f *File) AddToolchainStmt(name string) error {
return nil
}

// AddGodebug sets the first godebug line for key to value,
// preserving any existing comments for that line and removing all
// other godebug lines for key.
//
// If no line currently exists for key, AddGodebug adds a new line
// at the end of the last godebug block.
func (f *File) AddGodebug(key, value string) error {
need := true
for _, g := range f.Godebug {
if g.Key == key {
if need {
g.Value = value
f.Syntax.updateLine(g.Syntax, "godebug", key+"="+value)
need = false
} else {
g.Syntax.markRemoved()
*g = Godebug{}
}
}
}

if need {
f.addNewGodebug(key, value)
}
return nil
}

// addNewGodebug adds a new godebug key=value line at the end
// of the last godebug block, regardless of any existing godebug lines for key.
func (f *File) addNewGodebug(key, value string) {
line := f.Syntax.addLine(nil, "godebug", key+"="+value)
g := &Godebug{
Key: key,
Value: value,
Syntax: line,
}
f.Godebug = append(f.Godebug, g)
}

// AddRequire sets the first require line for path to version vers,
// preserving any existing comments for that line and removing all
// other lines for path.
Expand Down Expand Up @@ -1334,6 +1424,16 @@ func (f *File) SetRequireSeparateIndirect(req []*Require) {
f.SortBlocks()
}

func (f *File) DropGodebug(key string) error {
for _, g := range f.Godebug {
if g.Key == key {
g.Syntax.markRemoved()
*g = Godebug{}
}
}
return nil
}

func (f *File) DropRequire(path string) error {
for _, r := range f.Require {
if r.Mod.Path == path {
Expand Down
157 changes: 157 additions & 0 deletions modfile/rule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1581,6 +1581,139 @@ var modifyEmptyFilesTests = []struct {
},
}

var addGodebugTests = []struct {
desc string
in string
key string
value string
out string
}{
{
`existing`,
`
module m
godebug key=old
`,
"key", "new",
`
module m
godebug key=new
`,
},
{
`existing2`,
`
module m
godebug (
key=first // first
other=first-a // first-a
)
godebug key=second // second
godebug (
key=third // third
other=third-a // third-a
)
`,
"key", "new",
`
module m
godebug (
key=new // first
other=first-a// first-a
)
godebug other=third-a // third-a
`,
},
{
`new`,
`
module m
godebug other=foo
`,
"key", "new",
`
module m
godebug (
other=foo
key=new
)
`,
},
{
`new2`,
`
module m
godebug first=1
godebug second=2
`,
"third", "3",
`
module m
godebug first=1
godebug (
second=2
third=3
)
`,
},
}

var dropGodebugTests = []struct {
desc string
in string
key string
out string
}{
{
`existing`,
`
module m
godebug key=old
`,
"key",
`
module m
`,
},
{
`existing2`,
`
module m
godebug (
key=first // first
other=first-a // first-a
)
godebug key=second // second
godebug (
key=third // third
other=third-a // third-a
)
`,
"key",
`
module m
godebug other=first-a// first-a
godebug other=third-a // third-a
`,
},
{
`new`,
`
module m
godebug other=foo
`,
"key",
`
module m
godebug other=foo
`,
},
}

func fixV(path, version string) (string, error) {
if path != "example.com/m" {
return "", fmt.Errorf("module path must be example.com/m")
Expand All @@ -1600,6 +1733,18 @@ func TestAddRequire(t *testing.T) {
}
}

func TestAddGodebug(t *testing.T) {
for _, tt := range addGodebugTests {
t.Run(tt.desc, func(t *testing.T) {
testEdit(t, tt.in, tt.out, true, func(f *File) error {
err := f.AddGodebug(tt.key, tt.value)
f.Cleanup()
return err
})
})
}
}

func TestSetRequire(t *testing.T) {
for _, tt := range setRequireTests {
t.Run(tt.desc, func(t *testing.T) {
Expand Down Expand Up @@ -1696,6 +1841,18 @@ func TestDropToolchain(t *testing.T) {
}
}

func TestDropGodebug(t *testing.T) {
for _, tt := range dropGodebugTests {
t.Run(tt.desc, func(t *testing.T) {
testEdit(t, tt.in, tt.out, true, func(f *File) error {
f.DropGodebug(tt.key)
f.Cleanup()
return nil
})
})
}
}

func TestAddExclude(t *testing.T) {
for _, tt := range addExcludeTests {
t.Run(tt.desc, func(t *testing.T) {
Expand Down
Loading

0 comments on commit c0bdc7b

Please sign in to comment.