Help us shape the future of Go! Take the 2026 Go Developer Survey

Source file src/cmd/internal/bootstrap_test/reboot_test.go

     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  // Package bootstrap_test verifies that the current GOROOT can be used to bootstrap
     6  // itself.
     7  package bootstrap_test
     8  
     9  import (
    10  	"fmt"
    11  	"internal/goexperiment"
    12  	"internal/platform"
    13  	"internal/testenv"
    14  	"io"
    15  	"os"
    16  	"os/exec"
    17  	"path/filepath"
    18  	"runtime"
    19  	"strings"
    20  	"testing"
    21  	"time"
    22  )
    23  
    24  func TestRepeatBootstrap(t *testing.T) {
    25  	if testing.Short() {
    26  		t.Skip("skipping test that rebuilds the entire toolchain")
    27  	}
    28  	switch runtime.GOOS {
    29  	case "android", "ios", "js", "wasip1":
    30  		t.Skipf("skipping because the toolchain does not have to bootstrap on GOOS=%s", runtime.GOOS)
    31  	}
    32  
    33  	realGoroot := testenv.GOROOT(t)
    34  
    35  	// To ensure that bootstrapping doesn't unexpectedly depend
    36  	// on the Go repo's git metadata, add a fake (unreadable) git
    37  	// directory above the simulated GOROOT.
    38  	// This mimics the configuration one much have when
    39  	// building from distro-packaged source code
    40  	// (see https://go.dev/issue/54852).
    41  	parent := t.TempDir()
    42  	dotGit := filepath.Join(parent, ".git")
    43  	if err := os.Mkdir(dotGit, 000); err != nil {
    44  		t.Fatal(err)
    45  	}
    46  
    47  	overlayStart := time.Now()
    48  
    49  	goroot := filepath.Join(parent, "goroot")
    50  
    51  	gorootSrc := filepath.Join(goroot, "src")
    52  	if err := overlayDir(gorootSrc, filepath.Join(realGoroot, "src")); err != nil {
    53  		t.Fatal(err)
    54  	}
    55  
    56  	gorootLib := filepath.Join(goroot, "lib")
    57  	if err := overlayDir(gorootLib, filepath.Join(realGoroot, "lib")); err != nil {
    58  		t.Fatal(err)
    59  	}
    60  
    61  	t.Logf("GOROOT overlay set up in %s", time.Since(overlayStart))
    62  
    63  	if err := os.WriteFile(filepath.Join(goroot, "VERSION"), []byte(runtime.Version()), 0666); err != nil {
    64  		t.Fatal(err)
    65  	}
    66  
    67  	var makeScript string
    68  	switch runtime.GOOS {
    69  	case "windows":
    70  		makeScript = "make.bat"
    71  	case "plan9":
    72  		makeScript = "make.rc"
    73  	default:
    74  		makeScript = "make.bash"
    75  	}
    76  
    77  	var stdout strings.Builder
    78  	cmd := exec.Command(filepath.Join(goroot, "src", makeScript))
    79  	cmd.Dir = gorootSrc
    80  	cmd.Env = append(cmd.Environ(), "GOROOT=", "GOROOT_BOOTSTRAP="+realGoroot, "GOFIPS140=off")
    81  	cmd.Stderr = os.Stderr
    82  	cmd.Stdout = io.MultiWriter(os.Stdout, &stdout)
    83  	if err := cmd.Run(); err != nil {
    84  		t.Fatal(err)
    85  	}
    86  
    87  	t.Run("GOFIPS140=latest", func(t *testing.T) {
    88  		if !platform.FIPS140Supported(runtime.GOOS, runtime.GOARCH) {
    89  			t.Skipf("GOFIPS140 is not supported on %s/%s", runtime.GOOS, runtime.GOARCH)
    90  		}
    91  		if goexperiment.BoringCrypto {
    92  			t.Skip("GOEXPERIMENT=boringcrypto is not compatible with GOFIPS140")
    93  		}
    94  
    95  		cmd := exec.Command(filepath.Join(goroot, "src", makeScript))
    96  		cmd.Dir = gorootSrc
    97  		cmd.Env = append(cmd.Environ(), "GOROOT=", "GOROOT_BOOTSTRAP="+realGoroot, "GOFIPS140=latest")
    98  		cmd.Stderr = os.Stderr
    99  		cmd.Stdout = os.Stdout
   100  		if err := cmd.Run(); err != nil {
   101  			t.Fatal(err)
   102  		}
   103  
   104  		cmd = exec.Command(filepath.Join(goroot, "bin", "go"), "env", "GOFIPS140")
   105  		cmd.Env = append(cmd.Environ(), "GOROOT=", "GOFIPS140=")
   106  		out, err := cmd.CombinedOutput()
   107  		if err != nil {
   108  			t.Fatalf("%s failed: %v\n%s", cmd, err, out)
   109  		}
   110  		if got := strings.TrimSpace(string(out)); got != "latest" {
   111  			t.Errorf("default GOFIPS140 = %q, want latest", got)
   112  		}
   113  
   114  		mainGo := filepath.Join(t.TempDir(), "main.go")
   115  		const program = `package main
   116  
   117  import "crypto/fips140"
   118  
   119  func main() {
   120  	if !fips140.Enabled() {
   121  		panic("FIPS 140-3 mode is not enabled")
   122  	}
   123  }
   124  `
   125  		if err := os.WriteFile(mainGo, []byte(program), 0666); err != nil {
   126  			t.Fatal(err)
   127  		}
   128  		cmd = exec.Command(filepath.Join(goroot, "bin", "go"), "run", mainGo)
   129  		cmd.Env = append(cmd.Environ(), "GOROOT=", "GOFIPS140=")
   130  		if out, err := cmd.CombinedOutput(); err != nil {
   131  			t.Fatalf("%s failed: %v\n%s", cmd, err, out)
   132  		}
   133  	})
   134  
   135  	// Test that go.dev/issue/42563 hasn't regressed.
   136  	t.Run("PATH reminder", func(t *testing.T) {
   137  		var want string
   138  		switch gorootBin := filepath.Join(goroot, "bin"); runtime.GOOS {
   139  		default:
   140  			want = fmt.Sprintf("*** You need to add %s to your PATH.", gorootBin)
   141  		case "plan9":
   142  			want = fmt.Sprintf("*** You need to bind %s before /bin.", gorootBin)
   143  		}
   144  		if got := stdout.String(); !strings.Contains(got, want) {
   145  			t.Errorf("reminder %q is missing from %s stdout:\n%s", want, makeScript, got)
   146  		}
   147  	})
   148  }
   149  

View as plain text