Go 工具鏈

簡介

從 Go 1.21 開始,Go 發行版包含一個 go 命令和一個捆綁的 Go 工具鏈,它是標準函式庫以及編譯器、組譯器和其他工具。go 命令可以使用其捆綁的 Go 工具鏈以及在本地 PATH 中找到或視需要下載的其他版本。

正在使用的 Go 工具鏈的選擇取決於 GOTOOLCHAIN 環境設定以及主模組的 go.mod 檔案或目前工作區的 go.work 檔案中的 gotoolchain 行。當您在不同的主模組和工作區之間移動時,正在使用的工具鏈版本可能會有所不同,就像模組相依性版本一樣。

在標準組態中,go 指令會使用其內建工具鏈,只要該工具鏈至少與主模組或工作區中的 gotoolchain 行一樣新。例如,當在主模組中使用與 Go 1.21.3 內建的 go 指令,且該主模組指定 go 1.21.0 時,go 指令會使用 Go 1.21.3。當 gotoolchain 行比內建工具鏈新時,go 指令會改為執行較新的工具鏈。例如,當在主模組中使用與 Go 1.21.3 內建的 go 指令,且該主模組指定 go 1.21.9 時,go 指令會尋找並執行 Go 1.21.9。它會先在 PATH 中尋找名為 go1.21.9 的程式,否則會下載並快取 Go 1.21.9 工具鏈的副本。可以停用此自動工具鏈切換,但在這種情況下,為了更精確的前向相容性,go 指令會拒絕在 go 行需要較新版本的 Go 的主模組或工作區中執行。也就是說,go 行設定使用模組或工作區所需的最低 Go 版本。

作為其他模組相依項的模組可能需要設定比在該模組中直接運作時使用的首選工具鏈更低的最低 Go 版本需求。在此情況下,go.modgo.work 中的 toolchain 行會設定一個首選工具鏈,當 go 指令決定要使用哪個工具鏈時,這個首選工具鏈會優先於 go 行。

gotoolchain 行可以視為指定模組對 Go 工具鏈本身的相依項的版本需求,就像 go.mod 中的 require 行指定對其他模組的相依項的版本需求一樣。go get 指令會管理 Go 工具鏈相依項,就像管理對其他模組的相依項一樣。例如,go get go@latest 會更新模組以要求最新的已發布 Go 工具鏈。

GOTOOLCHAIN 環境設定可以強制使用特定 Go 版本,覆寫 gotoolchain 行。例如,要使用 Go 1.21rc3 測試套件

GOTOOLCHAIN=go1.21rc3 go test

預設 GOTOOLCHAIN 設定為 auto,這會啟用前面說明的工具鏈切換。替代形式 <name>+auto 會在決定是否進一步切換之前,設定要使用的預設工具鏈。例如 GOTOOLCHAIN=go1.21.3+auto 會指示 go 指令從預設使用 Go 1.21.3 開始決定,但如果 gotoolchain 行指示,仍會使用較新的工具鏈。由於預設 GOTOOLCHAIN 設定可以使用 go env -w 變更,如果您已安裝 Go 1.21.0 或更新版本,則

go env -w GOTOOLCHAIN=go1.21.3+auto

等於用 Go 1.21.3 取代您的 Go 1.21.0 安裝。

本文檔的其餘部分會更詳細地說明 Go 工具鏈的版本化、選擇和管理方式。

Go 版本

已發布的 Go 版本使用版本語法「1.N.P」,表示 Go 1.N 的第 P 個版本。初始版本為 1.N.0,例如「1.21.0」。後續版本(例如 1.N.9)通常稱為修補版本。

Go 1.N 發布候選版本(在 1.N.0 之前發布)使用版本語法「1.NrcR」。Go 1.N 的第一個發布候選版本為 1.Nrc1,例如「1.23rc1」。

語法「1.N」稱為「語言版本」。它表示實作該版本 Go 語言和標準函式庫的 Go 版本系列。

Go 版本的語言版本是將 N 之後的所有內容截斷的結果:1.21、1.21rc2 和 1.21.3 都實作語言版本 1.21。

已發布的 Go 工具鏈(例如 Go 1.21.0 和 Go 1.21rc1)會從 go versionruntime.Version 回報特定版本(例如 go1.21.0go1.21rc1)。未發布(仍處於開發階段)的 Go 工具鏈(從 Go 開發存放庫建置)只會回報語言版本(例如 go1.21)。

任何兩個 Go 版本都可以進行比較,以決定一個版本是否小於、大於或等於另一個版本。如果語言版本不同,則決定比較結果:1.21.9 < 1.22。在一個語言版本中,從最小到最大的順序為:語言版本本身,然後按 R 排序的候選版本,然後按 P 排序的版本。

例如,1.21 < 1.21rc1 < 1.21rc2 < 1.21.0 < 1.21.1 < 1.21.2。

在 Go 1.21 之前,Go 工具鏈的初始版本是 1.N,而不是 1.N.0,因此對於 N < 21,調整順序以將 1.N 置於候選版本之後。

例如,1.20rc1 < 1.20rc2 < 1.20rc3 < 1.20 < 1.20.1。

早期版本的 Go 有 beta 版本,版本類似於 1.18beta2。在版本順序中,beta 版本會立即置於候選版本之前。

例如,1.18beta1 < 1.18beta2 < 1.18rc1 < 1.18 < 1.18.1。

Go 工具鏈名稱

標準 Go 工具鏈的名稱為 goV,其中 V 是表示 beta 版本、候選版本或版本的 Go 版本。例如,go1.21rc1go1.21.0 是工具鏈名稱;go1.21go1.22 不是(初始版本是 go1.21.0go1.22.0),但 go1.20go1.19 是。

非標準工具鏈使用 goV-suffix 形式的名稱,其中 suffix 為任何字尾。

通過比較名稱中嵌入的版本 V(刪除初始 go 並捨棄以 - 開頭的任何字尾)來比較工具鏈。例如,go1.21.0go1.21.0-custom 在排序目的上相等。

模組和工作區設定

Go 模組和工作區會在各自的 go.modgo.work 檔案中指定與版本相關的設定。

go 行宣告使用模組或工作區所需的最低 Go 版本。基於相容性考量,如果 go.mod 檔案中省略 go 行,則模組會被視為具有內含的 go 1.16 行,如果 go.work 檔案中省略 go 行,則工作區會被視為具有內含的 go 1.18 行。

toolchain 行宣告建議與模組或工作區搭配使用的工具鏈。如下方「Go 工具鏈選擇」所述,如果預設工具鏈的版本低於建議工具鏈的版本,go 指令可能會在該模組或工作區中執行這個特定工具鏈。如果省略 toolchain 行,則模組或工作區會被視為具有內含的 toolchain goV 行,其中 Vgo 行中的 Go 版本。

例如,一個未包含 toolchain 行且宣告 go 1.21.0go.mod 會被解譯為包含 toolchain go1.21.0 行。

Go 工具鏈會拒絕載入宣告的最低 Go 版本高於工具鏈本身版本的模組或工作區。

例如,Go 1.21.2 會拒絕載入包含 go 1.21.3go 1.22 行的模組或工作區。

模組的 go 行必須宣告大於或等於 require 陳述式中所列模組宣告的 go 版本。工作區的 go 行必須宣告大於或等於 use 陳述式中所列模組宣告的 go 版本。

例如,如果模組 M 需要一個包含宣告 go 1.22.0go.mod 的相依性 D,則 Mgo.mod 不能宣告 go 1.21.3

每個模組的 go 行會設定編譯器在編譯該模組中的套件時執行的語言版本。語言版本可以使用 建置約束 依檔案變更。

例如,包含使用 Go 1.21 語言版本的程式碼的模組,應具有包含類似 go 1.21go 1.21.3go 行的 go.mod 檔案。如果特定原始檔應僅在使用較新的 Go 工具鏈時編譯,將 //go:build go1.22 新增至該原始檔,既可確保僅 Go 1.22 及更新的工具鏈會編譯該檔案,也會將該檔案的語言版本變更為 Go 1.22。

最方便且安全的修改 gotoolchain 行的方式是使用 go get;請參閱下方專門針對 go get 的區段

在 Go 1.21 之前,Go 工具鏈將 go 行視為建議性需求:如果建置成功,工具鏈假設一切運作正常,如果建置失敗,它會列印關於潛在版本不符的注意事項。Go 1.21 已將 go 行變更為強制性需求。此行為已部分回溯套用至較早的語言版本:從 Go 1.19.13 開始的 Go 1.19 版本,以及從 Go 1.20.8 開始的 Go 1.20 版本,拒絕載入宣告版本為 Go 1.22 或更新的工作區或模組。

在 Go 1.21 之前,工具鏈並不要求模組或工作區具有大於或等於其各依賴模組所需的 go 版本的 go 行。

GOTOOLCHAIN 設定

go 指令會根據 GOTOOLCHAIN 設定,選取要使用的 Go 工具鏈。go 指令會使用任何 Go 環境設定的標準規則,來尋找 GOTOOLCHAIN 設定

在標準 Go 工具鏈中,$GOROOT/go.env 檔案會設定預設的 GOTOOLCHAIN=auto,但重新封裝的 Go 工具鏈可能會變更此值。

如果遺失了 $GOROOT/go.env 檔案或未設定預設值,go 指令會假設 GOTOOLCHAIN=local

執行 go env GOTOOLCHAIN 會列印 GOTOOLCHAIN 設定。

Go 工具鏈選取

在啟動時,go 指令會選取要使用的 Go 工具鏈。它會諮詢 GOTOOLCHAIN 設定,其格式為 <name><name>+auto<name>+pathGOTOOLCHAIN=autoGOTOOLCHAIN=local+auto 的簡寫;類似地,GOTOOLCHAIN=pathGOTOOLCHAIN=local+path 的簡寫。<name> 設定預設的 Go 工具鏈:local 指示捆綁的 Go 工具鏈(與執行中的 go 指令一起附帶的工具鏈),否則 <name> 必須是特定的 Go 工具鏈名稱,例如 go1.21.0go 指令偏好執行預設的 Go 工具鏈。如上所述,從 Go 1.21 開始,Go 工具鏈拒絕在需要較新 Go 版本的工作區或模組中執行。相反地,它們會報告錯誤並結束執行。

GOTOOLCHAIN 設定為 local 時,go 指令會始終執行捆綁的 Go 工具鏈。

GOTOOLCHAIN 設定為 <name>(例如 GOTOOLCHAIN=go1.21.0)時,go 指令會始終執行該特定 Go 工具鏈。如果在系統 PATH 中找到具有該名稱的二進位檔,go 指令會使用它。否則,go 指令會使用它下載並驗證的 Go 工具鏈。

GOTOOLCHAIN 設為 <name>+auto<name>+path(或簡寫 autopath),go 指令會視需要選取並執行較新的 Go 版本。具體來說,它會參閱目前工作區的 go.work 檔案中的 toolchaingo 行,或在沒有工作區時參閱主模組的 go.mod 檔案。如果 go.workgo.mod 檔案有 toolchain <tname> 行,且 <tname> 比預設的 Go 工具鏈新,則 go 指令會執行 <tname>。如果檔案有 toolchain default 行,則 go 指令會執行預設的 Go 工具鏈,停用任何更新至 <name> 之外的嘗試。否則,如果檔案有 go <version> 行,且 <version> 比預設的 Go 工具鏈新,則 go 指令會執行 go<version>

若要執行非套件 Go 工具鏈,go 指令會在程序的可執行路徑(Unix 和 Plan 9 上為 $PATH,Windows 上為 %PATH%)中搜尋具有指定名稱的程式(例如 go1.21.3),並執行該程式。如果找不到此類程式,go 指令會下載並執行指定的 Go 工具鏈。使用 GOTOOLCHAIN 格式 <name>+path 會停用下載備援,導致 go 指令在搜尋可執行路徑後停止。

執行 go version 會列印選取的 Go 工具鏈版本(透過執行選取的工具鏈的 go version 實作)。

執行 GOTOOLCHAIN=local go version 會列印套件 Go 工具鏈版本。

Go 工具鏈切換

對於大多數指令,工作區的 go.work 或主模組的 go.mod 會有 go 行,由於版本排序設定需求,此行至少與任何模組相依項中的 go 行一樣新。在此情況下,啟動工具鏈選取會執行夠新的 Go 工具鏈來完成指令。

某些指令會在操作中納入新的模組版本:go get 會將新的模組相依性新增至主模組;go work use 會將新的本機模組新增至工作區;go work sync 會重新將工作區與自工作區建立以來可能已更新的本機模組同步;go install package@versiongo run package@version 會在一個空的模組中執行,並將 package@version 新增為新的相依性。所有這些指令都可能會遇到一個 go.mod go 行的模組,需要比目前執行的 Go 版本更新的 Go 版本。

當一個指令遇到需要更新的 Go 版本的模組,且 GOTOOLCHAIN 允許執行不同的工具鏈(它是 autopath 形式之一)時,go 指令會選擇並切換至適當的更新工具鏈,以繼續執行目前的指令。

go 指令在啟動工具鏈選擇後,每次切換工具鏈時,都會印出一個訊息,說明原因。例如

go: module example.com/widget@v1.2.3 requires go >= 1.24rc1; switching to go 1.27.9

如範例所示,go 指令可能會切換到比發現需求更新的工具鏈。一般來說,go 指令的目標是切換到支援的 Go 工具鏈。

為了選擇工具鏈,go 指令會先取得可用工具鏈的清單。對於 auto 形式,go 指令會下載可用工具鏈的清單。對於 path 形式,go 指令會掃描 PATH,找出任何命名為有效工具鏈的可執行檔,並使用它找到的所有工具鏈清單。go 指令會使用該工具鏈清單,找出最多三個候選者

以下是根據 Go 的發布政策所支援的 Go 發行版本。與最小版本選取一致,go 指令會保守地使用符合新需求的最小(最舊)版本的候選版本。

例如,假設 example.com/widget@v1.2.3 需要 Go 1.24rc1 或更新版本。go 指令會取得可用工具鏈的清單,並發現兩個最新 Go 工具鏈的最新修補程式版本為 Go 1.28.3 和 Go 1.27.9,且候選版本 Go 1.29rc2 也可用。在此情況下,go 指令會選擇 Go 1.27.9。如果 widget 需要 Go 1.28 或更新版本,go 指令會選擇 Go 1.28.3,因為 Go 1.27.9 太舊了。如果 widget 需要 Go 1.29 或更新版本,go 指令會選擇 Go 1.29rc2,因為 Go 1.27.9 和 Go 1.28.3 都太舊了。

包含需要新 Go 版本的新模組版本的指令會將新的最小 go 版本需求寫入目前的 Workspace 的 go.work 檔案或主模組的 go.mod 檔案,並更新 go 行。為了重複性,任何更新 go 行的指令也會更新 toolchain 行以記錄其自己的工具鏈名稱。下次 go 指令在該 Workspace 或模組中執行時,它會在工具鏈選取期間使用該更新的 toolchain 行。

例如,go get example.com/widget@v1.2.3 可能會列印如上所述的切換通知,並切換到 Go 1.27.9。Go 1.27.9 將完成 go get 並更新 toolchain 行,顯示 toolchain go1.27.9。在該模組或工作區中執行的下一個 go 命令將在啟動期間選擇 go1.27.9,且不會列印任何切換訊息。

一般來說,如果任何 go 命令執行兩次,如果第一次列印切換訊息,第二次則不會,因為第一次也更新了 go.workgo.mod,以便在啟動時選擇正確的工具鏈。例外情況是 go install package@versiongo run package@version 形式,它們在沒有工作區或主模組中執行,且無法寫入 toolchain 行。它們每次需要切換到較新的工具鏈時都會列印切換訊息。

下載工具鏈

在使用 GOTOOLCHAIN=autoGOTOOLCHAIN=<name>+auto 時,Go 命令會視需要下載較新的工具鏈。這些工具鏈會封裝為特殊模組,其模組路徑為 golang.org/toolchain,版本為 v0.0.1-goVERSION.GOOS-GOARCH。工具鏈的下載方式與任何其他模組相同,這表示工具鏈下載可以透過設定 GOPROXY 來代理,並由 Go 校驗總和資料庫檢查其校驗總和。由於所使用的特定工具鏈取決於系統本身的預設工具鏈,以及本機作業系統和架構 (GOOS 和 GOARCH),因此不切實際將工具鏈模組校驗總和寫入 go.sum。反之,如果 GOSUMDB=off,工具鏈下載會因缺乏驗證而失敗。GOPRIVATEGONOSUMDB 模式不適用於工具鏈下載。

使用 go get 管理 Go 版本模組需求

一般而言,go 指令會將 gotoolchain 行視為宣告主模組的版本化工具鏈相依性。go get 指令可以管理這些行,就像管理指定版本化模組相依性的 require 行一樣。

例如,go get go@1.22.1 toolchain@1.24rc1 會將主模組的 go.mod 檔案變更為 go 1.22.1toolchain go1.24rc1

go 指令了解 go 相依性需要一個版本大於或等於 Go 版本的 toolchain 相依性。

繼續這個範例,後面的 go get go@1.25.0 也會將工具鏈更新為 go1.25.0。當工具鏈完全符合 go 行時,可以省略並暗示,因此這個 go get 會刪除 toolchain 行。

降級時也適用相同的需求:如果 go.modgo 1.22.1toolchain go1.24rc1 開始,則 go get toolchain@go1.22.9 只會更新 toolchain 行,但 go get toolchain@go1.21.3 也會將 go 行降級為 go 1.21.3。結果只會留下 go 1.21.3 而沒有 toolchain 行。

特殊形式 toolchain@none 表示移除任何 toolchain 行,例如 go get toolchain@nonego get go@1.25.0 toolchain@none

go 指令了解 gotoolchain 相依性以及查詢的版本語法。

例如,就像 go get example.com/widget@v1.2 使用 example.com/widget 的最新 v1.2 版本(可能是 v1.2.3)一樣,go get go@1.22 使用 Go 1.22 語言版本的最新可用版本(可能是 1.22rc3,也可能是 1.22.3)。go get toolchain@go1.22 也適用相同規則。

go getgo mod tidy 指令會維護 go 行,使其大於或等於任何所需相依模組的 go 行。

例如,如果主模組有 go 1.22.1,而我們執行 go get example.com/widget@v1.2.3,其宣告 go 1.24rc1,則 go get 會將主模組的 go 行更新為 go 1.24rc1

繼續這個範例,後來的 go get go@1.22.1 會將 example.com/widget 降級至與 Go 1.22.1 相容的版本,或完全移除需求,就像降級 example.com/widget 的任何其他相依項一樣。

在 Go 1.21 之前,建議更新模組至新 Go 版本(例如 Go 1.22)的方式是 go mod tidy -go=1.22,以確保在更新 go 行的同時,對 Go 1.22 特有的任何調整都已套用至 go.mod。這種形式仍然有效,但現在建議使用更簡單的 go get go@1.22

當在工作區根目錄所包含的目錄中的模組中執行 go get 時,go get 大多會忽略工作區,但它會更新 go.work 檔案,以升級 go 行,否則工作區將會保留過舊的 go 行。

使用 go work 管理 Go 版本工作區需求

如前一節所述,在工作區根目錄內的目錄中執行 go get 會小心更新 go.work 檔案的 go 行,以大於或等於該根目錄內的任何模組。不過,工作區也可以參考根目錄外的模組;在那些目錄中執行 go get 可能會導致無效的工作區設定,其中 go.work 中宣告的 go 版本小於 use 指令中的其中一個或多個模組。

新增新 use 指令的指令 go work use,也會檢查 go.work 檔案中的 go 版本是否足夠新,以適用於所有現有的 use 指令。若要更新 go 版本與其模組不同步的工作區,請執行沒有引數的 go work use

指令 go work initgo work sync 也會視需要更新 go 版本。

若要從 go.work 檔案中移除 toolchain 行,請使用 go work edit -toolchain=none