Skip to content

Commit d22086e

Browse files
author
Bryan C. Mills
committed
cmd/go/internal/work: disallow testgo binary from installing to GOROOT
Installing to GOROOT makes tests non-parallelizable, since each test depends on the installed contents of GOROOT already being up-to-date and may reasonably assume that those contents do not change over the course of the test. Fixes #37573 Updates #30316 Change-Id: I2afe95ad11347bee3bb7c2d77a657db6d691cf05 Reviewed-on: https://go-review.googlesource.com/c/go/+/225897 Reviewed-by: Michael Matloob <[email protected]>
1 parent f9197a7 commit d22086e

File tree

6 files changed

+79
-10
lines changed

6 files changed

+79
-10
lines changed

src/cmd/go/go_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ func TestMain(m *testing.M) {
199199
return strings.TrimSpace(string(out))
200200
}
201201
testGOROOT = goEnv("GOROOT")
202+
os.Setenv("TESTGO_GOROOT", testGOROOT)
202203

203204
// The whole GOROOT/pkg tree was installed using the GOHOSTOS/GOHOSTARCH
204205
// toolchain (installed in GOROOT/pkg/tool/GOHOSTOS_GOHOSTARCH).

src/cmd/go/internal/work/exec.go

+42-7
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,6 @@ package work
88

99
import (
1010
"bytes"
11-
"cmd/go/internal/base"
12-
"cmd/go/internal/cache"
13-
"cmd/go/internal/cfg"
14-
"cmd/go/internal/load"
15-
"cmd/go/internal/str"
1611
"encoding/json"
1712
"errors"
1813
"fmt"
@@ -30,6 +25,12 @@ import (
3025
"strings"
3126
"sync"
3227
"time"
28+
29+
"cmd/go/internal/base"
30+
"cmd/go/internal/cache"
31+
"cmd/go/internal/cfg"
32+
"cmd/go/internal/load"
33+
"cmd/go/internal/str"
3334
)
3435

3536
// actionList returns the list of actions in the dag rooted at root
@@ -490,6 +491,10 @@ func (b *Builder) build(a *Action) (err error) {
490491
return nil
491492
}
492493

494+
if err := allowInstall(a); err != nil {
495+
return err
496+
}
497+
493498
// make target directory
494499
dir, _ := filepath.Split(a.Target)
495500
if dir != "" {
@@ -1192,6 +1197,10 @@ func (b *Builder) link(a *Action) (err error) {
11921197
return err
11931198
}
11941199

1200+
if err := allowInstall(a); err != nil {
1201+
return err
1202+
}
1203+
11951204
// make target directory
11961205
dir, _ := filepath.Split(a.Target)
11971206
if dir != "" {
@@ -1366,6 +1375,10 @@ func (b *Builder) getPkgConfigFlags(p *load.Package) (cflags, ldflags []string,
13661375
}
13671376

13681377
func (b *Builder) installShlibname(a *Action) error {
1378+
if err := allowInstall(a); err != nil {
1379+
return err
1380+
}
1381+
13691382
// TODO: BuildN
13701383
a1 := a.Deps[0]
13711384
err := ioutil.WriteFile(a.Target, []byte(filepath.Base(a1.Target)+"\n"), 0666)
@@ -1416,6 +1429,10 @@ func (b *Builder) linkShared(a *Action) (err error) {
14161429
}
14171430
defer b.flushOutput(a)
14181431

1432+
if err := allowInstall(a); err != nil {
1433+
return err
1434+
}
1435+
14191436
if err := b.Mkdir(a.Objdir); err != nil {
14201437
return err
14211438
}
@@ -1481,8 +1498,12 @@ func BuildInstallFunc(b *Builder, a *Action) (err error) {
14811498
// advertise it by touching the mtimes (usually the libraries are up
14821499
// to date).
14831500
if !a.buggyInstall && !b.IsCmdList {
1484-
now := time.Now()
1485-
os.Chtimes(a.Target, now, now)
1501+
if cfg.BuildN {
1502+
b.Showcmd("", "touch %s", a.Target)
1503+
} else if err := allowInstall(a); err == nil {
1504+
now := time.Now()
1505+
os.Chtimes(a.Target, now, now)
1506+
}
14861507
}
14871508
return nil
14881509
}
@@ -1493,6 +1514,9 @@ func BuildInstallFunc(b *Builder, a *Action) (err error) {
14931514
a.built = a1.built
14941515
return nil
14951516
}
1517+
if err := allowInstall(a); err != nil {
1518+
return err
1519+
}
14961520

14971521
if err := b.Mkdir(a.Objdir); err != nil {
14981522
return err
@@ -1522,6 +1546,13 @@ func BuildInstallFunc(b *Builder, a *Action) (err error) {
15221546
return b.moveOrCopyFile(a.Target, a1.built, perm, false)
15231547
}
15241548

1549+
// allowInstall returns a non-nil error if this invocation of the go command is
1550+
// allowed to install a.Target.
1551+
//
1552+
// (The build of cmd/go running under its own test is forbidden from installing
1553+
// to its original GOROOT.)
1554+
var allowInstall = func(*Action) error { return nil }
1555+
15251556
// cleanup removes a's object dir to keep the amount of
15261557
// on-disk garbage down in a large build. On an operating system
15271558
// with aggressive buffering, cleaning incrementally like
@@ -1685,6 +1716,10 @@ func (b *Builder) installHeader(a *Action) error {
16851716
return nil
16861717
}
16871718

1719+
if err := allowInstall(a); err != nil {
1720+
return err
1721+
}
1722+
16881723
dir, _ := filepath.Split(a.Target)
16891724
if dir != "" {
16901725
if err := b.Mkdir(dir); err != nil {

src/cmd/go/internal/work/testgo.go

+32-1
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,41 @@
88

99
package work
1010

11-
import "os"
11+
import (
12+
"cmd/go/internal/cfg"
13+
"cmd/go/internal/search"
14+
"fmt"
15+
"os"
16+
"path/filepath"
17+
"runtime"
18+
)
1219

1320
func init() {
1421
if v := os.Getenv("TESTGO_VERSION"); v != "" {
1522
runtimeVersion = v
1623
}
24+
25+
if testGOROOT := os.Getenv("TESTGO_GOROOT"); testGOROOT != "" {
26+
// Disallow installs to the GOROOT from which testgo was built.
27+
// Installs to other GOROOTs — such as one set explicitly within a test — are ok.
28+
allowInstall = func(a *Action) error {
29+
if cfg.BuildN {
30+
return nil
31+
}
32+
33+
rel := search.InDir(a.Target, testGOROOT)
34+
if rel == "" {
35+
return nil
36+
}
37+
38+
callerPos := ""
39+
if _, file, line, ok := runtime.Caller(1); ok {
40+
if shortFile := search.InDir(file, filepath.Join(testGOROOT, "src")); shortFile != "" {
41+
file = shortFile
42+
}
43+
callerPos = fmt.Sprintf("%s:%d: ", file, line)
44+
}
45+
return fmt.Errorf("%stestgo must not write to GOROOT (installing to %s)", callerPos, filepath.Join("GOROOT", rel))
46+
}
47+
}
1748
}

src/cmd/go/script_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ func (ts *testScript) setup() {
126126
"GOPROXY=" + proxyURL,
127127
"GOPRIVATE=",
128128
"GOROOT=" + testGOROOT,
129+
"TESTGO_GOROOT=" + testGOROOT,
129130
"GOSUMDB=" + testSumDBVerifierKey,
130131
"GONOPROXY=",
131132
"GONOSUMDB=",

src/cmd/go/testdata/script/README

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ Scripts also have access to these other environment variables:
3434
GOPATH=$WORK/gopath
3535
GOPROXY=<local module proxy serving from cmd/go/testdata/mod>
3636
GOROOT=<actual GOROOT>
37+
TESTGO_GOROOT=<GOROOT used to build cmd/go, for use in tests that may change GOROOT>
3738
HOME=/no-home
3839
PATH=<actual PATH>
3940
TMPDIR=$WORK/tmp

src/cmd/go/testdata/script/get_update_all.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33

44
[!net] skip
55

6-
go get -u .../
7-
! stderr 'duplicate loads of' # make sure old packages are removed from cache
6+
go get -u -n .../
7+
! stderr 'duplicate loads of' # make sure old packages are removed from cache

0 commit comments

Comments
 (0)