Toolchains in Go¶
This is a blog for Go toolchains, I will try to explain what is toolchain, how to use it and why Go needs it.
Why Go Toolchain¶
Go toolchain actually bring 2 useful parts:
- Be able to use any go version from the global go as an entry. It also enables users to build the projects with the same version easier.
- Specify the minimal module go toolchain requirement, so go will do a selection when it attempts to compile. For example, you are using
os.CopyFS
supported since go1.23, so you settoolchain
ingo.mod
asgo1.23.0
, if someone tries to compile your project bygo1.22.7
, go will select the higher go version and continue to compile.
How to Use Go Toolchain¶
Go toolchain feature was introduced since Go1.21. The go distribution consists a go command and a bundled go toolchain, which is the std libs as well as the compiler, assembler, and other tools.
$ pwd
/usr/local/go
$ ls bin
go gofmt
$ ls pkg/tool/darwin_arm64
addr2line buildid compile cover fix
nm pack preprofile trace
asm cgo covdata doc
link objdump pprof test2json vet
config it from:
- env
GOTOOLCHAIN
toolchain
ingo.mod
- current workspace
go.work
The go
has a bundle toolchain, and the toolchain
keyword restricts the go version applies to it. Higher go is compatible to the lower one, but if it's lower go will try to use cache or download the higher go version specified in toolchain keyword.
For a go module used as a dependency by the others, the go
version is usually lower than toolchain
version, so when working in this module toolchain
takes a higher precedence.
The go and toolchain lines can be thought of as specifying the version requirements for the module’s dependency on the Go toolchain itself, just as the require lines in go.mod specify the version requirements for dependencies on other modules.
GOTOOLCHAIN=go1.21rc3 go test
# begin decision with go1.21.3, instead of default go
GOTOOLCHAIN=go1.21.3+auto
By specifying GOTOOLCHAIN
, actually you can use your global go
to trigger any go
version you like.
$ GOTOOLCHAIN=go1.21.3+auto go version
go: downloading go1.21.3 (darwin/arm64)
go version go1.21.3 darwin/arm64
$ GOTOOLCHAIN=go1.21.3+auto go version
go version go1.21.3 darwin/arm64
$ go version
go version go1.23.1 darwin/arm64
Inside a project that has toolchain
setting up, it will calculate to pick up a proper go version with its toolchain bundle to use. For example, when you run the toolchain under a project with toolchain
setting up, it will help you automatically use a suitable go version for your project.
$ cat go.mod | grep toolchain
toolchain go1.22.7
$ go version
go version go1.23.1 darwin/arm64
$ GOTOOLCHAIN=local go version
go version go1.23.1 darwin/arm64
$ GOTOOLCHAIN=go1.21.3+auto go version
go: downloading go1.22.7 (darwin/arm64)
go version go1.22.7 darwin/arm64
$ GOTOOLCHAIN=go1.21.3 go version
go version go1.21.3 darwin/arm64
The toolchain line declares a suggested toolchain to use with the module or workspace.
- have
toolchain
-> use higher one between toolchain and thego
you run - omit -> the same toolchain with
go
directive line. ifgo
is omitted, go1.16 for go.mod and go1.18 for go.work.
Go toolchain refuses to load a module whose go version is higher(e.g. go1.22.1 wants to load go1.22.2).
The go line for each module sets the language version the compiler enforces when compiling packages in that module.
Go toolchain is useful when you want to use different version of go compiler to test, you can just set the env var so you can easily build your program with different go versions.
//go:build go1.22
package main
import (
"fmt"
"runtime"
)
func init() {
fmt.Println(runtime.Version())
}
Before Go 1.21, Go toolchains treated the go line as an advisory requirement: if builds succeeded the toolchain assumed everything worked, and if not it printed a note about the potential version mismatch. Go 1.21 changed the go line to be a mandatory requirement instead. Before Go 1.21, toolchains did not require a module or workspace to have a go line greater than or equal to the go version required by each of its dependency modules.
GOTOOLCHAIN Setting¶
- env first
- default value of
go env -w
-
$GOROOT/go.env
: you can view bycat $(go env GOROOT)/go.env
# This file contains the initial defaults for go command configuration. # Values set by 'go env -w' and written to the user's go/env file override. # The environment overrides everything else. # Use the Go module mirror and checksum database by default. # See https://proxy.golang.org for details. GOPROXY=https://proxy.golang.org,direct GOSUMDB=sum.golang.org # Automatically download newer toolchains as directed by go.mod files. # See https://go.dev/doc/toolchain for details. GOTOOLCHAIN=auto
-
the fallback value is
GOTOOLCHAIN=local
GOTOOLCHAIN Selection¶
<name>
-> always run the specified go version.<name>+auto
-> select as needed, will consult the toolchain in go.mod or go.work<name>+path
-> ditto, but it will disable the fallback. so if not found in PATH, error is reported.local
-> the bundle toolchain with the go you run.GOTOOLCHAIN=auto
equalsGOTOOLCHAIN=local+auto
GOTOOLCHAIN=path
=GOTOOLCHAIN=local+path
Manage Go Version by 'go get'¶
Go provides the go get
support for go and toolchain versions.
$ go get [email protected] [email protected]
go: upgraded go 1.21 => 1.22.7
$ git --no-pager diff
diff --git a/go.mod b/go.mod
index 6d7863e..7aeee8c 100644
--- a/go.mod
+++ b/go.mod
@@ -1,3 +1,3 @@
-go 1.21
+go 1.22.7
Differences Between 'go' and 'toolchain' in go.mod/go.work¶
After introducing toolchain feature in go1.21, a new configuration line toolchain
is introduced inside the go.mod
/go.work
. They're similar with each other, so a clarification is necessary.
go line¶
Before Go 1.21, Go toolchains treated the go line as an advisory requirement. It means that go will use its own toolchain for your project whatever the go line value in your module.
But after Go 1.21, it's a requirement and go compiler will refuse to continue compiling if the version in go line
is higher than itself.
toolchain line¶
The toolchain line declares a suggested toolchain to use with the module or workspace.
Toolchain is a suggestion for the go
command to run, and this suggestion could indeed affect users if their GOTOOLCHAIN
setting is auto
. The toolchain line version is usually higher than the go version.
For example, you're maintaining a module requires at least go1.22
but you highly recommend them to use a higher go version such as go1.23
because the newer, the better for a keep improving project.
But as a dependency of the other modules, you have had to set a minimum Go version requirement lower than the desired toolchain version. It's not viable to change your go line
into a higher go version like go1.23
directly.
In such case, you can use toolchain line
to suggest the compiler to select a higher version when it tries to compile. This is very useful as a library maintainer.