Skip to content

Commit 7a575a5

Browse files
Bryan C. Millsheschi
Bryan C. Mills
authored andcommitted
[release-branch.go1.18] 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 #54423. Fixes #54636. Change-Id: I8fc2127a7d877bb39a1174e398736bb51d03d4d2 Reviewed-on: https://go-review.googlesource.com/c/go/+/425205 TryBot-Result: Gopher Robot <[email protected]> Run-TryBot: Bryan Mills <[email protected]> Auto-Submit: Bryan Mills <[email protected]> Reviewed-by: Than McIntosh <[email protected]> (cherry picked from commit d5aa088) Reviewed-on: https://go-review.googlesource.com/c/go/+/425208
1 parent 66197f0 commit 7a575a5

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
@@ -167,8 +167,8 @@ func ExtraEnvVars() []cfg.EnvVar {
167167
// ExtraEnvVarsCostly returns environment variables that should not leak into child processes
168168
// but are costly to evaluate.
169169
func ExtraEnvVarsCostly() []cfg.EnvVar {
170-
var b work.Builder
171-
b.Init()
170+
b := work.NewBuilder("")
171+
172172
cppflags, cflags, cxxflags, fflags, ldflags, err := b.CFlags(&load.Package{})
173173
if err != nil {
174174
// 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
@@ -592,8 +592,7 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
592592
// Do we need to run a build to gather information?
593593
needStale := *listJson || strings.Contains(*listFmt, ".Stale")
594594
if needStale || *listExport || *listCompiled {
595-
var b work.Builder
596-
b.Init()
595+
b := work.NewBuilder("")
597596
b.IsCmdList = true
598597
b.NeedExport = *listExport
599598
b.NeedCompiledGoFiles = *listCompiled

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

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

8989
work.BuildInit()
90-
var b work.Builder
91-
b.Init()
90+
b := work.NewBuilder("")
9291
b.Print = printStderr
9392

9493
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
@@ -402,8 +402,7 @@ var runtimeVersion = runtime.Version()
402402
func runBuild(ctx context.Context, cmd *base.Command, args []string) {
403403
modload.InitWorkfile()
404404
BuildInit()
405-
var b Builder
406-
b.Init()
405+
b := NewBuilder("")
407406

408407
pkgs := load.PackagesAndErrors(ctx, load.PackageOpts{LoadVCS: true}, args)
409408
load.CheckPackageErrors(pkgs)
@@ -721,8 +720,8 @@ func InstallPackages(ctx context.Context, patterns []string, pkgs []*load.Packag
721720
}
722721
base.ExitIfErrors()
723722

724-
var b Builder
725-
b.Init()
723+
b := NewBuilder("")
724+
726725
depMode := ModeBuild
727726
if cfg.BuildI {
728727
depMode = ModeInstall

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

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

535-
var b work.Builder
536-
b.Init()
535+
b := work.NewBuilder(ts.workdir)
537536
ts.cmdExec(want, append(b.GccCmd(".", ""), args...))
538-
robustio.RemoveAll(b.WorkDir)
539537
}
540538

541539
// cd changes to a different directory.

0 commit comments

Comments
 (0)