"path/filepath"
"regexp"
"sort"
+ "strconv"
"strings"
"sync"
"time"
os.Unsetenv("GOFLAGS")
os.Setenv("GOWORK", "off")
+ // Create the go.mod for building toolchain2 and toolchain3. Toolchain1 and go_bootstrap are built with
+ // a separate go.mod (with a lower required go version to allow all allowed bootstrap toolchain versions)
+ // in bootstrapBuildTools.
+ modVer := goModVersion()
workdir = xworkdir()
- if err := os.WriteFile(pathf("%s/go.mod", workdir), []byte("module bootstrap"), 0666); err != nil {
+ if err := os.WriteFile(pathf("%s/go.mod", workdir), []byte("module bootstrap\n\ngo "+modVer+"\n"), 0666); err != nil {
fatalf("cannot write stub go.mod: %s", err)
}
xatexit(rmworkdir)
return version
}
+// goModVersion returns the go version declared in src/go.mod. This is the
+// go version to use in the go.mod building go_bootstrap, toolchain2, and toolchain3.
+// (toolchain1 must be built with requiredBootstrapVersion(goModVersion))
+func goModVersion() string {
+ goMod := readfile(pathf("%s/src/go.mod", goroot))
+ m := regexp.MustCompile(`(?m)^go (1.\d+)$`).FindStringSubmatch(goMod)
+ if m == nil {
+ fatalf("std go.mod does not contain go 1.X")
+ }
+ return m[1]
+}
+
+func requiredBootstrapVersion(v string) string {
+ minorstr, ok := strings.CutPrefix(v, "1.")
+ if !ok {
+ fatalf("go version %q in go.mod does not start with %q", v, "1.")
+ }
+ minor, err := strconv.Atoi(minorstr)
+ if err != nil {
+ fatalf("invalid go version minor component %q: %v", minorstr, err)
+ }
+ // Per go.dev/doc/install/source, for N >= 22, Go version 1.N will require a Go 1.M compiler,
+ // where M is N-2 rounded down to an even number. Example: Go 1.24 and 1.25 require Go 1.22.
+ requiredMinor := minor - 2 - minor%2
+ return "1." + strconv.Itoa(requiredMinor)
+}
+
// isGitRepo reports whether the working directory is inside a Git repository.
func isGitRepo() bool {
// NB: simply checking the exit code of `git rev-parse --git-dir` would
}
}
}
+
+func TestRequiredBootstrapVersion(t *testing.T) {
+ testCases := map[string]string{
+ "1.22": "1.20",
+ "1.23": "1.20",
+ "1.24": "1.22",
+ "1.25": "1.22",
+ "1.26": "1.24",
+ "1.27": "1.24",
+ }
+
+ for v, want := range testCases {
+ if got := requiredBootstrapVersion(v); got != want {
+ t.Errorf("requiredBootstrapVersion(%v): got %v, want %v", v, got, want)
+ }
+ }
+}
xmkdirall(base)
// Copy source code into $GOROOT/pkg/bootstrap and rewrite import paths.
- writefile("module bootstrap\ngo 1.20\n", pathf("%s/%s", base, "go.mod"), 0)
+ minBootstrapVers := requiredBootstrapVersion(goModVersion()) // require the minimum required go version to build this go version in the go.mod file
+ writefile("module bootstrap\ngo "+minBootstrapVers+"\n", pathf("%s/%s", base, "go.mod"), 0)
for _, dir := range bootstrapDirs {
recurse := strings.HasSuffix(dir, "/...")
dir = strings.TrimSuffix(dir, "/...")