From: Russ Cox Date: Wed, 24 May 2023 21:37:11 +0000 (-0400) Subject: cmd/go: switch to newer toolchain in go get as needed X-Git-Tag: go1.21rc1~185 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=301370c81cd4f05949a42e0d0222f1fe35c1f761;p=gostls13.git cmd/go: switch to newer toolchain in go get as needed If we run 'go get go@1.40' or 'go get m@v' where m has a go.mod that says 'go 1.40', we need to write a new go.mod that says 'go 1.40'. But we can't be sure we know how to write a Go 1.40-compatible go.mod. Instead, download the latest point release of Go 1.40 and invoke it to finish the get command. For #57001. Change-Id: I4133fc3c2ecf91226a6c09a3086275ecc517e223 Reviewed-on: https://go-review.googlesource.com/c/go/+/498118 TryBot-Result: Gopher Robot Reviewed-by: Bryan Mills Run-TryBot: Russ Cox Auto-Submit: Russ Cox --- diff --git a/src/cmd/go/internal/envcmd/env.go b/src/cmd/go/internal/envcmd/env.go index b99176f939..02a5343362 100644 --- a/src/cmd/go/internal/envcmd/env.go +++ b/src/cmd/go/internal/envcmd/env.go @@ -254,6 +254,12 @@ func runEnv(ctx context.Context, cmd *base.Command, args []string) { base.Fatalf("go: %v", cfg.ExperimentErr) } + for _, arg := range args { + if strings.Contains(arg, "=") { + base.Fatalf("go: invalid variable name %q (use -w to set variable)", arg) + } + } + env := cfg.CmdEnv env = append(env, ExtraEnvVars()...) diff --git a/src/cmd/go/internal/gover/gover.go b/src/cmd/go/internal/gover/gover.go index 2c4dae7757..fbea612227 100644 --- a/src/cmd/go/internal/gover/gover.go +++ b/src/cmd/go/internal/gover/gover.go @@ -10,7 +10,9 @@ // depending on the module path. package gover -import "cmp" +import ( + "cmp" +) // A version is a parsed Go version: major[.minor[.patch]][kind[pre]] // The numbers are the original decimal strings to avoid integer overflows @@ -76,6 +78,11 @@ func Lang(x string) string { return v.major + "." + v.minor } +// IsPrerelease reports whether v denotes a Go prerelease version. +func IsPrerelease(x string) bool { + return parse(x).kind != "" +} + // Prev returns the Go major release immediately preceding v, // or v itself if v is the first Go major release (1.0) or not a supported // Go version. diff --git a/src/cmd/go/internal/gover/gover_test.go b/src/cmd/go/internal/gover/gover_test.go index b79e2a0f0c..97b3b761c8 100644 --- a/src/cmd/go/internal/gover/gover_test.go +++ b/src/cmd/go/internal/gover/gover_test.go @@ -77,6 +77,7 @@ var isLangTests = []testCase1[string, bool]{ {"1.21", true}, {"1.20", false}, // == 1.20.0 {"1.19", false}, // == 1.20.0 + {"1.3", false}, // == 1.3.0 {"1.2", false}, // == 1.2.0 {"1", false}, // == 1.0.0 } @@ -113,6 +114,7 @@ type testCase3[In1, In2, In3, Out any] struct { } func test1[In, Out any](t *testing.T, tests []testCase1[In, Out], name string, f func(In) Out) { + t.Helper() for _, tt := range tests { if out := f(tt.in); !reflect.DeepEqual(out, tt.out) { t.Errorf("%s(%v) = %v, want %v", name, tt.in, out, tt.out) @@ -121,6 +123,7 @@ func test1[In, Out any](t *testing.T, tests []testCase1[In, Out], name string, f } func test2[In1, In2, Out any](t *testing.T, tests []testCase2[In1, In2, Out], name string, f func(In1, In2) Out) { + t.Helper() for _, tt := range tests { if out := f(tt.in1, tt.in2); !reflect.DeepEqual(out, tt.out) { t.Errorf("%s(%+v, %+v) = %+v, want %+v", name, tt.in1, tt.in2, out, tt.out) @@ -129,6 +132,7 @@ func test2[In1, In2, Out any](t *testing.T, tests []testCase2[In1, In2, Out], na } func test3[In1, In2, In3, Out any](t *testing.T, tests []testCase3[In1, In2, In3, Out], name string, f func(In1, In2, In3) Out) { + t.Helper() for _, tt := range tests { if out := f(tt.in1, tt.in2, tt.in3); !reflect.DeepEqual(out, tt.out) { t.Errorf("%s(%+v, %+v, %+v) = %+v, want %+v", name, tt.in1, tt.in2, tt.in3, out, tt.out) diff --git a/src/cmd/go/internal/gover/mod.go b/src/cmd/go/internal/gover/mod.go index 19f522c099..18e5635cdf 100644 --- a/src/cmd/go/internal/gover/mod.go +++ b/src/cmd/go/internal/gover/mod.go @@ -101,3 +101,12 @@ func ModIsPrefix(path, vers string) bool { } return true } + +// ModIsPrerelease reports whether v is a prerelease version for the module with the given path. +// The caller is assumed to have checked that ModIsValid(path, vers) is true. +func ModIsPrerelease(path, vers string) bool { + if IsToolchain(path) { + return IsPrerelease(vers) + } + return semver.Prerelease(vers) != "" +} diff --git a/src/cmd/go/internal/gover/toolchain.go b/src/cmd/go/internal/gover/toolchain.go index c7f6e3185e..48bc86b568 100644 --- a/src/cmd/go/internal/gover/toolchain.go +++ b/src/cmd/go/internal/gover/toolchain.go @@ -78,7 +78,7 @@ func (e *TooNewError) Error() string { explain += "toolchain " + Startup.AutoToolchain } } - return fmt.Sprintf("%v requires go %v (running go %v%v)", e.What, e.GoVersion, Local(), explain) + return fmt.Sprintf("%v requires go >= %v (running go %v%v)", e.What, e.GoVersion, Local(), explain) } var ErrTooNew = errors.New("module too new") diff --git a/src/cmd/go/internal/modget/get.go b/src/cmd/go/internal/modget/get.go index 3c86358ee3..eaa2b7d5db 100644 --- a/src/cmd/go/internal/modget/get.go +++ b/src/cmd/go/internal/modget/get.go @@ -43,6 +43,7 @@ import ( "cmd/go/internal/modload" "cmd/go/internal/par" "cmd/go/internal/search" + "cmd/go/internal/toolchain" "cmd/go/internal/work" "golang.org/x/mod/modfile" @@ -379,6 +380,14 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) { oldReqs := reqsFromGoMod(modload.ModFile()) if err := modload.WriteGoMod(ctx); err != nil { + if tooNew, ok := err.(*gover.TooNewError); ok { + // This can happen for 'go get go@newversion' + // when all the required modules are old enough + // but the command line is not. + // TODO(bcmills): Perhaps LoadModGraph should catch this, + // in which case the tryVersion here should be removed. + tryVersion(ctx, tooNew.GoVersion) + } base.Fatalf("go: %v", err) } @@ -1211,6 +1220,20 @@ func (r *resolver) resolveQueries(ctx context.Context, queries []*query) (change for { prevResolved := resolved + // If we found modules that were too new, find the max of the required versions + // and then try to switch to a newer toolchain. + goVers := "" + for _, q := range queries { + for _, cs := range q.candidates { + if e, ok := cs.err.(*gover.TooNewError); ok && gover.Compare(goVers, e.GoVersion) < 0 { + goVers = e.GoVersion + } + } + } + if goVers != "" { + tryVersion(ctx, goVers) + } + for _, q := range queries { unresolved := q.candidates[:0] @@ -1885,3 +1908,22 @@ func isNoSuchPackageVersion(err error) bool { var noPackage *modload.PackageNotInModuleError return isNoSuchModuleVersion(err) || errors.As(err, &noPackage) } + +// tryVersion tries to switch to a Go toolchain appropriate for version, +// which was either found in a go.mod file of a dependency or resolved +// on the command line from go@v. +func tryVersion(ctx context.Context, version string) { + if !gover.IsValid(version) { + fmt.Fprintf(os.Stderr, "go: misuse of tryVersion: invalid version %q\n", version) + return + } + if (!toolchain.HasAuto() && !toolchain.HasPath()) || gover.Compare(version, gover.Local()) <= 0 { + return + } + tv, err := toolchain.NewerToolchain(ctx, version) + if err != nil { + base.Errorf("go: %v\n", err) + } + fmt.Fprintf(os.Stderr, "go: switching to %v\n", tv) + toolchain.SwitchTo(tv) +} diff --git a/src/cmd/go/internal/modload/buildlist.go b/src/cmd/go/internal/modload/buildlist.go index 76500ab33f..517ecfcf66 100644 --- a/src/cmd/go/internal/modload/buildlist.go +++ b/src/cmd/go/internal/modload/buildlist.go @@ -609,7 +609,10 @@ func EditBuildList(ctx context.Context, add, mustSelect []module.Version) (chang // OverrideRoots edits the global requirement roots by replacing the specific module versions. func OverrideRoots(ctx context.Context, replace []module.Version) { - rs := requirements + requirements = overrideRoots(ctx, requirements, replace) +} + +func overrideRoots(ctx context.Context, rs *Requirements, replace []module.Version) *Requirements { drop := make(map[string]bool) for _, m := range replace { drop[m.Path] = true @@ -622,7 +625,7 @@ func OverrideRoots(ctx context.Context, replace []module.Version) { } roots = append(roots, replace...) gover.ModSort(roots) - requirements = newRequirements(rs.pruning, roots, rs.direct) + return newRequirements(rs.pruning, roots, rs.direct) } // A ConstraintError describes inconsistent constraints in EditBuildList diff --git a/src/cmd/go/internal/modload/init.go b/src/cmd/go/internal/modload/init.go index e8ff9204ad..db407b88a7 100644 --- a/src/cmd/go/internal/modload/init.go +++ b/src/cmd/go/internal/modload/init.go @@ -848,7 +848,16 @@ func loadModFile(ctx context.Context, opts *PackageOpts) *Requirements { // TODO(#45551): Do something more principled instead of checking // cfg.CmdName directly here. if cfg.BuildMod == "mod" && cfg.CmdName != "mod graph" && cfg.CmdName != "mod why" { + // go line is missing from go.mod; add one there and add to derived requirements. addGoStmt(MainModules.ModFile(mainModule), mainModule, gover.Local()) + if cfg.CmdName != "mod tidy" { + // We want to add the "go" line to the module load in general, + // if we do it in "mod tidy", then go mod tidy -go=older for some older version + // when we are in a module with no go line will see gover.Local() in the + // requirement graph and then report that -go=older is invalid. + // go test -run=Script/mod_tidy_version will fail without the tidy exclusion. + rs = overrideRoots(ctx, rs, []module.Version{{Path: "go", Version: gover.Local()}}) + } // We need to add a 'go' version to the go.mod file, but we must assume // that its existing contents match something between Go 1.11 and 1.16. @@ -1163,10 +1172,8 @@ func requirementsFromModFiles(ctx context.Context, workFile *modfile.WorkFile, m roots = append(roots, module.Version{Path: "go", Version: workFile.Go.Version}) direct["go"] = true } - if workFile.Toolchain != nil { - roots = append(roots, module.Version{Path: "toolchain", Version: workFile.Toolchain.Name}) - direct["toolchain"] = true - } + // Do not add toolchain to roots. + // We only want to see it in roots if it is on the command line. } else { pruning = pruningForGoVersion(MainModules.GoVersion()) if len(modFiles) != 1 { @@ -1197,10 +1204,8 @@ func requirementsFromModFiles(ctx context.Context, workFile *modfile.WorkFile, m roots = append(roots, module.Version{Path: "go", Version: modFile.Go.Version}) direct["go"] = true } - if modFile.Toolchain != nil { - roots = append(roots, module.Version{Path: "toolchain", Version: modFile.Toolchain.Name}) - direct["toolchain"] = true - } + // Do not add "toolchain" to roots. + // We only want to see it in roots if it is on the command line. } gover.ModSort(roots) rs := newRequirements(pruning, roots, direct) @@ -1546,8 +1551,10 @@ func commitRequirements(ctx context.Context) (err error) { var list []*modfile.Require toolchain := "" + wroteGo := false for _, m := range requirements.rootModules { if m.Path == "go" { + wroteGo = true forceGoStmt(modFile, mainModule, m.Version) continue } @@ -1561,27 +1568,49 @@ func commitRequirements(ctx context.Context) (err error) { }) } + var oldToolchain string + if modFile.Toolchain != nil { + oldToolchain = modFile.Toolchain.Name + } + oldToolVers := gover.FromToolchain(oldToolchain) + // Update go and toolchain lines. - tv := gover.FromToolchain(toolchain) + toolVers := gover.FromToolchain(toolchain) + // Set go version if missing. if modFile.Go == nil || modFile.Go.Version == "" { + wroteGo = true v := modFileGoVersion(modFile) - if tv != "" && gover.Compare(v, tv) > 0 { - v = tv + if toolVers != "" && gover.Compare(v, toolVers) > 0 { + v = toolVers } modFile.AddGoStmt(v) } if gover.Compare(modFile.Go.Version, gover.Local()) > 0 { - // TODO: Reinvoke the newer toolchain if GOTOOLCHAIN=auto. - base.Fatalf("go: %v", &gover.TooNewError{What: "updating go.mod", GoVersion: modFile.Go.Version}) + // We cannot assume that we know how to update a go.mod to a newer version. + return &gover.TooNewError{What: "updating go.mod", GoVersion: modFile.Go.Version} } - // If toolchain is older than go version, drop it. - if gover.Compare(modFile.Go.Version, tv) >= 0 { + // If we update the go line and don't have an explicit instruction + // for what to write in toolchain, make sure toolchain is at least our local version, + // for reproducibility. + if wroteGo && toolchain == "" && gover.Compare(oldToolVers, gover.Local()) < 0 && gover.Compare(modFile.Go.Version, GoStrictVersion) >= 0 { + toolVers = gover.Local() + toolchain = "go" + toolVers + } + + // Default to old toolchain. + if toolchain == "" { + toolchain = oldToolchain + toolVers = oldToolVers + } + if toolchain == "none" { toolchain = "" } + // Remove or add toolchain as needed. - if toolchain == "" { + // If toolchain is older than go version, drop it. + if toolchain == "" || gover.Compare(modFile.Go.Version, toolVers) >= 0 { modFile.DropToolchainStmt() } else { modFile.AddToolchainStmt(toolchain) diff --git a/src/cmd/go/internal/modload/modfile.go b/src/cmd/go/internal/modload/modfile.go index d97eb7cb62..77d2d7f86a 100644 --- a/src/cmd/go/internal/modload/modfile.go +++ b/src/cmd/go/internal/modload/modfile.go @@ -511,13 +511,16 @@ func (i *modFileIndex) modFileIsDirty(modFile *modfile.File) bool { } // go.mod files did not always require a 'go' version, so do not error out - // if one is missing — we may be inside an older module in the module cache + // if one is missing — we may be inside an older module // and want to bias toward providing useful behavior. // go lines are required if we need to declare version 1.17 or later. // Note that as of CL 303229, a missing go directive implies 1.16, // not “the latest Go version”. if goV != i.goVersion && i.goVersion == "" && cfg.BuildMod != "mod" && gover.Compare(goV, "1.17") < 0 { goV = "" + if toolchain != i.toolchain && i.toolchain == "" { + toolchain = "" + } } if goV != i.goVersion || diff --git a/src/cmd/go/internal/modload/query.go b/src/cmd/go/internal/modload/query.go index 038199f286..b26a036cac 100644 --- a/src/cmd/go/internal/modload/query.go +++ b/src/cmd/go/internal/modload/query.go @@ -546,7 +546,7 @@ func (qm *queryMatcher) filterVersions(ctx context.Context, versions []string) ( } } - if semver.Prerelease(v) != "" { + if gover.ModIsPrerelease(qm.path, v) { prereleases = append(prereleases, v) } else { releases = append(releases, v) diff --git a/src/cmd/go/internal/toolchain/exec.go b/src/cmd/go/internal/toolchain/exec.go new file mode 100644 index 0000000000..4e6a13e35f --- /dev/null +++ b/src/cmd/go/internal/toolchain/exec.go @@ -0,0 +1,55 @@ +// Copyright 2023 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 !js && !wasip1 + +package toolchain + +import ( + "cmd/go/internal/base" + "internal/godebug" + "os" + "os/exec" + "runtime" + "syscall" +) + +// execGoToolchain execs the Go toolchain with the given name (gotoolchain), +// GOROOT directory, and go command executable. +// The GOROOT directory is empty if we are invoking a command named +// gotoolchain found in $PATH. +func execGoToolchain(gotoolchain, dir, exe string) { + os.Setenv(gotoolchainSwitchEnv, "1") + if dir == "" { + os.Unsetenv("GOROOT") + } else { + os.Setenv("GOROOT", dir) + } + + // On Windows, there is no syscall.Exec, so the best we can do + // is run a subprocess and exit with the same status. + // Doing the same on Unix would be a problem because it wouldn't + // propagate signals and such, but there are no signals on Windows. + // We also use the exec case when GODEBUG=gotoolchainexec=0, + // to allow testing this code even when not on Windows. + if godebug.New("#gotoolchainexec").Value() == "0" || runtime.GOOS == "windows" { + cmd := exec.Command(exe, os.Args[1:]...) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err := cmd.Run() + if err != nil { + if e, ok := err.(*exec.ExitError); ok && e.ProcessState != nil { + if e.ProcessState.Exited() { + os.Exit(e.ProcessState.ExitCode()) + } + base.Fatalf("exec %s: %s", gotoolchain, e.ProcessState) + } + base.Fatalf("exec %s: %s", exe, err) + } + os.Exit(0) + } + err := syscall.Exec(exe, os.Args, os.Environ()) + base.Fatalf("exec %s: %v", gotoolchain, err) +} diff --git a/src/cmd/go/gotoolchain_stub.go b/src/cmd/go/internal/toolchain/exec_stub.go similarity index 56% rename from src/cmd/go/gotoolchain_stub.go rename to src/cmd/go/internal/toolchain/exec_stub.go index 9d1d7dbd46..e2123790a7 100644 --- a/src/cmd/go/gotoolchain_stub.go +++ b/src/cmd/go/internal/toolchain/exec_stub.go @@ -4,8 +4,10 @@ //go:build js || wasip1 -package main +package toolchain -// nop for systems that don't even define syscall.Exec, like js/wasm. -func switchGoToolchain() { +import "cmd/go/internal/base" + +func execGoToolchain(gotoolchain, dir, exe string) { + base.Fatalf("execGoToolchain unsupported") } diff --git a/src/cmd/go/gotoolchain.go b/src/cmd/go/internal/toolchain/toolchain.go similarity index 76% rename from src/cmd/go/gotoolchain.go rename to src/cmd/go/internal/toolchain/toolchain.go index ae442db82b..04d5474484 100644 --- a/src/cmd/go/gotoolchain.go +++ b/src/cmd/go/internal/toolchain/toolchain.go @@ -2,15 +2,13 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !js && !wasip1 - -package main +// Package toolchain implements dynamic switching of Go toolchains. +package toolchain import ( "context" "fmt" "go/build" - "internal/godebug" "io/fs" "log" "os" @@ -18,12 +16,12 @@ import ( "path/filepath" "runtime" "strings" - "syscall" "cmd/go/internal/base" "cmd/go/internal/cfg" "cmd/go/internal/gover" "cmd/go/internal/modcmd" + "cmd/go/internal/modfetch" "cmd/go/internal/modload" "cmd/go/internal/run" @@ -51,10 +49,11 @@ const ( gotoolchainSwitchEnv = "GOTOOLCHAIN_INTERNAL_SWITCH" ) -// switchGoToolchain invokes a different Go toolchain if directed by +// Switch invokes a different Go toolchain if directed by // the GOTOOLCHAIN environment variable or the user's configuration // or go.mod file. -func switchGoToolchain() { +// It must be called early in startup. +func Switch() { log.SetPrefix("go: ") defer log.SetPrefix("") @@ -93,7 +92,6 @@ func switchGoToolchain() { minToolchain = "go" + minVers } - pathOnly := gotoolchain == "path" if gotoolchain == "auto" || gotoolchain == "path" { gotoolchain = minToolchain @@ -155,6 +153,103 @@ func switchGoToolchain() { base.Fatalf("invalid GOTOOLCHAIN %q", gotoolchain) } + SwitchTo(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. +// +// 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. +// Otherwise we use the latest release. +func NewerToolchain(ctx context.Context, version string) (string, error) { + var versions *modfetch.Versions + err := modfetch.TryProxies(func(proxy string) error { + v, err := modfetch.Lookup(ctx, proxy, "go").Versions(ctx, "") + if err != nil { + return err + } + versions = v + return nil + }) + if err != nil { + return "", err + } + return newerToolchain(version, versions.List) +} + +// newerToolchain implements NewerToolchain where the list of choices is known. +// It is separated out for easier testing of this logic. +func newerToolchain(need string, list []string) (string, error) { + // Consider each release in the list, from newest to oldest, + // considering only entries >= need and then only entries + // that are the latest in their language family + // (the latest 1.40, the latest 1.39, and so on). + // We prefer the latest patch release before the most recent release family, + // so if the latest release is 1.40.1 we'll take the latest 1.39.X. + // Failing that, we prefer the latest patch release before the most recent + // prerelease family, so if the latest release is 1.40rc1 is out but 1.39 is okay, + // we'll still take 1.39.X. + // Failing that we'll take the latest release. + latest := "" + for i := len(list) - 1; i >= 0; i-- { + v := list[i] + if gover.Compare(v, need) < 0 { + break + } + if gover.Lang(latest) == gover.Lang(v) { + continue + } + newer := latest + latest = v + if newer != "" && !gover.IsPrerelease(newer) { + // latest is the last patch release of Go 1.X, and we saw a non-prerelease of Go 1.(X+1), + // so latest is the one we want. + break + } + } + if latest == "" { + return "", fmt.Errorf("no releases found for go >= %v", need) + } + return "go" + latest, nil +} + +// HasAuto reports whether the GOTOOLCHAIN setting allows "auto" upgrades. +func HasAuto() bool { + env := cfg.Getenv("GOTOOLCHAIN") + return env == "auto" || strings.HasSuffix(env, "+auto") +} + +// HasPath reports whether the GOTOOLCHAIN setting allows "path" upgrades. +func HasPath() bool { + env := cfg.Getenv("GOTOOLCHAIN") + return env == "path" || strings.HasSuffix(env, "+path") +} + +// 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 +// a toolchain if necessary. +func SwitchTo(gotoolchain string) { + log.SetPrefix("go: ") + + env := cfg.Getenv("GOTOOLCHAIN") + pathOnly := env == "path" || strings.HasSuffix(env, "+path") + + // For testing, if TESTGO_VERSION is already in use + // (only happens in the cmd/go test binary) + // and TESTGO_VERSION_SWITCH=1 is set, + // "switch" toolchains by changing TESTGO_VERSION + // and reinvoking the current binary. + if gover.TestVersion != "" && os.Getenv("TESTGO_VERSION_SWITCH") == "1" { + os.Setenv("TESTGO_VERSION", gotoolchain) + exe, err := os.Executable() + if err != nil { + base.Fatalf("%v", err) + } + execGoToolchain(gotoolchain, os.Getenv("GOROOT"), exe) + } + // Look in PATH for the toolchain before we download one. // This allows custom toolchains as well as reuse of toolchains // already installed using go install golang.org/dl/go1.2.3@latest. @@ -169,6 +264,7 @@ func switchGoToolchain() { } // Set up modules without an explicit go.mod, to download distribution. + modload.Reset() modload.ForceUseModules = true modload.RootMode = modload.NoRoot modload.Init() @@ -238,46 +334,6 @@ func switchGoToolchain() { execGoToolchain(gotoolchain, dir, filepath.Join(dir, "bin/go")) } -// execGoToolchain execs the Go toolchain with the given name (gotoolchain), -// GOROOT directory, and go command executable. -// The GOROOT directory is empty if we are invoking a command named -// gotoolchain found in $PATH. -func execGoToolchain(gotoolchain, dir, exe string) { - os.Setenv(gotoolchainSwitchEnv, "1") - if dir == "" { - os.Unsetenv("GOROOT") - } else { - os.Setenv("GOROOT", dir) - } - - // On Windows, there is no syscall.Exec, so the best we can do - // is run a subprocess and exit with the same status. - // Doing the same on Unix would be a problem because it wouldn't - // propagate signals and such, but there are no signals on Windows. - // We also use the exec case when GODEBUG=gotoolchainexec=0, - // to allow testing this code even when not on Windows. - if godebug.New("#gotoolchainexec").Value() == "0" || runtime.GOOS == "windows" { - cmd := exec.Command(exe, os.Args[1:]...) - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - fmt.Fprintln(os.Stderr, cmd.Args) - err := cmd.Run() - if err != nil { - if e, ok := err.(*exec.ExitError); ok && e.ProcessState != nil { - if e.ProcessState.Exited() { - os.Exit(e.ProcessState.ExitCode()) - } - base.Fatalf("exec %s: %s", gotoolchain, e.ProcessState) - } - base.Fatalf("exec %s: %s", exe, err) - } - os.Exit(0) - } - err := syscall.Exec(exe, os.Args, os.Environ()) - base.Fatalf("exec %s: %v", gotoolchain, err) -} - // modGoToolchain finds the enclosing go.work or go.mod file // and returns the go version and toolchain lines from the file. // The toolchain line overrides the version line diff --git a/src/cmd/go/internal/toolchain/toolchain_test.go b/src/cmd/go/internal/toolchain/toolchain_test.go new file mode 100644 index 0000000000..e8ed5664e1 --- /dev/null +++ b/src/cmd/go/internal/toolchain/toolchain_test.go @@ -0,0 +1,66 @@ +// Copyright 2023 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 toolchain + +import ( + "strings" + "testing" +) + +func TestNewerToolchain(t *testing.T) { + for _, tt := range newerToolchainTests { + out, err := newerToolchain(tt.need, tt.list) + if (err != nil) != (out == "") { + t.Errorf("newerToolchain(%v, %v) = %v, %v, want error", tt.need, tt.list, out, err) + continue + } + if out != tt.out { + t.Errorf("newerToolchain(%v, %v) = %v, %v want %v, nil", tt.need, tt.list, out, err, tt.out) + } + } +} + +var f = strings.Fields + +var relRC = []string{"1.39.0", "1.39.1", "1.39.2", "1.40.0", "1.40.1", "1.40.2", "1.41rc1"} +var rel2 = []string{"1.39.0", "1.39.1", "1.39.2", "1.40.0", "1.40.1", "1.40.2"} +var rel0 = []string{"1.39.0", "1.39.1", "1.39.2", "1.40.0"} +var newerToolchainTests = []struct { + need string + list []string + out string +}{ + {"1.30", rel0, "go1.39.2"}, + {"1.30", rel2, "go1.39.2"}, + {"1.30", relRC, "go1.39.2"}, + {"1.38", rel0, "go1.39.2"}, + {"1.38", rel2, "go1.39.2"}, + {"1.38", relRC, "go1.39.2"}, + {"1.38.1", rel0, "go1.39.2"}, + {"1.38.1", rel2, "go1.39.2"}, + {"1.38.1", relRC, "go1.39.2"}, + {"1.39", rel0, "go1.39.2"}, + {"1.39", rel2, "go1.39.2"}, + {"1.39", relRC, "go1.39.2"}, + {"1.39.2", rel0, "go1.39.2"}, + {"1.39.2", rel2, "go1.39.2"}, + {"1.39.2", relRC, "go1.39.2"}, + {"1.39.3", rel0, "go1.40.0"}, + {"1.39.3", rel2, "go1.40.2"}, + {"1.39.3", relRC, "go1.40.2"}, + {"1.40", rel0, "go1.40.0"}, + {"1.40", rel2, "go1.40.2"}, + {"1.40", relRC, "go1.40.2"}, + {"1.40.1", rel0, ""}, + {"1.40.1", rel2, "go1.40.2"}, + {"1.40.1", relRC, "go1.40.2"}, + {"1.41", rel0, ""}, + {"1.41", rel2, ""}, + {"1.41", relRC, "go1.41rc1"}, + {"1.41.0", rel0, ""}, + {"1.41.0", rel2, ""}, + {"1.41.0", relRC, ""}, + {"1.40", nil, ""}, +} diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go index e441b4ea01..c4a75f87e3 100644 --- a/src/cmd/go/main.go +++ b/src/cmd/go/main.go @@ -7,6 +7,7 @@ package main import ( + "cmd/go/internal/toolchain" "cmd/go/internal/workcmd" "context" "flag" @@ -91,7 +92,7 @@ var _ = go11tag func main() { log.SetFlags(0) - switchGoToolchain() + toolchain.Switch() flag.Usage = base.Usage flag.Parse() diff --git a/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.18.1.linux-amd64.txt b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.18.1.linux-amd64.txt new file mode 100644 index 0000000000..3713de33bd --- /dev/null +++ b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.18.1.linux-amd64.txt @@ -0,0 +1,8 @@ +golang.org/toolchain v0.0.1-go1.18.1.linux-amd64 +written by hand +-- .info -- +{"Version":"v0.0.1-go1.18.1.linux-amd64"} +-- .mod -- +golang.org/toolchain +-- go.mod -- +golang.org/toolchain diff --git a/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.18.3.linux-amd64.txt b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.18.3.linux-amd64.txt new file mode 100644 index 0000000000..8eda1eedb0 --- /dev/null +++ b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.18.3.linux-amd64.txt @@ -0,0 +1,8 @@ +golang.org/toolchain v0.0.1-go1.18.3.linux-amd64 +written by hand +-- .info -- +{"Version":"v0.0.1-go1.18.3.linux-amd64"} +-- .mod -- +golang.org/toolchain +-- go.mod -- +golang.org/toolchain diff --git a/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.18.5.linux-amd64.txt b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.18.5.linux-amd64.txt new file mode 100644 index 0000000000..d74ef250f2 --- /dev/null +++ b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.18.5.linux-amd64.txt @@ -0,0 +1,8 @@ +golang.org/toolchain v0.0.1-go1.18.5.linux-amd64 +written by hand +-- .info -- +{"Version":"v0.0.1-go1.18.5.linux-amd64"} +-- .mod -- +golang.org/toolchain +-- go.mod -- +golang.org/toolchain diff --git a/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.18.7.linux-amd64.txt b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.18.7.linux-amd64.txt new file mode 100644 index 0000000000..2fc7f85895 --- /dev/null +++ b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.18.7.linux-amd64.txt @@ -0,0 +1,8 @@ +golang.org/toolchain v0.0.1-go1.18.7.linux-amd64 +written by hand +-- .info -- +{"Version":"v0.0.1-go1.18.7.linux-amd64"} +-- .mod -- +golang.org/toolchain +-- go.mod -- +golang.org/toolchain diff --git a/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.18.9.linux-amd64.txt b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.18.9.linux-amd64.txt new file mode 100644 index 0000000000..7b0785106d --- /dev/null +++ b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.18.9.linux-amd64.txt @@ -0,0 +1,8 @@ +golang.org/toolchain v0.0.1-go1.18.9.linux-amd64 +written by hand +-- .info -- +{"Version":"v0.0.1-go1.18.9.linux-amd64"} +-- .mod -- +golang.org/toolchain +-- go.mod -- +golang.org/toolchain diff --git a/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.18.linux-amd64.txt b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.18.linux-amd64.txt new file mode 100644 index 0000000000..2c80ce5e2a --- /dev/null +++ b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.18.linux-amd64.txt @@ -0,0 +1,8 @@ +golang.org/toolchain v0.0.1-go1.18.linux-amd64 +written by hand +-- .info -- +{"Version":"v0.0.1-go1.18.linux-amd64"} +-- .mod -- +golang.org/toolchain +-- go.mod -- +golang.org/toolchain diff --git a/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.22.0.linux-amd64.txt b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.22.0.linux-amd64.txt new file mode 100644 index 0000000000..215c547788 --- /dev/null +++ b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.22.0.linux-amd64.txt @@ -0,0 +1,8 @@ +golang.org/toolchain v0.0.1-go1.22.0.linux-amd64 +written by hand +-- .info -- +{"Version":"v0.0.1-go1.22.0.linux-amd64"} +-- .mod -- +golang.org/toolchain +-- go.mod -- +golang.org/toolchain diff --git a/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.22.1.linux-amd64.txt b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.22.1.linux-amd64.txt new file mode 100644 index 0000000000..ac36e3fe65 --- /dev/null +++ b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.22.1.linux-amd64.txt @@ -0,0 +1,8 @@ +golang.org/toolchain v0.0.1-go1.22.1.linux-amd64 +written by hand +-- .info -- +{"Version":"v0.0.1-go1.22.1.linux-amd64"} +-- .mod -- +golang.org/toolchain +-- go.mod -- +golang.org/toolchain diff --git a/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.22.3.linux-amd64.txt b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.22.3.linux-amd64.txt new file mode 100644 index 0000000000..1178a48ab1 --- /dev/null +++ b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.22.3.linux-amd64.txt @@ -0,0 +1,8 @@ +golang.org/toolchain v0.0.1-go1.22.3.linux-amd64 +written by hand +-- .info -- +{"Version":"v0.0.1-go1.22.3.linux-amd64"} +-- .mod -- +golang.org/toolchain +-- go.mod -- +golang.org/toolchain diff --git a/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.22.5.linux-amd64.txt b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.22.5.linux-amd64.txt new file mode 100644 index 0000000000..d330127a79 --- /dev/null +++ b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.22.5.linux-amd64.txt @@ -0,0 +1,8 @@ +golang.org/toolchain v0.0.1-go1.22.5.linux-amd64 +written by hand +-- .info -- +{"Version":"v0.0.1-go1.22.5.linux-amd64"} +-- .mod -- +golang.org/toolchain +-- go.mod -- +golang.org/toolchain diff --git a/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.22.7.linux-amd64.txt b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.22.7.linux-amd64.txt new file mode 100644 index 0000000000..a72863b830 --- /dev/null +++ b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.22.7.linux-amd64.txt @@ -0,0 +1,8 @@ +golang.org/toolchain v0.0.1-go1.22.7.linux-amd64 +written by hand +-- .info -- +{"Version":"v0.0.1-go1.22.7.linux-amd64"} +-- .mod -- +golang.org/toolchain +-- go.mod -- +golang.org/toolchain diff --git a/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.22.9.linux-amd64.txt b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.22.9.linux-amd64.txt new file mode 100644 index 0000000000..ac558496c2 --- /dev/null +++ b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.22.9.linux-amd64.txt @@ -0,0 +1,8 @@ +golang.org/toolchain v0.0.1-go1.22.9.linux-amd64 +written by hand +-- .info -- +{"Version":"v0.0.1-go1.22.9.linux-amd64"} +-- .mod -- +golang.org/toolchain +-- go.mod -- +golang.org/toolchain diff --git a/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.22rc1.linux-amd64.txt b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.22rc1.linux-amd64.txt new file mode 100644 index 0000000000..b384f28a83 --- /dev/null +++ b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.22rc1.linux-amd64.txt @@ -0,0 +1,8 @@ +golang.org/toolchain v0.0.1-go1.22rc1.linux-amd64 +written by hand +-- .info -- +{"Version":"v0.0.1-go1.22rc1.linux-amd64"} +-- .mod -- +golang.org/toolchain +-- go.mod -- +golang.org/toolchain diff --git a/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.23.0.linux-amd64.txt b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.23.0.linux-amd64.txt new file mode 100644 index 0000000000..bbc137768e --- /dev/null +++ b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.23.0.linux-amd64.txt @@ -0,0 +1,8 @@ +golang.org/toolchain v0.0.1-go1.23.0.linux-amd64 +written by hand +-- .info -- +{"Version":"v0.0.1-go1.23.0.linux-amd64"} +-- .mod -- +golang.org/toolchain +-- go.mod -- +golang.org/toolchain diff --git a/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.23.5.linux-amd64.txt b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.23.5.linux-amd64.txt new file mode 100644 index 0000000000..206e8ad2b8 --- /dev/null +++ b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.23.5.linux-amd64.txt @@ -0,0 +1,8 @@ +golang.org/toolchain v0.0.1-go1.23.5.linux-amd64 +written by hand +-- .info -- +{"Version":"v0.0.1-go1.23.5.linux-amd64"} +-- .mod -- +golang.org/toolchain +-- go.mod -- +golang.org/toolchain diff --git a/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.23.9.linux-amd64.txt b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.23.9.linux-amd64.txt new file mode 100644 index 0000000000..7d03776418 --- /dev/null +++ b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.23.9.linux-amd64.txt @@ -0,0 +1,8 @@ +golang.org/toolchain v0.0.1-go1.23.9.linux-amd64 +written by hand +-- .info -- +{"Version":"v0.0.1-go1.23.9.linux-amd64"} +-- .mod -- +golang.org/toolchain +-- go.mod -- +golang.org/toolchain diff --git a/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.24rc1.linux-amd64.txt b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.24rc1.linux-amd64.txt new file mode 100644 index 0000000000..4b61dddaa2 --- /dev/null +++ b/src/cmd/go/testdata/mod/golang.org_toolchain_v0.0.1-go1.24rc1.linux-amd64.txt @@ -0,0 +1,8 @@ +golang.org/toolchain v0.0.1-go1.24rc1.linux-amd64 +written by hand +-- .info -- +{"Version":"v0.0.1-go1.24rc1.linux-amd64"} +-- .mod -- +golang.org/toolchain +-- go.mod -- +golang.org/toolchain diff --git a/src/cmd/go/testdata/mod/rsc.io_needall_v0.0.1.txt b/src/cmd/go/testdata/mod/rsc.io_needall_v0.0.1.txt new file mode 100644 index 0000000000..0a1582a577 --- /dev/null +++ b/src/cmd/go/testdata/mod/rsc.io_needall_v0.0.1.txt @@ -0,0 +1,25 @@ +rsc.io/needall 0.0.1 +written by hand + +-- .mod -- +module rsc.io/needall +go 1.23 + +require rsc.io/needgo121 v0.0.1 +require rsc.io/needgo122 v0.0.1 +require rsc.io/needgo123 v0.0.1 + +-- go.mod -- +module rsc.io/needall +go 1.23 + +require rsc.io/needgo121 v0.0.1 +require rsc.io/needgo122 v0.0.1 +require rsc.io/needgo123 v0.0.1 + +-- .info -- +{"Version":"v0.0.1"} +-- p.go -- +package p + +func F() {} diff --git a/src/cmd/go/testdata/mod/rsc.io_needgo1183_v0.0.1.txt b/src/cmd/go/testdata/mod/rsc.io_needgo1183_v0.0.1.txt new file mode 100644 index 0000000000..a41296e1c7 --- /dev/null +++ b/src/cmd/go/testdata/mod/rsc.io_needgo1183_v0.0.1.txt @@ -0,0 +1,17 @@ +rsc.io/needgo1183 v0.0.1 +written by hand + +-- .mod -- +module rsc.io/needgo1183 +go 1.18.3 + +-- go.mod -- +module rsc.io/needgo1183 +go 1.18.3 + +-- .info -- +{"Version":"v0.0.1"} +-- p.go -- +package p + +func F() {} diff --git a/src/cmd/go/testdata/mod/rsc.io_needgo118_v0.0.1.txt b/src/cmd/go/testdata/mod/rsc.io_needgo118_v0.0.1.txt new file mode 100644 index 0000000000..805eac7bb2 --- /dev/null +++ b/src/cmd/go/testdata/mod/rsc.io_needgo118_v0.0.1.txt @@ -0,0 +1,17 @@ +rsc.io/needgo118 0.0.1 +written by hand + +-- .mod -- +module rsc.io/needgo118 +go 1.18 + +-- go.mod -- +module rsc.io/needgo118 +go 1.18 + +-- .info -- +{"Version":"v0.0.1"} +-- p.go -- +package p + +func F() {} diff --git a/src/cmd/go/testdata/mod/rsc.io_needgo121_v0.0.1.txt b/src/cmd/go/testdata/mod/rsc.io_needgo121_v0.0.1.txt new file mode 100644 index 0000000000..5b059609c4 --- /dev/null +++ b/src/cmd/go/testdata/mod/rsc.io_needgo121_v0.0.1.txt @@ -0,0 +1,17 @@ +rsc.io/needgo121 0.0.1 +written by hand + +-- .mod -- +module rsc.io/needgo121 +go 1.21 + +-- go.mod -- +module rsc.io/needgo121 +go 1.21 + +-- .info -- +{"Version":"v0.0.1"} +-- p.go -- +package p + +func F() {} diff --git a/src/cmd/go/testdata/mod/rsc.io_needgo1223_v0.0.1.txt b/src/cmd/go/testdata/mod/rsc.io_needgo1223_v0.0.1.txt new file mode 100644 index 0000000000..f166a8226e --- /dev/null +++ b/src/cmd/go/testdata/mod/rsc.io_needgo1223_v0.0.1.txt @@ -0,0 +1,17 @@ +rsc.io/needgo1223 0.0.1 +written by hand + +-- .mod -- +module rsc.io/needgo1223 +go 1.22.3 + +-- go.mod -- +module rsc.io/needgo1223 +go 1.22.3 + +-- .info -- +{"Version":"v0.0.1"} +-- p.go -- +package p + +func F() {} diff --git a/src/cmd/go/testdata/mod/rsc.io_needgo122_v0.0.1.txt b/src/cmd/go/testdata/mod/rsc.io_needgo122_v0.0.1.txt new file mode 100644 index 0000000000..59116eb6ab --- /dev/null +++ b/src/cmd/go/testdata/mod/rsc.io_needgo122_v0.0.1.txt @@ -0,0 +1,17 @@ +rsc.io/needgo122 0.0.1 +written by hand + +-- .mod -- +module rsc.io/needgo122 +go 1.22 + +-- go.mod -- +module rsc.io/needgo122 +go 1.22 + +-- .info -- +{"Version":"v0.0.1"} +-- p.go -- +package p + +func F() {} diff --git a/src/cmd/go/testdata/mod/rsc.io_needgo123_v0.0.1.txt b/src/cmd/go/testdata/mod/rsc.io_needgo123_v0.0.1.txt new file mode 100644 index 0000000000..0ec55714da --- /dev/null +++ b/src/cmd/go/testdata/mod/rsc.io_needgo123_v0.0.1.txt @@ -0,0 +1,17 @@ +rsc.io/needgo123 0.0.1 +written by hand + +-- .mod -- +module rsc.io/needgo123 +go 1.23 + +-- go.mod -- +module rsc.io/needgo123 +go 1.23 + +-- .info -- +{"Version":"v0.0.1"} +-- p.go -- +package p + +func F() {} diff --git a/src/cmd/go/testdata/mod/rsc.io_needgo124_v0.0.1.txt b/src/cmd/go/testdata/mod/rsc.io_needgo124_v0.0.1.txt new file mode 100644 index 0000000000..634f50477f --- /dev/null +++ b/src/cmd/go/testdata/mod/rsc.io_needgo124_v0.0.1.txt @@ -0,0 +1,17 @@ +rsc.io/needgo124 0.0.1 +written by hand + +-- .mod -- +module rsc.io/needgo124 +go 1.24 + +-- go.mod -- +module rsc.io/needgo124 +go 1.24 + +-- .info -- +{"Version":"v0.0.1"} +-- p.go -- +package p + +func F() {} diff --git a/src/cmd/go/testdata/script/gotoolchain.txt b/src/cmd/go/testdata/script/gotoolchain.txt index 40a4b13c9d..abfa5c8cc0 100644 --- a/src/cmd/go/testdata/script/gotoolchain.txt +++ b/src/cmd/go/testdata/script/gotoolchain.txt @@ -73,13 +73,13 @@ env TESTGO_VERSION=go1.100 # toolchain local in go.mod cp go1999toolchainlocal go.mod ! go build -stderr '^go: go.mod requires go 1.999 \(running go 1.100; go.mod sets go 1.999, toolchain local\)$' +stderr '^go: go.mod requires go >= 1.999 \(running go 1.100; go.mod sets go 1.999, toolchain local\)$' # toolchain local in go.work cp empty go.mod cp go1999toolchainlocal go.work ! go build -stderr '^go: go.work requires go 1.999 \(running go 1.100; go.work sets go 1.999, toolchain local\)$' +stderr '^go: go.work requires go >= 1.999 \(running go 1.100; go.work sets go 1.999, toolchain local\)$' rm go.work # toolchain line in go.work diff --git a/src/cmd/go/testdata/script/mod_get_exec_toolchain.txt b/src/cmd/go/testdata/script/mod_get_exec_toolchain.txt new file mode 100644 index 0000000000..a9aa27ec0a --- /dev/null +++ b/src/cmd/go/testdata/script/mod_get_exec_toolchain.txt @@ -0,0 +1,130 @@ +env TESTGO_VERSION=go1.21 +env TESTGO_VERSION_SWITCH=1 + +# GOTOOLCHAIN=auto should run the newer toolchain +env GOTOOLCHAIN=auto +cp go.mod.new go.mod +go get rsc.io/needgo121 rsc.io/needgo122 rsc.io/needgo123 rsc.io/needall +stderr '^go: switching to go1.23.9$' +stderr '^go: added rsc.io/needall v0.0.1' +! stderr 'requires go >= 1.23' +grep 'go 1.23' go.mod +grep 'toolchain go1.23.9' go.mod + +# GOTOOLCHAIN=min+auto should run the newer toolchain +env GOTOOLCHAIN=go1.21+auto +cp go.mod.new go.mod +go get rsc.io/needgo121 rsc.io/needgo122 rsc.io/needgo123 rsc.io/needall +stderr '^go: switching to go1.23.9$' +stderr '^go: added rsc.io/needall v0.0.1' +! stderr 'requires go >= 1.23' +grep 'go 1.23' go.mod +grep 'toolchain go1.23.9' go.mod + +# GOTOOLCHAIN=go1.21 should NOT run the newer toolchain +env GOTOOLCHAIN=go1.21 +cp go.mod.new go.mod +! go get rsc.io/needgo121 rsc.io/needgo122 rsc.io/needgo123 rsc.io/needall +! stderr 'switching to go' +stderr 'rsc.io/needgo122@v0.0.1 requires go >= 1.22' +stderr 'rsc.io/needgo123@v0.0.1 requires go >= 1.23' +stderr 'rsc.io/needall@v0.0.1 requires go >= 1.23' +stderr 'requires go >= 1.23' +! stderr 'requires go >= 1.21' # that's us! +cmp go.mod go.mod.new + +# GOTOOLCHAIN=local should NOT run the newer toolchain +env GOTOOLCHAIN=local +cp go.mod.new go.mod +! go get rsc.io/needgo121 rsc.io/needgo122 rsc.io/needgo123 rsc.io/needall +! stderr 'switching to go' +stderr 'rsc.io/needgo122@v0.0.1 requires go >= 1.22' +stderr 'rsc.io/needgo123@v0.0.1 requires go >= 1.23' +stderr 'rsc.io/needall@v0.0.1 requires go >= 1.23' +stderr 'requires go >= 1.23' +! stderr 'requires go >= 1.21' # that's us! +cmp go.mod go.mod.new + +# go get go@1.22 should resolve to the latest 1.22 +env GOTOOLCHAIN=local +cp go.mod.new go.mod +! go get go@1.22 +stderr '^go: updating go.mod requires go >= 1.22.9 \(running go 1.21; GOTOOLCHAIN=local\)' + +env GOTOOLCHAIN=auto +cp go.mod.new go.mod +go get go@1.22 +stderr '^go: switching to go1.22.9$' + +# go get go@1.22rc1 should use 1.22rc1 exactly, not a later release. +env GOTOOLCHAIN=local +cp go.mod.new go.mod +! go get go@1.22rc1 +stderr '^go: updating go.mod requires go >= 1.22rc1 \(running go 1.21; GOTOOLCHAIN=local\)' + +env GOTOOLCHAIN=auto +cp go.mod.new go.mod +go get go@1.22rc1 +stderr '^go: switching to go1.22.9$' +stderr '^go: upgraded go 1.1 => 1.22rc1$' +stderr '^go: added toolchain go1.22.9$' + +# go get go@1.22.1 should use 1.22.1 exactly, not a later release. +env GOTOOLCHAIN=local +cp go.mod.new go.mod +! go get go@1.22.1 +stderr '^go: updating go.mod requires go >= 1.22.1 \(running go 1.21; GOTOOLCHAIN=local\)' + +env GOTOOLCHAIN=auto +cp go.mod.new go.mod +go get go@1.22.1 +stderr '^go: switching to go1.22.9$' +stderr '^go: upgraded go 1.1 => 1.22.1$' +stderr '^go: added toolchain go1.22.9$' + +# go get needgo122 (says 'go 1.22') should use 1.22.0, the earliest release we have available +# (ignoring prereleases). +env GOTOOLCHAIN=local +cp go.mod.new go.mod +! go get rsc.io/needgo122 +stderr '^go: rsc.io/needgo122@v0.0.1 requires go >= 1.22 \(running go 1.21; GOTOOLCHAIN=local\)' + +env GOTOOLCHAIN=auto +cp go.mod.new go.mod +go get rsc.io/needgo122 +stderr '^go: upgraded go 1.1 => 1.22$' +stderr '^go: switching to go1.22.9$' +stderr '^go: added toolchain go1.22.9$' + +# go get needgo1223 (says 'go 1.22.3') should use go 1.22.3 +env GOTOOLCHAIN=local +cp go.mod.new go.mod +! go get rsc.io/needgo1223 +stderr '^go: rsc.io/needgo1223@v0.0.1 requires go >= 1.22.3 \(running go 1.21; GOTOOLCHAIN=local\)' + +env GOTOOLCHAIN=auto +cp go.mod.new go.mod +go get rsc.io/needgo1223 +stderr '^go: upgraded go 1.1 => 1.22.3$' +stderr '^go: switching to go1.22.9$' +stderr '^go: added toolchain go1.22.9$' + +# go get needgo124 (says 'go 1.24') should use go 1.24rc1, the only version available +env GOTOOLCHAIN=local +cp go.mod.new go.mod +! go get rsc.io/needgo124 +stderr '^go: rsc.io/needgo124@v0.0.1 requires go >= 1.24 \(running go 1.21; GOTOOLCHAIN=local\)' + +env GOTOOLCHAIN=auto +cp go.mod.new go.mod +go get rsc.io/needgo124 +stderr '^go: switching to go1.24rc1' +stderr '^go: upgraded go 1.1 => 1.24$' +stderr '^go: added toolchain go1.24rc1$' + +-- go.mod.new -- +module m +go 1.1 + +-- p.go -- +package p diff --git a/src/cmd/go/testdata/script/mod_get_future.txt b/src/cmd/go/testdata/script/mod_get_future.txt index 997e5cb288..6f2985af86 100644 --- a/src/cmd/go/testdata/script/mod_get_future.txt +++ b/src/cmd/go/testdata/script/mod_get_future.txt @@ -1,6 +1,6 @@ env TESTGO_VERSION=go1.21 ! go mod download rsc.io/future@v1.0.0 -stderr '^go: rsc.io/future@v1.0.0 requires go 1.999 \(running go 1.21; go.mod sets go 1.21\)$' +stderr '^go: rsc.io/future@v1.0.0 requires go >= 1.999 \(running go 1.21; go.mod sets go 1.21\)$' -- go.mod -- module m diff --git a/src/cmd/go/testdata/script/mod_go_version.txt b/src/cmd/go/testdata/script/mod_go_version.txt index f0af3ae307..4e6baf89f1 100644 --- a/src/cmd/go/testdata/script/mod_go_version.txt +++ b/src/cmd/go/testdata/script/mod_go_version.txt @@ -4,9 +4,9 @@ env GO111MODULE=on env TESTGO_VERSION=go1.21 ! go list -stderr -count=1 '^go: sub@v1.0.0: sub requires go 1.999 \(running go 1.21; go.mod sets go 1.1\)$' +stderr -count=1 '^go: sub@v1.0.0: sub requires go >= 1.999 \(running go 1.21; go.mod sets go 1.1\)$' ! go build sub -stderr -count=1 '^go: sub@v1.0.0: sub requires go 1.999 \(running go 1.21; go.mod sets go 1.1\)$' +stderr -count=1 '^go: sub@v1.0.0: sub requires go >= 1.999 \(running go 1.21; go.mod sets go 1.1\)$' -- go.mod -- module m diff --git a/src/cmd/go/testdata/script/mod_goline_too_new.txt b/src/cmd/go/testdata/script/mod_goline_too_new.txt index d34efb5bd3..97b0af52c1 100644 --- a/src/cmd/go/testdata/script/mod_goline_too_new.txt +++ b/src/cmd/go/testdata/script/mod_goline_too_new.txt @@ -3,24 +3,24 @@ # go.mod too new env GOTOOLCHAIN=local ! go build . -stderr '^go: go.mod requires go 1.99999 \(running go 1\..+\)$' +stderr '^go: go.mod requires go >= 1.99999 \(running go 1\..+\)$' # go.mod referenced from go.work too new cp go.work.old go.work ! go build . -stderr '^go: go.mod requires go 1.99999 \(running go 1\..+\)$' +stderr '^go: go.mod requires go >= 1.99999 \(running go 1\..+\)$' # go.work too new cp go.work.new go.work cp go.mod.old go.mod ! go build . -stderr '^go: go.work requires go 1.99999 \(running go 1\..+\)$' +stderr '^go: go.work requires go >= 1.99999 \(running go 1\..+\)$' # vendor too new rm go.work mv notvendor vendor ! go build -mod=vendor . -stderr '^go: golang.org/x/text in vendor'${/}'modules.txt requires go 1.99999 \(running go 1\..+\)$' +stderr '^go: golang.org/x/text in vendor'${/}'modules.txt requires go >= 1.99999 \(running go 1\..+\)$' -- go.mod -- module example diff --git a/src/cmd/go/testdata/script/mod_toolchain.txt b/src/cmd/go/testdata/script/mod_toolchain.txt index bdaa859bd9..d0f8b913e7 100644 --- a/src/cmd/go/testdata/script/mod_toolchain.txt +++ b/src/cmd/go/testdata/script/mod_toolchain.txt @@ -1,68 +1,69 @@ -[!net:golang.org] skip - -env GOPROXY=https://proxy.golang.org/ env TESTGO_VERSION=go1.100 -go get toolchain@go1.20.1 -stderr '^go: added toolchain go1.20.1$' +env TESTGO_VERSION_SWITCH=1 + +go get toolchain@go1.22.1 +stderr '^go: added toolchain go1.22.1$' ! stderr '(added|removed|upgraded|downgraded) go' -grep 'toolchain go1.20.1' go.mod +grep 'toolchain go1.22.1' go.mod go get toolchain@none -stderr '^go: removed toolchain go1.20.1$' +stderr '^go: removed toolchain go1.22.1$' ! stderr '(added|removed|upgraded|downgraded) go' ! grep toolchain go.mod -go get toolchain@go1.20.1 -stderr '^go: added toolchain go1.20.1$' +go get toolchain@go1.22.1 +stderr '^go: added toolchain go1.22.1$' ! stderr '(added|removed|upgraded|downgraded) go' -grep 'toolchain go1.20.1' go.mod +grep 'toolchain go1.22.1' go.mod + +go get go@1.22.3 +stderr '^go: upgraded go 1.10 => 1.22.3$' +stderr '^go: upgraded toolchain go1.22.1 => go1.100$' +grep 'go 1.22.3' go.mod -cat go.mod -go get go@1.20.3 -stderr '^go: upgraded go 1.10 => 1.20.3$' -stderr '^go: removed toolchain go1.20.1$' -grep 'go 1.20.3' go.mod +go get go@1.22.3 toolchain@1.22.3 +stderr '^go: removed toolchain go1.100$' ! grep toolchain go.mod -go get go@1.20.1 toolchain@go1.20.3 -stderr '^go: downgraded go 1.20.3 => 1.20.1$' -stderr '^go: added toolchain go1.20.3$' -grep 'go 1.20.1' go.mod -grep 'toolchain go1.20.3' go.mod +go get go@1.22.1 toolchain@go1.22.3 +stderr '^go: downgraded go 1.22.3 => 1.22.1$' +stderr '^go: added toolchain go1.22.3$' +grep 'go 1.22.1' go.mod +grep 'toolchain go1.22.3' go.mod -go get go@1.20.3 -stderr '^go: upgraded go 1.20.1 => 1.20.3$' -stderr '^go: removed toolchain go1.20.3$' -grep 'go 1.20.3' go.mod +go get go@1.22.3 toolchain@1.22.3 +stderr '^go: upgraded go 1.22.1 => 1.22.3$' +stderr '^go: removed toolchain go1.22.3$' +grep 'go 1.22.3' go.mod ! grep toolchain go.mod -go get toolchain@1.20.1 -stderr '^go: downgraded go 1.20.3 => 1.20.1$' - # ! stderr toolchain -grep 'go 1.20.1' go.mod +go get toolchain@1.22.1 +stderr '^go: downgraded go 1.22.3 => 1.22.1$' +! stderr toolchain # already gone, was not added +grep 'go 1.22.1' go.mod +! grep toolchain go.mod -env TESTGO_VERSION=go1.20.1 +env TESTGO_VERSION=go1.22.1 env GOTOOLCHAIN=local -! go get go@1.20.3 -stderr 'go: updating go.mod requires go 1.20.3 \(running go 1.20.1; GOTOOLCHAIN=local\)$' - -go get toolchain@1.20.3 -grep 'toolchain go1.20.3' go.mod +! go get go@1.22.3 +stderr 'go: updating go.mod requires go >= 1.22.3 \(running go 1.22.1; GOTOOLCHAIN=local\)$' env TESTGO_VERSION=go1.30 -go get go@1.20.1 -grep 'go 1.20.1' go.mod +go get toolchain@1.22.3 +grep 'toolchain go1.22.3' go.mod + +go get go@1.22.1 +grep 'go 1.22.1' go.mod go get m2@v1.0.0 -stderr '^go: upgraded go 1.20.1 => 1.22$' +stderr '^go: upgraded go 1.22.1 => 1.23$' stderr '^go: added m2 v1.0.0$' -grep 'go 1.22' go.mod +grep 'go 1.23$' go.mod -go mod edit -toolchain=go1.29.0 # cannot go get because it doesn't exist -go get go@1.28.0 +go get toolchain@go1.23.9 go@1.23.5 go get toolchain@none -stderr '^go: removed toolchain go1.29.0' +stderr '^go: removed toolchain go1.23.9' ! stderr ' go 1' -grep 'go 1.28.0' go.mod +grep 'go 1.23.5' go.mod -- go.mod -- module m @@ -72,4 +73,4 @@ replace m2 v1.0.0 => ./m2 -- m2/go.mod -- module m2 -go 1.22 +go 1.23 diff --git a/src/cmd/go/testdata/script/old_tidy_toolchain.txt b/src/cmd/go/testdata/script/old_tidy_toolchain.txt new file mode 100644 index 0000000000..d4b5af2f76 --- /dev/null +++ b/src/cmd/go/testdata/script/old_tidy_toolchain.txt @@ -0,0 +1,28 @@ +# Commands in an old module with no go line and no toolchain line, +# or with only a go line, should succeed. +# (They should not fail due to the go.mod not being tidy.) + +# No go line, no toolchain line. +go list + +# Old go line, no toolchain line. +go mod edit -go=1.16 +go list + +go mod edit -go=1.20 +go list + +# New go line, no toolchain line, using same toolchain. +env TESTGO_VERSION=1.21 +go mod edit -go=1.21 +go list + +# New go line, no toolchain line, using newer Go version. +# (Until we need to update the go line, no toolchain addition.) +env TESTGO_VERSION=1.21.0 +go list + +-- go.mod -- +module m +-- p.go -- +package p