mirror of
https://github.com/golang/go
synced 2024-11-24 22:00:09 -07:00
cmd/go: additional doc-inspired tests and bug fixes
Additional tests and bug fixes realized while writing go.dev/doc/gotoolchain (CL 500775). - Handle go get toolchain@go1.22 (resolve to latest patch release, same as go get go@1.22). (See modload/query.go and gover/mod.go.) - Handle go get go@patch toolchain@patch. (See modload/query.go and gover/mod.go.) - Remove prefix-goVERSION-suffix form for toolchain name, standardizing on goVERSION-suffix. I have no good explanation for having two forms, so simplify to one. (See vendor and gover.) - Fail toolchain downloads when GOSUMDB=off. Because toolchain downloads cannot always be predicted (especially during switching rather than selection), they cannot be listed in go.sum. We rely on the checksum database for integrity of the download, especially if proxied. If the checksum database is disabled, this integrity check won't happen, so fail toolchain downloads. (See modfetch/sumdb.go and script/gotoolchain_net.txt) - Use names from documentation in package toolchain (Select, Switch; SwitchTo renamed to Exec to avoid both names; reqs.go renamed to switch.go; toolchain.go renamed to select.go.) - Make "go env GOTOOLCHAIN" and "go env -w GOTOOLCHAIN" work even when GOTOOLCHAIN is misconfigured. (See special case at top of Select in select.go.) - Clarify what goInstallVersion does (report whether this is go install or go run pkg@version) and explain the potential version switch more clearly. Use the Switcher directly instead of reimplementing it. (See select.go.) - Document go@ and toolchain@ forms in go help get, linking to go.dev/doc/toolchain. (See modget/get.go.) - Update URL of documentation in $GOROOT/go.env. For #57001. Change-Id: I895ef3519ff95db8710ed23b36ebaf4f648120cb Reviewed-on: https://go-review.googlesource.com/c/go/+/500797 Reviewed-by: Michael Matloob <matloob@golang.org> Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Bypass: Russ Cox <rsc@golang.org>
This commit is contained in:
parent
bf016520e2
commit
35268a9960
2
go.env
2
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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 ""
|
||||
}
|
||||
|
@ -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", ""},
|
||||
}
|
||||
|
@ -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 != "" {
|
||||
|
@ -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
|
||||
|
@ -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 }
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
@ -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")
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ var _ = go11tag
|
||||
|
||||
func main() {
|
||||
log.SetFlags(0)
|
||||
toolchain.Switch()
|
||||
toolchain.Select()
|
||||
|
||||
flag.Usage = base.Usage
|
||||
flag.Parse()
|
||||
|
96
src/cmd/go/testdata/script/gotoolchain_local.txt
vendored
96
src/cmd/go/testdata/script/gotoolchain_local.txt
vendored
@ -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'
|
||||
|
29
src/cmd/go/testdata/script/gotoolchain_net.txt
vendored
29
src/cmd/go/testdata/script/gotoolchain_net.txt
vendored
@ -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
|
||||
|
@ -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
|
||||
|
102
src/cmd/go/testdata/script/mod_get_toolchain.txt
vendored
Normal file
102
src/cmd/go/testdata/script/mod_get_toolchain.txt
vendored
Normal file
@ -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
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user