Go Modules 參考
前言
Modules 是 Go 管理依賴關係的方式。
本文檔是 Go 模組系統的詳細參考手冊。關於建立 Go 專案的入門教學,請參閱如何編寫 Go 程式碼。有關使用模組、將專案遷移至模組以及其他主題的資訊,請參閱從使用 Go Modules 開始的系列部落格。
模組、套件與版本
模組 (Module) 是一組共同發布、進行版本控制並共同分發的套件 (package) 集合。模組可以直接從版本控制儲存庫或模組代理伺服器 (module proxy server) 下載。
模組透過模組路徑進行識別,該路徑宣告於 go.mod 檔案中,並包含該模組依賴關係的相關資訊。模組根目錄是包含 go.mod 檔案的目錄。主模組 (main module) 是包含呼叫 go 指令所在目錄的模組。
模組內的每個套件都是同一目錄下編譯在一起的原始程式碼檔案集合。套件路徑是由模組路徑加上包含該套件的子目錄(相對於模組根目錄)所組成。例如,模組 "golang.org/x/net" 在 "html" 目錄中包含一個套件。該套件的路徑即為 "golang.org/x/net/html"。
模組路徑
模組路徑是模組的規範名稱,透過模組 go.mod 檔案中的 module 指令宣告。模組路徑是模組內所有套件路徑的前綴。
模組路徑應描述模組的功能及其存放位置。通常,模組路徑由儲存庫根路徑、儲存庫內的目錄(通常為空)以及主要版本後綴(僅適用於主要版本 2 或更高)組成。
- 儲存庫根路徑是模組路徑中對應於開發該模組的版本控制儲存庫根目錄的部分。大多數模組定義在其儲存庫的根目錄中,因此這通常就是完整路徑。例如,
golang.org/x/net是同名模組的儲存庫根路徑。有關go指令如何透過模組路徑導出的 HTTP 請求來定位儲存庫的資訊,請參閱為模組路徑尋找儲存庫。 - 如果模組並未定義在儲存庫的根目錄中,模組子目錄就是模組路徑中命名該目錄的部分(不包含主要版本後綴)。這也作為語意版本標籤的前綴。例如,模組
golang.org/x/tools/gopls位於根路徑為golang.org/x/tools的儲存庫中的gopls子目錄內,因此其模組子目錄為gopls。請參閱將版本對應至提交與儲存庫內的模組目錄。 - 如果模組發布的主要版本為 2 或更高,模組路徑必須以像
/v2這樣的主要版本後綴結尾。這可能是也可能不是子目錄名稱的一部分。例如,路徑為golang.org/x/repo/sub/v2的模組可能位於儲存庫golang.org/x/repo的/sub或/sub/v2子目錄中。
如果一個模組可能會被其他模組依賴,則必須遵循這些規則,以便 go 指令能夠找到並下載該模組。模組路徑中允許的字元還有一些詞法限制。
若一個模組永遠不會被當作其他模組的依賴項來取得,則可以使用任何有效的套件路徑作為其模組路徑,但必須注意不要與模組依賴項或 Go 標準函式庫可能使用的路徑衝突。Go 標準函式庫使用的套件路徑,其第一個路徑元素不包含點號(dot),且 go 指令不會嘗試從網路伺服器解析此類路徑。example 與 test 路徑保留給使用者使用:它們不會在標準函式庫中使用,非常適合用於獨立模組,例如教學課程、範例程式碼,或作為測試的一部分而建立和操作的模組。
版本
版本識別模組的一個不可變快照,可以是正式發布版本 (release) 或預發布版本 (pre-release)。每個版本都以字母 v 開頭,後接語意版本號。關於版本如何格式化、解讀與比較,詳見 語意版本規範 2.0.0 (Semantic Versioning 2.0.0)。
總結來說,語意版本由三個非負整數(由左至右分別為主版本號、次版本號與修補版本號)組成,並以點號分隔。修補版本號後可接選用的以連字號開頭的預發布字串。預發布字串或修補版本號後可接以加號開頭的建置元資料字串。例如,v0.0.0、v1.12.134、v8.0.5-pre 與 v2.0.9+meta 均為有效版本。
版本的每個部分都指示該版本是否穩定,以及是否與先前版本相容。
- 當模組的公共介面或已記錄功能進行了向後不相容的變更(例如刪除套件)後,必須增加主版本號,並將次版本號與修補版本號歸零。
- 當進行了向後相容的變更(例如新增功能)後,必須增加次版本號,並將修補版本號歸零。
- 當進行了不影響模組公共介面的變更(例如錯誤修正或最佳化)後,必須增加修補版本號。
- 預發布後綴表示該版本為預發布版本。預發布版本的排序在對應的正式發布版本之前。例如,
v1.2.3-pre排在v1.2.3之前。 - 在比較版本時會忽略建置元資料後綴。go 指令接受帶有建置元資料的版本,並將其轉換為虛擬版本 (pseudo-versions),以維護版本間的總排序。
- 特殊後綴
+incompatible表示在遷移至模組系統前發布的主要版本 2 或更高版本(請參閱與非模組儲存庫的相容性)。 - 當使用 Go 工具鏈 1.24 或更高版本,且在工作目錄中包含未提交變更的有效本地版本控制系統 (VCS) 儲存庫內建置二進位檔案時,特殊後綴
+dirty會附加在版本資訊之後。
- 特殊後綴
如果主版本號為 0 或具有預發布後綴,則該版本被視為不穩定。不穩定版本不受相容性要求的約束。例如,v0.2.0 可能與 v0.1.0 不相容,而 v1.5.0-beta 可能與 v1.5.0 不相容。
Go 可以使用不遵循這些慣例的標籤、分支或修訂版本來存取版本控制系統中的模組。然而,在主模組內,go 指令會自動將不符合此標準的修訂名稱轉換為規範版本。作為此過程的一部分,go 指令也會移除建置元資料後綴(+incompatible 除外)。這可能會產生一個虛擬版本 (pseudo-version),這是一種包含版本控制系統中的修訂識別碼(如 Git 提交雜湊)與時間戳記的預發布版本。例如,指令 go get golang.org/x/net@daa7c041 會將提交雜湊 daa7c041 轉換為虛擬版本 v0.0.0-20191109021931-daa7c04131f5。規範版本在主模組外是必須的,如果 go.mod 檔案中出現像 master 這樣的非規範版本,go 指令會報告錯誤。
虛擬版本
虛擬版本 (pseudo-version) 是一種經過特殊格式化的預發布版本,它編碼了版本控制儲存庫中特定修訂版本的相關資訊。例如,v0.0.0-20191109021931-daa7c04131f5 即為一個虛擬版本。
虛擬版本可用於指向那些沒有可用語意版本標籤的修訂版本。它們可以用於在建立版本標籤之前測試提交,例如在開發分支上。
每個虛擬版本包含三個部分
- 基礎版本前綴(
vX.0.0或vX.Y.Z-0),這要麼源自於修訂版本之前的語意版本標籤,如果沒有此類標籤,則為vX.0.0。 - 時間戳記(
yyyymmddhhmmss),即修訂版本建立時的 UTC 時間。在 Git 中,這是提交時間,而非作者時間。 - 修訂識別碼(
abcdefabcdef),這是提交雜湊的 12 字元前綴;在 Subversion 中則為補零的修訂號。
根據基礎版本的不同,每個虛擬版本可能有三種形式之一。這些形式確保虛擬版本在比較時高於其基礎版本,但低於下一個標籤版本。
vX.0.0-yyyymmddhhmmss-abcdefabcdef用於沒有已知基礎版本時。與所有版本一樣,主版本號X必須與模組的主要版本後綴相符。vX.Y.Z-pre.0.yyyymmddhhmmss-abcdefabcdef用於基礎版本為如vX.Y.Z-pre的預發布版本時。vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdefabcdef用於基礎版本為如v1.2.3的正式發布版本時。例如,若基礎版本為v1.2.3,則虛擬版本可能是v1.2.4-0.20191109021931-daa7c04131f5。
多個虛擬版本可以透過使用不同的基礎版本指向同一個提交。當在寫入虛擬版本後標記了較低的版本時,這種情況會自然發生。
這些形式賦予虛擬版本兩個有用的屬性
- 具有已知基礎版本的虛擬版本排序高於這些版本,但低於後續版本的其他預發布版本。
- 具有相同基礎版本前綴的虛擬版本按時間順序排序。
go 指令會執行多次檢查,以確保模組作者能夠控制虛擬版本與其他版本的比較方式,並確保虛擬版本指向的是模組提交歷史中實際存在的修訂版本。
- 如果指定了基礎版本,則必須存在一個對應的語意版本標籤,且該標籤必須是虛擬版本所描述修訂版本的祖先。這可以防止開發者使用像
v1.999.999-99999999999999-daa7c04131f5這樣比較結果高於所有標籤版本的虛擬版本,進而繞過最小版本選擇。 - 時間戳記必須與修訂版本的時間戳記相符。這可以防止攻擊者使用數量無限且除此之外完全相同的虛擬版本來淹沒模組代理伺服器。這也防止了模組消費者改變版本的相對順序。
- 修訂版本必須是模組儲存庫分支或標籤的祖先。這可以防止攻擊者指向未經核准的變更或提取請求 (pull requests)。
虛擬版本永遠不需要手動輸入。許多指令接受提交雜湊或分支名稱,並會自動將其轉換為虛擬版本(若有可用標籤則轉換為標籤版本)。例如
go get example.com/mod@master
go list -m -json example.com/mod@abcd1234
主要版本後綴
從主要版本 2 開始,模組路徑必須具有一個與主版本號相符的主要版本後綴,如 /v2。例如,如果模組在 v1.0.0 時的路徑為 example.com/mod,則在 v2.0.0 版本時其路徑必須為 example.com/mod/v2。
主要版本後綴實現了匯入相容性規則 (import compatibility rule)
如果舊套件與新套件具有相同的匯入路徑,則新套件必須與舊套件向後相容。
根據定義,模組新主要版本的套件與前一個主要版本中對應的套件並不向後相容。因此,從 v2 開始,套件需要新的匯入路徑。這是透過在模組路徑中新增主要版本後綴來實現的。由於模組路徑是模組內每個套件匯入路徑的前綴,因此在模組路徑中新增主要版本後綴為每個不相容的版本提供了截然不同的匯入路徑。
主要版本 v0 或 v1 不允許使用主要版本後綴。在 v0 與 v1 之間無需變更模組路徑,因為 v0 版本不穩定且沒有相容性保證。此外,對於大多數模組而言,v1 與最後一個 v0 版本向後相容;v1 版本是對相容性的承諾,而非表示相較於 v0 有不相容的變更。
作為特殊情況,以 gopkg.in/ 開頭的模組路徑必須始終具有主要版本後綴,即使在 v0 與 v1 也是如此。後綴必須以點號而非斜線開頭(例如,gopkg.in/yaml.v2)。
主要版本後綴允許模組的多個主要版本在同一個建置中共存。由於鑽石依賴問題 (diamond dependency problem),這可能是必要的。通常情況下,如果遞移依賴項需要模組的兩個不同版本,則會使用較高的版本。但是,如果這兩個版本不相容,則沒有任何版本能滿足所有客戶端。由於不相容的版本必須具有不同的主版本號,因此根據主要版本後綴,它們也必須具有不同的模組路徑。這解決了衝突:具有不同後綴的模組被視為獨立的模組,並且它們的套件(即使是相對於模組根目錄位於相同子目錄的套件)也是不同的。
許多 Go 專案在遷移至模組系統前(或許是在模組引入之前)就已發布了 v2 或更高版本,且未使用主要版本後綴。這些版本使用 +incompatible 建置標籤進行標註(例如 v2.0.0+incompatible)。詳見與非模組儲存庫的相容性以獲取更多資訊。
將套件解析為模組
當 go 指令使用套件路徑載入套件時,它需要確定哪個模組提供了該套件。
go 指令首先搜尋建置清單,尋找路徑為套件路徑前綴的模組。例如,如果匯入了套件 example.com/a/b,且模組 example.com/a 在建置清單中,go 指令將檢查 example.com/a 是否包含該套件(在 b 目錄中)。目錄中必須至少有一個副檔名為 .go 的檔案,該目錄才會被視為套件。建置約束在此目的下並不適用。如果建置清單中恰好有一個模組提供該套件,則使用該模組。如果沒有模組提供該套件,或兩個以上的模組提供該套件,go 指令會報告錯誤。-mod=mod 旗標會指示 go 指令嘗試尋找提供缺失套件的新模組,並更新 go.mod 與 go.sum。go get 與 go mod tidy 指令會自動執行此操作。
當 go 指令為套件路徑尋找新模組時,它會檢查 GOPROXY 環境變數,這是一個以逗號分隔的代理伺服器 URL 列表,或是 direct 或 off 關鍵字。代理伺服器 URL 指示 go 指令應使用 GOPROXY 協定聯絡模組代理伺服器。direct 指示 go 指令應與版本控制系統通訊。off 指示不應嘗試通訊。GOPRIVATE 與 GONOPROXY 環境變數也可用於控制此行為。
對於 GOPROXY 列表中的每個項目,go 指令會請求可能提供該套件的每個模組路徑的最新版本(即套件路徑的每個前綴)。對於每個成功請求的模組路徑,go 指令將下載最新版本的模組,並檢查該模組是否包含所請求的套件。如果一個或多個模組包含所請求的套件,則使用路徑最長的模組。如果找到一個或多個模組但都不包含所請求的套件,則報告錯誤。如果找不到模組,go 指令會嘗試 GOPROXY 列表中的下一個項目。如果沒有剩餘項目,則報告錯誤。
例如,假設 go 指令正在尋找提供套件 golang.org/x/net/html 的模組,且 GOPROXY 設為 https://corp.example.com,https://proxy.golang.org。go 指令可能會發出以下請求
- 對
https://corp.example.com/(並行請求)- 請求
golang.org/x/net/html的最新版本 - 請求
golang.org/x/net的最新版本 - 請求
golang.org/x的最新版本 - 請求
golang.org的最新版本
- 請求
- 對
https://proxy.golang.org/,若所有對https://corp.example.com/的請求均因 404 或 410 而失敗- 請求
golang.org/x/net/html的最新版本 - 請求
golang.org/x/net的最新版本 - 請求
golang.org/x的最新版本 - 請求
golang.org的最新版本
- 請求
在找到合適的模組後,go 指令會將一個新的需求 (requirement)(包含新模組的路徑與版本)新增至主模組的 go.mod 檔案中。這確保了未來再次載入相同套件時,將使用相同版本的相同模組。如果解析出的套件未被主模組中的任何套件匯入,則該新需求會帶有 // indirect 註解。
go.mod 檔案
模組由其根目錄中名為 go.mod 的 UTF-8 編碼文字檔定義。go.mod 檔案是面向行的。每一行包含單一指令,由關鍵字後接參數組成。例如
module example.com/my/thing
go 1.23.0
require example.com/other/thing v1.0.2
require example.com/new/thing/v2 v2.3.4
exclude example.com/old/thing v1.2.3
replace example.com/bad/thing v1.4.5 => example.com/good/thing v1.4.5
retract [v1.9.0, v1.9.5]
領先的關鍵字可以從相鄰的行中提取出來以建立區塊,類似於 Go 中的匯入語法。
require (
example.com/new/thing/v2 v2.3.4
example.com/old/thing v1.2.3
)
go.mod 檔案的設計目標是人類可讀且機器可寫。go 指令提供了幾個變更 go.mod 檔案的子指令。例如,go get 可以升級或降級特定的依賴項。載入模組圖的指令會在需要時自動更新 go.mod。go mod edit 可以執行低階編輯。golang.org/x/mod/modfile 套件可供 Go 程式以程式化方式進行相同的變更。
主模組以及任何以本地檔案路徑指定的替換模組都必須有 go.mod 檔案。然而,一個缺少明確 go.mod 檔案的模組仍然可以作為依賴項被需求 (required),或者作為以模組路徑和版本指定的替換項來使用;詳見與非模組儲存庫的相容性。
詞法元素
當 go.mod 檔案被解析時,其內容會被拆分為一系列標記 (tokens)。標記有多種:空白字元、註解、標點符號、關鍵字、識別碼與字串。
空白字元 由空格 (U+0020)、定位字元 (U+0009)、歸位字元 (U+000D) 與換行符 (U+000A) 組成。除換行符外,空白字元沒有作用,僅用於分隔本應合併在一起的標記。換行符是重要的標記。
註解 以 // 開頭並延伸至行尾。不允許使用 /* */ 註解。
標點符號 標記包括 (、) 與 =>。
關鍵字 用於區分 go.mod 檔案中的不同指令類型。允許的關鍵字為 module、go、require、replace、exclude 與 retract。
識別碼 是非空白字元的序列,例如模組路徑或語意版本。
字串 是引號括起來的字元序列。有兩種類型的字串:以引號(",U+0022)開頭與結尾的解讀字串 (interpreted strings),以及以反引號(`,U+0060)開頭與結尾的原始字串 (raw strings)。解讀字串可以包含由反斜線(\,U+005C)後接另一個字元組成的轉義序列。轉義的引號(\")不會終止解讀字串。解讀字串的未引號值是引號之間的字元序列,其中每個轉義序列替換為反斜線後的字元(例如,\" 替換為 ",\n 替換為換行符)。相比之下,原始字串的未引號值只是反引號之間的字元序列;反斜線在原始字串內沒有特殊含義。
識別碼與字串在 go.mod 語法中是可以互換的。
模組路徑與版本
go.mod 檔案中的大多數識別碼與字串要麼是模組路徑,要麼是版本。
模組路徑必須滿足以下要求
- 路徑必須由一個或多個以斜線(
/,U+002F)分隔的路徑元素組成。它不能以斜線開頭或結尾。 - 每個路徑元素都是由 ASCII 字母、ASCII 數字與有限的 ASCII 標點符號(
-、.、_與~)組成的非空字串。 - 路徑元素不能以點號(
.,U+002E)開頭或結尾。 - 直到第一個點號為止的元素前綴,不能是 Windows 上的保留檔案名稱,不區分大小寫(例如
CON、com1、NuL等)。 - 直到第一個點號為止的元素前綴,不能以波浪號後接一個或多個數字結尾(例如
EXAMPL~1.COM)。
如果模組路徑出現在 require 指令中且未被替換,或者如果模組路徑出現在 replace 指令的右側,則 go 指令可能需要下載具有該路徑的模組,因此必須滿足一些額外要求。
- 領先的路徑元素(直到第一個斜線為止,若有),按照慣例為網域名稱,必須僅包含小寫 ASCII 字母、ASCII 數字、點號(
.,U+002E)與連字號(-,U+002D);它必須至少包含一個點號且不能以連字號開頭。 - 對於形式為
/vN的最終路徑元素,其中N看起來是數字(ASCII 數字與點號),N不能以零開頭,不能是/v1,且不能包含任何點號。- 對於以
gopkg.in/開頭的路徑,此要求被替換為該路徑必須遵循 gopkg.in 服務慣例的要求。
- 對於以
go.mod 檔案中的版本可以是規範 (canonical) 或非規範的。
規範版本以字母 v 開頭,後接遵循 語意版本規範 2.0.0 的語意版本。詳見版本以獲取更多資訊。
大多數其他識別碼與字串可用作非規範版本,儘管為了避免檔案系統、儲存庫與模組代理伺服器的問題,有一些限制。非規範版本僅允許在主模組的 go.mod 檔案中使用。當 go 指令自動更新 go.mod 檔案時,會嘗試將每個非規範版本替換為對等的規範版本。
在模組路徑與版本相關聯的地方(如 require、replace 與 exclude 指令),最終路徑元素必須與版本一致。詳見主要版本後綴。
語法
go.mod 語法如下文使用擴充巴科斯-諾爾範式 (EBNF) 指定。詳見 Go 語言規範中的符號章節以獲取關於 EBNF 語法的詳細資訊。
GoMod = { Directive } .
Directive = ModuleDirective |
GoDirective |
ToolDirective |
IgnoreDirective |
RequireDirective |
ExcludeDirective |
ReplaceDirective |
RetractDirective .
換行符、識別碼與字串分別表示為 newline、ident 與 string。
模組路徑與版本表示為 ModulePath 與 Version。
ModulePath = ident | string . /* see restrictions above */
Version = ident | string . /* see restrictions above */
module 指令
module 指令定義了主模組的路徑。go.mod 檔案必須恰好包含一個 module 指令。
ModuleDirective = "module" ( ModulePath | "(" newline ModulePath newline ")" ) newline .
範例
module golang.org/x/net
棄用 (Deprecation)
模組可以在段落開頭包含字串 Deprecated:(區分大小寫)的註解區塊中被標記為棄用。棄用訊息在冒號之後開始並延伸至該段落末尾。註解可能出現在 module 指令之前,或其後的同一行。
範例
// Deprecated: use example.com/mod/v2 instead.
module example.com/mod
自 Go 1.17 起,go list -m -u 會檢查建置清單中所有已棄用模組的資訊。go get 會檢查建置命令列上指定套件所需的已棄用模組。
當 go 指令擷取模組的棄用資訊時,它會從符合 @latest 版本查詢的版本載入 go.mod 檔案,而不考慮撤銷 (retractions) 或排除 (exclusions)。go 指令會從同一個 go.mod 檔案載入撤銷版本 (retracted versions) 的清單。
要棄用一個模組,作者可以新增 // Deprecated: 註解並標記一個新的發布版本。作者可以在更高的版本中更改或移除棄用訊息。
棄用適用於模組的所有次版本。高於 v2 的主要版本出於此目的被視為獨立的模組,因為它們的主要版本後綴賦予了它們獨特的模組路徑。
棄用訊息旨在通知使用者該模組已不再受到支援,並提供遷移說明,例如遷移至最新的主要版本。個別的次版本與修補版本無法棄用;retract 可能更適合此類情況。
go 指令
go 指令表示該模組是在假設特定版本 Go 語意的情況下編寫的。版本必須是有效的 Go 版本,例如 1.14、1.21rc1 或 1.23.0。
go 指令設定使用此模組所需的最低 Go 版本。在 Go 1.21 之前,該指令僅供建議;現在它是一項強制要求:Go 工具鏈會拒絕使用宣告較新 Go 版本的模組。
go 指令是選擇執行哪個 Go 工具鏈的輸入來源。詳情請參閱“Go 工具鏈”。
go 指令影響新語言特性的使用
- 對於模組內的套件,編譯器會拒絕使用在
go指令指定的版本之後引入的語言特性。例如,如果一個模組具有指令go 1.12,其套件可能無法使用如1_000_000這樣的數值字面值,因為該特性是在 Go 1.13 中引入的。 - 如果較舊的 Go 版本建置了該模組的一個套件並遇到編譯錯誤,錯誤會註明該模組是為較新的 Go 版本編寫的。例如,假設一個模組具有
go 1.13且一個套件使用了數值字面值1_000_000。如果該套件是用 Go 1.12 建置的,編譯器會註明該程式碼是為 Go 1.13 編寫的。
go 指令也會影響 go 指令的行為
- 在
go 1.14或更高版本中,自動廠商化 (vendoring) 可能被啟用。如果vendor/modules.txt檔案存在且與go.mod一致,則無需明確使用-mod=vendor旗標。 - 在
go 1.16或更高版本中,all套件模式僅匹配由主模組中的套件與測試所遞移匯入的套件。這是自模組引入以來go mod vendor保留的同一套件集合。在較低版本中,all還包括主模組中套件所匯入套件的測試、這些套件的測試,依此類推。 - 在
go 1.17或更高版本中go.mod檔案包含每個模組的明確require指令,該模組提供由主模組中的套件或測試所遞移匯入的任何套件。(在go 1.16及更低版本中,僅在最小版本選擇會選擇不同版本時,才會包含間接依賴項。)此額外資訊啟用了模組圖修剪 (module graph pruning) 與延遲載入 (lazy module loading)。- 由於可能存在比先前
go版本更多的// indirect依賴項,間接依賴項被記錄在go.mod檔案內的獨立區塊中。 go mod vendor會省略廠商化依賴項的go.mod與go.sum檔案。(這允許在vendor子目錄內呼叫go指令來識別正確的主模組。)go mod vendor會將每個依賴項go.mod檔案中的go版本記錄在vendor/modules.txt中。
- 在
go 1.21或更高版本中go行宣告了與此模組一起使用的最低 Go 版本要求。go行必須大於或等於所有依賴項的go行。go指令不再嘗試維持與先前較舊 Go 版本的相容性。go指令在go.sum檔案中維持go.mod檔案校驗和 (checksums) 方面更加謹慎。
go.mod 檔案最多可包含一個 go 指令。如果不存在,大多數指令會新增一個包含當前 Go 版本的 go 指令。
如果缺少 go 指令,則預設為 go 1.16。
GoDirective = "go" GoVersion newline .
GoVersion = string | ident . /* valid release version; see above */
範例
go 1.23.0
toolchain 指令
toolchain 指令宣告了與模組一起使用的建議 Go 工具鏈。建議的 Go 工具鏈版本不能低於 go 指令中宣告的所需 Go 版本。toolchain 指令僅在模組是主模組且預設工具鏈版本低於建議工具鏈版本時生效。
為了可重現性,每當 go 指令更新 go.mod 檔案中的 go 版本時(通常是在 go get 期間),它都會在 toolchain 行中寫入其自己的工具鏈名稱。
詳情請參閱“Go 工具鏈”。
ToolchainDirective = "toolchain" ToolchainName newline .
ToolchainName = string | ident . /* valid toolchain name; see “Go toolchains” */
範例
toolchain go1.21.0
godebug 指令
godebug 指令宣告了當此模組作為主模組時要套用的單一 GODEBUG 設定。可以有多個這樣的行,且可以進行因數分解。如果主模組命名了一個不存在的 GODEBUG 金鑰,則是錯誤的。godebug key=value 的效果就像每個正在編譯的主套件都包含一個列出 //go:debug key=value 的原始檔。
GodebugDirective = "godebug" ( GodebugSpec | "(" newline { GodebugSpec } ")" newline ) .
GodebugSpec = GodebugKey "=" GodebugValue newline.
GodebugKey = GodebugChar { GodebugChar }.
GodebugValue = GodebugChar { GodebugChar }.
GodebugChar = any non-space character except , " ` ' (comma and quotes).
範例
godebug default=go1.21
godebug (
panicnil=1
asynctimerchan=0
)
require 指令
require 指令宣告了給定模組依賴項的最低要求版本。對於每個要求的模組版本,go 指令會載入該版本的 go.mod 檔案並合併該檔案中的需求。載入所有需求後,go 指令會使用最小版本選擇 (MVS) 將其解析為建置清單。
go 指令會自動為某些需求新增 // indirect 註解。// indirect 註解表示主模組中的任何套件都沒有直接匯入該所要求模組中的任何套件。
如果 go 指令指定 go 1.16 或更低版本,當模組選定版本高於主模組其他依賴項已(遞移)隱含的版本時,go 指令會新增間接需求。這可能是由於明確升級 (go get -u ./...)、刪除先前強加該要求的其他依賴項 (go mod tidy),或依賴項匯入了一個在其自己的 go.mod 檔案中沒有對應要求的套件(例如完全缺少 go.mod 檔案的依賴項)所導致的。
在 go 1.17 及更高版本中,go 指令會為每個模組新增間接需求,只要該模組提供了由主模組中的套件或測試(甚至間接地)所匯入、或作為引數傳遞給 go get 的任何套件即可。這些更全面的需求啟用了模組圖修剪與延遲載入。
RequireDirective = "require" ( RequireSpec | "(" newline { RequireSpec } ")" newline ) .
RequireSpec = ModulePath Version newline .
範例
require golang.org/x/net v1.2.3
require (
golang.org/x/crypto v1.4.5 // indirect
golang.org/x/text v1.6.7
)
tool 指令
tool 指令新增套件作為當前模組的依賴項。當當前工作目錄位於此模組內,或位於包含此模組的工作區內時,它也使其可用於透過 go tool 執行。
如果工具套件不在當前模組中,則必須存在指定要使用之工具版本的 require 指令。
tool 元模式會解析為當前模組 go.mod 中定義的工具清單,或在工作區模式下,解析為工作區中所有模組所定義工具的並集。
ToolDirective = "tool" ( ToolSpec | "(" newline { ToolSpec } ")" newline ) .
ToolSpec = ModulePath newline .
範例
tool golang.org/x/tools/cmd/stringer
tool (
example.com/module/cmd/a
example.com/module/cmd/b
)
ignore 指令
ignore 指令會導致 go 指令在匹配套件模式時忽略以斜線分隔的目錄路徑,以及遞迴包含在其中的任何檔案或目錄。
如果路徑以 ./ 開頭,則該路徑被解釋為相對於模組根目錄,並且在匹配套件模式時,該目錄以及遞迴包含在其中的任何目錄或檔案都將被忽略。
否則,在模組中任何深度的該路徑下的任何目錄,以及遞迴包含在其中的任何目錄或檔案都將被忽略。
IgnoreDirective = "ignore" ( IgnoreSpec | "(" newline { IgnoreSpec } ")" newline ) .
IgnoreSpec = RelativeFilePath newline .
RelativeFilePath = /* slash-separated relative file path */ .
範例
ignore ./node_modules
ignore (
static
content/html
./third_party/javascript
)
exclude 指令
exclude 指令可防止 go 指令載入特定的模組版本。
自 Go 1.16 起,如果任何 go.mod 檔案中的 require 指令所引用的版本被主模組 go.mod 檔案中的 exclude 指令排除,則該需求會被忽略。這可能會導致如 go get 與 go mod tidy 等指令將更高版本的新需求新增至 go.mod,並在適當時附帶 // indirect 註解。
在 Go 1.16 之前,如果 require 指令引用了已排除的版本,go 指令會列出該模組的可用版本(如 go list -m -versions 所示)並載入下一個較高的非排除版本。這可能導致不確定的版本選擇,因為下一個較高的版本可能會隨時間而變更。正式發布版本與預發布版本均會被考慮,但虛擬版本則不會。如果沒有更高的版本,go 指令會報告錯誤。
exclude 指令僅適用於主模組的 go.mod 檔案,在其他模組中會被忽略。詳情請參閱最小版本選擇。
ExcludeDirective = "exclude" ( ExcludeSpec | "(" newline { ExcludeSpec } ")" newline ) .
ExcludeSpec = ModulePath Version newline .
範例
exclude golang.org/x/net v1.2.3
exclude (
golang.org/x/crypto v1.4.5
golang.org/x/text v1.6.7
)
replace 指令
replace 指令將模組特定版本或所有版本的內容替換為其他地方的內容。替換項可以指定為另一個模組路徑與版本,或特定於平台的檔案路徑。
如果箭頭 (=>) 左側存在版本,則僅替換該特定版本的模組;其他版本將正常存取。如果省略左側版本,則替換模組的所有版本。
如果箭頭右側的路徑是絕對路徑或相對路徑(以 ./ 或 ../ 開頭),則它被解釋為替換模組根目錄的本地檔案路徑,該路徑必須包含 go.mod 檔案。此情況下必須省略替換版本。
如果右側的路徑不是本地路徑,則它必須是有效的模組路徑。此情況下,需要版本。同一模組版本不能同時出現在建置清單中。
無論替換項是透過本地路徑還是模組路徑指定,如果替換模組具有 go.mod 檔案,其 module 指令必須與其所替換的模組路徑相符。
replace 指令僅適用於主模組的 go.mod 檔案,在其他模組中會被忽略。詳情請參閱最小版本選擇。
如果有多個主模組,所有主模組的 go.mod 檔案皆適用。主模組之間衝突的 replace 指令是不允許的,必須在 go.work 檔案的 replace 中移除或覆寫。
請注意,僅僅使用 replace 指令不會將模組新增至模組圖。還需要一個引用被替換模組版本的 require 指令,無論是在主模組的 go.mod 檔案中還是依賴項的 go.mod 檔案中。如果左側的模組版本未被需求,則 replace 指令無效。
ReplaceDirective = "replace" ( ReplaceSpec | "(" newline { ReplaceSpec } ")" newline ) .
ReplaceSpec = ModulePath [ Version ] "=>" FilePath newline
| ModulePath [ Version ] "=>" ModulePath Version newline .
FilePath = /* platform-specific relative or absolute file path */
範例
replace golang.org/x/net v1.2.3 => example.com/fork/net v1.4.5
replace (
golang.org/x/net v1.2.3 => example.com/fork/net v1.4.5
golang.org/x/net => example.com/fork/net v1.4.5
golang.org/x/net v1.2.3 => ./fork/net
golang.org/x/net => ./fork/net
)
retract 指令
retract 指令表示不應依賴 go.mod 定義的模組版本或版本範圍。當版本過早發布或在版本發布後發現嚴重問題時,retract 指令非常有用。已撤銷的版本應保留在版本控制儲存庫與模組代理伺服器上,以確保依賴它們的建置不會損壞。撤銷 (retract) 一詞借鑑自學術文獻:撤銷的論文仍然可用,但它有問題,不應成為後續工作的基礎。
當模組版本被撤銷時,使用者不會自動使用 go get、go mod tidy 或其他指令升級到該版本。依賴已撤銷版本的建置應能繼續工作,但當使用者使用 go list -m -u 檢查更新或使用 go get 更新相關模組時,會收到撤銷通知。
要撤銷版本,模組作者應在 go.mod 中新增 retract 指令,然後發布包含該指令的新版本。新版本必須高於其他正式發布或預發布版本;即 @latest 版本查詢應在考慮撤銷之前解析為新版本。go 指令會從 go list -m -retracted $modpath@latest(其中 $modpath 為模組路徑)顯示的版本載入並應用撤銷。
除非使用 -retracted 旗標,否則已撤銷的版本會隱藏在 go list -m -versions 列印的版本清單中。在解析如 @>=v1.2.3 或 @latest 等版本查詢時,已撤銷的版本會被排除。
包含撤銷的版本可以撤銷其自身。如果模組的最高正式發布或預發布版本撤銷了其自身,則在排除已撤銷版本後,@latest 查詢會解析為較低版本。
舉個例子,假設模組 example.com/m 的作者不小心發布了 v1.0.0 版本。為了防止使用者升級到 v1.0.0,作者可以在 go.mod 中新增兩個 retract 指令,然後用撤銷標記 v1.0.1。
retract (
v1.0.0 // Published accidentally.
v1.0.1 // Contains retractions only.
)
當使用者執行 go get example.com/m@latest 時,go 指令會讀取已是最高版本的 v1.0.1 中的撤銷。v1.0.0 與 v1.0.1 均被撤銷,因此 go 指令會升級(或降級!)到下一個最高版本,可能是 v0.9.5。
retract 指令可以寫為單一版本(例如 v1.0.0),或寫為具有上限與下限的封閉版本區間,以 [ 與 ] 定界(例如 [v1.1.0, v1.2.0])。單一版本等同於上限與下限相同的區間。與其他指令一樣,多個 retract 指令可以在行末以 ( 定界並在獨立行上以 ) 結束的區塊中分組。
每個 retract 指令應包含解釋撤銷原因的註解,儘管這不是強制性的。go 指令可能會在關於已撤銷版本的警告以及 go list 輸出中顯示原因註解。原因註解可以寫在 retract 指令的正上方(中間無空白行)或同一行之後。如果註解出現在區塊上方,它適用於區塊內沒有自己註解的所有 retract 指令。原因註解可以跨越多行。
RetractDirective = "retract" ( RetractSpec | "(" newline { RetractSpec } ")" newline ) .
RetractSpec = ( Version | "[" Version "," Version "]" ) newline .
範例
- 撤銷
v1.0.0與v1.9.9之間的所有版本
retract v1.0.0
retract [v1.0.0, v1.9.9]
retract (
v1.0.0
[v1.0.0, v1.9.9]
)
- 在過早發布
v1.0.0版本後恢復為未標記版本
retract [v0.0.0, v1.0.1] // assuming v1.0.1 contains this retraction.
- 抹除包含所有虛擬版本與標籤版本的模組
retract [v0.0.0-0, v0.15.2] // assuming v0.15.2 contains this retraction.
retract 指令是在 Go 1.16 中新增的。如果 主模組的 go.mod 檔案中寫有 retract 指令,Go 1.15 及更低版本會報告錯誤,並忽略依賴項 go.mod 檔案中的 retract 指令。
自動更新
大多數指令在 go.mod 缺少資訊或無法準確反映現實時會報告錯誤。go get 與 go mod tidy 指令可用於修正大多數這類問題。此外,-mod=mod 旗標可與大多數模組感知指令(go build、go test 等)一起使用,以指示 go 指令自動修正 go.mod 與 go.sum 中的問題。
例如,考慮這個 go.mod 檔案
module example.com/M
go 1.23.0
require (
example.com/A v1
example.com/B v1.0.0
example.com/C v1.0.0
example.com/D v1.2.3
example.com/E dev
)
exclude example.com/D v1.2.3
透過 -mod=mod 觸發的更新會將非規範版本識別碼重寫為規範語意版本形式,因此 example.com/A 的 v1 變為 v1.0.0,而 example.com/E 的 dev 變為 dev 分支上最新提交的虛擬版本,可能是 v0.0.0-20180523231146-b3f5c0f6e5f1。
此更新會修改需求以遵守排除項,因此對已排除的 example.com/D v1.2.3 的需求會更新為使用 example.com/D 的下一個可用版本,可能是 v1.2.4 或 v1.3.0。
此更新會移除冗餘或誤導性的需求。例如,如果 example.com/A v1.0.0 本身需要 example.com/B v1.2.0 與 example.com/C v1.0.0,那麼 go.mod 中對 example.com/B v1.0.0 的需求就是誤導性的(已被 example.com/A 對 v1.2.0 的需求所取代),而對 example.com/C v1.0.0 的需求則是冗餘的(隱含在 example.com/A 對相同版本的需求中),因此兩者都會被移除。如果主模組包含直接匯入 example.com/B 或 example.com/C 套件的套件,則需求會被保留但更新為實際使用的版本。
最後,此更新會以規範格式重新格式化 go.mod,以便未來的機械變更產生最小的 diff。如果僅需格式化變更,go 指令不會更新 go.mod。
由於模組圖定義了匯入語句的含義,任何載入套件的指令也會使用 go.mod,因此可以更新它,包括 go build、go get、go install、go list、go test、go mod tidy。
在 Go 1.15 及更低版本中,-mod=mod 旗標預設啟用,因此更新會自動執行。自 Go 1.16 起,go 指令的行為就如同已設定 -mod=readonly 一樣:如果需要對 go.mod 進行任何變更,go 指令會報告錯誤並建議修正。
最小版本選擇 (MVS)
Go 使用一種稱為 最小版本選擇 (Minimal version selection, MVS) 的演算法,用於在建置套件時選擇要使用的一組模組版本。Russ Cox 在 最小版本選擇 一文中對 MVS 進行了詳細描述。
從概念上講,MVS 在模組的有向圖上運作,並由 go.mod 檔案指定。圖中的每個頂點代表一個模組版本。每條邊代表依賴項的最低要求版本,使用 require 指令指定。圖表可能會被主模組 go.mod 檔案中的 exclude 與 replace 指令,以及 go.work 檔案中的 replace 指令所修改。
MVS 產生建置清單作為輸出,這是用於建置的模組版本清單。
MVS 從主模組(圖中沒有版本的特殊頂點)開始,並遍歷圖表,追蹤每個模組的最高要求版本。遍歷結束時,最高的選擇版本構成了建置清單:它們是滿足所有需求的最小版本。
建置清單可以使用指令 go list -m all 進行檢查。與其他依賴管理系統不同,建置清單不會儲存在“鎖定”檔案中。MVS 是確定性的,當發布依賴項的新版本時,建置清單不會變更,因此每次模組感知指令啟動時,都會使用 MVS 來計算它。
考慮下圖中的範例。主模組要求模組 A 為 1.2 或更高版本,模組 B 為 1.2 或更高版本。A 1.2 與 B 1.2 分別要求 C 1.3 與 C 1.4。C 1.3 與 C 1.4 都要求 D 1.2。
MVS 訪問並載入藍色高亮顯示的每個模組版本的 go.mod 檔案。在圖遍歷結束時,MVS 返回一個包含粗體版本的建置清單:A 1.2、B 1.2、C 1.4 與 D 1.2。請注意,雖然有 B 與 D 的更高版本可用,但 MVS 沒有選擇它們,因為沒有任何需求要求它們。
替換
模組的內容(包括其 go.mod 檔案)可以使用主模組 go.mod 檔案或工作區 go.work 檔案中的 replace 指令進行替換。replace 指令可以應用於模組的特定版本或模組的所有版本。
替換會變更模組圖,因為替換模組可能具有與被替換版本不同的依賴關係。
考慮下方的範例,其中 C 1.4 已被 R 替換。R 依賴 D 1.3 而非 D 1.2,因此 MVS 返回的建置清單包含 A 1.2、B 1.2、C 1.4(已替換為 R)與 D 1.3。
排除
模組也可以使用主模組 go.mod 檔案中的 exclude 指令在特定版本被排除。
排除也會變更模組圖。當一個版本被排除時,它會從模組圖中移除,且對它的需求會被重定向到下一個較高的版本。
考慮下方的範例。C 1.3 已被排除。MVS 的作用如同 A 1.2 要求 C 1.4(下一個較高版本)而非 C 1.3。
升級
go get 指令可用於升級一組模組。若要執行升級,go 指令會在執行 MVS 之前,透過新增從訪問版本到升級版本的邊來變更模組圖。
考慮下方的範例。模組 B 可能從 1.2 升級至 1.3,C 可能從 1.3 升級至 1.4,D 可能從 1.2 升級至 1.3。
升級(與降級)可能會新增或移除間接依賴項。在此例中,E 1.1 與 F 1.1 在升級後出現在建置清單中,因為 B 1.3 要求 E 1.1。
為了保留升級,go 指令會更新 go.mod 中的需求。它會將對 B 的需求變更為 1.3 版本。它還會新增對 C 1.4 與 D 1.3 的需求並附帶 // indirect 註解,因為否則這些版本將不會被選擇。
降級
go get 指令也可用於降級一組模組。若要執行降級,go 指令會透過移除高於降級版本的版本來變更模組圖。它還會移除依賴於已移除版本的其他模組版本,因為它們可能與其依賴項的降級版本不相容。如果主模組要求因降級而被移除的模組版本,則需求會變更為尚未被移除的先前版本。如果沒有可用的先前版本,需求則會被丟棄。
考慮下方的範例。假設在 C 1.4 中發現問題,因此我們降級至 C 1.3。C 1.4 從模組圖中移除。B 1.2 也被移除,因為它要求 C 1.4 或更高版本。主模組對 B 的需求變更為 1.1。
go get 也可以完全移除依賴項,在參數後使用 @none 後綴即可。這與降級類似。該命名模組的所有版本都會從模組圖中移除。
模組圖修剪
如果主模組處於 go 1.17 或更高版本,用於最小版本選擇的模組圖僅包含每個在自身 go.mod 檔案中指定 go 1.17 或更高版本的模組依賴項的直接需求,除非該版本的模組也被其他處於 go 1.16 或更低版本的依賴項(遞移地)要求。(go 1.17 依賴項的遞移依賴項會從模組圖中修剪掉。)
由於 go 1.17 的 go.mod 檔案包含建置該模組中任何套件或測試所需之每個依賴項的 require 指令,因此修剪後的模組圖包含了 go build 或 go test 主模組明確要求的任何依賴項中的套件所需的所有依賴項。一個不是建置給定模組中任何套件或測試所需的模組,無法影響其套件的執行時行為,因此從模組圖中修剪掉的依賴項只會導致不相關模組之間的干擾。
需求已被修剪掉的模組仍然會出現在模組圖中,並且仍會由 go list -m all 回報:它們的選擇版本是已知且明確定義的,並且可以從這些模組載入套件(例如,作為從其他模組載入之測試的遞移依賴項)。然而,由於 go 指令無法輕易識別這些模組中的哪些依賴項被滿足,go build 與 go test 的參數不能包含來自需求已被修剪掉之模組的套件。go get 會將包含每個命名套件的模組提升為明確依賴項,從而允許對該套件呼叫 go build 或 go test。
由於 Go 1.16 及更早版本不支援模組圖修剪,因此對於每個指定 go 1.16 或更低版本的模組,仍包含完整的遞移依賴閉包(包括遞移的 go 1.17 依賴項)。(在 go 1.16 及更低版本中,go.mod 檔案僅包含直接依賴項,因此必須載入更大的圖表以確保包含所有間接依賴項。)
由 go mod tidy 為某模組記錄的 go.sum 檔案預設包含 Go 版本低於其 go 指令中所指定版本一個級別所需的校驗和。因此,一個 go 1.17 模組包含了 Go 1.16 所載入的完整模組圖所需的校驗和,但一個 go 1.18 模組將僅包含 Go 1.17 所載入的修剪後模組圖所需的校驗和。-compat 旗標可用於覆寫預設版本(例如,在 go 1.17 模組中更激進地修剪 go.sum 檔案)。
詳情請參閱設計文件。
延遲載入 (Lazy module loading)
為模組圖修剪新增的更全面需求,在模組內工作時也實現了另一種最佳化。如果主模組處於 go 1.17 或更高版本,go 指令會避免載入完整的模組圖,直到(並且除非)有需要時。相反,它僅載入主模組的 go.mod 檔案,然後嘗試僅使用這些需求來載入要建置的套件。如果待匯入的套件(例如,主模組外部套件的測試依賴項)在這些需求中找不到,則會在需要時載入模組圖的其餘部分。
如果可以在不載入模組圖的情況下找到所有匯入的套件,go 指令隨後將僅為包含這些套件的模組載入 go.mod 檔案,並將其需求與主模組的需求進行比對,以確保它們在本地是一致的。(不一致可能因版本控制合併、手動編輯以及使用本地檔案系統路徑替換的模組變更而產生。)
工作區 (Workspaces)
工作區 (Workspace) 是磁碟上一組模組的集合,在執行最小版本選擇 (MVS) 時用作主模組。
工作區可以在 go.work 檔案中宣告,該檔案指定了工作區中每個模組的相對路徑。當不存在 go.work 檔案時,工作區由包含當前目錄的單一模組組成。
大多數處理模組的 go 子指令都會在當前工作區(workspace)所決定的模組集合上運作。go mod init、go mod why、go mod edit、go mod tidy、go mod vendor 和 go get 則始終在單一主模組(main module)上運作。
指令會先檢查 GOWORK 環境變數,以確定是否處於工作區上下文中。若 GOWORK 設定為 off,該指令將在單一模組上下文中運作。若該變數為空或未提供,指令會搜尋當前工作目錄,隨後搜尋各層父目錄以尋找 go.work 檔案。若找到該檔案,指令將在它定義的工作區中運作;否則,工作區將僅包含含有工作目錄的模組。若 GOWORK 指定了現有且以 .work 結尾的檔案路徑,將會啟用工作區模式。若為其他任何值則會導致錯誤。您可以使用 go env GOWORK 指令來判斷 go 指令目前正在使用哪一個 go.work 檔案。若 go 指令不在工作區模式下,go env GOWORK 將會是空的。
go.work 檔案
工作區由一個名為 go.work 的 UTF-8 編碼文字檔定義。go.work 檔案以行為單位。每一行包含一個指令,由關鍵字後接參數組成。例如
go 1.23.0
use ./my/first/thing
use ./my/second/thing
replace example.com/bad/thing v1.4.5 => example.com/good/thing v1.4.5
與 go.mod 檔案一樣,可以將開頭的關鍵字從相鄰的行中提取出來,以建立一個區塊。
use (
./my/first/thing
./my/second/thing
)
go 指令提供了幾個用於操作 go.work 檔案的子指令。go work init 可建立新的 go.work 檔案。go work use 可將模組目錄新增至 go.work 檔案中。go work edit 可執行低階編輯。Go 程式可以使用 golang.org/x/mod/modfile 套件以程式設計方式進行相同的變更。
go 指令會維護一個 go.work.sum 檔案,該檔案會追蹤工作區所使用的雜湊值,這些雜湊值並未包含在集體工作區模組的 go.sum 檔案中。
通常不建議將 go.work 檔案提交到版本控制系統中,原因有二:
- 簽入的
go.work檔案可能會覆蓋開發者在父目錄中自有的go.work檔案,當他們的use指令不適用時,會造成混淆。 - 簽入的
go.work檔案可能會導致持續整合(CI)系統選擇並測試錯誤的模組依賴版本。CI 系統通常不應被允許使用go.work檔案,以便它們能夠測試模組在被其他模組需求時的行為,在這種情況下,模組內的go.work檔案將不會有任何影響。
話雖如此,在某些情況下提交 go.work 檔案是有意義的。例如,當儲存庫中的模組僅相互開發,而不與外部模組共同開發時,開發者可能沒有理由在工作區中使用不同的模組組合。在這種情況下,模組作者應確保個別模組已正確測試並發布。
詞法元素
go.work 檔案中的語彙元素定義方式與 go.mod 檔案完全相同。
語法
go.work 的語法在下方使用擴充巴科斯範式(EBNF)指定。請參閱 Go 語言規範中的記法章節以取得 EBNF 語法的詳細資訊。
GoWork = { Directive } .
Directive = GoDirective |
ToolchainDirective |
UseDirective |
ReplaceDirective .
換行符、識別碼與字串分別表示為 newline、ident 與 string。
模組路徑和版本分別以 ModulePath 和 Version 表示。模組路徑和版本的指定方式與 go.mod 檔案完全相同。
ModulePath = ident | string . /* see restrictions above */
Version = ident | string . /* see restrictions above */
go 指令
有效的 go.work 檔案中必須包含 go 指令。版本必須是有效的 Go 發布版本:一個正整數後接一個點,再接一個非負整數(例如 1.18、1.19)。
go 指令表示 go.work 檔案預期運作的 go 工具鏈版本。若對 go.work 檔案格式進行了變更,未來版本的工具鏈將根據其指定的版本來解讀該檔案。
一個 go.work 檔案最多只能包含一個 go 指令。
GoDirective = "go" GoVersion newline .
GoVersion = string | ident . /* valid release version; see above */
範例
go 1.23.0
toolchain 指令
toolchain 指令用於宣告工作區中建議使用的 Go 工具鏈。僅當預設工具鏈比建議的工具鏈舊時,它才會生效。
詳情請參閱“Go 工具鏈”。
ToolchainDirective = "toolchain" ToolchainName newline .
ToolchainName = string | ident . /* valid toolchain name; see “Go toolchains” */
範例
toolchain go1.21.0
godebug 指令
godebug 指令用於宣告在該工作區運作時應套用的單一 GODEBUG 設定。其語法與效果與 go.mod 檔案的 godebug 指令相同。當工作區處於啟用狀態時,go.mod 檔案中的 godebug 指令將被忽略。
use 指令
use 指令會將磁碟上的模組新增至工作區的主模組集合中。其參數為指向包含該模組 go.mod 檔案目錄的相對路徑。use 指令不會將其參數目錄的子目錄中包含的模組新增進來。這些模組可以由包含其 go.mod 檔案的目錄透過單獨的 use 指令新增。
UseDirective = "use" ( UseSpec | "(" newline { UseSpec } ")" newline ) .
UseSpec = FilePath newline .
FilePath = /* platform-specific relative or absolute file path */
範例
use ./mymod // example.com/mymod
use (
../othermod
./subdir/thirdmod
)
replace 指令
與 go.mod 檔案中的 replace 指令類似,go.work 檔案中的 replace 指令會將特定版本的模組,或該模組的所有版本,替換為其他地方的內容。go.work 中的萬用字元替換會覆蓋 go.mod 檔案中特定版本的 replace。
go.work 檔案中的 replace 指令會覆蓋工作區模組中相同模組或模組版本的任何替換。
ReplaceDirective = "replace" ( ReplaceSpec | "(" newline { ReplaceSpec } ")" newline ) .
ReplaceSpec = ModulePath [ Version ] "=>" FilePath newline
| ModulePath [ Version ] "=>" ModulePath Version newline .
FilePath = /* platform-specific relative or absolute file path */
範例
replace golang.org/x/net v1.2.3 => example.com/fork/net v1.4.5
replace (
golang.org/x/net v1.2.3 => example.com/fork/net v1.4.5
golang.org/x/net => example.com/fork/net v1.4.5
golang.org/x/net v1.2.3 => ./fork/net
golang.org/x/net => ./fork/net
)
與非模組儲存庫的相容性
為了確保從 GOPATH 順利過渡到模組,go 指令可以透過新增一個 go.mod 檔案,從尚未遷移到模組的儲存庫中,以模組感知模式(module-aware mode)下載並建置套件。
當 go 指令從儲存庫中直接下載指定版本的模組時,它會查找模組路徑的儲存庫 URL,將版本對應到儲存庫內的修訂版本,然後提取該修訂版本的儲存庫封存檔。若模組路徑等於儲存庫根路徑,且儲存庫根目錄不包含 go.mod 檔案,go 指令會在模組快取中合成一個 go.mod 檔案,其中僅包含一個 module 指令。由於合成的 go.mod 檔案不包含依賴項的 require 指令,其他依賴它們的模組可能需要額外的 require 指令(帶有 // indirect 註解)以確保每個依賴項在每次建置時都以相同版本擷取。
當 go 指令從代理(proxy)下載模組時,它會將 go.mod 檔案與模組其餘內容分開下載。若原始模組沒有 go.mod 檔案,代理預期會提供一個合成的 go.mod 檔案。
+incompatible 版本
發布於主版本 2 或更高版本的模組,其模組路徑必須具有相符的主版本後綴。例如,若模組發布於 v2.0.0,其路徑必須有一個 /v2 後綴。這允許 go 指令將專案的多個主版本視為不同的模組,即使它們是在同一個儲存庫中開發的。
主版本後綴要求是在 go 指令加入模組支援時引入的,許多儲存庫在此之前就已經標記了主版本 2 或更高的版本。為了維持與這些儲存庫的相容性,go 指令會為那些主版本 2 或更高且沒有 go.mod 檔案的版本加上 +incompatible 後綴。+incompatible 表示該版本與較低主版本的版本屬於同一個模組;因此,go 指令可能會自動升級到更高的 +incompatible 版本,即使這可能會破壞建置。
考慮以下需求範例
require example.com/m v4.1.2+incompatible
版本 v4.1.2+incompatible 指的是儲存庫中提供 example.com/m 模組的語意版本標籤 v4.1.2。該模組必須位於儲存庫根目錄中(即儲存庫根路徑也必須是 example.com/m),且不得存在 go.mod 檔案。該模組可能擁有如 v1.5.2 這樣的較低主版本,而 go 指令可能會從這些版本自動升級到 v4.1.2+incompatible(請參閱最小版本選擇 (MVS) 以了解升級運作方式的相關資訊)。
在標記版本 v2.0.0 之後遷移到模組的儲存庫,通常應該發布一個新的主版本。在上面的例子中,作者應該建立一個路徑為 example.com/m/v5 的模組,並發布版本 v5.0.0。作者還應該更新模組中套件的匯入,將前綴從 example.com/m 改為 example.com/m/v5。更多詳細範例請參閱 Go Modules: v2 and Beyond。
請注意,+incompatible 後綴不應出現在儲存庫的標籤中;類似 v4.1.2+incompatible 的標籤將會被忽略。此後綴僅出現在 go 指令所使用的版本中。關於版本與標籤之間的區別,請參閱版本對應至提交。
同時請注意,+incompatible 後綴可能出現在偽版本(pseudo-versions)上。例如,v2.0.1-20200722182040-012345abcdef+incompatible 可能是一個有效的偽版本。
最小模組相容性
發布於主版本 2 或更高版本的模組,要求其模組路徑必須具有主版本後綴。該模組可以在其儲存庫內的主版本子目錄中開發,也可以不在。這對在 GOPATH 模式下建置時匯入該模組內套件的專案會有影響。
通常在 GOPATH 模式下,套件儲存在其儲存庫根路徑與其在儲存庫內目錄路徑的結合目錄中。例如,在儲存庫根路徑為 example.com/repo 且位於 sub 子目錄中的套件,將儲存於 $GOPATH/src/example.com/repo/sub,並作為 example.com/repo/sub 匯入。
對於具有主版本後綴的模組,可能會預期在 $GOPATH/src/example.com/repo/v2/sub 目錄中找到 example.com/repo/v2/sub 套件。這會要求該模組在其儲存庫的 v2 子目錄中開發。go 指令支援這一點,但不強制要求(請參閱版本對應至提交)。
如果一個模組不是在主版本子目錄中開發的,那麼它在 GOPATH 中的目錄將不包含主版本後綴,且其套件在匯入時可能不需要主版本後綴。在上面的例子中,套件將會在 $GOPATH/src/example.com/repo/sub 目錄中被找到,並作為 example.com/repo/sub 匯入。
這為那些旨在模組模式和 GOPATH 模式下建置的套件帶來了問題:模組模式需要後綴,而 GOPATH 模式則不需要。
為了修正此問題,最小模組相容性(minimal module compatibility) 於 Go 1.11 中加入,並向後移植到了 Go 1.9.7 和 1.10.3。當匯入路徑在 GOPATH 模式下解析為目錄時:
- 當解析
$modpath/$vn/$dir形式的匯入時,其中:$modpath是有效的模組路徑,$vn是主版本後綴,$dir是可能為空的子目錄,
- 若以下條件全部成立:
- 套件
$modpath/$vn/$dir不存在於任何相關的vendor目錄中。 - 匯入該套件的檔案所在目錄,或直至
$GOPATH/src根目錄的任何父目錄中,存在go.mod檔案, - 不存在
$GOPATH[i]/src/$modpath/$vn/$suffix目錄(對於任何根目錄$GOPATH[i]), - 檔案
$GOPATH[d]/src/$modpath/go.mod存在(對於某個根目錄$GOPATH[d])且宣告該模組路徑為$modpath/$vn,
- 套件
- 那麼
$modpath/$vn/$dir的匯入將解析為$GOPATH[d]/src/$modpath/$dir目錄。
此規則允許已經遷移到模組的套件,在 GOPATH 模式下建置時,匯入其他同樣遷移到模組的套件,即使未採取主版本子目錄結構。
模組感知指令
大多數 go 指令皆可在模組感知模式或GOPATH 模式下執行。在模組感知模式下,go 指令使用 go.mod 檔案來尋找版本化的依賴項,通常從模組快取載入套件,並在缺少時下載模組。在 GOPATH 模式下,go 指令會忽略模組;它會從vendor 目錄和 GOPATH 中尋找依賴項。
從 Go 1.16 開始,預設啟用模組感知模式,無論是否包含 go.mod 檔案。在較舊的版本中,模組感知模式是在當前目錄或任何父目錄中存在 go.mod 檔案時啟用的。
模組感知模式可透過 GO111MODULE 環境變數進行控制,可設定為 on、off 或 auto。
- 若
GO111MODULE=off,go指令會忽略go.mod檔案並以GOPATH模式運作。 - 若
GO111MODULE=on或未設定,go指令將以模組感知模式運作,即使沒有go.mod檔案存在。並非所有指令在沒有go.mod檔案時都能運作:請參閱模組外的模組指令。 - 若
GO111MODULE=auto,則當當前目錄或任何父目錄中存在go.mod檔案時,go指令將以模組感知模式運作。在 Go 1.15 及更低版本中,這是預設行為。go mod子指令和使用版本查詢的go install即便在沒有go.mod檔案時也會以模組感知模式執行。
在模組感知模式下,GOPATH 不再定義建置期間匯入的意義,但它仍然儲存下載的依賴項(於 GOPATH/pkg/mod;請參閱模組快取)以及安裝的指令(於 GOPATH/bin,除非設定了 GOBIN)。
建置指令
所有載入套件資訊的指令皆為模組感知的。這包括:
go buildgo fixgo generatego installgo listgo rungo testgo vet
在模組感知模式下執行時,這些指令使用 go.mod 檔案來解讀命令列上列出或 Go 原始程式碼中寫入的匯入路徑。這些指令接受以下標記,這些標記為所有模組指令所共用。
-mod標記控制go.mod是否可自動更新,以及是否使用vendor目錄。-mod=mod指示go指令忽略 vendor 目錄並自動更新go.mod,例如,當某個匯入的套件未由任何已知模組提供時。-mod=readonly指示go指令忽略vendor目錄,若go.mod需要更新則回報錯誤。-mod=vendor指示go指令使用vendor目錄。在此模式下,go指令不會使用網路或模組快取。- 預設情況下,若
go.mod中的go版本為1.14或更高且存在vendor目錄,go指令的行為將如同使用了-mod=vendor。否則,go指令的行為將如同使用了-mod=readonly。 go get會拒絕此標記,因為該指令的目的是修改依賴項,這僅允許透過-mod=mod執行。
-modcacherw標記指示go指令在模組快取中建立新的目錄時使用讀寫權限,而非預設的唯讀權限。當持續使用此標記(通常透過設定環境變數GOFLAGS=-modcacherw或執行go env -w GOFLAGS=-modcacherw)時,模組快取可以透過rm -r之類的指令刪除,而無需先變更權限。go clean -modcache指令可用於刪除模組快取,無論是否使用了-modcacherw。-modfile=file.mod標記指示go指令讀取(並可能寫入)另一個檔案,而非模組根目錄中的go.mod。檔案名稱必須以.mod結尾。儘管仍需存在名為go.mod的檔案以確定模組根目錄,但它不會被存取。當指定了-modfile時,也會使用對應的go.sum檔案:其路徑是透過去除-modfile標記路徑的.mod副檔名並附加.sum來推導。
Vendoring
使用模組時,go 指令通常透過從來源將模組下載到模組快取,然後從下載的副本中載入套件來滿足依賴需求。Vendoring 可用於與舊版 Go 進行互通,或確保建置所需的所有檔案都儲存在單一檔案樹中。
go mod vendor 指令會在主模組的根目錄下建立一個名為 vendor 的目錄,其中包含建置和測試主模組中套件所需的所有套件副本。僅由主模組外部套件的測試所匯入的套件將不被包含。與 go mod tidy 及其他模組指令一樣,在建置 vendor 目錄時,除了 ignore 之外的建置約束(build constraints)將不被考慮。
go mod vendor 還會建立 vendor/modules.txt 檔案,其中包含已 vendored 套件的列表以及它們複製來源的模組版本。當啟用 vendoring 時,此清單會作為模組版本資訊的來源,正如 go list -m 和 go version -m 所報告。當 go 指令讀取 vendor/modules.txt 時,它會檢查模組版本是否與 go.mod 一致。若 go.mod 在 vendor/modules.txt 產生後已變更,go 指令將回報錯誤。此時應重新執行 go mod vendor 以更新 vendor 目錄。
若主模組的根目錄中存在 vendor 目錄,且主模組的 go.mod 檔案中的 go 版本為 1.14 或更高,則會自動使用該目錄。若要明確啟用 vendoring,請以 -mod=vendor 標記執行 go 指令。若要停用,請使用 -mod=readonly 或 -mod=mod 標記。
當啟用 vendoring 時,像 go build 和 go test 這樣的建置指令會從 vendor 目錄載入套件,而不會存取網路或本地模組快取。go list -m 指令僅列印 go.mod 中列出的模組資訊。go mod 指令(如 go mod download 和 go mod tidy)在啟用 vendoring 時的工作方式並無不同,它們仍會下載模組並存取模組快取。go get 在啟用 vendoring 時的運作方式也無不同。
與 GOPATH 模式下的 vendoring 不同,go 指令會忽略主模組根目錄以外的 vendor 目錄。此外,由於不使用其他模組中的 vendor 目錄,go 指令在建置模組 zip 檔案時不包含 vendor 目錄(但請參閱已知 bug #31562 和 #37397)。
go get
用法
go get [-d] [-t] [-u] [build flags] [packages]
範例
# Upgrade a specific module.
$ go get golang.org/x/net
# Upgrade modules that provide packages imported by packages in the main module.
$ go get -u ./...
# Upgrade or downgrade to a specific version of a module.
$ go get golang.org/x/text@v0.3.2
# Update to the commit on the module's master branch.
$ go get golang.org/x/text@master
# Remove a dependency on a module and downgrade modules that require it
# to versions that don't require it.
$ go get golang.org/x/text@none
# Upgrade the minimum required Go version for the main module.
$ go get go
# Upgrade the suggested Go toolchain, leaving the minimum Go version alone.
$ go get toolchain
# Upgrade to the latest patch release of the suggested Go toolchain.
$ go get toolchain@patch
go get 指令會更新主模組的 go.mod 檔案中的模組依賴項,然後建置並安裝命令列上列出的套件。
第一步是確定要更新哪些模組。go get 接受套件列表、套件模式和模組路徑作為參數。若指定了套件參數,go get 會更新提供該套件的模組。若指定了套件模式(例如 all 或帶有 ... 萬用字元的路徑),go get 會將該模式展開為一組套件,然後更新這些套件所屬的模組。若參數僅命名了模組而非套件(例如模組 golang.org/x/net 在其根目錄中沒有套件),go get 將更新該模組但不會建置套件。若未指定參數,go get 的行為如同指定了 .(當前目錄下的套件);這可以與 -u 標記結合使用,以更新提供匯入套件的模組。
每個參數皆可包含一個表示所需版本的版本查詢後綴,如 go get golang.org/x/text@v0.3.0。版本查詢後綴由 @ 符號後接一個版本查詢組成,該查詢可指示特定版本 (v0.3.0)、版本前綴 (v0.3)、分支或標籤名稱 (master)、修訂版本 (1234abcd),或特殊查詢 latest、upgrade、patch 或 none。若未提供版本,go get 使用 @upgrade 查詢。
一旦 go get 將參數解析為特定的模組和版本,它會新增、更改或刪除主模組 go.mod 檔案中的 require 指令,以確保模組在未來保持在所需的版本。請注意,go.mod 檔案中的需求版本是最低版本,隨著新依賴項的加入,這些版本可能會自動增加。關於模組感知指令如何選擇版本及解決衝突,請參閱最小版本選擇 (MVS)。
當命令列上命名的模組被新增、升級或降級時,其他模組也可能被升級,前提是該已命名模組的新版本需要更高版本的其他模組。例如,假設模組 example.com/a 升級到 v1.5.0,且該版本要求模組 example.com/b 為 v1.2.0。若 example.com/b 目前需求版本為 v1.1.0,則 go get example.com/a@v1.5.0 也會將 example.com/b 升級到 v1.2.0。
當命令列上命名的模組被降級或刪除時,其他模組也可能被降級。繼續上面的例子,假設 example.com/b 被降級到 v1.1.0,那麼模組 example.com/a 也會被降級到一個需要 example.com/b 版本 v1.1.0 或更低的版本。
可以使用版本後綴 @none 來刪除模組需求。這是一種特殊的降級方式。依賴於被刪除模組的模組將根據需要進行降級或刪除。即使其一個或多個套件被主模組中的套件匯入,模組需求仍可被刪除。在這種情況下,下一個建置指令可能會新增新的模組需求。
若模組在兩個不同版本中皆有需求(在命令列參數中明確指定,或為滿足升級和降級要求),go get 將回報錯誤。
在 go get 選擇了一組新版本後,它會檢查是否有任何新選取的模組版本或提供命令列上所列套件的模組被撤回(retracted)或棄用(deprecated)。go get 會為找到的每個撤回版本或棄用模組列印警告。go list -m -u all 可用於檢查所有依賴項中的撤回與棄用情況。
在 go get 更新 go.mod 檔案後,它會建置命令列上命名的套件。可執行檔將安裝在 GOBIN 環境變數所指定的目錄中,若未設定 GOPATH 環境變數,則預設為 $GOPATH/bin 或 $HOME/go/bin。
go get 支援以下標記:
-d標記告訴go get不要建置或安裝套件。當使用-d時,go get將僅管理go.mod中的依賴項。不建議使用不帶-d的go get來建置和安裝套件(自 Go 1.17 起)。在 Go 1.18 中,-d將始終處於啟用狀態。-u標記告訴go get升級那些提供命令列上指定套件(直接或間接匯入)的模組。每個由-u選擇的模組都將升級到其最新版本,除非它已經被要求使用更高的版本(預發布版本)。-u=patch標記(非-u patch)同樣告訴go get升級依賴項,但go get會將每個依賴項升級到最新的修補版本(類似@patch版本查詢)。-t標記告訴go get考慮建置命令列上指定套件的測試所需模組。當-t和-u同時使用時,go get也會更新測試依賴項。-insecure標記不再應該使用。它允許go get解析自定義匯入路徑,並透過不安全的協定(如 HTTP)從儲存庫和模組代理獲取內容。應使用提供更細緻控制的GOINSECURE環境變數來取代。
自 Go 1.16 起,go install 是建議用來建置和安裝程式的指令。當與版本後綴(如 @latest 或 @v1.4.6)一起使用時,go install 會在模組感知模式下建置套件,並忽略當前目錄或任何父目錄中可能存在的 go.mod 檔案。
go get 更專注於管理 go.mod 中的需求。-d 標記已被棄用,在 Go 1.18 中,它將始終處於啟用狀態。
go install
用法
go install [build flags] [packages]
範例
# Install the latest version of a program,
# ignoring go.mod in the current directory (if any).
$ go install golang.org/x/tools/gopls@latest
# Install a specific version of a program.
$ go install golang.org/x/tools/gopls@v0.6.4
# Install a program at the version selected by the module in the current directory.
$ go install golang.org/x/tools/gopls
# Install all programs in a directory.
$ go install ./cmd/...
go install 指令會建置並安裝命令列路徑指定的套件。可執行檔(main 套件)會安裝到 GOBIN 環境變數所指定的目錄中,若未設定 GOPATH 環境變數,則預設為 $GOPATH/bin 或 $HOME/go/bin。$GOROOT 中的可執行檔會安裝在 $GOROOT/bin 或 $GOTOOLDIR 中,而不是 $GOBIN。非可執行套件會被建置並快取,但不會被安裝。
自 Go 1.16 起,若參數具有版本後綴(如 @latest 或 @v1.0.0),go install 會在模組感知模式下建置套件,忽略當前目錄或任何父目錄中可能存在的 go.mod 檔案。這對於安裝可執行檔而不影響主模組的依賴項非常有用。
為消除建置中所使用模組版本的歧義,參數必須滿足以下約束:
- 參數必須是套件路徑或套件模式(帶有 “
...” 萬用字元)。它們不得是標準套件(如fmt)、元模式(std、cmd、all、work、tool),或相對/絕對檔案路徑。 - 所有參數必須具有相同的版本後綴。不允許使用不同的查詢,即使它們指向相同的版本。
- 所有參數必須指向同一模組中的同一個版本。
- 套件路徑參數必須指向
main套件。模式參數將僅匹配main套件。 - 沒有模組被視為主模組。
- 若包含命令列指定套件的模組擁有
go.mod檔案,該檔案不得包含任何會導致它在成為主模組時被不同方式解讀的指令(replace和exclude)。 - 模組不得要求比自身更高的版本。
- 不使用 vendor 目錄(vendor 目錄不包含在模組 zip 檔案中,因此
go install不會下載它們)。
- 若包含命令列指定套件的模組擁有
關於支援的版本查詢語法,請參閱版本查詢。Go 1.15 及更低版本不支援將版本查詢與 go install 一起使用。
若參數沒有版本後綴,go install 可能會根據 GO111MODULE 環境變數和 go.mod 檔案的存在與否,在模組感知模式或 GOPATH 模式下執行。詳細資訊請參閱模組感知指令。若啟用了模組感知模式,go install 將在主模組的上下文中執行,這可能與包含所安裝套件的模組不同。
go list -m
用法
go list -m [-u] [-retracted] [-versions] [list flags] [modules]
範例
$ go list -m all
$ go list -m -versions example.com/m
$ go list -m -json example.com/m@latest
-m 標記會使 go list 列出模組而非套件。在此模式下,go list 的參數可以是模組、模組模式(包含 ... 萬用字元)、版本查詢,或是特殊模式 all(匹配建置列表中的所有模組)。若未指定參數,則列出主模組。
列出模組時,-f 標記仍指定應用於 Go 結構的格式模板,但現在是 Module 結構:
type Module struct {
Path string // module path
Version string // module version
Versions []string // available module versions
Replace *Module // replaced by this module
Time *time.Time // time version was created
Update *Module // available update (with -u)
Main bool // is this the main module?
Indirect bool // module is only indirectly needed by main module
Dir string // directory holding local copy of files, if any
GoMod string // path to go.mod file describing module, if any
GoVersion string // go version used in module
Retracted []string // retraction information, if any (with -retracted or -u)
Deprecated string // deprecation message, if any (with -u)
Error *ModuleError // error loading module
}
type ModuleError struct {
Err string // the error itself
}
預設輸出會列印模組路徑,然後是版本和替換資訊(如果有)。例如,go list -m all 可能會列印:
example.com/main/module
golang.org/x/net v0.1.0
golang.org/x/text v0.3.0 => /tmp/text
rsc.io/pdf v0.1.1
Module 結構有一個 String 方法可格式化此輸出行,因此預設格式等同於 -f '{{.String}}'。
請注意,當模組已被替換時,其 Replace 欄位會描述替換後的模組,而 Dir 欄位若存在則指向替換後的模組原始程式碼。(即若 Replace 非 nil,則 Dir 設為 Replace.Dir,且無法存取被替換前的原始程式碼。)
-u 標記會新增關於可用升級的資訊。當指定模組的最新版本比當前版本新時,list -u 會將該模組的 Update 欄位設定為更新模組的相關資訊。list -u 還會列印當前選定的版本是否已撤回,以及該模組是否被棄用。模組的 String 方法透過在當前版本後的方括號中格式化更新版本來標示可用升級。例如,go list -m -u all 可能會列印:
example.com/main/module
golang.org/x/old v1.9.9 (deprecated)
golang.org/x/net v0.1.0 (retracted) [v0.2.0]
golang.org/x/text v0.3.0 [v0.4.0] => /tmp/text
rsc.io/pdf v0.1.1 [v0.1.2]
(對於工具而言,使用 go list -m -u -json all 可能更方便解析。)
-versions 標記會使 list 將模組的 Versions 欄位設定為該模組所有已知版本的列表,並按語意版本號由低到高排序。此標記還會將預設輸出格式變更為顯示模組路徑,後接以空格分隔的版本列表。除非同時指定了 -retracted 標記,否則撤回版本將從此列表中省略。
-retracted 標記指示 list 在使用 -versions 標記列印的列表中顯示撤回版本,並在解析版本查詢時考慮撤回版本。例如,go list -m -retracted example.com/m@latest 會顯示模組 example.com/m 的最高發布或預發布版本,即使該版本已被撤回。retract 指令和棄用資訊會從該版本的 go.mod 檔案中載入。-retracted 標記是在 Go 1.16 中加入的。
模板函數 module 接受單一字串參數(必須是模組路徑或查詢),並將指定模組傳回為 Module 結構。若發生錯誤,結果將是一個具有非 nil Error 欄位的 Module 結構。
go mod download
用法
go mod download [-x] [-json] [-reuse=old.json] [modules]
範例
$ go mod download
$ go mod download golang.org/x/mod@v0.2.0
go mod download 指令將指定模組下載到模組快取中。參數可以是模組路徑、模組模式(選擇主模組的依賴項)或 path@version 形式的版本查詢。在沒有參數的情況下,download 適用於主模組的所有依賴項。
go 指令在一般執行期間會根據需要自動下載模組。go mod download 指令主要用於預先填入模組快取或載入要由模組代理提供的資料。
預設情況下,download 不會向標準輸出寫入任何內容。它會將進度訊息和錯誤列印到標準錯誤輸出。
-json 標記會使 download 將一系列 JSON 物件列印到標準輸出,描述每個下載的模組(或失敗情況),對應於以下 Go 結構:
type Module struct {
Path string // module path
Query string // version query corresponding to this version
Version string // module version
Error string // error loading module
Info string // absolute path to cached .info file
GoMod string // absolute path to cached .mod file
Zip string // absolute path to cached .zip file
Dir string // absolute path to cached source root directory
Sum string // checksum for path, version (as in go.sum)
GoModSum string // checksum for go.mod (as in go.sum)
Origin any // provenance of module
Reuse bool // reuse of old module info is safe
}
-x 標記會使 download 將執行過的指令列印到標準錯誤輸出。
-reuse 標記接受一個檔案名稱,該檔案包含先前 ‘go mod download -json’ 呼叫的 JSON 輸出。go 指令可能會使用此檔案來確定某個模組自上次呼叫後並未變更,從而避免重新下載。未重新下載的模組將在新的輸出中透過設定 Reuse 欄位為 true 來標記。通常模組快取會自動提供這種重用;-reuse 標記對於不保留模組快取的系統非常有用。
go mod edit
用法
go mod edit [editing flags] [-fmt|-print|-json] [go.mod]
範例
# Add a replace directive.
$ go mod edit -replace example.com/a@v1.0.0=./a
# Remove a replace directive.
$ go mod edit -dropreplace example.com/a@v1.0.0
# Set the go version, add a requirement, and print the file
# instead of writing it to disk.
$ go mod edit -go=1.14 -require=example.com/m@v1.0.0 -print
# Format the go.mod file.
$ go mod edit -fmt
# Format and print a different .mod file.
$ go mod edit -print tools.mod
# Print a JSON representation of the go.mod file.
$ go mod edit -json
go mod edit 指令提供了一個用於編輯和格式化 go.mod 檔案的命令列介面,主要供工具和腳本使用。go mod edit 僅讀取一個 go.mod 檔案;它不會查找關於其他模組的資訊。預設情況下,go mod edit 讀寫主模組的 go.mod 檔案,但可以在編輯標記之後指定不同的目標檔案。
編輯標記指定了一系列編輯操作。
-module標記用於變更模組路徑(go.mod檔案中的 module 行)。-go=version標記用於設定預期的 Go 語言版本。-require=path@version和-droprequire=path標記用於新增或移除給定模組路徑和版本的需求。請注意,-require會覆蓋該路徑上的任何現有需求。這些標記主要用於了解模組圖的工具。使用者應優先使用go get path@version或go get path@none,因為這會根據需要進行其他go.mod調整以滿足其他模組施加的約束。請參閱go get。-exclude=path@version和-dropexclude=path@version標記用於新增或移除給定模組路徑和版本的排除項目。請注意,若排除項目已存在,-exclude=path@version將不執行任何操作。-replace=old[@v]=new[@v]標記用於新增給定模組路徑與版本對的替換。若old@v中的@v被省略,則新增一個左側沒有版本的替換,這適用於舊模組路徑的所有版本。若new@v中的@v被省略,新路徑應為本地模組根目錄,而非模組路徑。請注意,-replace會覆蓋old[@v]的任何冗餘替換,因此省略@v將移除特定版本的替換。-dropreplace=old[@v]標記用於移除給定模組路徑與版本對的替換。若提供了@v,則刪除給定版本的替換。左側不帶版本的現有替換可能仍然會取代該模組。若省略@v,則刪除不帶版本的替換。-retract=version和-dropretract=version標記用於新增或移除給定版本的撤回,該版本可以是單一版本(如v1.2.3)或區間(如[v1.1.0,v1.2.0])。請注意,-retract標記無法為retract指令新增理由註解。建議加入理由註解,且這些註解可能會由go list -m -u等指令顯示。-tool=path和-droptool=path標記用於新增或移除給定路徑的tool指令。請注意,這不會將必要依賴項新增至建置圖中。使用者應優先使用go get -tool path來新增工具,或go get -tool path@none來移除工具。
編輯標記可以重複使用。變更會依照給定的順序執行。
go mod edit 具有控制輸出的額外標記。
-fmt標記會在不進行其他變更的情況下重新格式化go.mod檔案。任何使用或重寫go.mod檔案的其他修改也會隱含此重新格式化。只有在未指定其他標記時才需要此標記,例如go mod edit -fmt。-print標記會將最終的go.mod以文字格式列印出來,而不寫回磁碟。-json標記會將最終的go.mod以 JSON 格式列印出來,而不以文字格式寫回磁碟。JSON 輸出對應於這些 Go 類型:
type Module struct {
Path string
Version string
}
type GoMod struct {
Module ModPath
Go string
Require []Require
Exclude []Module
Replace []Replace
Retract []Retract
}
type ModPath struct {
Path string
Deprecated string
}
type Require struct {
Path string
Version string
Indirect bool
}
type Replace struct {
Old Module
New Module
}
type Retract struct {
Low string
High string
Rationale string
}
type Tool struct {
Path string
}
請注意,這僅描述了 go.mod 檔案本身,而非間接參照的其他模組。若要取得建置可用的完整模組集合,請使用 go list -m -json all。請參閱 go list -m。
例如,工具可以透過解析 go mod edit -json 的輸出將 go.mod 檔案作為資料結構取得,然後透過執行帶有 -require、-exclude 等的 go mod edit 來進行變更。
工具也可以使用 golang.org/x/mod/modfile 套件來解析、編輯和格式化 go.mod 檔案。
go mod graph
用法
go mod graph [-go=version]
go mod graph 指令會以文字形式列印模組需求圖(已套用替換)。例如:
example.com/main example.com/a@v1.1.0
example.com/main example.com/b@v1.2.0
example.com/a@v1.1.0 example.com/b@v1.1.1
example.com/a@v1.1.0 example.com/c@v1.3.0
example.com/b@v1.1.0 example.com/c@v1.1.0
example.com/b@v1.2.0 example.com/c@v1.2.0
模組圖中的每個頂點代表模組的一個特定版本。圖中的每條邊代表對依賴項最低版本的要求。
go mod graph 會列印圖的邊,每行一條。每行有兩個以空格分隔的欄位:模組版本及其依賴項之一。每個模組版本皆識別為 path@version 形式的字串。主模組沒有 @version 後綴,因為它沒有版本。
-go 標記會使 go mod graph 報告由給定 Go 版本所載入的模組圖,而非由 go.mod 檔案中go 指令所指示的版本。
關於版本如何選擇的詳細資訊,請參閱最小版本選擇 (MVS)。另請參閱 go list -m 以列印選定的版本,以及 go mod why 以了解為什麼需要某個模組。
go mod init
用法
go mod init [module-path]
範例
go mod init
go mod init example.com/m
go mod init 指令會初始化並在當前目錄中寫入一個新的 go.mod 檔案,實際上是在當前目錄中建立了一個新的模組根目錄。go.mod 檔案必須尚未存在。
init 接受一個可選參數,即新模組的模組路徑。選擇模組路徑的相關說明請參閱模組路徑。若省略模組路徑參數,init 將嘗試使用 .go 檔案中的匯入註解和當前目錄(若在 GOPATH 中)來推斷模組路徑。
go mod tidy
用法
go mod tidy [-e] [-v] [-x] [-diff] [-go=version] [-compat=version]
go mod tidy 可確保 go.mod 檔案與模組中的原始程式碼相符。它會新增建置當前模組套件及其依賴項所需的任何遺失模組需求,並移除那些不提供任何相關套件的模組需求。它還會為 go.sum 新增遺失的條目並移除不必要的條目。
-e 標記(於 Go 1.16 加入)使 go mod tidy 在載入套件時遇到錯誤仍嘗試繼續執行。
-v 標記使 go mod tidy 將關於已移除模組的資訊列印到標準錯誤輸出。
-x 標記使 go mod tidy 將 tidy 執行的指令列印出來。
-diff 標記使 go mod tidy 不會修改 go.mod 或 go.sum,而是將必要的變更列印為統一差異檔(unified diff)。若差異不為空,它將以非零代碼退出。
go mod tidy 的運作方式是遞迴載入主模組中的所有套件、其所有工具以及這些套件所匯入的所有套件。這包括測試匯入的套件(包括其他模組中的測試)。go mod tidy 的行為如同所有建置標籤皆已啟用,因此它會考慮平台特定的原始程式碼檔案以及需要自定義建置標籤的檔案,即使這些原始程式碼檔案在正常建置下不會被編譯。有一個例外:ignore 建置標籤未啟用,因此帶有建置約束 // +build ignore 的檔案將不被考慮。請注意,go mod tidy 不會考慮名為 testdata 的目錄中,或名稱以 . 或 _ 開頭的目錄中的主模組套件,除非這些套件被其他套件明確匯入。
一旦 go mod tidy 載入了這組套件,它會確保每個提供一個或多個套件的模組在主模組的 go.mod 檔案中都有一個 require 指令,或者(若主模組版本為 go 1.16 或更低)被其他需求的模組所需求。go mod tidy 將為每個缺失的模組新增對最新版本的需求(關於 latest 版本定義,請參閱版本查詢)。go mod tidy 將移除那些不提供上述集合中任何套件的模組 require 指令。
go mod tidy 可能也會在 require 指令上新增或移除 // indirect 註解。// indirect 註解表示該模組不提供由主模組中的套件所匯入的套件。(關於何時新增 // indirect 依賴項和註解,請參閱 require 指令中的詳細說明。)
若設定了 -go 標記,go mod tidy 將把 go 指令更新為指定版本,並根據該版本啟用或停用模組圖剪枝和懶載入模組(lazy module loading)(並根據需要新增或移除間接需求)。
預設情況下,go mod tidy 會檢查當模組圖由 go 指令中指示版本的前一個 Go 版本載入時,模組的選定版本是否不會變更。檢查相容性的版本也可以透過 -compat 標記明確指定。
go mod vendor
用法
go mod vendor [-e] [-v] [-o]
go mod vendor 指令會在主模組根目錄下建立一個名為 vendor 的目錄,其中包含支援建置和測試主模組中套件所需的所有套件副本。僅由主模組外部套件的測試所匯入的套件將不被包含。與 go mod tidy 及其他模組指令一樣,在建置 vendor 目錄時,除了 ignore 之外的建置約束將不被考慮。
當啟用 vendoring 時,go 指令將從 vendor 目錄載入套件,而不是從來源下載模組到模組快取並使用這些下載的副本。詳細資訊請參閱 Vendoring。
go mod vendor 還會建立 vendor/modules.txt 檔案,其中包含已 vendored 套件的列表以及它們複製來源的模組版本。當啟用 vendoring 時,此清單會作為模組版本資訊的來源,正如 go list -m 和 go version -m 所報告。當 go 指令讀取 vendor/modules.txt 時,它會檢查模組版本是否與 go.mod 一致。若 go.mod 在 vendor/modules.txt 產生後已變更,則應重新執行 go mod vendor。
請注意,go mod vendor 在重新建置之前若 vendor 目錄存在,會先將其移除。不應對 vendored 套件進行本地修改。go 指令不會檢查 vendor 目錄中的套件是否已被修改,但您可以透過執行 go mod vendor 並檢查是否沒有任何變更來驗證 vendor 目錄的完整性。
-e 標記(於 Go 1.16 加入)使 go mod vendor 在載入套件時遇到錯誤仍嘗試繼續執行。
-v 標記使 go mod vendor 將 vendored 模組和套件的名稱列印到標準錯誤輸出。
-o 標記(於 Go 1.18 加入)使 go mod vendor 將 vendor 樹輸出到指定的目錄,而非 vendor。參數可以是絕對路徑,或是相對於模組根目錄的路徑。
go mod verify
用法
go mod verify
go mod verify 檢查儲存在模組快取中的主模組依賴項自下載後是否未經修改。為了執行此檢查,go mod verify 會對每個下載的模組 .zip 檔案和提取後的目錄進行雜湊計算,然後將這些雜湊值與首次下載模組時記錄的雜湊值進行比較。go mod verify 會檢查建置列表中的每個模組(可透過 go list -m all 列印)。
若所有模組皆未經修改,go mod verify 會列印 “all modules verified”。否則,它會回報哪些模組已被變更並以非零狀態退出。
請注意,所有模組感知指令皆會驗證主模組 go.sum 檔案中的雜湊值是否與下載到模組快取中的模組所記錄的雜湊值相符。若 go.sum 中缺少雜湊值(例如,因為模組是首次使用),go 指令會使用校驗和資料庫(checksum database)來驗證其雜湊值(除非模組路徑符合 GOPRIVATE 或 GONOSUMDB)。詳細資訊請參閱 驗證模組。
相反地,go mod verify 檢查模組 .zip 檔案及其提取後的目錄是否具有與首次下載時記錄在模組快取中的雜湊值相符的雜湊值。這對於檢測模組下載並驗證之後在模組快取中檔案的變更非常有用。go mod verify 不會為不在快取中的模組下載內容,也不會使用 go.sum 檔案來驗證模組內容。不過,go mod verify 可能會為了執行最小版本選擇而下載 go.mod 檔案。它會使用 go.sum 來驗證這些檔案,並可能為遺失的雜湊值新增 go.sum 條目。
go mod why
用法
go mod why [-m] [-vendor] packages...
go mod why 會顯示從主模組到命令列所列出每個套件的匯入圖中最短路徑。
輸出是一系列區段,每個命令列上命名的套件或模組一個,以空白行分隔。每個區段以開頭為 # 的註解行開始,給出目標套件或模組。隨後的行給出匯入圖中的路徑,每行一個套件。若套件或模組未從主模組中參照,該區段將顯示一個括號內的註解以指示此事實。
例如
$ go mod why golang.org/x/text/language golang.org/x/text/encoding
# golang.org/x/text/language
rsc.io/quote
rsc.io/sampler
golang.org/x/text/language
# golang.org/x/text/encoding
(main module does not need package golang.org/x/text/encoding)
-m 標記會使 go mod why 將其參數視為模組列表。go mod why 將列印到每個模組中任何套件的路徑。請注意,即使使用了 -m,go mod why 查詢的仍是套件圖,而非 go mod graph 所列印的模組圖。
-vendor 標記會使 go mod why 忽略主模組外部套件測試中的匯入(如同 go mod vendor 一樣)。預設情況下,go mod why 會考慮 all 模式所匹配的套件圖。此標記在 Go 1.16 之後,對於宣告了 go 1.16 或更高版本的模組(在 go.mod 中使用 go 指令)沒有作用,因為 all 的含義已變更為匹配 go mod vendor 所匹配的套件集合。
go version -m
用法
go version [-m] [-v] [file ...]
範例
# Print Go version used to build go.
$ go version
# Print Go version used to build a specific executable.
$ go version ~/go/bin/gopls
# Print Go version and module versions used to build a specific executable.
$ go version -m ~/go/bin/gopls
# Print Go version and module versions used to build executables in a directory.
$ go version -m ~/go/bin/
go version 會報告建置命令列上每個指定可執行檔所使用的 Go 版本。
若命令列上未指定任何檔案,go version 將列印其自身的版本資訊。
若指定了目錄,go version 會遞迴搜尋該目錄,尋找已識別的 Go 二進位檔並報告其版本。預設情況下,go version 不報告目錄掃描期間發現的未識別檔案。-v 標記可使其報告未識別的檔案。
-m 標記會使 go version 在可用時列印每個可執行檔嵌入的模組版本資訊。對於每個可執行檔,go version -m 會列印一個帶有定位點(tab)分隔欄位的表格,如下所示:
$ go version -m ~/go/bin/goimports
/home/jrgopher/go/bin/goimports: go1.14.3
path golang.org/x/tools/cmd/goimports
mod golang.org/x/tools v0.0.0-20200518203908-8018eb2c26ba h1:0Lcy64USfQQL6GAJma8BdHCgeofcchQj+Z7j0SXYAzU=
dep golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
dep golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
表格的格式在未來可能會有所變動。相同的資訊可以從 runtime.debug.ReadBuildInfo 取得。
表格中每一行的含義由第一欄中的單字決定。
path: 用於建置該可執行檔的main套件路徑。mod: 包含main套件的模組。欄位分別為模組路徑、版本和雜湊值。主模組的版本為(devel)且沒有雜湊值。dep: 提供了一個或多個連結至可執行檔之套件的模組。格式與mod相同。=>: 前一行模組的替換。若替換為本地目錄,僅列出目錄路徑(無版本或雜湊值)。若替換為模組版本,則列出路徑、版本和雜湊值,與mod和dep一樣。被替換的模組沒有雜湊值。
go clean -modcache
用法
go clean [-modcache]
-modcache 標記會使 go clean 移除整個模組快取,包括版本化依賴項的未封裝原始程式碼。
這通常是移除模組快取的最佳方式。預設情況下,模組快取中的大多數檔案和目錄都是唯讀的,以防止測試和編輯器在檔案經過驗證後意外變更它們。遺憾的是,這會導致像 rm -r 之類的指令失敗,因為若不先使其父目錄具備寫入權限,就無法移除檔案。
-modcacherw 標記(由 go build 和其他模組感知指令所接受)會使模組快取中的新目錄具備可寫入權限。若要將 -modcacherw 傳遞給所有模組感知指令,請將其新增至 GOFLAGS 變數。GOFLAGS 可以在環境中設定,或使用 go env -w 設定。例如,以下指令可永久設定此變數:
go env -w GOFLAGS=-modcacherw
-modcacherw 應謹慎使用;開發者應小心不要對模組快取中的檔案進行修改。go mod verify 可用於檢查快取中的檔案是否與主模組 go.sum 檔案中的雜湊值相符。
版本查詢
數個指令允許您使用版本查詢來指定模組版本,該查詢出現在命令列上模組或套件路徑後的 @ 字元之後。
範例
go get example.com/m@latest
go mod download example.com/m@master
go list -m -json example.com/m@e3702bed2
版本查詢可以是以下其中之一:
- 完全指定的語意版本,如
v1.2.3,用於選擇特定版本。語法請參閱 版本。 - 語意版本前綴,如
v1或v1.2,用於選擇具有該前綴的最高可用版本。 - 語意版本比較,如
<v1.2.3或>=v1.5.6,用於選擇最接近比較目標的可用版本(>和>=選擇最低版本,<和<=選擇最高版本)。 - 底層原始程式碼儲存庫的修訂識別碼,例如提交雜湊前綴、修訂標籤或分支名稱。若修訂版本已標記為語意版本,此查詢會選擇該版本。否則,此查詢會為底層提交選擇一個偽版本。請注意,名稱與其他版本查詢匹配的分支和標籤不能以這種方式選擇。例如,查詢
v2選擇以v2開頭的最新版本,而非名為v2的分支。 - 字串
latest,用於選擇最高可用的發布版本。若沒有發布版本,latest會選擇最高的預發布版本。若沒有標記版本,latest會為儲存庫預設分支頂端的提交選擇一個偽版本。 - 字串
upgrade,其運作方式類似latest,差異在於若模組目前的需求版本高於latest查詢所選擇的版本(例如預發布版本),upgrade將選擇當前版本。 - 字串
patch,用於選擇與當前需求版本具有相同主版本號和次版本號的最新可用版本。若目前未需求任何版本,patch等同於latest。自 Go 1.16 起,go get在使用patch時需要當前版本(但-u=patch標記沒有此要求)。
除了針對特定命名版本或修訂版本的查詢外,所有查詢都會考慮 go list -m -versions 所報告的可用版本(請參閱 go list -m)。此列表僅包含標記版本,不包含偽版本。主模組 go.mod 檔案中被 exclude 指令所禁止的模組版本將不被考慮。除非同時使用 -retracted 標記與 go list -m,以及載入 retract 指令時,否則 go.mod 檔案中被相同模組最新版本中 retract 指令所涵蓋的版本也將被忽略。
發布版本會優先於預發布版本。例如,若版本 v1.2.2 和 v1.2.3-pre 皆可用,latest 查詢將選擇 v1.2.2,即使 v1.2.3-pre 版本更高。<v1.2.4 查詢也會選擇 v1.2.2,即使 v1.2.3-pre 更接近 v1.2.4。若沒有可用的發布或預發布版本,latest、upgrade 和 patch 查詢將為儲存庫預設分支頂端的提交選擇一個偽版本。其他查詢將回報錯誤。
模組外的模組指令
模組感知的 Go 指令通常在工作目錄或父目錄中由 go.mod 檔案定義的主模組上下文中執行。某些指令可以在沒有 go.mod 檔案的情況下以模組感知模式執行,但大多數指令在沒有 go.mod 檔案時會有不同的運作方式或回報錯誤。
請參閱 模組感知指令 以獲取有關啟用和停用模組感知模式的資訊。
| 指令 | 行為 |
|---|---|
go buildgo docgo fixgo fmtgo generatego installgo listgo rungo testgo vet
|
僅能載入、匯入及建置標準函式庫中的套件,以及命令列中指定的 .go 檔案。來自其他模組的套件無法建置,因為沒有地方可以記錄模組需求並確保建置的確定性。 |
go get |
套件與可執行檔可以照常建置及安裝。請注意,當 go get 在沒有 go.mod 檔案的情況下執行時,沒有主模組,因此 replace 和 exclude 指令不會生效。 |
go list -m |
除非使用 -versions 旗標,否則大多數引數都需要明確的 版本查詢。 |
go mod download |
大多數引數都需要明確的 版本查詢。 |
go mod edit |
需要明確的檔案引數。 |
go mod graphgo mod tidygo mod vendorgo mod verifygo mod why
|
這些指令需要 go.mod 檔案,如果不存在,將會回報錯誤。 |
go work init
用法
go work init [moddirs]
Init 會在當前目錄中初始化並寫入一個新的 go.work 檔案,實際上是在當前目錄中建立一個新的工作區。
go work init 可選擇性地接受工作區模組的路徑作為引數。如果省略引數,將會建立一個不含模組的空工作區。
每個引數路徑都會被加入到 go.work 檔案中的 use 指令。當前的 Go 版本也會列在 go.work 檔案中。
go work edit
用法
go work edit [editing flags] [go.work]
go work edit 指令提供了一個用於編輯 go.work 的命令列介面,主要供工具或腳本使用。它只會讀取 go.work;不會查找有關所涉及模組的資訊。如果未指定檔案,Edit 會在當前目錄及其父目錄中查找 go.work 檔案。
編輯標記指定了一系列編輯操作。
-fmt旗標會在不進行其他變更的情況下重新格式化 go.work 檔案。任何使用或重寫go.work檔案的其他修改也隱含了此重新格式化操作。只有在未指定其他旗標時才需要此旗標,例如 'go work edit-fmt'。-use=path和-dropuse=path旗標用於從go.work檔案的模組目錄集合中新增或移除 use 指令。-replace=old[@v]=new[@v]旗標用於新增給定模組路徑與版本配對的取代項目。如果old@v中的@v被省略,則會新增一個左側沒有版本的取代項目,這將適用於該舊模組路徑的所有版本。如果new@v中的@v被省略,則新路徑應為本地模組根目錄,而非模組路徑。請注意,-replace會覆寫old[@v]的任何冗餘取代項目,因此省略@v將會移除現有針對特定版本的取代項目。-dropreplace=old[@v]旗標用於移除給定模組路徑與版本配對的取代項目。如果省略@v,則會移除左側沒有版本的取代項目。-go=version標記用於設定預期的 Go 語言版本。
編輯標記可以重複使用。變更會依照給定的順序執行。
go work edit 具有其他控制其輸出的旗標
- -print 旗標會以文字格式列印最終的 go.work,而不是將其寫回 go.mod。
- -json 旗標會以 JSON 格式列印最終的 go.work 檔案,而不是將其寫回 go.mod。JSON 輸出對應於這些 Go 型別
type Module struct {
Path string
Version string
}
type GoWork struct {
Go string
Directory []Directory
Replace []Replace
}
type Use struct {
Path string
ModulePath string
}
type Replace struct {
Old Module
New Module
}
go work use
用法
go work use [-r] [moddirs]
go work use 指令提供了一個用於將目錄(可選擇性地遞迴)新增至 go.work 檔案的命令列介面。
若命令列中列出的每個目錄引數在磁碟上存在,則會在 go.work 檔案中新增一個 use 指令;若磁碟上不存在,則會從 go.work 檔案中移除該目錄。
-r 旗標會遞迴搜尋引數目錄中的模組,use 指令的操作方式就像每個目錄都被指定為引數一樣。
go work sync
用法
go work sync
go work sync 指令會將工作區的建置列表同步回工作區的模組中。
工作區的建置列表是工作區中用於進行建置的所有(傳遞性)依賴模組的版本集合。go work sync 使用 最小版本選擇 (MVS) 演算法產生該建置列表,然後將這些版本同步回工作區中指定的每個模組(使用 use 指令)。
一旦計算出工作區建置列表,工作區中每個模組的 go.mod 檔案都會被重寫,將與該模組相關的依賴項升級以匹配工作區建置列表。請注意,最小版本選擇保證建置列表中每個模組的版本始終等於或高於每個工作區模組中的版本。
模組代理 (Module proxies)
GOPROXY 協定
模組代理 是一個 HTTP 伺服器,可以回應下述路徑的 GET 請求。請求沒有查詢參數,也不需要特定的標頭,因此即使是從固定檔案系統(包括 file:// URL)提供服務的網站也可以作為模組代理。
成功的 HTTP 回應必須具有狀態碼 200 (OK)。重新導向 (3xx) 會被追隨。狀態碼 4xx 和 5xx 的回應會被視為錯誤。錯誤代碼 404 (Not Found) 和 410 (Gone) 表示請求的模組或版本在代理上不可用,但可能在其他地方找到。錯誤回應應具有內容類型 text/plain,且 charset 為 utf-8 或 us-ascii。
go 指令可以設定為使用 GOPROXY 環境變數來聯繫代理或原始碼控制伺服器,該變數接受代理 URL 列表。列表可以包含關鍵字 direct 或 off(詳細資訊請參閱 環境變數)。列表元素可以使用逗號 (,) 或管線 (|) 分隔,這決定了錯誤時的回退行為。當 URL 後面接逗號時,go 指令僅在收到 404 (Not Found) 或 410 (Gone) 回應後才會回退到後續來源。當 URL 後面接管線時,go 指令會在發生任何錯誤(包括逾時等非 HTTP 錯誤)後回退到後續來源。這種錯誤處理行為讓代理可以充當未知模組的守門人。例如,代理可以針對不在核准清單上的模組回應 403 (Forbidden) 錯誤(請參閱 提供私有模組的私有代理)。
下表指定了模組代理必須回應的查詢。對於每個路徑,$base 是代理 URL 的路徑部分,$module 是模組路徑,$version 是版本。例如,如果代理 URL 為 https://example.com/mod,而用戶端正在請求模組 golang.org/x/text 版本 v0.3.2 的 go.mod 檔案,用戶端將會對 https://example.com/mod/golang.org/x/text/@v/v0.3.2.mod 發送 GET 請求。
為了避免在區分大小寫的檔案系統中提供服務時產生歧義,$module 和 $version 元素會進行大小寫編碼,方法是將每個大寫字母替換為驚嘆號,後接相應的小寫字母。這允許模組 example.com/M 和 example.com/m 都儲存在磁碟上,因為前者被編碼為 example.com/!m。
| 路徑 | 說明 |
|---|---|
$base/$module/@v/list |
以純文字格式傳回給定模組的已知版本列表,每行一個。此列表不應包含偽版本 (pseudo-versions)。 |
$base/$module/@v/$version.info |
傳回關於模組特定版本的 JSON 格式元資料。回應必須是一個對應於以下 Go 資料結構的 JSON 物件 type Info struct {
Version string // version string
Time time.Time // commit time
}
未來可能會新增更多欄位,因此其他名稱已被保留。 |
$base/$module/@v/$version.mod |
傳回模組特定版本的 go.mod 檔案。如果模組在請求版本時沒有 go.mod 檔案,則必須傳回僅包含請求模組路徑的 module 陳述式的檔案。否則,必須傳回原始的、未經修改的 go.mod 檔案。 |
$base/$module/@v/$version.zip |
傳回包含模組特定版本內容的 zip 檔案。有關此 zip 檔案必須如何格式化的詳細資訊,請參閱 模組 zip 檔案。 |
$base/$module/@latest |
傳回關於模組最新已知版本的 JSON 格式元資料,格式與 $base/$module/@v/$version.info 相同。如果 $base/$module/@v/list 為空或沒有合適的版本,最新版本應為 go 指令應該使用的模組版本。此端點是選填的,模組代理不需要實作它。 |
當解析模組的最新版本時,go 指令將會請求 $base/$module/@v/list,如果沒有找到合適的版本,則請求 $base/$module/@latest。go 指令的優先順序為:語意上最高的發布版本、語意上最高的預發布版本,以及按時間順序最近的偽版本。在 Go 1.12 及更早版本中,go 指令會將 $base/$module/@v/list 中的偽版本視為預發布版本,但自 Go 1.13 起已不再如此。
模組代理必須始終為 $base/$module/$version.mod 和 $base/$module/$version.zip 查詢的成功回應提供相同的內容。此內容是使用 go.sum 檔案 以及預設情況下的 總和資料庫 進行 密碼學驗證 的。
go 指令會將其從模組代理下載的大部分內容快取在 $GOPATH/pkg/mod/cache/download 的模組快取中。即使直接從版本控制系統下載,go 指令也會合成明確的 info、mod 和 zip 檔案並將其儲存在此目錄中,就像直接從代理下載一樣。快取佈局與代理 URL 空間相同,因此在 https://example.com/proxy 提供 $GOPATH/pkg/mod/cache/download(或將其複製到該處)將允許使用者透過將 GOPROXY 設定為 https://example.com/proxy 來存取快取的模組版本。
與代理通訊
go 指令可以從 模組代理 下載模組原始碼和元資料。GOPROXY 環境變數可用於設定 go 指令可以連線到的代理,以及它是否可以直接與 版本控制系統 通訊。下載的模組資料會儲存在 模組快取 中。go 指令僅在需要快取中尚不存在的資訊時才會聯繫代理。
GOPROXY 協定章節描述了可以發送給 GOPROXY 伺服器的請求。然而,了解 go 指令何時發出這些請求也很有幫助。例如,go build 遵循以下程序
- 透過讀取
go.mod檔案 並執行 最小版本選擇 (MVS) 來計算 建置列表。 - 讀取命令列中命名的套件及其匯入的套件。
- 如果建置列表中的任何模組都沒有提供套件,則找到一個提供該套件的模組。在
go.mod中新增對其最新版本的模組需求,然後重新開始。 - 載入所有內容後建置套件。
當 go 指令計算建置列表時,它會載入 模組圖 中每個模組的 go.mod 檔案。如果快取中沒有 go.mod 檔案,go 指令會使用 $module/@v/$version.mod 請求(其中 $module 是模組路徑,$version 是版本)從代理下載它。這些請求可以使用像 curl 這樣的工具進行測試。例如,下方的指令會下載 golang.org/x/mod 版本 v0.2.0 的 go.mod 檔案
$ curl https://proxy.golang.org/golang.org/x/mod/@v/v0.2.0.mod
module golang.org/x/mod
go 1.12
require (
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898
)
為了載入套件,go 指令需要提供該套件的模組原始碼。模組原始碼以 .zip 檔案分發,並解壓縮至模組快取中。如果快取中沒有模組 .zip 檔案,go 指令會使用 $module/@v/$version.zip 請求下載它。
$ curl -O https://proxy.golang.org/golang.org/x/mod/@v/v0.2.0.zip
$ unzip -l v0.2.0.zip | head
Archive: v0.2.0.zip
Length Date Time Name
--------- ---------- ----- ----
1479 00-00-1980 00:00 golang.org/x/mod@v0.2.0/LICENSE
1303 00-00-1980 00:00 golang.org/x/mod@v0.2.0/PATENTS
559 00-00-1980 00:00 golang.org/x/mod@v0.2.0/README
21 00-00-1980 00:00 golang.org/x/mod@v0.2.0/codereview.cfg
214 00-00-1980 00:00 golang.org/x/mod@v0.2.0/go.mod
1476 00-00-1980 00:00 golang.org/x/mod@v0.2.0/go.sum
5224 00-00-1980 00:00 golang.org/x/mod@v0.2.0/gosumcheck/main.go
請注意,.mod 和 .zip 請求是分開的,即使 go.mod 檔案通常包含在 .zip 檔案中。go 指令可能需要下載許多不同模組的 go.mod 檔案,而 .mod 檔案比 .zip 檔案小得多。此外,如果 Go 專案沒有 go.mod 檔案,代理將提供一個僅包含 module 指令 的合成 go.mod 檔案。合成的 go.mod 檔案由 go 指令在從 版本控制系統 下載時產生。
如果 go 指令需要載入建置列表中沒有模組提供的套件,它會嘗試找到一個提供該套件的新模組。將套件解析為模組 章節描述了此過程。總而言之,go 指令會請求關於每個可能包含該套件的模組路徑的最新版本資訊。例如,對於套件 golang.org/x/net/html,go 指令會嘗試找到模組 golang.org/x/net/html、golang.org/x/net、golang.org/x/ 和 golang.org 的最新版本。只有 golang.org/x/net 實際存在並提供該套件,因此 go 指令使用該模組的最新版本。如果多個模組提供該套件,go 指令將使用路徑最長的模組。
當 go 指令請求模組的最新版本時,它首先發送 $module/@v/list 的請求。如果列表為空或沒有傳回的版本可以使用,它會發送 $module/@latest 的請求。一旦選定版本,go 指令會發送 metadata 的 $module/@v/$version.info 請求。隨後,它可能會發送 $module/@v/$version.mod 和 $module/@v/$version.zip 請求以載入 go.mod 檔案和原始碼。
$ curl https://proxy.golang.org/golang.org/x/mod/@v/list
v0.1.0
v0.2.0
$ curl https://proxy.golang.org/golang.org/x/mod/@v/v0.2.0.info
{"Version":"v0.2.0","Time":"2020-01-02T17:33:45Z"}
下載 .mod 或 .zip 檔案後,go 指令會計算密碼學雜湊值,並檢查它是否與主模組 go.sum 檔案中的雜湊值相符。如果雜湊值不存在於 go.sum 中,預設情況下,go 指令會從 總和資料庫 檢索它。如果計算出的雜湊值不符,go 指令會回報安全性錯誤,且不會將檔案安裝在模組快取中。GOPRIVATE 和 GONOSUMDB 環境變數可用於針對特定模組停用對總和資料庫的請求。GOSUMDB 環境變數也可以設定為 off 以完全停用對總和資料庫的請求。有關詳細資訊,請參閱 驗證模組。請注意,針對 .info 請求傳回的版本列表和版本元資料未經過驗證,且可能會隨時間變更。
直接從代理提供模組服務
大多數模組都是從版本控制儲存庫開發並提供的。在 直接模式 下,go 指令使用版本控制工具下載此類模組(請參閱 版本控制系統)。也可以直接從模組代理提供模組服務。這對於那些希望在不暴露版本控制伺服器的情況下提供模組,以及使用 go 指令不支援的版本控制工具的組織來說非常有用。
當 go 指令在直接模式下下載模組時,它會首先根據模組路徑以 HTTP GET 請求查找模組伺服器的 URL。它會在 HTML 回應中查找名稱為 go-import 的 <meta> 標籤。標籤的內容必須包含 儲存庫根路徑、版本控制系統和 URL,並以空格分隔。詳細資訊請參閱 尋找模組路徑的儲存庫。
如果版本控制系統是 mod,go 指令會使用 GOPROXY 協定 從給定的 URL 下載模組。
例如,假設 go 指令嘗試下載模組 example.com/gopher 版本 v1.0.0。它發送一個請求到 https://example.com/gopher?go-get=1。伺服器以包含標籤的 HTML 文件回應
<meta name="go-import" content="example.com/gopher mod https://modproxy.example.com">
根據此回應,go 指令透過發送對 https://modproxy.example.com/example.com/gopher/@v/v1.0.0.info、v1.0.0.mod 和 v1.0.0.zip 的請求來下載模組。
請注意,直接從代理提供的模組無法在 GOPATH 模式下使用 go get 下載。
版本控制系統
go 指令可以直接從版本控制儲存庫下載模組原始碼和元資料。從 代理 下載模組通常更快,但如果代理不可用,或者模組的儲存庫對代理不可存取(對於私有儲存庫通常如此),則直接連線到儲存庫是必要的。支援 Git、Subversion、Mercurial、Bazaar 和 Fossil。必須在 PATH 的目錄中安裝版本控制工具,以便 go 指令使用它。
若要從原始碼儲存庫而不是代理下載特定模組,請設定 GOPRIVATE 或 GONOPROXY 環境變數。若要將 go 指令設定為直接從原始碼儲存庫下載所有模組,請將 GOPROXY 設定為 direct。有關詳細資訊,請參閱 環境變數。
尋找模組路徑的儲存庫
當 go 指令在 direct 模式下下載模組時,它會從定位包含該模組的儲存庫開始。
如果模組路徑的路徑組件末尾具有 VCS 限定詞(.bzr、.fossil、.git、.hg、.svn 之一),go 指令將使用該路徑限定詞之前的所有內容作為儲存庫 URL。例如,對於模組 example.com/foo.git/bar,go 指令使用 git 下載 example.com/foo 的儲存庫,預期在 bar 子目錄中找到模組。go 指令將根據版本控制工具支援的協定來猜測要使用的協定。
如果模組路徑沒有限定詞,go 指令會向從模組路徑派生的 URL 發送一個帶有 ?go-get=1 查詢字串的 HTTP GET 請求。例如,對於模組 golang.org/x/mod,go 指令可能會發送以下請求
https://golang.com.tw/x/mod?go-get=1 (preferred)
https://golang.com.tw/x/mod?go-get=1 (fallback, only with GOINSECURE)
go 指令會追隨重新導向,但除此之外會忽略回應狀態碼,因此伺服器可能會以 404 或任何其他錯誤狀態回應。可以設定 GOINSECURE 環境變數以允許針對特定模組回退並重新導向至未加密的 HTTP。
伺服器必須以一個包含 <meta> 標籤的文件在 <head> 中的 HTML 文件回應。<meta> 標籤應出現在文件的前部,以避免混淆 go 指令的受限解析器。特別是,它應該出現在任何原始 JavaScript 或 CSS 之前。<meta> 標籤必須具有以下形式
<meta name="go-import" content="root-path vcs repo-url [subdirectory]">
root-path 是儲存庫根路徑,即模組路徑中對應於儲存庫根目錄的部分,或者如果存在並使用 Go 1.25 或更高版本,則對應於 subdirectory(請參閱下方有關 subdirectory 的章節)。它必須是請求模組路徑的前綴或完全匹配。如果不是完全匹配,則會針對該前綴進行另一次請求,以驗證 <meta> 標籤是否匹配。
vcs 是版本控制系統。它必須是下表中列出的工具之一,或者是關鍵字 mod,這會指示 go 指令使用 GOPROXY 協定 從給定的 URL 下載模組。有關詳細資訊,請參閱 直接從代理提供模組服務。
repo-url 是儲存庫的 URL。如果 URL 不包含配置(因為模組路徑具有 VCS 限定詞,或者 <meta> 標籤缺少配置),go 指令將嘗試版本控制系統支援的每個協定。例如,對於 Git,go 指令將嘗試 https://,然後嘗試 git+ssh://。不安全的協定(如 http:// 和 git://)僅在模組路徑與 GOINSECURE 環境變數匹配時才能使用。
subdirectory(如果存在)是儲存庫中與 root-path 對應的斜線分隔子目錄,它會覆寫儲存庫根目錄的預設值。僅 Go 1.25 及更高版本識別提供 subdirectory 的 go-import meta 標籤。在較早版本的 Go 上嘗試解析模組會忽略該 meta 標籤,如果模組無法在其他地方解析,則會導致解析失敗。
| 名稱 | 指令 | GOVCS 預設 | 安全協定 |
|---|---|---|---|
| Bazaar | bzr |
僅私有 | https, bzr+ssh |
| Fossil | fossil |
僅私有 | https |
| Git | git |
公共與私有 | https, git+ssh, ssh |
| Mercurial | hg |
公共與私有 | https, ssh |
| Subversion | svn |
僅私有 | https, svn+ssh |
作為範例,再次考慮 golang.org/x/mod。go 指令發送一個請求到 https://golang.com.tw/x/mod?go-get=1。伺服器以包含標籤的 HTML 文件回應
<meta name="go-import" content="golang.org/x/mod git https://go.googlesource.com/mod">
從此回應中,go 指令將使用遠端 URL https://go.googlesource.com/mod 的 Git 儲存庫。
GitHub 和其他熱門託管服務會回應所有儲存庫的 ?go-get=1 查詢,因此對於託管在這些網站上的模組,通常不需要伺服器設定。
在找到儲存庫 URL 後,go 指令會將儲存庫複製到模組快取中。通常,go 指令會盡量避免從儲存庫擷取不必要的資料。然而,實際使用的指令因版本控制系統而異,並可能隨時間變更。對於 Git,go 指令可以在不下載提交的情況下列出大多數可用版本。它通常會在不下載祖先提交的情況下擷取提交,但有時必須這樣做。
將版本對映至提交
go 指令可以在特定 規範版本(如 v1.2.3、v2.4.0-beta 或 v3.0.0+incompatible)下檢出儲存庫中的模組。每個模組版本都應在儲存庫中具有一個 語意版本標籤,指示應為給定版本檢出哪個修訂版。
如果模組定義在儲存庫根目錄或根目錄的主版本子目錄中,則每個版本標籤名稱都等於相應的版本。例如,模組 golang.org/x/text 定義在其儲存庫的根目錄中,因此版本 v0.3.2 在該儲存庫中具有標籤 v0.3.2。這對於大多數模組來說是正確的。
如果模組定義在儲存庫中的子目錄中,也就是說,模組路徑的 模組子目錄 部分不為空,則每個標籤名稱必須以模組子目錄作為字首,後接斜線。例如,模組 golang.org/x/tools/gopls 定義在儲存庫的 gopls 子目錄中,其根路徑為 golang.org/x/tools。該模組的版本 v0.4.0 必須在該儲存庫中具有名為 gopls/v0.4.0 的標籤。
語意版本標籤的主版本號必須與模組路徑的主版本字尾(如果有的話)一致。例如,標籤 v1.0.0 可以屬於模組 example.com/mod,但不能屬於 example.com/mod/v2,後者應具有像 v2.0.0 這樣的標籤。
如果沒有 go.mod 檔案,且模組位於儲存庫根目錄中,則主版本為 v2 或更高的標籤可能屬於沒有主版本字尾的模組。這種版本用字尾 +incompatible 表示。版本標籤本身不得具有該字尾。請參閱 與非模組儲存庫的相容性。
標籤建立後,不應刪除或變更為不同的修訂版。版本會經過 驗證 以確保安全、可重複的建置。如果修改了標籤,用戶端在下載時可能會看到安全性錯誤。即使在刪除標籤後,其內容仍可能在 模組代理 上可用。
將偽版本對映至提交
go 指令可以在特定修訂版下檢出儲存庫中的模組,編碼為 偽版本,如 v1.3.2-0.20191109021931-daa7c04131f5。
偽版本的最後 12 個字元(上述範例中的 daa7c04131f5)指示要檢出的儲存庫中的修訂版。其含義取決於版本控制系統。對於 Git 和 Mercurial,這是提交雜湊值的前綴。對於 Subversion,這是補零的修訂號。
在檢出提交之前,go 指令會驗證時間戳記(上述的 20191109021931)是否與提交日期相符。它還會驗證基礎版本(上述範例中 v1.3.2 之前的版本 v1.3.1)是否對應於作為提交祖先的語意版本標籤。這些檢查確保模組作者對偽版本如何與其他發布版本進行比較擁有完全控制權。
更多資訊請參閱 偽版本。
將分支和提交對映至版本
可以使用 版本查詢 在特定分支、標籤或修訂版下檢出模組。
go get example.com/mod@master
go 指令將這些名稱轉換為可用於 最小版本選擇 (MVS) 的 規範版本。MVS 取決於能夠明確排序版本的能力。分支名稱和修訂版無法隨時間可靠地進行比較,因為它們取決於可能變更的儲存庫結構。
如果修訂版標有一個或多個像 v1.2.3 這樣的語意版本標籤,則將使用最高有效版本的標籤。go 指令僅考慮可能屬於目標模組的語意版本標籤;例如,標籤 v1.5.2 不會被視為 example.com/mod/v2,因為主版本與模組路徑的字尾不符。
如果修訂版沒有標有有效的語意版本標籤,go 指令將產生一個 偽版本。如果修訂版具有帶有有效語意版本標籤的祖先,則最高祖先版本將用作偽版本基礎。請參閱 偽版本。
儲存庫中的模組目錄
一旦模組的儲存庫已在特定修訂版下檢出,go 指令必須定位包含模組 go.mod 檔案的目錄(模組的根目錄)。
回想一下,模組路徑 由三個部分組成:儲存庫根路徑(對應於儲存庫根目錄)、模組子目錄和主版本字尾(僅適用於 v2 或更高版本發布的模組)。
對於大多數模組,模組路徑等於儲存庫根路徑,因此模組的根目錄即為儲存庫的根目錄。
模組有時定義在儲存庫子目錄中。這通常適用於具有多個需要獨立發布和版本控制組件的大型儲存庫。此類模組預期可以在與模組路徑中儲存庫根路徑之後的部分相符的子目錄中找到。例如,假設模組 example.com/monorepo/foo/bar 在根路徑為 example.com/monorepo 的儲存庫中。其 go.mod 檔案必須位於 foo/bar 子目錄中。
如果模組以主版本 v2 或更高版本發布,其路徑必須具有 主版本字尾。具有主版本字尾的模組可以定義在兩個子目錄中的一個:一個帶有字尾,一個不帶。例如,假設上述模組的新版本以路徑 example.com/monorepo/foo/bar/v2 發布。其 go.mod 檔案可以位於 foo/bar 或 foo/bar/v2 中。
帶有主版本字尾的子目錄是 主版本子目錄。它們可用於在單一分支上開發模組的多個主版本。當多個主版本的開發在單獨的分支上進行時,這可能是沒有必要的。然而,主版本子目錄有一個重要的屬性:在 GOPATH 模式下,套件匯入路徑完全符合 GOPATH/src 下的目錄。go 指令在 GOPATH 模式下提供最小的模組相容性(請參閱 與非模組儲存庫的相容性),因此主版本子目錄對於與在 GOPATH 模式下建置的專案相容而言並不總是必要的。不過,不支援最小模組相容性的舊工具可能會出現問題。
一旦 go 指令找到模組根目錄,它會建立目錄內容的 .zip 檔案,然後將 .zip 檔案解壓縮至模組快取中。有關 .zip 檔案中可以包含哪些檔案的詳細資訊,請參閱 檔案路徑與大小限制。.zip 檔案的內容在解壓縮至模組快取之前會進行 驗證,方式與直接從代理下載 .zip 檔案時相同。
模組 zip 檔案不包含 vendor 目錄的內容或任何巢狀模組(包含 go.mod 檔案的子目錄)。這意味著模組必須小心,不要參照其目錄之外或位於其他模組中的檔案。例如,//go:embed 模式不得匹配巢狀模組中的檔案。此行為在需要將檔案排除在模組之外的情況下,可以作為一種有用的變通方法。例如,如果儲存庫中有大檔案被簽入 testdata 目錄中,模組作者可以在 testdata 中加入一個空的 go.mod 檔案,這樣他們的使用者就不需要下載這些檔案。當然,這可能會降低測試依賴項的使用者的測試涵蓋率。
LICENSE 檔案的特殊情況
當 go 指令為不在儲存庫根目錄中的模組建立 .zip 檔案時,如果模組的根目錄(與 go.mod 同級)中沒有名為 LICENSE 的檔案,且儲存庫根目錄中存在該檔案,go 指令將會從該處複製名為 LICENSE 的檔案。
這種特殊情況允許相同的 LICENSE 檔案適用於儲存庫內的所有模組。這僅適用於專門名為 LICENSE 的檔案,不含像 .txt 這樣的副檔名。遺憾的是,在不破壞現有模組密碼學總和的情況下,無法對此進行擴充;請參閱 驗證模組。其他工具和網站(如 pkg.go.dev)可能會識別其他名稱的檔案。
另請注意,go 指令在建立模組 .zip 檔案時不包含符號連結;請參閱 檔案路徑與大小限制。因此,如果儲存庫的根目錄中沒有 LICENSE 檔案,作者可以改為在定義在子目錄中的模組內建立授權檔案的副本,以確保這些檔案包含在模組 .zip 檔案中。
使用 GOVCS 控制版本控制工具
go 指令使用像 git 這樣的版本控制指令下載模組的能力,對於去中心化的套件生態系統至關重要,其中程式碼可以從任何伺服器匯入。如果惡意伺服器找到一種方法導致呼叫的版本控制指令執行非預期的程式碼,這也是一個潛在的安全問題。
為了平衡功能性和安全性考量,go 指令預設僅使用 git 和 hg 從公共伺服器下載程式碼。它將使用任何 已知的版本控制系統 從私有伺服器下載程式碼,定義為那些託管與 GOPRIVATE 環境變數 匹配的套件的伺服器。僅允許 Git 和 Mercurial 的理由是,這兩個系統在作為不受信任伺服器的用戶端執行時,對於此類問題的關注度最高。相比之下,Bazaar、Fossil 和 Subversion 主要用於受信任的、經過身份驗證的環境中,且作為攻擊面的審查程度不如前者。
版本控制指令限制僅在使用直接版本控制存取下載程式碼時適用。當從代理下載模組時,go 指令會改用 GOPROXY 協定,該協定始終是被允許的。預設情況下,go 指令針對公共模組使用 Go 模組鏡像(proxy.golang.org),並且僅在私有模組或鏡像拒絕提供公共套件(通常是基於法律原因)時才會回退到版本控制。因此,用戶端預設仍可存取從 Bazaar、Fossil 或 Subversion 儲存庫提供的公共程式碼,因為這些下載使用 Go 模組鏡像,該鏡像承擔了使用自定義沙盒執行版本控制指令的安全風險。
GOVCS 變數可用於更改針對特定模組所允許的版本控制系統。GOVCS 變數在模組感知模式和 GOPATH 模式下建置套件時皆適用。使用模組時,模式會匹配模組路徑。使用 GOPATH 時,模式會匹配對應於版本控制儲存庫根目錄的匯入路徑。
GOVCS 變數的一般形式是 pattern:vcslist 規則的逗號分隔列表。pattern 是一個 glob 模式,必須匹配模組或匯入路徑的一個或多個前導元素。vcslist 是一個以管線分隔的允許版本控制指令列表,或者設為 all 以允許使用任何已知指令,或設為 off 以不允許任何指令。請注意,如果模組匹配 vcslist 為 off 的模式,如果來源伺服器使用 mod 配置,它仍可能被下載,這會指示 go 指令使用 GOPROXY 協定 下載模組。列表中最早匹配的模式適用,即使後續模式也可能匹配。
例如,考慮
GOVCS=github.com:git,evil.com:off,*:git|hg
透過此設定,以 github.com/ 開頭的模組或匯入路徑的程式碼只能使用 git;evil.com 上的路徑不能使用任何版本控制指令,所有其他路徑(* 匹配所有內容)只能使用 git 或 hg。
特殊模式 public 和 private 匹配公共和私有模組或匯入路徑。如果路徑匹配 GOPRIVATE 變數,則該路徑為私有;否則為公共。
如果 GOVCS 變數中沒有規則匹配特定的模組或匯入路徑,go 指令會應用其預設規則,現在可以總結為 GOVCS 符號:public:git|hg,private:all。
若要允許對任何套件無限制地使用任何版本控制系統,請使用
GOVCS=*:all
若要停用所有版本控制的使用,請使用
GOVCS=*:off
go env -w 指令可用於為未來的 go 指令呼叫設定 GOVCS 變數。
GOVCS 是在 Go 1.16 中引入的。較早版本的 Go 可能會對任何模組使用任何已知的版本控制工具。
模組 zip 檔案
模組版本以 .zip 檔案形式分發。幾乎不需要直接與這些檔案互動,因為 go 指令會自動從 模組代理 和版本控制儲存庫建立、下載並解壓縮它們。然而,了解這些檔案以了解跨平台相容性限制或在實作模組代理時仍然很有用。
go mod download 指令會下載一個或多個模組的 zip 檔案,然後將這些檔案解壓縮至 模組快取 中。取決於 GOPROXY 和其他 環境變數,go 指令可能會從代理下載 zip 檔案,或者複製原始碼控制儲存庫並從中建立 zip 檔案。-json 旗標可用於尋找下載的 zip 檔案及其在模組快取中解壓縮後的內容的位置。
golang.org/x/mod/zip 套件可用於以程式設計方式建立、解壓縮或檢查 zip 檔案的內容。
檔案路徑與大小限制
模組 zip 檔案的內容有許多限制。這些限制確保 zip 檔案可以在廣泛的平台上安全且一致地解壓縮。
- 模組 zip 檔案的大小最多為 500 MiB。其檔案總解壓縮大小也限制為 500 MiB。
go.mod檔案限制為 16 MiB。LICENSE檔案也限制為 16 MiB。這些限制旨在減輕對使用者、代理和模組生態系統其他部分的拒絕服務攻擊。在模組目錄樹中包含超過 500 MiB 檔案的儲存庫,應在僅包含建置模組套件所需檔案的提交上標記模組版本;影片、模型和其他大型資產通常不需要用於建置。 - 模組 zip 檔案中的每個檔案必須以
$module@$version/作為字首,其中$module是模組路徑,$version是版本,例如golang.org/x/mod@v0.3.0/。模組路徑必須有效,版本必須有效且規範,且版本必須與模組路徑的主版本字尾匹配。有關具體定義和限制,請參閱 模組路徑與版本。 - 檔案模式、時間戳記和其他元資料會被忽略。
- 空目錄(路徑以斜線結尾的項目)可以包含在模組 zip 檔案中,但不會被解壓縮。
go指令不會在其建立的 zip 檔案中包含空目錄。 - 符號連結和其他不規則檔案在建立 zip 檔案時會被忽略,因為它們在作業系統和檔案系統之間不可移植,且 zip 檔案格式中沒有可移植的方式來表示它們。
- 目錄名為
vendor內的檔案在建立 zip 檔案時會被忽略,因為主模組之外的vendor目錄從未使用過。 - 模組根目錄之外,包含
go.mod檔案的目錄內的檔案在建立 zip 檔案時會被忽略,因為它們不屬於該模組。go指令在解壓縮 zip 檔案時會忽略包含go.mod檔案的子目錄。 - zip 檔案中沒有兩個檔案的路徑在 Unicode 大小寫摺疊下可以相等(請參閱
strings.EqualFold)。這確保 zip 檔案可以在區分大小寫的檔案系統上解壓縮而不發生衝突。 go.mod檔案可能會也可能不會出現在頂層目錄($module@$version/go.mod)中。如果存在,它必須命名為go.mod(全小寫)。命名為go.mod的檔案不允許出現在任何其他目錄中。- 模組內的檔案和目錄名稱可以由 Unicode 字母、ASCII 數字、ASCII 空格字元 (U+0020) 和 ASCII 標點符號
!#$%&()+,-.=@[]^_{}~組成。請注意,套件路徑可能不包含所有這些字元。有關差異,請參閱module.CheckFilePath和module.CheckImportPath。 - 直到第一個點為止的檔案或目錄名稱不得為 Windows 上的保留檔案名稱,無論大小寫(
CON、com1、NuL等)。
私有模組
Go 模組經常在公共網際網路上不可用的版本控制伺服器和模組代理上開發和分發。go 指令可以從私有來源下載和建置模組,儘管這通常需要一些設定。
以下環境變數可用於設定對私有模組的存取。詳細資訊請參閱 環境變數。有關控制發送到公共伺服器的資訊的資訊,也請參閱 隱私權。
GOPROXY— 模組代理 URL 列表。go指令將嘗試依序從每個伺服器下載模組。關鍵字direct指示go指令從開發模組的版本控制儲存庫下載,而不是使用代理。GOPRIVATE— 應視為私有的模組路徑字首的 glob 模式列表。作為GONOPROXY和GONOSUMDB的預設值。GONOPROXY— 不應從代理下載的模組路徑字首的 glob 模式列表。go指令將從開發模組的版本控制儲存庫下載匹配的模組,而不考慮GOPROXY。GONOSUMDB— 不應使用公共總和資料庫 sum.golang.org 進行檢查的模組路徑字首的 glob 模式列表。GOINSECURE— 可以透過 HTTP 和其他不安全協定檢索的模組路徑字首的 glob 模式列表。
這些變數可以在開發環境中設定(例如,在 .profile 檔案中),也可以使用 go env -w 永久設定。
本章節的其餘部分描述了為私有模組代理和版本控制儲存庫提供存取的常見模式。
提供所有模組的私有代理
一個為所有模組(公共和私有)提供服務的集中式私有代理伺服器,為管理員提供了最大的控制權,並為個別開發人員提供了最少的設定。
若要將 go 指令設定為使用此類伺服器,請設定以下環境變數,並將 https://proxy.corp.example.com 替換為您的代理 URL,將 corp.example.com 替換為您的模組字首
GOPROXY=https://proxy.corp.example.com
GONOSUMDB=corp.example.com
GOPROXY 設定指示 go 指令僅從 https://proxy.corp.example.com 下載模組;go 指令不會連線到其他代理或版本控制儲存庫。
GONOSUMDB 設定指示 go 指令不要使用公共總和資料庫來驗證路徑以 corp.example.com 開頭的模組。
在此配置下執行的代理可能需要對私有版本控制伺服器的讀取權限。它還需要存取公共網際網路才能下載公共模組的新版本。
有幾種現有的 GOPROXY 伺服器實作可以透過這種方式使用。最小的實作將從 模組快取 目錄提供檔案,並使用 go mod download(配合適當的設定)來檢索遺失的模組。
提供私有模組的私有代理
私有代理伺服器可以在不提供公共模組的情況下提供私有模組。go 指令可以設定為針對私有伺服器上不可用的模組回退到公共來源。
若要將 go 指令設定為以這種方式工作,請設定以下環境變數,並將 https://proxy.corp.example.com 替換為代理 URL,將 corp.example.com 替換為模組字首
GOPROXY=https://proxy.corp.example.com,https://proxy.golang.org,direct
GONOSUMDB=corp.example.com
GOPROXY 設定指示 go 指令先嘗試從 https://proxy.corp.example.com 下載模組。如果該伺服器以 404 (Not Found) 或 410 (Gone) 回應,go 指令將回退到 https://proxy.golang.org,然後直接連線到儲存庫。
GONOSUMDB 設定指示 go 指令不要使用公共總和資料庫來驗證路徑以 corp.example.com 開頭的模組。
請注意,以此配置使用的代理即使不提供公共模組,仍可能控制對它們的存取。如果代理以 404 或 410 以外的錯誤狀態回應請求,go 指令將不會回退到 GOPROXY 列表中的後續項目。例如,代理可以針對授權不合適或具有已知安全性漏洞的模組回應 403 (Forbidden)。
直接存取私有模組
go 指令可以設定為繞過公共代理並直接從版本控制伺服器下載私有模組。當無法執行私有代理伺服器時,這非常有用。
若要將 go 指令設定為以這種方式工作,請設定 GOPRIVATE,將 corp.example.com 替換為私有模組字首
GOPRIVATE=corp.example.com
在這種情況下不需要更改 GOPROXY 變數。它預設為 https://proxy.golang.org,direct,指示 go 指令先嘗試從 https://proxy.golang.org 下載模組,如果該代理以 404 (Not Found) 或 410 (Gone) 回應,則回退到直接連線。
GOPRIVATE 設定指示 go 指令不要連線到代理或總和資料庫來獲取以 corp.example.com 開頭的模組。
內部 HTTP 伺服器可能仍需要用於 將模組路徑解析為儲存庫 URL。例如,當 go 指令下載模組 corp.example.com/mod 時,它會發送一個 GET 請求到 https://corp.example.com/mod?go-get=1,並在回應中查找儲存庫 URL。若要避免此要求,請確保每個私有模組路徑都有一個標記儲存庫根字首的 VCS 字尾(如 .git)。例如,當 go 指令下載模組 corp.example.com/repo.git/mod 時,它將在不發送額外請求的情況下複製 https://corp.example.com/repo.git 或 ssh://corp.example.com/repo.git 的 Git 儲存庫。
開發人員需要對包含私有模組的儲存庫的讀取權限。這可以在全域 VCS 設定檔案(如 .gitconfig)中設定。最好將 VCS 工具設定為不需要互動式驗證提示。預設情況下,呼叫 Git 時,go 指令會透過設定 GIT_TERMINAL_PROMPT=0 來停用互動式提示,但它會尊重明確的設定。
將憑證傳遞給私有代理
go 指令在與代理伺服器通訊時支援 HTTP 基本身份驗證。
憑證可以在 .netrc 檔案 中指定。例如,一個包含以下內容的 .netrc 檔案將設定 go 指令以給定的使用者名稱和密碼連線到機器 proxy.corp.example.com。
machine proxy.corp.example.com
login jrgopher
password hunter2
檔案的位置可以使用 NETRC 環境變數設定。如果未設定 NETRC,go 指令將讀取 UNIX 類平台上的 $HOME/.netrc 或 Windows 上的 %USERPROFILE%\_netrc。
.netrc 中的欄位以空格、定位字元和換行符分隔。遺憾的是,這些字元不能在使用者名稱或密碼中使用。另請注意,機器名稱不能是完整 URL,因此無法為同一台機器上的不同路徑指定不同的使用者名稱和密碼。
或者,可以在 GOPROXY URL 中直接指定憑證。例如
GOPROXY=https://jrgopher:hunter2@proxy.corp.example.com
採取這種方法時要小心:環境變數可能會出現在 shell 歷史記錄和日誌中。
將憑證傳遞給私有儲存庫
go 指令可以直接從版本控制儲存庫下載模組。如果不使用私有代理,對於私有模組來說這是必要的。有關設定,請參閱 直接存取私有模組。
go 指令在直接下載模組時會執行版本控制工具(如 git)。這些工具執行自己的身份驗證,因此您可能需要在工具特定的設定檔案(如 .gitconfig)中設定憑證。
為確保運作順暢,請確保 go 指令使用正確的儲存庫 URL,且版本控制工具不需要互動式輸入密碼。go 指令偏好 https:// URL,而不是像 ssh:// 這樣的其他配置,除非在 查找儲存庫 URL 時指定了該配置。對於 GitHub 儲存庫,go 指令特別假定為 https://。
對於大多數伺服器,您可以設定您的用戶端透過 HTTP 進行身份驗證。例如,GitHub 支援將 OAuth 個人存取權杖作為 HTTP 密碼。您可以像在 將憑證傳遞給私有代理 時一樣,將 HTTP 密碼儲存在 .netrc 檔案中。
或者,您可以將 https:// URL 重寫為其他配置。例如,在 .gitconfig 中
[url "git@github.com:"]
insteadOf = https://github.com/
更多資訊請參閱 為什麼 "go get" 在複製儲存庫時使用 HTTPS?
隱私權
go 指令可以從模組代理伺服器和版本控制系統下載模組和元資料。環境變數 GOPROXY 控制使用哪些伺服器。環境變數 GOPRIVATE 和 GONOPROXY 控制哪些模組從代理獲取。
GOPROXY 的預設值為
https://proxy.golang.org,direct
透過此設定,當 go 指令下載模組或模組元資料時,它首先會向 Google 營運的公共模組代理 proxy.golang.org 發送請求(隱私權政策)。有關每個請求中發送的資訊的詳細資訊,請參閱 GOPROXY 協定。go 指令不會傳輸個人識別資訊,但會傳輸所請求的完整模組路徑。如果代理以 404 (Not Found) 或 410 (Gone) 狀態回應,go 指令將嘗試直接連線到提供該模組的版本控制系統。詳細資訊請參閱 版本控制系統。
GOPRIVATE 或 GONOPROXY 環境變數可以設定為匹配私有且不應從任何代理請求的模組字首的 glob 模式列表。例如
GOPRIVATE=*.corp.example.com,*.research.example.com
GOPRIVATE 只是作為 GONOPROXY 和 GONOSUMDB 的預設值,因此除非 GONOSUMDB 需要不同的值,否則無需設定 GONOPROXY。當模組路徑與 GONOPROXY 匹配時,go 指令會忽略該模組的 GOPROXY 並直接從其版本控制儲存庫獲取它。當沒有代理提供私有模組時,這非常有用。請參閱 直接存取私有模組。
如果有 提供所有模組的受信任代理,則不應設定 GONOPROXY。例如,如果 GOPROXY 設定為一個來源,go 指令將不會從其他來源下載模組。在這種情況下仍應設定 GONOSUMDB。
GOPROXY=https://proxy.corp.example.com
GONOSUMDB=*.corp.example.com,*.research.example.com
如果有 僅提供私有模組的受信任代理,則不應設定 GONOPROXY,但必須小心確保代理回應正確的狀態碼。例如,考慮以下配置
GOPROXY=https://proxy.corp.example.com,https://proxy.golang.org
GONOSUMDB=*.corp.example.com,*.research.example.com
假設由於打字錯誤,開發人員嘗試下載一個不存在的模組。
go mod download corp.example.com/secret-product/typo@latest
go 指令首先從 proxy.corp.example.com 請求此模組。如果該代理回應 404 (Not Found) 或 410 (Gone),go 指令將回退到 proxy.golang.org,在請求 URL 中傳輸 secret-product 路徑。如果私有代理以任何其他錯誤代碼回應,go 指令將列印錯誤,且不會回退到其他來源。
除了代理之外,go 指令還可能連線到總和資料庫以驗證未在 go.sum 中列出的模組的密碼學雜湊值。GOSUMDB 環境變數設定總和資料庫的名稱、URL 和公共金鑰。GOSUMDB 的預設值為 sum.golang.org,即 Google 營運的公共總和資料庫(隱私權政策)。有關每個請求發送的內容的詳細資訊,請參閱 總和資料庫。與代理一樣,go 指令不會傳輸個人識別資訊,但會傳輸所請求的完整模組路徑,且總和資料庫無法計算非公共模組的總和。
GONOSUMDB 環境變數可以設定為指示哪些模組是私有的且不應從總和資料庫請求的模式。GOPRIVATE 作為 GONOSUMDB 和 GONOPROXY 的預設值,因此除非 GONOPROXY 需要不同的值,否則無需設定 GONOSUMDB。
代理可以 鏡像總和資料庫。如果 GOPROXY 中的代理執行此操作,go 指令將不會直接連線到總和資料庫。
GOSUMDB 可以設定為 off 以完全停用總和資料庫的使用。在此設定下,go 指令不會驗證已下載的模組,除非它們已經在 go.sum 中。請參閱 驗證模組。
模組快取
模組快取 是 go 指令儲存下載模組檔案的目錄。模組快取與包含已編譯套件和其他建置工件的建置快取不同。
模組快取的預設位置是 $GOPATH/pkg/mod。若要使用不同的位置,請設定 GOMODCACHE 環境變數。
模組快取沒有最大大小,且 go 指令不會自動移除其內容。
快取可以由同一台機器上開發的多個 Go 專案共用。無論主模組位於何處,go 指令都會使用相同的快取。多個 go 指令執行個體可以安全地同時存取相同的模組快取。
go 指令以唯讀權限在快取中建立模組原始檔和目錄,以防止在模組下載後發生意外修改。這有一個不幸的副作用,使得快取很難用 rm -rf 等指令刪除。快取可以使用 go clean -modcache 刪除。或者,當使用 -modcacherw 旗標時,go 指令將以讀寫權限建立新目錄。這增加了編輯器、測試和其他程式修改模組快取中檔案的風險。go mod verify 指令可用於偵測對主模組依賴項的修改。它會掃描每個模組依賴項的解壓縮內容,並確認它們與 go.sum 中的預期雜湊值相符。
下表解釋了模組快取中大多數檔案的目的。省略了一些暫存檔案(鎖定檔案、暫存目錄)。對於每個路徑,$module 是模組路徑,$version 是版本。以斜線 (/) 結尾的路徑是目錄。模組路徑和版本中的大寫字母使用驚嘆號轉義(Azure 轉義為 !azure),以避免在區分大小寫的檔案系統上發生衝突。
| 路徑 | 說明 |
|---|---|
$module@$version/ |
包含模組 .zip 檔案解壓縮內容的目錄。這作為下載模組的模組根目錄。如果原始模組沒有 go.mod 檔案,這裡也不會包含。 |
cache/download/ |
包含從模組代理下載的檔案以及從 版本控制系統 派生的檔案的目錄。此目錄的佈局遵循 GOPROXY 協定,因此當由 HTTP 檔案伺服器提供服務或透過 file:// URL 參照時,此目錄可用作代理。 |
cache/download/$module/@v/list |
已知版本列表(請參閱 GOPROXY 協定)。這可能會隨時間變更,因此 go 指令通常會取得新副本,而不是重複使用此檔案。 |
cache/download/$module/@v/$version.info |
關於版本的 JSON 元資料。(請參閱 GOPROXY 協定)。這可能會隨時間變更,因此 go 指令通常會取得新副本,而不是重複使用此檔案。 |
cache/download/$module/@v/$version.mod |
此版本的 go.mod 檔案(請參閱 GOPROXY 協定)。如果原始模組沒有 go.mod 檔案,這是一個沒有需求的合成檔案。 |
cache/download/$module/@v/$version.zip |
模組的壓縮內容(請參閱 GOPROXY 協定 和 模組 zip 檔案)。 |
cache/download/$module/@v/$version.ziphash |
.zip 檔案中檔案的密碼學雜湊值。請注意,.zip 檔案本身沒有進行雜湊處理,因此檔案順序、壓縮、對齊和元資料不會影響雜湊值。使用模組時,go 指令會驗證此雜湊值是否與 go.sum 中的相應行相符。go mod verify 指令會檢查模組 .zip 檔案和解壓縮目錄的雜湊值是否與這些檔案相符。 |
cache/download/sumdb/ |
包含從 總和資料庫(通常為 sum.golang.org)下載的檔案的目錄。 |
cache/vcs/ |
包含直接從來源獲取的模組的複製版本控制儲存庫。目錄名稱是從儲存庫類型和 URL 派生的十六進位編碼雜湊值。儲存庫會針對磁碟大小進行最佳化。例如,複製的 Git 儲存庫在可能的情況下是裸儲存庫 (bare) 且淺層 (shallow) 的。 |
驗證模組
當 go 指令將模組 zip 檔案 或 go.mod 檔案 下載至 模組快取 時,它會計算一個密碼學雜湊值,並將其與已知值進行比較,以驗證檔案自首次下載以來未發生變更。如果下載的檔案沒有正確的雜湊值,go 指令會回報安全性錯誤。
對於 go.mod 檔案,go 指令從檔案內容計算雜湊值。對於模組 zip 檔案,go 指令以確定性順序從存檔內檔案的名稱和內容計算雜湊值。雜湊值不受檔案順序、壓縮、對齊和其他元資料的影響。有關雜湊實作的詳細資訊,請參閱 golang.org/x/mod/sumdb/dirhash。
go 指令將每個雜湊值與主模組 go.sum 檔案 中的相應行進行比較。如果雜湊值與 go.sum 中的雜湊值不同,go 指令會回報安全性錯誤並刪除下載的檔案,而不將其加入模組快取中。
如果 go.sum 檔案不存在,或者不包含下載檔案的雜湊值,go 指令可能會使用 總和資料庫(公共可用模組的全球雜湊來源)驗證雜湊值。一旦雜湊值經過驗證,go 指令會將其加入 go.sum 並將下載的檔案加入模組快取。如果模組是私有的(匹配 GOPRIVATE 或 GONOSUMDB 環境變數)或者總和資料庫已停用(透過設定 GOSUMDB=off),go 指令會接受雜湊值並將檔案加入模組快取,而不進行驗證。
模組快取通常由系統上的所有 Go 專案共用,每個模組可能擁有自己的 go.sum 檔案,其中包含可能不同的雜湊值。為了避免信任其他模組的需要,go 指令在存取模組快取中的檔案時,始終會驗證主模組的 go.sum。Zip 檔案雜湊值的計算成本很高,因此 go 指令會檢查與 zip 檔案一起儲存的預先計算雜湊值,而不是重新計算檔案雜湊。go mod verify 指令可用於檢查 zip 檔案和解壓縮目錄自加入模組快取後是否未發生修改。
go.sum 檔案
模組可能會在其根目錄中(與其 go.mod 檔案同級)擁有一個名為 go.sum 的文字檔案。go.sum 檔案包含模組直接和間接依賴項的密碼學雜湊值。當 go 指令將模組 .mod 或 .zip 檔案下載到 模組快取 時,它會計算雜湊值並檢查該雜湊值是否與主模組 go.sum 檔案中的相應雜湊值相符。如果模組沒有依賴項,或者如果所有依賴項都使用 replace 指令 取代為本地目錄,則 go.sum 可能是空的或不存在。
go.sum 中的每一行都有三個由空格分隔的欄位:模組路徑、版本(可能以 /go.mod 結尾)和雜湊值。
- 模組路徑是雜湊值所屬的模組名稱。
- 版本是雜湊值所屬的模組版本。如果版本以
/go.mod結尾,則雜湊值僅適用於模組的go.mod檔案;否則,雜湊值適用於模組.zip檔案內的檔案。 - 雜湊值欄位由演算法名稱(如
h1)和 base64 編碼的密碼學雜湊值組成,並以冒號 (:) 分隔。目前,SHA-256 (h1) 是唯一支援的雜湊演算法。如果未來發現 SHA-256 的漏洞,將會新增對另一種演算法的支援(命名為h2等)。
go.sum 檔案可能包含模組多個版本的雜湊值。為了執行 最小版本選擇,go 指令可能需要從依賴項的多個版本載入 go.mod 檔案。go.sum 也可能包含不再需要的模組版本的雜湊值(例如,升級後)。go mod tidy 將會新增遺失的雜湊值並從 go.sum 中移除不必要的雜湊值。
總和資料庫
總和資料庫是 go.sum 行的全球來源。go 指令可以在許多情況下使用它來偵測代理或來源伺服器的不當行為。
總和資料庫允許所有公共可用模組版本的全球一致性和可靠性。它使不受信任的代理成為可能,因為它們無法提供錯誤的程式碼而不被注意到。它還確保與特定版本關聯的位元不會從一天變更到下一天,即使模組作者隨後更改了其儲存庫中的標籤。
總和資料庫由 Google 營運的 sum.golang.org 提供服務。它是一個 go.sum 行雜湊值的 透明記錄(或稱「Merkle Tree」),由 Trillian 提供支援。Merkle Tree 的主要優點是獨立稽核人員可以驗證它未經竄改,因此它比簡單的資料庫更值得信賴。
go 指令使用最初在 提議:確保公共 Go 模組生態系統安全 中概述的協定與總和資料庫互動。
下表指定了總和資料庫必須回應的查詢。對於每個路徑,$base 是總和資料庫 URL 的路徑部分,$module 是模組路徑,$version 是版本。例如,如果總和資料庫 URL 為 https://sum.golang.org,而用戶端正在請求模組 golang.org/x/text 版本 v0.3.2 的記錄,用戶端將會對 https://sum.golang.org/lookup/golang.org/x/text@v0.3.2 發送 GET 請求。
為了避免在區分大小寫的檔案系統中提供服務時產生歧義,$module 和 $version 元素會經過大小寫編碼 (case-encoded),方法是將每個大寫字母替換為驚嘆號,後接對應的小寫字母。這使得模組 example.com/M 和 example.com/m 可以同時儲存在磁碟上,因為前者會被編碼為 example.com/!m。
路徑中被方括號包圍的部分(如 [.p/$W])代表選填值。
| 路徑 | 說明 |
|---|---|
$base/latest |
傳回最新日誌的已簽章且編碼過的樹狀描述。此已簽章的描述採用 note 的形式,即由一個或多個伺服器金鑰簽署的文字,並可使用伺服器的公開金鑰進行驗證。該樹狀描述提供了樹的大小以及該大小下樹頭 (tree head) 的雜湊值。此編碼方式說明於 golang.org/x/mod/sumdb/tlog#FormatTree。 |
$base/lookup/$module@$version |
傳回關於 $module 在 $version 的條目的日誌記錄編號,後接該記錄的資料(即 $module 在 $version 的 go.sum 行),以及包含該記錄的已簽章且編碼過的樹狀描述。 |
$base/tile/$H/$L/$K[.p/$W] |
傳回一個 [日誌磚 (log tile)](https://research.swtch.com/tlog#serving_tiles),這是一組構成日誌片段的雜湊值。每個磚塊定義於二維座標中,位於磚塊層級 $L,從左側起算第 $K 個,磚塊高度為 $H。選填的 .p/$W 後綴表示只有 $W 個雜湊值的局部日誌磚。如果找不到局部磚塊,用戶端必須退回至擷取完整磚塊。 |
$base/tile/$H/data/$K[.p/$W] |
傳回 /tile/$H/0/$K[.p/$W] 中葉節點雜湊值的記錄資料(包含一個字面上的 data 路徑元素)。 |
如果 go 指令查詢校驗和資料庫,第一步是透過 /lookup 端點擷取記錄資料。如果該模組版本尚未記錄在日誌中,校驗和資料庫會在回覆前嘗試從原始伺服器擷取它。此 /lookup 資料提供該模組版本的總和 (sum) 及其在日誌中的位置,這告知用戶端應擷取哪些磚塊以執行證明。go 指令會執行「包含 (inclusion)」證明(證明特定記錄存在於日誌中)以及「一致性 (consistency)」證明(證明樹未被篡改),然後再將新的 go.sum 行加入主模組的 go.sum 檔案中。重點是,來自 /lookup 的資料絕不能在未先對已簽章樹狀雜湊進行驗證,以及未對用戶端的已簽章樹狀雜湊時間軸驗證該已簽章樹狀雜湊之前就使用。
由校驗和資料庫提供的已簽章樹狀雜湊和新磚塊會儲存在模組快取中,因此 go 指令只需要擷取缺失的磚塊。
go 指令不需要直接連線到校驗和資料庫。它可以透過一個鏡像校驗和資料庫並支援上述協定的模組代理來請求模組總和。這對於阻擋組織外部請求的私人企業代理特別有用。
GOSUMDB 環境變數用於識別要使用的校驗和資料庫名稱,並可選地提供其公開金鑰和 URL,例如:
GOSUMDB="sum.golang.org"
GOSUMDB="sum.golang.org+<publickey>"
GOSUMDB="sum.golang.org+<publickey> https://sum.golang.org"
go 指令已知 sum.golang.org 的公開金鑰,也知道 sum.golang.google.cn 這個名稱(在中國大陸境內可用)會連線到 sum.golang.org 校驗和資料庫;使用任何其他資料庫都需要明確提供公開金鑰。URL 預設為 https:// 後接資料庫名稱。
GOSUMDB 預設為 sum.golang.org,這是由 Google 運行的 Go 校驗和資料庫。服務的隱私權政策請參閱 https://sum.golang.org/privacy。
如果 GOSUMDB 設定為 off,或者在呼叫 go get 時帶有 -insecure 旗標,則不會查詢校驗和資料庫,且所有未識別的模組都會被接受,代價是放棄對所有模組進行驗證且可重複下載的安全保證。若要針對特定模組繞過校驗和資料庫,更好的方式是使用 GOPRIVATE 或 GONOSUMDB 環境變數。詳細資訊請參閱 私有模組。
go env -w 指令可用於設定這些變數,以供未來呼叫 go 指令時使用。
環境變數
go 指令中的模組行為可以使用下表列出的環境變數進行設定。此清單僅包含與模組相關的環境變數。有關 go 指令可識別的所有環境變數清單,請參閱 go help environment。
| 變數 | 說明 |
|---|---|
GO111MODULE |
控制
更多資訊請參閱 模組感知指令。 |
GOMODCACHE |
如果未設定 |
GOINSECURE |
以逗號分隔的 Glob 模式清單(使用 Go 的 與 |
GONOPROXY |
以逗號分隔的 Glob 模式清單(使用 Go 的 如果未設定 |
GONOSUMDB |
以逗號分隔的 Glob 模式清單(使用 Go 的 如果未設定 |
GOPATH |
在 在模組感知模式下,模組快取儲存在第一個 如果未設定 |
GOPRIVATE |
以逗號分隔的 Glob 模式清單(使用 Go 的 path.Match 語法),指定應被視為私有的模組路徑前綴。GOPRIVATE 是 GONOPROXY 和 GONOSUMDB 的預設值。請參閱 隱私權。GOPRIVATE 也決定了對於 GOVCS 而言,模組是否被視為私有。 |
GOPROXY |
以逗號 (
GOPROXY=file://$(go env GOMODCACHE)/cache/download 可以使用兩個關鍵字來代替代理 URL:
|
GOSUMDB |
識別要使用的校驗和資料庫名稱,並可選地提供其公開金鑰和 URL。例如: GOSUMDB="sum.golang.org" GOSUMDB="sum.golang.org+<publickey>" GOSUMDB="sum.golang.org+<publickey> https://sum.golang.org"
如果 |
GOVCS |
控制 如果未設定 public:git|hg,private:all 完整說明請參閱 使用 |
GOWORK |
`GOWORK` 環境變數指示 |
詞彙表
建置限制 (build constraint): 一種條件,用於決定在編譯套件時是否使用某個 Go 原始檔。建置限制可以使用檔案名稱後綴(例如 foo_linux_amd64.go)或建置限制註解(例如 // +build linux,amd64)來表達。請參閱 建置限制。
建置清單 (build list): 將用於建置指令(如 go build、go list 或 go test)的模組版本清單。建置清單是從 主模組 的 go.mod 檔案,以及使用 最小版本選擇 (MVS) 的傳遞性必要模組中的 go.mod 檔案所決定。建置清單包含 模組圖 中所有模組的版本,而不僅僅是與特定指令相關的模組。
標準版本 (canonical version): 一種格式正確的 版本,且不包含 +incompatible 以外的建置元資料後綴。例如,v1.2.3 是標準版本,但 v1.2.3+meta 不是。
當前模組 (current module): 主模組 的同義詞。
已棄用模組 (deprecated module): 不再由其作者支援的模組(儘管在此用途中,主版本被視為不同的模組)。已棄用的模組會在最新版本的 go.mod 檔案 中標記有 棄用註解。
直接相依項 (direct dependency): 一個套件,其路徑出現在 主模組 中某個套件或測試的 .go 原始檔的 import 宣告中,或包含此類套件的模組中。(比較 間接相依項。)
直接模式 (direct mode): 環境變數 的一項設定,導致 go 指令直接從 版本控制系統 下載模組,而不是從 模組代理。GOPROXY=direct 對所有模組執行此操作。GOPRIVATE 和 GONOPROXY 則針對符合模式清單的模組執行此操作。
go.mod 檔案: 定義模組路徑、需求與其他元資料的檔案。出現在 模組根目錄 中。請參閱關於 go.mod 檔案 的章節。
go.work 檔案: 定義在 工作區 中使用的模組集合的檔案。請參閱關於 go.work 檔案 的章節。
匯入路徑 (import path): 在 Go 原始檔中用於匯入套件的字串。與 套件路徑 同義。
間接相依項 (indirect dependency): 一個被 主模組 中的套件或測試所傳遞匯入的套件,但其路徑未出現在主模組的任何 import 宣告中;或是出現在 模組圖 中,但不提供任何被主模組直接匯入之套件的模組。(比較 直接相依項。)
延遲模組載入 (lazy module loading): Go 1.17 中的一項變更,對於指定 go 1.17 或更高版本的模組,在不需要的指令中避免載入 模組圖。請參閱 延遲模組載入。
主模組 (main module): 執行 go 指令所在的模組。主模組由目前目錄或父目錄中的 go.mod 檔案 定義。請參閱 模組、套件與版本。
主版本號 (major version): 語意版本中的第一個數字(v1.2.3 中的 1)。在具有不相容變更的版本中,必須遞增主版本號,並將次版本號和修補程式版本號歸零。主版本號為 0 的語意版本被視為不穩定。
主版本號子目錄 (major version subdirectory): 版本控制儲存庫中符合模組 主版本號後綴 的子目錄,其中可能定義了模組。例如,儲存庫中 根路徑 為 example.com/mod 的模組 example.com/mod/v2,可能定義在儲存庫根目錄或主版本號子目錄 v2 中。請參閱 儲存庫內的模組目錄。
主版本號後綴 (major version suffix): 符合主版本號的模組路徑後綴。例如,example.com/mod/v2 中的 /v2。在 v2.0.0 及更高版本中必須使用主版本號後綴,而在更早的版本中則不允許使用。請參閱關於 主版本號後綴 的章節。
最小版本選擇 (MVS): 用於決定建置中所使用之所有模組版本的演算法。詳細資訊請參閱關於 最小版本選擇 的章節。
次版本號 (minor version): 語意版本中的第二個數字(v1.2.3 中的 2)。在具有向後相容的新功能的版本中,必須遞增次版本號,並將修補程式版本號歸零。
模組 (module): 一組一起發布、版本化與分發的套件集合。
模組快取 (module cache): 儲存已下載模組的本機目錄,位於 GOPATH/pkg/mod。請參閱 模組快取。
模組圖 (module graph): 以 主模組 為根的模組需求有向圖。圖中的每個節點都是一個模組;每條邊都是來自 go.mod 檔案中 require 陳述式的版本(受主模組 go.mod 檔案中的 replace 和 exclude 陳述式影響)。
模組圖修剪 (module graph pruning): Go 1.17 中的一項變更,透過省略指定 go 1.17 或更高版本之模組的傳遞相依項,減少模組圖的大小。請參閱 模組圖修剪。
模組路徑 (module path): 識別模組並作為模組內套件匯入路徑之前綴的路徑。例如 "golang.org/x/net"。
模組代理 (module proxy): 實作 GOPROXY 協定 的 Web 伺服器。go 指令從模組代理下載版本資訊、go.mod 檔案與模組 ZIP 檔案。
模組根目錄 (module root directory): 包含定義模組之 go.mod 檔案的目錄。
模組子目錄 (module subdirectory): 模組路徑 中在 儲存庫根路徑 之後的部分,指出定義模組的子目錄。當不為空時,模組子目錄也是 語意版本標籤 的前綴。模組子目錄不包含 主版本號後綴(如果有),即使模組位於 主版本號子目錄 中也是如此。請參閱 模組路徑。
套件 (package): 同一目錄中一起編譯的原始檔集合。請參閱 Go 語言規格中的 套件章節。
套件路徑 (package path): 唯一識別套件的路徑。套件路徑是 模組路徑 與模組內子目錄的結合。例如,"golang.org/x/net/html" 是模組 "golang.org/x/net" 中 "html" 子目錄下的套件路徑。與 匯入路徑 同義。
修補程式版本號 (patch version): 語意版本中的第三個數字(v1.2.3 中的 3)。在沒有變更模組公開介面的版本中,必須遞增修補程式版本號。
預發布版本 (pre-release version): 在修補程式版本號之後,緊接著一個破折號與一系列以點分隔之識別符的版本,例如 v1.2.3-beta4。預發布版本被視為不穩定,且不被假定與其他版本相容。預發布版本的排序在對應的發布版本之前:v1.2.3-pre 在 v1.2.3 之前。另請參閱 發布版本。
偽版本 (pseudo-version): 一種編碼了版本控制系統中的修訂識別符(例如 Git 提交雜湊值)與時間戳記的版本。例如 v0.0.0-20191109021931-daa7c04131f5。用於 與非模組儲存庫的相容性,以及在無標籤版本可用時的其他情況。
發布版本 (release version): 沒有預發布後綴的版本。例如 v1.2.3,而非 v1.2.3-pre。另請參閱 預發布版本。
儲存庫根路徑 (repository root path): 模組路徑 中對應於版本控制儲存庫根目錄的部分。請參閱 模組路徑。
已撤回版本 (retracted version): 不應被依賴的版本,原因可能是發布過早,或是發布後發現了嚴重問題。請參閱 retract 指令。
語意版本標籤 (semantic version tag): 版本控制儲存庫中將 版本 對應至特定修訂版的標籤。請參閱 將版本對應至提交。
已選版本 (selected version): 由 最小版本選擇 所選擇的給定模組版本。已選版本是在 模組圖 中找到的該模組路徑之最高版本。
供應商目錄 (vendor directory): 名為 vendor 的目錄,包含建置主模組中的套件所需之其他模組的套件。透過 go mod vendor 維護。請參閱 Vendoring (引入供應商)。
版本 (version): 模組不可變快照的識別符,書寫為字母 v 後接語意版本。請參閱關於 版本 的章節。
工作區 (workspace): 磁碟上模組的集合,在執行 最小版本選擇 (MVS) 時用作主模組。請參閱關於 工作區 的章節