diff --git a/go.env b/go.env index 9bab8ffd73d..6ff2b921d46 100644 --- a/go.env +++ b/go.env @@ -8,5 +8,5 @@ GOPROXY=https://proxy.golang.org,direct GOSUMDB=sum.golang.org # Automatically download newer toolchains as directed by go.mod files. -# See https://go.dev/s/gotoolchain for details. +# See https://go.dev/doc/toolchain for details. GOTOOLCHAIN=auto diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go index 7ef763d6be6..05ee094ea74 100644 --- a/src/cmd/go/alldocs.go +++ b/src/cmd/go/alldocs.go @@ -671,6 +671,14 @@ // // go get example.com/mod@none // +// To upgrade the minimum required Go version to the latest released Go version: +// +// go get go@latest +// +// To upgrade the Go toolchain to the latest patch release of the current Go toolchain: +// +// go get toolchain@patch +// // See https://golang.org/ref/mod#go-get for details. // // In earlier versions of Go, 'go get' was used to build and install packages. @@ -705,6 +713,9 @@ // // For more about modules, see https://golang.org/ref/mod. // +// For more about using 'go get' to update the minimum Go version and +// suggested Go toolchain, see https://go.dev/doc/toolchain. +// // For more about specifying packages, see 'go help packages'. // // This text describes the behavior of get using modules to manage source diff --git a/src/cmd/go/internal/gover/mod.go b/src/cmd/go/internal/gover/mod.go index 18e5635cdf7..d3cc17068de 100644 --- a/src/cmd/go/internal/gover/mod.go +++ b/src/cmd/go/internal/gover/mod.go @@ -84,6 +84,9 @@ func ModIsValid(path, vers string) bool { // The caller is assumed to have checked that ModIsValid(path, vers) is true. func ModIsPrefix(path, vers string) bool { if IsToolchain(path) { + if path == "toolchain" { + return IsLang(FromToolchain(vers)) + } return IsLang(vers) } // Semver @@ -110,3 +113,15 @@ func ModIsPrerelease(path, vers string) bool { } return semver.Prerelease(vers) != "" } + +// ModMajorMinor returns the "major.minor" truncation of the version v, +// for use as a prefix in "@patch" queries. +func ModMajorMinor(path, vers string) string { + if IsToolchain(path) { + if path == "toolchain" { + return "go" + Lang(FromToolchain(vers)) + } + return Lang(vers) + } + return semver.MajorMinor(vers) +} diff --git a/src/cmd/go/internal/gover/toolchain.go b/src/cmd/go/internal/gover/toolchain.go index bd0d52ad840..efa2de46a51 100644 --- a/src/cmd/go/internal/gover/toolchain.go +++ b/src/cmd/go/internal/gover/toolchain.go @@ -14,20 +14,17 @@ import ( // FromToolchain returns the Go version for the named toolchain, // derived from the name itself (not by running the toolchain). -// A toolchain is named "goVERSION" or "anything-goVERSION". +// A toolchain is named "goVERSION". // A suffix after the VERSION introduced by a +, -, space, or tab is removed. // Examples: // // FromToolchain("go1.2.3") == "1.2.3" // FromToolchain("go1.2.3-bigcorp") == "1.2.3" -// FromToolchain("gccgo-go1.23rc4") == "1.23rc4" // FromToolchain("invalid") == "" func FromToolchain(name string) string { var v string if strings.HasPrefix(name, "go") { v = name[2:] - } else if i := strings.Index(name, "-go"); i >= 0 { - v = name[i+3:] } else { return "" } diff --git a/src/cmd/go/internal/gover/toolchain_test.go b/src/cmd/go/internal/gover/toolchain_test.go index 7d05f1d0c39..d1c22fbc37c 100644 --- a/src/cmd/go/internal/gover/toolchain_test.go +++ b/src/cmd/go/internal/gover/toolchain_test.go @@ -14,6 +14,6 @@ var fromToolchainTests = []testCase1[string, string]{ {"go1.2.3+bigcorp", ""}, {"go1.2.3-bigcorp", "1.2.3"}, {"go1.2.3-bigcorp more text", "1.2.3"}, - {"gccgo-go1.23rc4", "1.23rc4"}, - {"gccgo-go1.23rc4-bigdwarf", "1.23rc4"}, + {"gccgo-go1.23rc4", ""}, + {"gccgo-go1.23rc4-bigdwarf", ""}, } diff --git a/src/cmd/go/internal/modfetch/sumdb.go b/src/cmd/go/internal/modfetch/sumdb.go index 492b03bd84a..6e60e7d9764 100644 --- a/src/cmd/go/internal/modfetch/sumdb.go +++ b/src/cmd/go/internal/modfetch/sumdb.go @@ -33,6 +33,14 @@ import ( // useSumDB reports whether to use the Go checksum database for the given module. func useSumDB(mod module.Version) bool { + if mod.Path == "golang.org/toolchain" { + // Downloaded toolchains cannot be listed in go.sum, + // so we require checksum database lookups even if + // GOSUMDB=off or GONOSUMDB matches the pattern. + // If GOSUMDB=off, then the eventual lookup will fail + // with a good error message. + return true + } return cfg.GOSUMDB != "off" && !module.MatchPrefixPatterns(cfg.GONOSUMDB, mod.Path) } @@ -70,6 +78,10 @@ func dbDial() (dbName string, db *sumdb.Client, err error) { gosumdb = "sum.golang.org https://sum.golang.google.cn" } + if gosumdb == "off" { + return "", nil, fmt.Errorf("checksum database disabled by GOSUMDB=off") + } + key := strings.Fields(gosumdb) if len(key) >= 1 { if k := knownGOSUMDB[key[0]]; k != "" { diff --git a/src/cmd/go/internal/modget/get.go b/src/cmd/go/internal/modget/get.go index 43708a3cab6..5d9eb70489e 100644 --- a/src/cmd/go/internal/modget/get.go +++ b/src/cmd/go/internal/modget/get.go @@ -72,6 +72,14 @@ To remove a dependency on a module and downgrade modules that require it: go get example.com/mod@none +To upgrade the minimum required Go version to the latest released Go version: + + go get go@latest + +To upgrade the Go toolchain to the latest patch release of the current Go toolchain: + + go get toolchain@patch + See https://golang.org/ref/mod#go-get for details. In earlier versions of Go, 'go get' was used to build and install packages. @@ -106,6 +114,9 @@ from a repository. For more about modules, see https://golang.org/ref/mod. +For more about using 'go get' to update the minimum Go version and +suggested Go toolchain, see https://go.dev/doc/toolchain. + For more about specifying packages, see 'go help packages'. This text describes the behavior of get using modules to manage source diff --git a/src/cmd/go/internal/modload/query.go b/src/cmd/go/internal/modload/query.go index 945b6e1642d..81c32d27a19 100644 --- a/src/cmd/go/internal/modload/query.go +++ b/src/cmd/go/internal/modload/query.go @@ -393,7 +393,7 @@ func newQueryMatcher(path string, query, current string, allowed AllowedFunc) (* qm.mayUseLatest = true } else { qm.mayUseLatest = module.IsPseudoVersion(current) - qm.prefix = semver.MajorMinor(current) + "." + qm.prefix = gover.ModMajorMinor(qm.path, current) + "." qm.filter = func(mv string) bool { return gover.ModCompare(qm.path, mv, current) >= 0 } } diff --git a/src/cmd/go/internal/toolchain/toolchain.go b/src/cmd/go/internal/toolchain/select.go similarity index 73% rename from src/cmd/go/internal/toolchain/toolchain.go rename to src/cmd/go/internal/toolchain/select.go index e6ff584480a..6aac8c1eaad 100644 --- a/src/cmd/go/internal/toolchain/toolchain.go +++ b/src/cmd/go/internal/toolchain/select.go @@ -81,11 +81,12 @@ func FilterEnv(env []string) []string { return out } -// Switch invokes a different Go toolchain if directed by +// Select invokes a different Go toolchain if directed by // the GOTOOLCHAIN environment variable or the user's configuration // or go.mod file. // It must be called early in startup. -func Switch() { +// See https://go.dev/doc/toolchain#select. +func Select() { log.SetPrefix("go: ") defer log.SetPrefix("") @@ -93,6 +94,21 @@ func Switch() { return } + // As a special case, let "go env GOTOOLCHAIN" and "go env -w GOTOOLCHAIN=..." + // be handled by the local toolchain, since an older toolchain may not understand it. + // This provides an easy way out of "go env -w GOTOOLCHAIN=go1.19" and makes + // sure that "go env GOTOOLCHAIN" always prints the local go command's interpretation of it. + // We look for these specific command lines in order to avoid mishandling + // + // GOTOOLCHAIN=go1.999 go env -newflag GOTOOLCHAIN + // + // where -newflag is a flag known to Go 1.999 but not known to us. + if (len(os.Args) == 3 && os.Args[1] == "env" && os.Args[2] == "GOTOOLCHAIN") || + (len(os.Args) == 4 && os.Args[1] == "env" && os.Args[2] == "-w" && strings.HasPrefix(os.Args[3], "GOTOOLCHAIN=")) { + return + } + + // Interpret GOTOOLCHAIN to select the Go toolchain to run. gotoolchain := cfg.Getenv("GOTOOLCHAIN") gover.Startup.GOTOOLCHAIN = gotoolchain if gotoolchain == "" { @@ -105,77 +121,70 @@ func Switch() { return } + // Note: minToolchain is what https://go.dev/doc/toolchain#select calls the default toolchain. minToolchain := gover.LocalToolchain() minVers := gover.Local() - if min, mode, ok := strings.Cut(gotoolchain, "+"); ok { // go1.2.3+auto - v := gover.FromToolchain(min) - if v == "" { - base.Fatalf("invalid GOTOOLCHAIN %q: invalid minimum toolchain %q", gotoolchain, min) + var mode string + if gotoolchain == "auto" { + mode = "auto" + } else if gotoolchain == "path" { + mode = "path" + } else { + min, suffix, plus := strings.Cut(gotoolchain, "+") // go1.2.3+auto + if min != "local" { + v := gover.FromToolchain(gotoolchain) + if v == "" { + if plus { + base.Fatalf("invalid GOTOOLCHAIN %q: invalid minimum toolchain %q", gotoolchain, min) + } + base.Fatalf("invalid GOTOOLCHAIN %q", gotoolchain) + } + minToolchain = min + minVers = v } - minToolchain = min - minVers = v - if mode != "auto" && mode != "path" { + if plus && suffix != "auto" && suffix != "path" { base.Fatalf("invalid GOTOOLCHAIN %q: only version suffixes are +auto and +path", gotoolchain) } - gotoolchain = mode + mode = suffix } - if gotoolchain == "auto" || gotoolchain == "path" { - gotoolchain = minToolchain - - // Locate and read go.mod or go.work. - // For go install m@v, it's the installed module's go.mod. - if m, goVers, ok := goInstallVersion(); ok { - if gover.Compare(goVers, minVers) > 0 { - // Always print, because otherwise there's no way for the user to know - // that a non-default toolchain version is being used here. - // (Normally you can run "go version", but go install m@v ignores the - // context that "go version" works in.) - var err error - gotoolchain, err = NewerToolchain(context.Background(), goVers) - if err != nil { - fmt.Fprintf(os.Stderr, "go: %v\n", err) - gotoolchain = "go" + goVers - } - fmt.Fprintf(os.Stderr, "go: using %s for %v\n", gotoolchain, m) - } + gotoolchain = minToolchain + if (mode == "auto" || mode == "path") && !goInstallVersion() { + // Read go.mod to find new minimum and suggested toolchain. + file, goVers, toolchain := modGoToolchain() + gover.Startup.AutoFile = file + if toolchain == "default" { + // "default" means always use the default toolchain, + // which is already set, so nothing to do here. + // Note that if we have Go 1.21 installed originally, + // GOTOOLCHAIN=go1.30.0+auto or GOTOOLCHAIN=go1.30.0, + // and the go.mod says "toolchain default", we use Go 1.30, not Go 1.21. + // That is, default overrides the "auto" part of the calculation + // but not the minimum that the user has set. + // Of course, if the go.mod also says "go 1.35", using Go 1.30 + // will provoke an error about the toolchain being too old. + // That's what people who use toolchain default want: + // only ever use the toolchain configured by the user + // (including its environment and go env -w file). + gover.Startup.AutoToolchain = toolchain } else { - file, goVers, toolchain := modGoToolchain() - gover.Startup.AutoFile = file - if toolchain == "local" { - // Local means always use the default local toolchain, - // which is already set, so nothing to do here. - // Note that if we have Go 1.21 installed originally, - // GOTOOLCHAIN=go1.30.0+auto or GOTOOLCHAIN=go1.30.0, - // and the go.mod says "toolchain local", we use Go 1.30, not Go 1.21. - // That is, local overrides the "auto" part of the calculation - // but not the minimum that the user has set. - // Of course, if the go.mod also says "go 1.35", using Go 1.30 - // will provoke an error about the toolchain being too old. - // That's what people who use toolchain local want: - // only ever use the toolchain configured in the local system - // (including its environment and go env -w file). - gover.Startup.AutoToolchain = toolchain - gotoolchain = "local" - } else { - if toolchain != "" { - // Accept toolchain only if it is >= our min. - toolVers := gover.FromToolchain(toolchain) - if toolVers == "" || (!strings.HasPrefix(toolchain, "go") && !strings.Contains(toolchain, "-go")) { - base.Fatalf("invalid toolchain %q in %s", toolchain, base.ShortPath(file)) - } - if gover.Compare(toolVers, minVers) >= 0 { - gotoolchain = toolchain - minVers = toolVers - gover.Startup.AutoToolchain = toolchain - } + if toolchain != "" { + // Accept toolchain only if it is >= our min. + toolVers := gover.FromToolchain(toolchain) + if toolVers == "" || (!strings.HasPrefix(toolchain, "go") && !strings.Contains(toolchain, "-go")) { + base.Fatalf("invalid toolchain %q in %s", toolchain, base.ShortPath(file)) } - if gover.Compare(goVers, minVers) > 0 { - gotoolchain = "go" + goVers - gover.Startup.AutoGoVersion = goVers - gover.Startup.AutoToolchain = "" // in case we are overriding it for being too old + if gover.Compare(toolVers, minVers) >= 0 { + gotoolchain = toolchain + minVers = toolVers + gover.Startup.AutoToolchain = toolchain } } + if gover.Compare(goVers, minVers) > 0 { + gotoolchain = "go" + goVers + gover.Startup.AutoGoVersion = goVers + gover.Startup.AutoToolchain = "" // in case we are overriding it for being too old + } } } @@ -219,11 +228,12 @@ func Switch() { base.Fatalf("invalid GOTOOLCHAIN %q", gotoolchain) } - SwitchTo(gotoolchain) + Exec(gotoolchain) } // NewerToolchain returns the name of the toolchain to use when we need -// to reinvoke a newer toolchain that must support at least the given Go version. +// to switch to a newer toolchain that must support at least the given Go version. +// See https://go.dev/doc/toolchain#switch. // // If the latest major release is 1.N.0, we use the latest patch release of 1.(N-1) if that's >= version. // Otherwise we use the latest 1.N if that's allowed. @@ -345,14 +355,14 @@ func HasPath() bool { // // "switch" - simulate version switches by reinvoking the test go binary with a different TESTGO_VERSION. // "mismatch" - like "switch" but forget to set TESTGO_VERSION, so it looks like we invoked a mismatched toolchain -// "loop" - like "switch" but +// "loop" - like "mismatch" but forget the target check, causing a toolchain switching loop var TestVersionSwitch string -// SwitchTo invokes the specified Go toolchain or else prints an error and exits the process. -// If $GOTOOLCHAIN is set to path or min+path, SwitchTo only considers the PATH -// as a source of Go toolchains. Otherwise SwitchTo tries the PATH but then downloads +// Exec invokes the specified Go toolchain or else prints an error and exits the process. +// If $GOTOOLCHAIN is set to path or min+path, Exec only considers the PATH +// as a source of Go toolchains. Otherwise Exec tries the PATH but then downloads // a toolchain if necessary. -func SwitchTo(gotoolchain string) { +func Exec(gotoolchain string) { log.SetPrefix("go: ") count, _ := strconv.Atoi(os.Getenv(countEnv)) @@ -496,28 +506,32 @@ func modGoToolchain() (file, goVers, toolchain string) { return file, gover.GoModLookup(data, "go"), gover.GoModLookup(data, "toolchain") } -// goInstallVersion looks at the command line to see if it is go install m@v or go run m@v. -// If so, it returns the m@v and the go version from that module's go.mod. -func goInstallVersion() (m module.Version, goVers string, found bool) { +// goInstallVersion reports whether the command line is go install m@v or go run m@v. +// If so, Select must not read the go.mod or go.work file in "auto" or "path" mode. +func goInstallVersion() bool { // Note: We assume there are no flags between 'go' and 'install' or 'run'. // During testing there are some debugging flags that are accepted // in that position, but in production go binaries there are not. if len(os.Args) < 3 || (os.Args[1] != "install" && os.Args[1] != "run") { - return module.Version{}, "", false + return false } + // Check for pkg@version. var arg string switch os.Args[1] { + default: + return false case "install": - // Cannot parse 'go install' command line precisely, because there - // may be new flags we don't know about. Instead, assume the final - // argument is a pkg@version we can use. + // We would like to let 'go install -newflag pkg@version' work even + // across a toolchain switch. To make that work, assume the pkg@version + // is the last argument and skip the flag parsing. arg = os.Args[len(os.Args)-1] case "run": - // For run, the pkg@version can be anywhere on the command line. - // We don't know the flags, so we can't strictly speaking do this correctly. - // We do the best we can by interrogating the CmdRun flags and assume - // that any unknown flag does not take an argument. + // For run, the pkg@version can be anywhere on the command line, + // because it is preceded by run flags and followed by arguments to the + // program being run. To handle that precisely, we have to interpret the + // flags a little bit, to know whether each flag takes an optional argument. + // We can still allow unknown flags as long as they have an explicit =value. args := os.Args[2:] for i := 0; i < len(args); i++ { a := args[i] @@ -526,19 +540,21 @@ func goInstallVersion() (m module.Version, goVers string, found bool) { break } if a == "-" { - break + // non-flag but also non-pkg@version + return false } if a == "--" { - if i+1 < len(args) { - arg = args[i+1] + if i+1 >= len(args) { + return false } + arg = args[i+1] break } a = strings.TrimPrefix(a, "-") a = strings.TrimPrefix(a, "-") if strings.HasPrefix(a, "-") { - // non-flag but also non-m@v - break + // non-flag but also non-pkg@version + return false } if strings.Contains(a, "=") { // already has value @@ -546,8 +562,8 @@ func goInstallVersion() (m module.Version, goVers string, found bool) { } f := run.CmdRun.Flag.Lookup(a) if f == nil { - // Unknown flag. Assume it doesn't take a value: best we can do. - continue + // Unknown flag. Give up. The command is going to fail in flag parsing. + return false } if bf, ok := f.Value.(interface{ IsBoolFlag() bool }); ok && bf.IsBoolFlag() { // Does not take value. @@ -557,13 +573,24 @@ func goInstallVersion() (m module.Version, goVers string, found bool) { } } if !strings.Contains(arg, "@") || build.IsLocalImport(arg) || filepath.IsAbs(arg) { - return module.Version{}, "", false + return false } - m.Path, m.Version, _ = strings.Cut(arg, "@") - if m.Path == "" || m.Version == "" || gover.IsToolchain(m.Path) { - return module.Version{}, "", false + path, version, _ := strings.Cut(arg, "@") + if path == "" || version == "" || gover.IsToolchain(path) { + return false } + // It would be correct to simply return true here, bypassing use + // of the current go.mod or go.work, and let "go run" or "go install" + // do the rest, including a toolchain switch. + // Our goal instead is, since we have gone to the trouble of handling + // unknown flags to some degree, to run the switch now, so that + // these commands can switch to a newer toolchain directed by the + // go.mod which may actually understand the flag. + // This was brought up during the go.dev/issue/57001 proposal discussion + // and may end up being common in self-contained "go install" or "go run" + // command lines if we add new flags in the future. + // Set up modules without an explicit go.mod, to download go.mod. modload.ForceUseModules = true modload.RootMode = modload.NoRoot @@ -573,21 +600,17 @@ func goInstallVersion() (m module.Version, goVers string, found bool) { // See internal/load.PackagesAndErrorsOutsideModule ctx := context.Background() allowed := modload.CheckAllowed - if modload.IsRevisionQuery(m.Path, m.Version) { + if modload.IsRevisionQuery(path, version) { // Don't check for retractions if a specific revision is requested. allowed = nil } noneSelected := func(path string) (version string) { return "none" } - _, err := modload.QueryPackages(ctx, m.Path, m.Version, noneSelected, allowed) - if tooNew := (*gover.TooNewError)(nil); errors.As(err, &tooNew) { - m.Path, m.Version, _ = strings.Cut(tooNew.What, "@") - return m, tooNew.GoVersion, true + _, err := modload.QueryPackages(ctx, path, version, noneSelected, allowed) + if errors.Is(err, gover.ErrTooNew) { + // Run early switch, same one go install or go run would eventually do, + // if it understood all the command-line flags. + SwitchOrFatal(ctx, err) } - // QueryPackages succeeded, or it failed for a reason other than - // this Go toolchain being too old for the modules encountered. - // Either way, we identified the m@v on the command line, - // so return found == true so the caller does not fall back to - // consulting go.mod. - return m, "", true + return true // pkg@version found } diff --git a/src/cmd/go/internal/toolchain/reqs.go b/src/cmd/go/internal/toolchain/switch.go similarity index 98% rename from src/cmd/go/internal/toolchain/reqs.go rename to src/cmd/go/internal/toolchain/switch.go index e5ca8d0eb4c..b35198748d4 100644 --- a/src/cmd/go/internal/toolchain/reqs.go +++ b/src/cmd/go/internal/toolchain/switch.go @@ -22,6 +22,8 @@ import ( // *gover.TooNewErrors (potentially wrapped) and switching is // permitted by GOTOOLCHAIN, Switch switches to a new toolchain. // Otherwise Switch prints all the errors using base.Error. +// +// See https://go.dev/doc/toolchain#switch. type Switcher struct { TooNew *gover.TooNewError // max go requirement observed Errors []error // errors collected so far @@ -91,7 +93,7 @@ func (s *Switcher) Switch(ctx context.Context) { } fmt.Fprintf(os.Stderr, "go: %v requires go >= %v; switching to %v\n", s.TooNew.What, s.TooNew.GoVersion, tv) - SwitchTo(tv) + Exec(tv) panic("unreachable") } diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go index af13f01240c..d050792998a 100644 --- a/src/cmd/go/main.go +++ b/src/cmd/go/main.go @@ -92,7 +92,7 @@ var _ = go11tag func main() { log.SetFlags(0) - toolchain.Switch() + toolchain.Select() flag.Usage = base.Usage flag.Parse() diff --git a/src/cmd/go/testdata/script/gotoolchain_local.txt b/src/cmd/go/testdata/script/gotoolchain_local.txt index 18b4faabde7..313c5415015 100644 --- a/src/cmd/go/testdata/script/gotoolchain_local.txt +++ b/src/cmd/go/testdata/script/gotoolchain_local.txt @@ -70,15 +70,15 @@ go mod edit -go=1.700 -toolchain=go1.300 go version stdout go1.700 # toolchain too old, ignored -go mod edit -go=1.300 -toolchain=local +go mod edit -go=1.300 -toolchain=default go version stdout go1.500 -go mod edit -go=1.700 -toolchain=local +go mod edit -go=1.700 -toolchain=default go version stdout go1.500 # toolchain local is like GOTOOLCHAIN=local and wins ! go build -stderr '^go: go.mod requires go >= 1.700 \(running go 1.500; go.mod sets toolchain local\)' +stderr '^go: go.mod requires go >= 1.700 \(running go 1.500; go.mod sets toolchain default\)' # GOTOOLCHAIN=path does the same. env GOTOOLCHAIN=path @@ -94,25 +94,33 @@ go mod edit -go=1.700 -toolchain=go1.300 go version stdout go1.700 # toolchain too old, ignored -go mod edit -go=1.300 -toolchain=local +go mod edit -go=1.300 -toolchain=default go version stdout go1.500 -go mod edit -go=1.700 -toolchain=local +go mod edit -go=1.700 -toolchain=default go version -stdout go1.500 # toolchain applies even if older than go line +stdout go1.500 # toolchain default applies even if older than go line ! go build -stderr '^go: go.mod requires go >= 1.700 \(running go 1.500; GOTOOLCHAIN=path; go.mod sets toolchain local\)' +stderr '^go: go.mod requires go >= 1.700 \(running go 1.500; GOTOOLCHAIN=path; go.mod sets toolchain default\)' -# GOTOOLCHAIN names can have prefix- or -suffix +# GOTOOLCHAIN=min+auto with toolchain default uses min, not local + +env GOTOOLCHAIN=go1.400+auto +go mod edit -go=1.300 -toolchain=default +go version +stdout 1.400 # not 1.500 local toolchain + +env GOTOOLCHAIN=go1.600+auto +go mod edit -go=1.300 -toolchain=default +go version +stdout 1.600 # not 1.500 local toolchain + +# GOTOOLCHAIN names can have -suffix env GOTOOLCHAIN=go1.800-bigcorp go version stdout go1.800-bigcorp -env GOTOOLCHAIN=bigcorp-go1.100 -go version -stdout bigcorp-go1.100 - env GOTOOLCHAIN=auto go mod edit -go=1.999 -toolchain=go1.800-bigcorp go version @@ -122,14 +130,6 @@ go mod edit -go=1.777 -toolchain=go1.800-bigcorp go version stdout go1.800-bigcorp -go mod edit -go=1.999 -toolchain=bigcorp-go1.800 -go version -stdout go1.999 - -go mod edit -go=1.777 -toolchain=bigcorp-go1.800 -go version -stdout bigcorp-go1.800 - # go.work takes priority over go.mod go mod edit -go=1.700 -toolchain=go1.999-wrong go work init @@ -137,13 +137,13 @@ go work edit -go=1.400 -toolchain=go1.600-right go version stdout go1.600-right -go work edit -go=1.400 -toolchain=local +go work edit -go=1.400 -toolchain=default go version stdout go1.500 # go.work misconfiguration does not break go work edit # ('go 1.600 / toolchain local' forces use of 1.500 which can't normally load that go.work; allow work edit to fix it.) -go work edit -go=1.600 -toolchain=local +go work edit -go=1.600 -toolchain=default go version stdout go1.500 @@ -154,7 +154,7 @@ stdout go1.600 rm go.work # go.mod misconfiguration does not break go mod edit -go mod edit -go=1.600 -toolchain=local +go mod edit -go=1.600 -toolchain=default go version stdout go1.500 @@ -177,19 +177,6 @@ go mod edit -go=1.501 -toolchain=none go version stdout go1.501 -env TESTGO_VERSION=bigcorp-go1.500 -go mod edit -go=1.499 -toolchain=none -go version -stdout bigcorp-go1.500 - -go mod edit -go=1.500 -toolchain=none -go version -stdout bigcorp-go1.500 - -go mod edit -go=1.501 -toolchain=none -go version -stdout go1.501 - env TESTGO_VERSION='go1.500 (bigcorp)' go mod edit -go=1.499 -toolchain=none go version @@ -208,60 +195,55 @@ env TESTGO_VERSION=go1.2.3 go mod edit -go=1.999 -toolchain=go1.998 ! go install rsc.io/fortune/nonexist@v0.0.1 -stderr '^go: using go1.22.9 for rsc.io/fortune@v0.0.1' +stderr '^go: rsc.io/fortune@v0.0.1 requires go >= 1.21rc999; switching to go1.22.9$' stderr '^go: rsc.io/fortune/nonexist@v0.0.1: module rsc.io/fortune@v0.0.1 found, but does not contain package rsc.io/fortune/nonexist' ! go run rsc.io/fortune/nonexist@v0.0.1 -stderr '^go: using go1.22.9 for rsc.io/fortune@v0.0.1' +stderr '^go: rsc.io/fortune@v0.0.1 requires go >= 1.21rc999; switching to go1.22.9$' stderr '^go: rsc.io/fortune/nonexist@v0.0.1: module rsc.io/fortune@v0.0.1 found, but does not contain package rsc.io/fortune/nonexist' # go install should handle unknown flags to find m@v ! go install -unknownflag rsc.io/fortune/nonexist@v0.0.1 -stderr '^go: using go1.22.9 for rsc.io/fortune@v0.0.1' +stderr '^go: rsc.io/fortune@v0.0.1 requires go >= 1.21rc999; switching to go1.22.9$' stderr '^flag provided but not defined: -unknownflag' ! go install -unknownflag arg rsc.io/fortune/nonexist@v0.0.1 -stderr '^go: using go1.22.9 for rsc.io/fortune@v0.0.1' +stderr '^go: rsc.io/fortune@v0.0.1 requires go >= 1.21rc999; switching to go1.22.9$' stderr '^flag provided but not defined: -unknownflag' -# go run should handle unknown boolean flags and flags with =arg +# go run cannot handle unknown boolean flags ! go run -unknownflag rsc.io/fortune/nonexist@v0.0.1 -stderr '^go: using go1.22.9 for rsc.io/fortune@v0.0.1' +! stderr switching stderr '^flag provided but not defined: -unknownflag' +! go run -unknownflag oops rsc.io/fortune/nonexist@v0.0.1 +! stderr switching +stderr '^flag provided but not defined: -unknownflag' + +# go run can handle unknown flag with argument. ! go run -unknown=flag rsc.io/fortune/nonexist@v0.0.1 -stderr '^go: using go1.22.9 for rsc.io/fortune@v0.0.1' +stderr '^go: rsc.io/fortune@v0.0.1 requires go >= 1.21rc999; switching to go1.22.9$' stderr '^flag provided but not defined: -unknown' -# go run assumes unknown flags don't take arguments -! go run -unknownflag rsc.io/fortune/nonexist@v0.0.1 -stderr '^go: using go1.22.9 for rsc.io/fortune@v0.0.1' -stderr '^flag provided but not defined: -unknownflag' - -! go run -unknownflag oops rsc.io/fortune/nonexist@v0.0.1 # lost parse, cannot find m@v -! stderr go1.22.9 -! stderr '^go: using' -stderr '^flag provided but not defined: -unknownflag' - # go install m@v should handle queries ! go install rsc.io/fortune/nonexist@v0.0 -stderr '^go: using go1.22.9 for rsc.io/fortune@v0.0.1' +stderr '^go: rsc.io/fortune@v0.0.1 requires go >= 1.21rc999; switching to go1.22.9$' stderr '^go: rsc.io/fortune/nonexist@v0.0: module rsc.io/fortune@v0.0 found \(v0.0.1\), but does not contain package rsc.io/fortune/nonexist' # go run m@v should handle queries ! go install rsc.io/fortune/nonexist@v0 -stderr '^go: using go1.22.9 for rsc.io/fortune@v0.0.1' +stderr '^go: rsc.io/fortune@v0.0.1 requires go >= 1.21rc999; switching to go1.22.9$' stderr '^go: rsc.io/fortune/nonexist@v0: module rsc.io/fortune@v0 found \(v0.0.1\), but does not contain package rsc.io/fortune/nonexist' # go install m@v should use local toolchain if not upgrading ! go install rsc.io/fortune/nonexist@v1 ! stderr go1.22.9 -! stderr '^go: using' +! stderr switching stderr '^go: downloading rsc.io/fortune v1.0.0$' stderr '^go: rsc.io/fortune/nonexist@v1: module rsc.io/fortune@v1 found \(v1.0.0\), but does not contain package rsc.io/fortune/nonexist' # go run m@v should use local toolchain if not upgrading ! go run rsc.io/fortune/nonexist@v1 ! stderr go1.22.9 -! stderr '^go: using' +! stderr switching stderr '^go: rsc.io/fortune/nonexist@v1: module rsc.io/fortune@v1 found \(v1.0.0\), but does not contain package rsc.io/fortune/nonexist' diff --git a/src/cmd/go/testdata/script/gotoolchain_net.txt b/src/cmd/go/testdata/script/gotoolchain_net.txt index d04229d2937..72bb2b72526 100644 --- a/src/cmd/go/testdata/script/gotoolchain_net.txt +++ b/src/cmd/go/testdata/script/gotoolchain_net.txt @@ -1,8 +1,6 @@ # This test only checks that basic network lookups work. # The full test of toolchain version selection is in gotoolchain.txt. -[short] skip - env TESTGO_VERSION=go1.21actual # GOTOOLCHAIN from network, does not exist @@ -16,7 +14,34 @@ env GOTOOLCHAIN=go1.999testmod go version stderr 'go: downloading go1.999testmod \(.*/.*\)' +# GOTOOLCHAIN cached from network +go version +! stderr downloading +stdout go1.999testmod + +# GOTOOLCHAIN with GOSUMDB enabled but at a bad URL should operate in cache and not try badurl +env oldsumdb=$GOSUMDB +env GOSUMDB=$oldsumdb' http://badurl' +go version +! stderr downloading +stdout go1.999testmod + +# GOTOOLCHAIN with GOSUMB=off should fail, because it cannot access even the cached sumdb info +# without the sumdb name. +env GOSUMDB=off +! go version +stderr '^go: golang.org/toolchain@v0.0.1-go1.999testmod.[a-z0-9\-]*: verifying module: checksum database disabled by GOSUMDB=off$' + +# GOTOOLCHAIN with GOSUMDB enabled but at a bad URL should fail if cache is incomplete +env GOSUMDB=$oldsumdb' http://badurl' +rm $GOPATH/pkg/mod/cache/download/sumdb +! go version +! stderr downloading +stderr 'panic: use of network' # test catches network access +env GOSUMDB=$oldsumdb + # Test a real GOTOOLCHAIN +[short] skip [!net:golang.org] skip [!GOOS:darwin] [!GOOS:windows] [!GOOS:linux] skip [!GOARCH:amd64] [!GOARCH:arm64] skip diff --git a/src/cmd/go/testdata/script/mod_edit_toolchain.txt b/src/cmd/go/testdata/script/mod_edit_toolchain.txt index 525e4dd54a7..bb544be344d 100644 --- a/src/cmd/go/testdata/script/mod_edit_toolchain.txt +++ b/src/cmd/go/testdata/script/mod_edit_toolchain.txt @@ -7,8 +7,8 @@ env GO111MODULE=on go mod edit -toolchain=go1.9 grep 'toolchain go1.9' go.mod -go mod edit -toolchain=local -grep 'toolchain local' go.mod +go mod edit -toolchain=default +grep 'toolchain default' go.mod go mod edit -toolchain=none ! grep toolchain go.mod diff --git a/src/cmd/go/testdata/script/mod_get_toolchain.txt b/src/cmd/go/testdata/script/mod_get_toolchain.txt new file mode 100644 index 00000000000..143ad32a4e1 --- /dev/null +++ b/src/cmd/go/testdata/script/mod_get_toolchain.txt @@ -0,0 +1,102 @@ +# setup +env TESTGO_VERSION=go1.99.0 +env TESTGO_VERSION_SWITCH=switch + +# go get go should use the latest Go 1.23 +cp go.mod.orig go.mod +go get go +stderr '^go: upgraded go 1.21 => 1.23.9$' +grep 'go 1.23.9' go.mod +grep 'toolchain go1.99.0' go.mod + +# go get go@1.23 should use the latest Go 1.23 +cp go.mod.orig go.mod +go get go@1.23 +stderr '^go: upgraded go 1.21 => 1.23.9$' +grep 'go 1.23.9' go.mod +grep 'toolchain go1.99.0' go.mod + +# go get go@1.22 should use the latest Go 1.22 +cp go.mod.orig go.mod +go get go@1.22 +stderr '^go: upgraded go 1.21 => 1.22.9$' +grep 'go 1.22.9' go.mod +grep 'toolchain go1.99.0' go.mod + +# go get go@patch should use the latest patch release +go get go@1.22.1 +go get go@patch +stderr '^go: upgraded go 1.22.1 => 1.22.9$' +grep 'go 1.22.9' go.mod +grep 'toolchain go1.99.0' go.mod + +# go get go@1.24 does NOT find the release candidate +cp go.mod.orig go.mod +! go get go@1.24 +stderr '^go: go@1.24: no matching versions for query "1.24"$' + +# go get go@1.24rc1 works +cp go.mod.orig go.mod +go get go@1.24rc1 +stderr '^go: upgraded go 1.21 => 1.24rc1$' +grep 'go 1.24rc1' go.mod +grep 'toolchain go1.99.0' go.mod + +# go get go@latest finds the latest Go 1.23 +cp go.mod.orig go.mod +go get go@latest +stderr '^go: upgraded go 1.21 => 1.23.9$' +grep 'go 1.23.9' go.mod +grep 'toolchain go1.99.0' go.mod + +# Again, with toolchains. + +# go get toolchain should find go1.999testmod. +go get toolchain +stderr '^go: upgraded toolchain go1.99.0 => go1.999testmod$' +grep 'go 1.23.9' go.mod +grep 'toolchain go1.999testmod' go.mod + +# go get toolchain@go1.23 should use the latest Go 1.23 +go get toolchain@go1.23 +stderr '^go: removed toolchain go1.999testmod$' +grep 'go 1.23.9' go.mod +! grep 'toolchain go1.23.9' go.mod # implied + +# go get toolchain@go1.22 should use the latest Go 1.22 and downgrade go. +go get toolchain@go1.22 +stderr '^go: downgraded go 1.23.9 => 1.22.9$' +grep 'go 1.22.9' go.mod +! grep 'toolchain go1.22.9' go.mod # implied + +# go get toolchain@patch should use the latest patch release +go get toolchain@go1.22.1 +go get toolchain@patch +stderr '^go: added toolchain go1.22.9$' +grep 'go 1.22.1' go.mod +grep 'toolchain go1.22.9' go.mod +go get go@1.22.9 toolchain@none +grep 'go 1.22.9' go.mod +! grep 'toolchain go1.22.9' go.mod + +# go get toolchain@go1.24 does NOT find the release candidate +! go get toolchain@go1.24 +stderr '^go: toolchain@go1.24: no matching versions for query "go1.24"$' + +# go get toolchain@go1.24rc1 works +go get toolchain@go1.24rc1 +stderr '^go: added toolchain go1.24rc1$' +grep 'go 1.22.9' go.mod # no longer implied +grep 'toolchain go1.24rc1' go.mod + +# go get toolchain@latest finds go1.999testmod. +cp go.mod.orig go.mod +go get toolchain@latest +stderr '^go: added toolchain go1.999testmod$' +grep 'go 1.21' go.mod +grep 'toolchain go1.999testmod' go.mod + +-- go.mod.orig -- +module m + +go 1.21 diff --git a/src/cmd/go/testdata/script/work_edit_toolchain.txt b/src/cmd/go/testdata/script/work_edit_toolchain.txt index a171296707b..b4e260d2387 100644 --- a/src/cmd/go/testdata/script/work_edit_toolchain.txt +++ b/src/cmd/go/testdata/script/work_edit_toolchain.txt @@ -7,8 +7,8 @@ env GO111MODULE=on go work edit -toolchain=go1.9 grep 'toolchain go1.9' go.work -go work edit -toolchain=local -grep 'toolchain local' go.work +go work edit -toolchain=default +grep 'toolchain default' go.work go work edit -toolchain=none ! grep toolchain go.work