summaryrefslogtreecommitdiffstats
path: root/misc/reboot
diff options
context:
space:
mode:
Diffstat (limited to 'misc/reboot')
-rw-r--r--misc/reboot/experiment_toolid_test.go101
-rw-r--r--misc/reboot/overlaydir_test.go85
-rw-r--r--misc/reboot/reboot_test.go70
3 files changed, 256 insertions, 0 deletions
diff --git a/misc/reboot/experiment_toolid_test.go b/misc/reboot/experiment_toolid_test.go
new file mode 100644
index 0000000..87a828e
--- /dev/null
+++ b/misc/reboot/experiment_toolid_test.go
@@ -0,0 +1,101 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build explicit
+// +build explicit
+
+// Package experiment_toolid_test verifies that GOEXPERIMENT settings built
+// into the toolchain influence tool ids in the Go command.
+// This test requires bootstrapping the toolchain twice, so it's very expensive.
+// It must be run explicitly with -tags=explicit.
+// Verifies golang.org/issue/33091.
+package reboot_test
+
+import (
+ "bytes"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "testing"
+)
+
+func TestExperimentToolID(t *testing.T) {
+ // Set up GOROOT
+ goroot, err := os.MkdirTemp("", "experiment-goroot")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(goroot)
+
+ gorootSrc := filepath.Join(goroot, "src")
+ if err := overlayDir(gorootSrc, filepath.Join(runtime.GOROOT(), "src")); err != nil {
+ t.Fatal(err)
+ }
+
+ if err := os.WriteFile(filepath.Join(goroot, "VERSION"), []byte("go1.999"), 0666); err != nil {
+ t.Fatal(err)
+ }
+ env := append(os.Environ(), "GOROOT=", "GOROOT_BOOTSTRAP="+runtime.GOROOT())
+
+ // Use a clean cache.
+ gocache, err := os.MkdirTemp("", "experiment-gocache")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(gocache)
+ env = append(env, "GOCACHE="+gocache)
+
+ // Build the toolchain without GOEXPERIMENT.
+ var makeScript string
+ switch runtime.GOOS {
+ case "windows":
+ makeScript = "make.bat"
+ case "plan9":
+ makeScript = "make.rc"
+ default:
+ makeScript = "make.bash"
+ }
+ makeScriptPath := filepath.Join(runtime.GOROOT(), "src", makeScript)
+ runCmd(t, gorootSrc, env, makeScriptPath)
+
+ // Verify compiler version string.
+ goCmdPath := filepath.Join(goroot, "bin", "go")
+ if runtime.GOOS == "windows" {
+ goCmdPath += ".exe"
+ }
+ gotVersion := bytes.TrimSpace(runCmd(t, gorootSrc, env, goCmdPath, "tool", "compile", "-V=full"))
+ wantVersion := []byte(`compile version go1.999`)
+ if !bytes.Equal(gotVersion, wantVersion) {
+ t.Errorf("compile version without experiment: got %q, want %q", gotVersion, wantVersion)
+ }
+
+ // Build a package in a mode not handled by the make script.
+ runCmd(t, gorootSrc, env, goCmdPath, "build", "-race", "archive/tar")
+
+ // Rebuild the toolchain with GOEXPERIMENT.
+ env = append(env, "GOEXPERIMENT=fieldtrack")
+ runCmd(t, gorootSrc, env, makeScriptPath)
+
+ // Verify compiler version string.
+ gotVersion = bytes.TrimSpace(runCmd(t, gorootSrc, env, goCmdPath, "tool", "compile", "-V=full"))
+ wantVersion = []byte(`compile version go1.999 X:fieldtrack,framepointer`)
+ if !bytes.Equal(gotVersion, wantVersion) {
+ t.Errorf("compile version with experiment: got %q, want %q", gotVersion, wantVersion)
+ }
+
+ // Build the same package. We should not get a cache conflict.
+ runCmd(t, gorootSrc, env, goCmdPath, "build", "-race", "archive/tar")
+}
+
+func runCmd(t *testing.T, dir string, env []string, path string, args ...string) []byte {
+ cmd := exec.Command(path, args...)
+ cmd.Dir = dir
+ cmd.Env = env
+ out, err := cmd.Output()
+ if err != nil {
+ t.Fatal(err)
+ }
+ return out
+}
diff --git a/misc/reboot/overlaydir_test.go b/misc/reboot/overlaydir_test.go
new file mode 100644
index 0000000..71faf09
--- /dev/null
+++ b/misc/reboot/overlaydir_test.go
@@ -0,0 +1,85 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package reboot_test
+
+import (
+ "io"
+ "io/fs"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+// overlayDir makes a minimal-overhead copy of srcRoot in which new files may be added.
+//
+// TODO: Once we no longer need to support the misc module in GOPATH mode,
+// factor this function out into a package to reduce duplication.
+func overlayDir(dstRoot, srcRoot string) error {
+ dstRoot = filepath.Clean(dstRoot)
+ if err := os.MkdirAll(dstRoot, 0777); err != nil {
+ return err
+ }
+
+ srcRoot, err := filepath.Abs(srcRoot)
+ if err != nil {
+ return err
+ }
+
+ return filepath.WalkDir(srcRoot, func(srcPath string, entry fs.DirEntry, err error) error {
+ if err != nil || srcPath == srcRoot {
+ return err
+ }
+ if filepath.Base(srcPath) == "testdata" {
+ // We're just building, so no need to copy those.
+ return fs.SkipDir
+ }
+
+ suffix := strings.TrimPrefix(srcPath, srcRoot)
+ for len(suffix) > 0 && suffix[0] == filepath.Separator {
+ suffix = suffix[1:]
+ }
+ dstPath := filepath.Join(dstRoot, suffix)
+
+ info, err := entry.Info()
+ perm := info.Mode() & os.ModePerm
+ if info.Mode()&os.ModeSymlink != 0 {
+ info, err = os.Stat(srcPath)
+ if err != nil {
+ return err
+ }
+ perm = info.Mode() & os.ModePerm
+ }
+
+ // Always make copies of directories.
+ // If we add a file in the overlay, we don't want to add it in the original.
+ if info.IsDir() {
+ return os.MkdirAll(dstPath, perm|0200)
+ }
+
+ // If we can use a hard link, do that instead of copying bytes.
+ // Go builds don't like symlinks in some cases, such as go:embed.
+ if err := os.Link(srcPath, dstPath); err == nil {
+ return nil
+ }
+
+ // Otherwise, copy the bytes.
+ src, err := os.Open(srcPath)
+ if err != nil {
+ return err
+ }
+ defer src.Close()
+
+ dst, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm)
+ if err != nil {
+ return err
+ }
+
+ _, err = io.Copy(dst, src)
+ if closeErr := dst.Close(); err == nil {
+ err = closeErr
+ }
+ return err
+ })
+}
diff --git a/misc/reboot/reboot_test.go b/misc/reboot/reboot_test.go
new file mode 100644
index 0000000..a1b513b
--- /dev/null
+++ b/misc/reboot/reboot_test.go
@@ -0,0 +1,70 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package reboot_test verifies that the current GOROOT can be used to bootstrap
+// itself.
+package reboot_test
+
+import (
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "testing"
+ "time"
+)
+
+func TestRepeatBootstrap(t *testing.T) {
+ if testing.Short() {
+ t.Skipf("skipping test that rebuilds the entire toolchain")
+ }
+
+ realGoroot, err := filepath.Abs(filepath.Join("..", ".."))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // To ensure that bootstrapping doesn't unexpectedly depend
+ // on the Go repo's git metadata, add a fake (unreadable) git
+ // directory above the simulated GOROOT.
+ // This mimics the configuration one much have when
+ // building from distro-packaged source code
+ // (see https://go.dev/issue/54852).
+ parent := t.TempDir()
+ dotGit := filepath.Join(parent, ".git")
+ if err := os.Mkdir(dotGit, 000); err != nil {
+ t.Fatal(err)
+ }
+ goroot := filepath.Join(parent, "goroot")
+
+ gorootSrc := filepath.Join(goroot, "src")
+ overlayStart := time.Now()
+ if err := overlayDir(gorootSrc, filepath.Join(realGoroot, "src")); err != nil {
+ t.Fatal(err)
+ }
+ t.Logf("GOROOT/src overlay set up in %s", time.Since(overlayStart))
+
+ if err := os.WriteFile(filepath.Join(goroot, "VERSION"), []byte(runtime.Version()), 0666); err != nil {
+ t.Fatal(err)
+ }
+
+ var makeScript string
+ switch runtime.GOOS {
+ case "windows":
+ makeScript = "make.bat"
+ case "plan9":
+ makeScript = "make.rc"
+ default:
+ makeScript = "make.bash"
+ }
+
+ cmd := exec.Command(filepath.Join(goroot, "src", makeScript))
+ cmd.Dir = gorootSrc
+ cmd.Env = append(cmd.Environ(), "GOROOT=", "GOROOT_BOOTSTRAP="+realGoroot)
+ cmd.Stderr = os.Stderr
+ cmd.Stdout = os.Stdout
+ if err := cmd.Run(); err != nil {
+ t.Fatal(err)
+ }
+}