1// Copyright 2019 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5//go:build explicit 6 7package bootstrap_test 8 9import ( 10 "bytes" 11 "errors" 12 "internal/testenv" 13 "os" 14 "os/exec" 15 "path/filepath" 16 "runtime" 17 "testing" 18) 19 20// TestExperimentToolID verifies that GOEXPERIMENT settings built 21// into the toolchain influence tool ids in the Go command. 22// This test requires bootstrapping the toolchain twice, so it's very expensive. 23// It must be run explicitly with -tags=explicit. 24// Verifies go.dev/issue/33091. 25func TestExperimentToolID(t *testing.T) { 26 if testing.Short() { 27 t.Skip("skipping test that rebuilds the entire toolchain twice") 28 } 29 switch runtime.GOOS { 30 case "android", "ios", "js", "wasip1": 31 t.Skipf("skipping because the toolchain does not have to bootstrap on GOOS=%s", runtime.GOOS) 32 } 33 34 realGoroot := testenv.GOROOT(t) 35 36 // Set up GOROOT. 37 goroot := t.TempDir() 38 gorootSrc := filepath.Join(goroot, "src") 39 if err := overlayDir(gorootSrc, filepath.Join(realGoroot, "src")); err != nil { 40 t.Fatal(err) 41 } 42 gorootLib := filepath.Join(goroot, "lib") 43 if err := overlayDir(gorootLib, filepath.Join(realGoroot, "lib")); err != nil { 44 t.Fatal(err) 45 } 46 if err := os.WriteFile(filepath.Join(goroot, "VERSION"), []byte("go1.999"), 0666); err != nil { 47 t.Fatal(err) 48 } 49 env := append(os.Environ(), "GOROOT=", "GOROOT_BOOTSTRAP="+realGoroot) 50 51 // Use a clean cache. 52 gocache := t.TempDir() 53 env = append(env, "GOCACHE="+gocache) 54 55 // Build the toolchain without GOEXPERIMENT. 56 var makeScript string 57 switch runtime.GOOS { 58 case "windows": 59 makeScript = "make.bat" 60 case "plan9": 61 makeScript = "make.rc" 62 default: 63 makeScript = "make.bash" 64 } 65 makeScriptPath := filepath.Join(realGoroot, "src", makeScript) 66 runCmd(t, gorootSrc, env, makeScriptPath) 67 68 // Verify compiler version string. 69 goCmdPath := filepath.Join(goroot, "bin", "go") 70 gotVersion := bytes.TrimSpace(runCmd(t, gorootSrc, env, goCmdPath, "tool", "compile", "-V=full")) 71 wantVersion := []byte(`compile version go1.999`) 72 if !bytes.Equal(gotVersion, wantVersion) { 73 t.Errorf("compile version without experiment is unexpected:\ngot %q\nwant %q", gotVersion, wantVersion) 74 } 75 76 // Build a package in a mode not handled by the make script. 77 runCmd(t, gorootSrc, env, goCmdPath, "build", "-race", "archive/tar") 78 79 // Rebuild the toolchain with GOEXPERIMENT. 80 env = append(env, "GOEXPERIMENT=fieldtrack") 81 runCmd(t, gorootSrc, env, makeScriptPath) 82 83 // Verify compiler version string. 84 gotVersion = bytes.TrimSpace(runCmd(t, gorootSrc, env, goCmdPath, "tool", "compile", "-V=full")) 85 wantVersion = []byte(`compile version go1.999 X:fieldtrack`) 86 if !bytes.Equal(gotVersion, wantVersion) { 87 t.Errorf("compile version with experiment is unexpected:\ngot %q\nwant %q", gotVersion, wantVersion) 88 } 89 90 // Build the same package. We should not get a cache conflict. 91 runCmd(t, gorootSrc, env, goCmdPath, "build", "-race", "archive/tar") 92} 93 94func runCmd(t *testing.T, dir string, env []string, path string, args ...string) []byte { 95 cmd := exec.Command(path, args...) 96 cmd.Dir = dir 97 cmd.Env = env 98 out, err := cmd.Output() 99 if err != nil { 100 if ee := (*exec.ExitError)(nil); errors.As(err, &ee) { 101 out = append(out, ee.Stderr...) 102 } 103 t.Fatalf("%s failed:\n%s\n%s", cmd, out, err) 104 } 105 return out 106} 107