package toolchain
import (
+ "bytes"
"context"
"errors"
"flag"
"fmt"
"go/build"
+ "internal/godebug"
+ "io"
"io/fs"
"log"
"os"
}
var counterErrorsInvalidToolchainInFile = counter.New("go/errors:invalid-toolchain-in-file")
+var toolchainTrace = godebug.New("#toolchaintrace").Value() == "1"
// Select invokes a different Go toolchain if directed by
// the GOTOOLCHAIN environment variable or the user's configuration
minToolchain := gover.LocalToolchain()
minVers := gover.Local()
var mode string
+ var toolchainTraceBuffer bytes.Buffer
if gotoolchain == "auto" {
mode = "auto"
} else if gotoolchain == "path" {
base.Fatalf("invalid GOTOOLCHAIN %q: only version suffixes are +auto and +path", gotoolchain)
}
mode = suffix
+ if toolchainTrace {
+ fmt.Fprintf(&toolchainTraceBuffer, "go: default toolchain set to %s from GOTOOLCHAIN=%s\n", minToolchain, gotoolchain)
+ }
}
gotoolchain = minToolchain
base.Fatalf("invalid toolchain %q in %s", toolchain, base.ShortPath(file))
}
if gover.Compare(toolVers, minVers) > 0 {
+ if toolchainTrace {
+ modeFormat := mode
+ if strings.Contains(cfg.Getenv("GOTOOLCHAIN"), "+") { // go1.2.3+auto
+ modeFormat = fmt.Sprintf("<name>+%s", mode)
+ }
+ fmt.Fprintf(&toolchainTraceBuffer, "go: upgrading toolchain to %s (required by toolchain line in %s; upgrade allowed by GOTOOLCHAIN=%s)\n", toolchain, base.ShortPath(file), modeFormat)
+ }
gotoolchain = toolchain
minVers = toolVers
gover.Startup.AutoToolchain = toolchain
}
gover.Startup.AutoGoVersion = goVers
gover.Startup.AutoToolchain = "" // in case we are overriding it for being too old
+ if toolchainTrace {
+ modeFormat := mode
+ if strings.Contains(cfg.Getenv("GOTOOLCHAIN"), "+") { // go1.2.3+auto
+ modeFormat = fmt.Sprintf("<name>+%s", mode)
+ }
+ fmt.Fprintf(&toolchainTraceBuffer, "go: upgrading toolchain to %s (required by go line in %s; upgrade allowed by GOTOOLCHAIN=%s)\n", gotoolchain, base.ShortPath(file), modeFormat)
+ }
}
}
}
return
}
+ if toolchainTrace {
+ // Flush toolchain tracing buffer only in the parent process (targetEnv is unset).
+ io.Copy(os.Stderr, &toolchainTraceBuffer)
+ }
+
if gotoolchain == "local" || gotoolchain == gover.LocalToolchain() {
// Let the current binary handle the command.
+ if toolchainTrace {
+ fmt.Fprintf(os.Stderr, "go: using local toolchain %s\n", gover.LocalToolchain())
+ }
return
}
--- /dev/null
+# Test the GODEBUG=toolchaintrace behavior
+# See https://go.dev/issue/63939
+env GODEBUG=toolchaintrace=1
+env TESTGO_VERSION=go1.21.0
+env TESTGO_VERSION_SWITCH=switch
+env GOTOOLCHAIN=auto
+
+# Go line is newer than local go version.
+go mod init m
+go mod edit -go=1.21.1
+go version
+stderr -count=1 'go: upgrading toolchain to go1.21.1 \(required by go line in go.mod; upgrade allowed by GOTOOLCHAIN=auto\)'
+stderr -count=1 'go: using go1.21.1 toolchain from cache located at .*'
+stdout 'go version go1.21.1'
+rm go.mod
+
+# Toolchain line is newer than go line.
+go mod init m
+go mod edit -go=1.21.1 -toolchain=go1.21.2
+go version
+stderr -count=1 'go: upgrading toolchain to go1.21.2 \(required by toolchain line in go.mod; upgrade allowed by GOTOOLCHAIN=auto\)'
+stderr -count=1 'go: using go1.21.2 toolchain from cache located at .*'
+stdout 'go version go1.21.2'
+rm go.mod
+
+# Go line is newer than local go version and toolchain line.
+go mod init m
+go mod edit -go=1.22 -toolchain=go1.21.2
+go version
+stderr -count=1 'go: upgrading toolchain to go1.21.2 \(required by toolchain line in go.mod; upgrade allowed by GOTOOLCHAIN=auto\)'
+stderr -count=1 'go: upgrading toolchain to go1.22.0 \(required by go line in go.mod; upgrade allowed by GOTOOLCHAIN=auto\)'
+stderr -count=1 'go: using go1.22.0 toolchain from cache located at .*'
+stdout 'go version go1.22.0'
+rm go.mod
+
+# No switch.
+go mod init m
+go mod edit -go=1.21.0 -toolchain=go1.21.0
+go version
+stderr -count=1 'go: using local toolchain go1.21.0'
+! stderr 'go: upgrading toolchain'
+stdout 'go version go1.21.0'
+rm go.mod
+
+# GOTOOLCHAIN+auto is older than go line and toolchain line.
+go mod init m
+go mod edit -go=1.22 -toolchain=go1.21.2
+env GOTOOLCHAIN=go1.21.0+auto
+go version
+stderr -count=1 'go: default toolchain set to go1.21.0 from GOTOOLCHAIN=go1.21.0\+auto'
+stderr -count=1 'go: upgrading toolchain to go1.21.2 \(required by toolchain line in go.mod; upgrade allowed by GOTOOLCHAIN=<name>\+auto\)'
+stderr -count=1 'go: upgrading toolchain to go1.22.0 \(required by go line in go.mod; upgrade allowed by GOTOOLCHAIN=<name>\+auto\)'
+stderr -count=1 'go: using go1.22.0 toolchain from cache located at .*'
+stdout 'go version go1.22.0'
+rm go.mod
+
+# GOTOOLCHAIN is older than go line and toolchain line.
+go mod init m
+go mod edit -go=1.22 -toolchain=go1.21.2
+env GOTOOLCHAIN=go1.21.1
+go version
+stderr -count=1 'go: default toolchain set to go1.21.1 from GOTOOLCHAIN=go1.21.1'
+stderr -count=1 'go: using go1.21.1 toolchain from cache located at .*'
+! stderr 'go: upgrading toolchain'
+stdout 'go version go1.21.1'
+rm go.mod
+env GOTOOLCHAIN=auto
+
+# GOTOOLCHAIN+auto is newer than go line and toolchain line.
+go mod init m
+go mod edit -go=1.21.1 -toolchain=go1.21.2
+env GOTOOLCHAIN=go1.22.0+auto
+go version
+stderr -count=1 'go: default toolchain set to go1.22.0 from GOTOOLCHAIN=go1.22.0\+auto'
+stderr -count=1 'go: using go1.22.0 toolchain from cache located at .*'
+stdout 'go version go1.22.0'
+rm go.mod
+
+# GOTOOLCHAIN=local
+env GOTOOLCHAIN=local
+go mod init m
+go mod edit -go=1.21.1 -toolchain=go1.21.2
+go version
+stderr -count=1 'go: default toolchain set to go1.21.0 from GOTOOLCHAIN=local'
+stderr -count=1 'go: using local toolchain go1.21.0'
+stdout 'go version go1.21.0'
+rm go.mod
+
+[short] stop 'requires build'
+# If toolchain found in PATH, ensure we print that.
+env GOTOOLCHAIN=auto
+env TESTGO_VERSION_SWITCH=
+mkdir $WORK/bin
+go build -o $WORK/bin/go1.22.0$GOEXE ./fake/fakego.go # adds .exe extension implicitly on Windows
+[!GOOS:plan9] env PATH=$WORK/bin
+[GOOS:plan9] env path=$WORK/bin
+go mod init m
+go mod edit -go=1.22.0
+! go version
+stderr -count=1 'go: upgrading toolchain to go1.22.0 \(required by go line in go.mod; upgrade allowed by GOTOOLCHAIN=auto\)'
+stderr -count=1 'go: using go1.22.0 toolchain located in system PATH \('$WORK'[/\\]bin[/\\]go1.22.0'$GOEXE'\)'
+stderr 'running go1.22.0 from PATH'
+rm go.mod
+
+
+-- fake/fakego.go --
+package main
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+func main() {
+ exe, _ := os.Executable()
+ name := filepath.Base(exe)
+ name = strings.TrimSuffix(name, ".exe")
+ fmt.Fprintf(os.Stderr, "running %s from PATH\n", name)
+ os.Exit(1) // fail in case we are running this accidentally (like in "go mod edit")
+}