Skip to content

Commit af3b81d

Browse files
Bryan C. Millsbradfitz
Bryan C. Mills
authored andcommitted
[release-branch.go1.19] cmd/go: avoid registering AtExit handlers in tests
Ever since 'go build' was added (in CL 5483069), it has used an atexit handler to clean up working directories. CL 154109 introduced 'cc' command to the script test framework that called Init on a builder once per invocation. Unfortunately, since base.AtExit is unsynchronized, the Init added there caused any script that invokes that command to be unsafe for concurrent use. This change fixes the race by having the 'cc' command pass in its working directory instead of allowing the Builder to allocate one. Following modern Go best practices, it also replaces the in-place Init method (which is prone to typestate and aliasing bugs) with a NewBuilder constructor function. Updates golang#54423. Fixes golang#54637. Change-Id: I8fc2127a7d877bb39a1174e398736bb51d03d4d2 Reviewed-on: https://go-review.googlesource.com/c/go/+/425205 TryBot-Result: Gopher Robot <gobot@golang.org> Run-TryBot: Bryan Mills <bcmills@google.com> Auto-Submit: Bryan Mills <bcmills@google.com> Reviewed-by: Than McIntosh <thanm@google.com> (cherry picked from commit d5aa088) Reviewed-on: https://go-review.googlesource.com/c/go/+/425207
1 parent 505e77f commit af3b81d

File tree

8 files changed

+33
-21
lines changed

8 files changed

+33
-21
lines changed

Diff for: src/cmd/go/internal/envcmd/env.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,8 @@ func ExtraEnvVars() []cfg.EnvVar {
174174
// ExtraEnvVarsCostly returns environment variables that should not leak into child processes
175175
// but are costly to evaluate.
176176
func ExtraEnvVarsCostly() []cfg.EnvVar {
177-
var b work.Builder
178-
b.Init()
177+
b := work.NewBuilder("")
178+
179179
cppflags, cflags, cxxflags, fflags, ldflags, err := b.CFlags(&load.Package{})
180180
if err != nil {
181181
// Should not happen - b.CFlags was given an empty package.

Diff for: src/cmd/go/internal/list/list.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -689,8 +689,7 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
689689
// Do we need to run a build to gather information?
690690
needStale := (listJson && listJsonFields.needAny("Stale", "StaleReason")) || strings.Contains(*listFmt, ".Stale")
691691
if needStale || *listExport || *listCompiled {
692-
var b work.Builder
693-
b.Init()
692+
b := work.NewBuilder("")
694693
b.IsCmdList = true
695694
b.NeedExport = *listExport
696695
b.NeedCompiledGoFiles = *listCompiled

Diff for: src/cmd/go/internal/run/run.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,7 @@ func runRun(ctx context.Context, cmd *base.Command, args []string) {
9191
}
9292

9393
work.BuildInit()
94-
var b work.Builder
95-
b.Init()
94+
b := work.NewBuilder("")
9695
b.Print = printStderr
9796

9897
i := 0

Diff for: src/cmd/go/internal/test/test.go

+12-4
Original file line numberDiff line numberDiff line change
@@ -744,8 +744,7 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) {
744744
}
745745
}
746746

747-
var b work.Builder
748-
b.Init()
747+
b := work.NewBuilder("")
749748

750749
if cfg.BuildI {
751750
fmt.Fprint(os.Stderr, "go: -i flag is deprecated\n")
@@ -800,7 +799,16 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) {
800799
if !testC || a.Failed {
801800
return
802801
}
803-
b.Init()
802+
803+
// TODO(bcmills): I have no idea why the Builder must be reset here, but
804+
// without this reset dance, TestGoTestDashIDashOWritesBinary fails with
805+
// lots of "vet config not found" errors. This was added in CL 5699088,
806+
// which had almost no public discussion, a very short commit description,
807+
// and left no comment in the code to explain what is going on here. 🤯
808+
//
809+
// Maybe this has the effect of removing actions that were registered by the
810+
// call to CompileAction above?
811+
b = work.NewBuilder("")
804812
}
805813

806814
var builds, runs, prints []*work.Action
@@ -916,7 +924,7 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) {
916924
ensureImport(p, "sync/atomic")
917925
}
918926

919-
buildTest, runTest, printTest, err := builderTest(&b, ctx, pkgOpts, p, allImports[p])
927+
buildTest, runTest, printTest, err := builderTest(b, ctx, pkgOpts, p, allImports[p])
920928
if err != nil {
921929
str := err.Error()
922930
str = strings.TrimPrefix(str, "\n")

Diff for: src/cmd/go/internal/vet/vet.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,7 @@ func runVet(ctx context.Context, cmd *base.Command, args []string) {
9494
base.Fatalf("no packages to vet")
9595
}
9696

97-
var b work.Builder
98-
b.Init()
97+
b := work.NewBuilder("")
9998

10099
root := &work.Action{Mode: "go vet"}
101100
for _, p := range pkgs {

Diff for: src/cmd/go/internal/work/action.go

+12-2
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,13 @@ const (
240240
ModeVetOnly = 1 << 8
241241
)
242242

243-
func (b *Builder) Init() {
243+
// NewBuilder returns a new Builder ready for use.
244+
//
245+
// If workDir is the empty string, NewBuilder creates a WorkDir if needed
246+
// and arranges for it to be removed in case of an unclean exit.
247+
func NewBuilder(workDir string) *Builder {
248+
b := new(Builder)
249+
244250
b.Print = func(a ...any) (int, error) {
245251
return fmt.Fprint(os.Stderr, a...)
246252
}
@@ -249,7 +255,9 @@ func (b *Builder) Init() {
249255
b.toolIDCache = make(map[string]string)
250256
b.buildIDCache = make(map[string]string)
251257

252-
if cfg.BuildN {
258+
if workDir != "" {
259+
b.WorkDir = workDir
260+
} else if cfg.BuildN {
253261
b.WorkDir = "$WORK"
254262
} else {
255263
tmp, err := os.MkdirTemp(cfg.Getenv("GOTMPDIR"), "go-build")
@@ -306,6 +314,8 @@ func (b *Builder) Init() {
306314
base.Exit()
307315
}
308316
}
317+
318+
return b
309319
}
310320

311321
func CheckGOOSARCHPair(goos, goarch string) error {

Diff for: src/cmd/go/internal/work/build.go

+3-4
Original file line numberDiff line numberDiff line change
@@ -403,8 +403,7 @@ var RuntimeVersion = runtime.Version()
403403
func runBuild(ctx context.Context, cmd *base.Command, args []string) {
404404
modload.InitWorkfile()
405405
BuildInit()
406-
var b Builder
407-
b.Init()
406+
b := NewBuilder("")
408407

409408
pkgs := load.PackagesAndErrors(ctx, load.PackageOpts{LoadVCS: true}, args)
410409
load.CheckPackageErrors(pkgs)
@@ -728,8 +727,8 @@ func InstallPackages(ctx context.Context, patterns []string, pkgs []*load.Packag
728727
}
729728
base.ExitIfErrors()
730729

731-
var b Builder
732-
b.Init()
730+
b := NewBuilder("")
731+
733732
depMode := ModeBuild
734733
if cfg.BuildI {
735734
depMode = ModeInstall

Diff for: src/cmd/go/script_test.go

+1-3
Original file line numberDiff line numberDiff line change
@@ -556,10 +556,8 @@ func (ts *testScript) cmdCc(want simpleStatus, args []string) {
556556
ts.fatalf("usage: cc args... [&]")
557557
}
558558

559-
var b work.Builder
560-
b.Init()
559+
b := work.NewBuilder(ts.workdir)
561560
ts.cmdExec(want, append(b.GccCmd(".", ""), args...))
562-
robustio.RemoveAll(b.WorkDir)
563561
}
564562

565563
// cd changes to a different directory.

0 commit comments

Comments
 (0)