cmd/go: add loaderstate to Switcher

Temporarily add modload.State field to the Switcher implementation.
Note that we cannot modify the gover.Switcher interface because doing
so creates an import cycle.

This commit is part of the overall effort to eliminate global
modloader state.

Change-Id: I20dba76328aa3d0df58caff75b174522bf9df9d5
Reviewed-on: https://go-review.googlesource.com/c/go/+/712703
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Michael Matloob <matloob@google.com>
Reviewed-by: Michael Matloob <matloob@golang.org>
This commit is contained in:
Ian Alexander 2025-10-16 17:43:58 -04:00
parent bcf7da1595
commit 4dc3dd9a86
6 changed files with 25 additions and 17 deletions

View file

@ -211,7 +211,7 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) {
// toolchain version) or only one module (as is used by the Go Module Proxy).
if infosErr != nil {
var sw toolchain.Switcher
sw := toolchain.NewSwitcher(modload.LoaderState)
sw.Error(infosErr)
if sw.NeedSwitch() {
sw.Switch(ctx)
@ -292,7 +292,7 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) {
// which may include dependencies that are normally pruned out
// of the individual modules in the workspace.
if haveExplicitArgs || modload.WorkFilePath(modload.LoaderState) != "" {
var sw toolchain.Switcher
sw := toolchain.NewSwitcher(modload.LoaderState)
// Add errors to the Switcher in deterministic order so that they will be
// logged deterministically.
for _, m := range mods {

View file

@ -141,6 +141,6 @@ func runTidy(ctx context.Context, cmd *base.Command, args []string) {
LoadTests: true,
AllowErrors: tidyE,
SilenceMissingStdImports: true,
Switcher: new(toolchain.Switcher),
Switcher: toolchain.NewSwitcher(modload.LoaderState),
}, "all")
}

View file

@ -1260,7 +1260,7 @@ func (r *resolver) loadPackages(ctx context.Context, patterns []string, findPack
LoadTests: *getT,
AssumeRootsImported: true, // After 'go get foo', imports of foo should build.
SilencePackageErrors: true, // May be fixed by subsequent upgrades or downgrades.
Switcher: new(toolchain.Switcher),
Switcher: toolchain.NewSwitcher(modload.LoaderState),
}
opts.AllowPackage = func(ctx context.Context, path string, m module.Version) error {
@ -1346,7 +1346,7 @@ func (r *resolver) resolveQueries(ctx context.Context, queries []*query) (change
// If we found modules that were too new, find the max of the required versions
// and then try to switch to a newer toolchain.
var sw toolchain.Switcher
sw := toolchain.NewSwitcher(modload.LoaderState)
for _, q := range queries {
for _, cs := range q.candidates {
sw.Error(cs.err)

View file

@ -283,7 +283,7 @@ func Select() {
}
counterSelectExec.Inc()
Exec(gotoolchain)
Exec(modload.LoaderState, gotoolchain)
}
var counterSelectExec = counter.New("go/toolchain/select-exec")
@ -300,7 +300,7 @@ var TestVersionSwitch string
// 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 Exec(gotoolchain string) {
func Exec(s *modload.State, gotoolchain string) {
log.SetPrefix("go: ")
writeBits = sysWriteBits()
@ -352,10 +352,10 @@ func Exec(gotoolchain string) {
}
// Set up modules without an explicit go.mod, to download distribution.
modload.LoaderState.Reset()
modload.LoaderState.ForceUseModules = true
modload.LoaderState.RootMode = modload.NoRoot
modload.Init(modload.LoaderState)
s.Reset()
s.ForceUseModules = true
s.RootMode = modload.NoRoot
modload.Init(s)
// Download and unpack toolchain module into module cache.
// Note that multiple go commands might be doing this at the same time,
@ -709,7 +709,7 @@ func maybeSwitchForGoInstallVersion(minVers string) {
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.
var s Switcher
s := NewSwitcher(modload.LoaderState)
s.Error(err)
if s.TooNew != nil && gover.Compare(s.TooNew.GoVersion, minVers) > 0 {
SwitchOrFatal(ctx, err)

View file

@ -16,6 +16,7 @@ import (
"cmd/go/internal/cfg"
"cmd/go/internal/gover"
"cmd/go/internal/modfetch"
"cmd/go/internal/modload"
"cmd/internal/telemetry/counter"
)
@ -31,8 +32,15 @@ import (
//
// See https://go.dev/doc/toolchain#switch.
type Switcher struct {
TooNew *gover.TooNewError // max go requirement observed
Errors []error // errors collected so far
TooNew *gover.TooNewError // max go requirement observed
Errors []error // errors collected so far
loaderstate *modload.State // temporarily here while we eliminate global module loader state
}
func NewSwitcher(s *modload.State) *Switcher {
sw := new(Switcher)
sw.loaderstate = s
return sw
}
// Error reports the error to the Switcher,
@ -100,7 +108,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)
counterSwitchExec.Inc()
Exec(tv)
Exec(s.loaderstate, tv)
panic("unreachable")
}
@ -109,7 +117,7 @@ var counterSwitchExec = counter.New("go/toolchain/switch-exec")
// SwitchOrFatal attempts a toolchain switch based on the information in err
// and otherwise falls back to base.Fatal(err).
func SwitchOrFatal(ctx context.Context, err error) {
var s Switcher
s := NewSwitcher(modload.LoaderState)
s.Error(err)
s.Switch(ctx)
base.Exit()

View file

@ -94,7 +94,7 @@ func workUse(ctx context.Context, gowork string, wf *modfile.WorkFile, args []st
// all entries for the absolute path should be removed.
keepDirs := make(map[string]string)
var sw toolchain.Switcher
sw := toolchain.NewSwitcher(modload.LoaderState)
// lookDir updates the entry in keepDirs for the directory dir,
// which is either absolute or relative to the current working directory