1// Copyright 2023 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package gover 6 7import ( 8 "cmd/go/internal/base" 9 "context" 10 "errors" 11 "fmt" 12 "strings" 13) 14 15// FromToolchain returns the Go version for the named toolchain, 16// derived from the name itself (not by running the toolchain). 17// A toolchain is named "goVERSION". 18// A suffix after the VERSION introduced by a -, space, or tab is removed. 19// Examples: 20// 21// FromToolchain("go1.2.3") == "1.2.3" 22// FromToolchain("go1.2.3-bigcorp") == "1.2.3" 23// FromToolchain("invalid") == "" 24func FromToolchain(name string) string { 25 if strings.ContainsAny(name, "\\/") { 26 // The suffix must not include a path separator, since that would cause 27 // exec.LookPath to resolve it from a relative directory instead of from 28 // $PATH. 29 return "" 30 } 31 32 var v string 33 if strings.HasPrefix(name, "go") { 34 v = name[2:] 35 } else { 36 return "" 37 } 38 // Some builds use custom suffixes; strip them. 39 if i := strings.IndexAny(v, " \t-"); i >= 0 { 40 v = v[:i] 41 } 42 if !IsValid(v) { 43 return "" 44 } 45 return v 46} 47 48func maybeToolchainVersion(name string) string { 49 if IsValid(name) { 50 return name 51 } 52 return FromToolchain(name) 53} 54 55// ToolchainMax returns the maximum of x and y interpreted as toolchain names, 56// compared using Compare(FromToolchain(x), FromToolchain(y)). 57// If x and y compare equal, Max returns x. 58func ToolchainMax(x, y string) string { 59 if Compare(FromToolchain(x), FromToolchain(y)) < 0 { 60 return y 61 } 62 return x 63} 64 65// Startup records the information that went into the startup-time version switch. 66// It is initialized by switchGoToolchain. 67var Startup struct { 68 GOTOOLCHAIN string // $GOTOOLCHAIN setting 69 AutoFile string // go.mod or go.work file consulted 70 AutoGoVersion string // go line found in file 71 AutoToolchain string // toolchain line found in file 72} 73 74// A TooNewError explains that a module is too new for this version of Go. 75type TooNewError struct { 76 What string 77 GoVersion string 78 Toolchain string // for callers if they want to use it, but not printed 79} 80 81func (e *TooNewError) Error() string { 82 var explain string 83 if Startup.GOTOOLCHAIN != "" && Startup.GOTOOLCHAIN != "auto" { 84 explain = "; GOTOOLCHAIN=" + Startup.GOTOOLCHAIN 85 } 86 if Startup.AutoFile != "" && (Startup.AutoGoVersion != "" || Startup.AutoToolchain != "") { 87 explain += fmt.Sprintf("; %s sets ", base.ShortPath(Startup.AutoFile)) 88 if Startup.AutoToolchain != "" { 89 explain += "toolchain " + Startup.AutoToolchain 90 } else { 91 explain += "go " + Startup.AutoGoVersion 92 } 93 } 94 return fmt.Sprintf("%v requires go >= %v (running go %v%v)", e.What, e.GoVersion, Local(), explain) 95} 96 97var ErrTooNew = errors.New("module too new") 98 99func (e *TooNewError) Is(err error) bool { 100 return err == ErrTooNew 101} 102 103// A Switcher provides the ability to switch to a new toolchain in response to TooNewErrors. 104// See [cmd/go/internal/toolchain.Switcher] for documentation. 105type Switcher interface { 106 Error(err error) 107 Switch(ctx context.Context) 108} 109