Go Modules 介紹與基本操作
Go 自 1.11 以來,包含對 Module 版本的支持。初始原型 vgo 于 2018 年 2 月宣布。2018 年 7 月,Module 版本進入 Go 代碼倉庫主分支。
Go 1.11 和 1.12 包含對 Go Modules 的初步支持,Go 的新依賴項管理系統使依賴關系版本信息更加明確且更易于管理。
Module 是存儲在文件樹中的 Go 包的集合,其根目錄有 go.mod 文件。go.mod 文件定義了 Module 的模塊路徑,該路徑也是用于根目錄的導入路徑,以及其依賴項要求,這些依賴項要求是成功構建所需的其他模塊。每個依賴項要求都編寫為模塊路徑和特定的語義版本。
自 Go 1.11 起,可以顯式啟用模塊模式(通過設置 GO111MODULE=on),go 命令允許在當前目錄或任何父目錄具有 go.mod 文件時使用模塊模式,前提是該目錄位于 $GOPATH/src 之外。($GOPATH/src 內部,為了兼容性,go 命令仍以舊的 GOPATH 模式運行,即使找到 go.mod 文件。
在 Go 1.13,無需顯式設置啟用模塊模式,只需設置 GO111MODULE=auto,如果發現任何 go.mod,即使在 GOPATH 內部,也表示啟用模塊模式。(在 Go 1.13 之前,GO111MODULE=auto 永遠不會在 GOPATH 內啟用模塊模式)。
自 Go 1.14 以來,模塊支持被視為可供生產環境使用,鼓勵所有用戶從其他依賴管理系統遷移到 Module。并且改回需顯式設置啟用模塊模式(通過設置 GO111MODULE=on),如果不存在 go.mod 文件,大多數模塊命令的功能更有限。
在 Go 1.15,可以通過 GOMODCACHE 環境變量設置模塊緩存的位置。GOMODCACHE 的默認值是 GOPATH[0]/pkg/mod,可以在此更改模塊緩存的位置。
本文將學習使用模塊開發 Go 代碼時出現的一系列基本操作,示例假定在 Linux 系統中 gopher 用戶的家目錄。
在 $GOPATH/src 外的任何位置,創建一個新的空目錄,進入新創建的目錄,創建一個新的源碼文件:hello.go。
再創建一個測試源碼文件:hello_test.go。
此時,目錄包含包,但不包含模塊,因為沒有 go.mod 文件。如果我們在 /home/gopher/hello 目錄去運行測試命令 go test,我們將看到:
因為我們在 $GOPATH 之外,并且在任何模塊之外的目錄運行,go 命令不知道當前目錄的導入路徑,根據目錄名稱組成一個假的導入路徑:_/home/gopher/hello。
讓我們使用 go mod init 使當前目錄成為模塊的根目錄,然后重試測試:
祝賀!您已經編寫并測試了第一個模塊。go mod init 命令寫了一個 go.mod 文件:
go.mod 文件僅出現在模塊的根目錄中。子目錄中的包具有導入路徑,包括模塊路徑和子目錄路徑。例如,如果我們創建了一個子目錄 world,我們不需要(也不想)運行 go mod init。包將自動識別為該模塊 example.com/hello 的一部分,導入路徑 example.com/hello/world。
Go modules 的主要目的是改進使用其他開發人員編寫的代碼(即添加依賴項)的體驗。
讓我們更新我們的 hello.go 導入 rsc.io/quote 并用它來實現函數 Hello:
現在讓我們再次運行 go test:
go test 命令使用 go.mod 文件中列出的特定依賴項模塊版本解析導入。當它遇到 go.mod 文件中任何模塊未提供的包的導入時,go 命令會自動通過「最新版本」來備份包含該包的模塊并將其添加到 go.mod。「最新」定義為最新的標記穩定(非預發行)版本,或者最新的標記預發行版本,或者最新的未標記版本。
在我們的示例中,go test 導入 rsc.io/quote 模塊的最新版本 rsc.io/quote v1.5.2。
它還下載了兩個間接依賴項,
rsc.io/sampler 和 golang.org/x/text。
僅將直接依賴項記錄在 go.mod 文件中:
再次運行 go test 命令,不會重復下載檢索工作,因為 go.mod 現在是最新的,下載的模塊在本地緩存目錄中($GOPATH[0]/pkg/mod):
請注意,雖然 go 命令使添加新的依賴項變得快速而簡單,但它并非沒有成本。您的模塊現在實際上依賴于關鍵領域(如正確性、安全性和正確許可等)中的新依賴關系。
正如我們上面看到的,添加一個直接依賴關系通常也會帶來其他間接依賴關系。命令 go list -m all 列出了當前模塊及其所有依賴項:
在 go 列表輸出中,當前模塊(也稱為主模塊)始終是第一行,后跟按模塊路徑排序的依賴項。
golang.org/x/text 版本 v0.0.0-20170915032832-14c0d48ead0c 是偽版本的示例,這是特定未標記提交的命令版本語法。
除了 go.mod 之外,go 命令還維護一個名為 go.sum 的文件,其中包含特定模塊版本內容的預期加密哈希:
go 命令使用 go.sum 文件來確保這些模塊的未來下載檢索與第一次下載相同的位,以確保項目所依賴的模塊不會意外更改,無論是出于惡意、意外還是其他原因。go. mod 和 go. sum 都應簽入版本控制。
使用 Go modules,版本使用語義版本標記進行引用。語義版本由三個部分組成:主要版本、次要版本和修補程序版本。例如,對于 v0.1.2,主要版本為 0,次要版本為 1,修補程序版本為 2。讓我們演練幾個次要版本升級。
從 go list -m all的輸出中,我們可以看到我們使用的是未標記的 golang.org/x/text。讓我們升級到最新的標記版本,并測試一切仍然有效:
運行成功,讓我們再看看 go list -m all 和 go.mod 文件:
golang.org/x/text 已升級到最新的標記版本 (v0.3.0)。go.mod 文件已更新,指定 v0.3.0。注釋「indirect」指示依賴項不直接由此模塊使用,僅由其他模塊依賴項間接使用。
現在,讓我們嘗試升級 rsc.io/sampler 版本。通過運行 go get 和運行 go test:
go test 運行失敗表明最新版本的 rsc.io/sampler 與我們的用法不兼容。讓我們列出該模塊的可用標記版本:
我們一直在使用v1.3.0,v1.99.99 明顯不兼容。也許我們可以嘗試使用 v1.3.1 代替:
請注意在 go get 中的顯式內容??@v1.3.1 指定 Module 版本。通常,傳遞給 go get 的每個參數都可以獲取顯式版本,默認值為@latest,解析為前面定義的最新版本。
讓我們在我們的包中添加一個新函數:func Proverb 返回 Go 并發原語,通過調用 quote.Concurrency,由模塊 rsc.io/quote/v3 提供。首先我們更新 hello.go 添加新函數:
然后,我們添加一個 hello_test.go:
然后,我們可以測試我們的代碼:
請注意,我們的模塊現在依賴于 rsc.io/quote 和 rsc.io/quote/v3:
Go modules 的每個不同主要版本(v1、v2 等)使用不同的模塊路徑:從 v2 開始,路徑必須以主要版本結束。
在示例中,rsc.io/quote的 v3 版本不再 rsc.io/quote:而是由模塊路徑 rsc.io/quote/v3。
此約定稱為語義導入版本控制,它為不兼容的包(具有不同主要版本的包)提供不同的名稱。
相比之下,rsc.io/quote 的 v1.6.0 應與 v1.5.2 向后兼容,因此它重用 rsc.io/quote。(在上一節中,rsc.io/sampler v1.99.99 應與 rsc.io/sampler v1.3.0 向后兼容,但模塊行為的錯誤或不正確的客戶端假設都可能發生。
go 命令允許生成最多包含任何特定模塊路徑的一個版本,這意味著最多包含每個主要版本的一個版本:一個 rsc.io/quote、一個 rsc.io/quote/v2、rsc.io/quote/v3,等等。這為模塊作者提供了關于單個模塊路徑可能重復的清晰規則:程序不可能同時使用 rsc.io/quote v1.5.2 和 rsc.io/quote v1.6.0 構建。同時,允許模塊的不同主要版本(因為它們具有不同的路徑)使模塊使用者能夠逐步升級到新的主要版本。
在此示例中,我們希望使用 rsc/quote/v3 v3.1.0 的 quote.Concurrency,但尚未準備好遷移我們使用 rsc.io/quote v1.5.2。我們暫且通過起別名的方式使用。在大型程序或代碼庫中,增量遷移的能力尤為重要。
讓我們完成從同時使用 rsc.io/quote 和 rsc.io/quote/v3 到僅使用 rsc.io/quote/v3 的依賴項升級。由于主要版本更改,我們預期某些 API 可能已被刪除、重命名或以其他方式以不兼容的方式更改。
運行 go doc rsc.io/quote/v3 命令,閱讀文檔,?我們可以看到, Hello() 已成為 Hellov3():
(輸出中還有一個已知錯誤,顯示的導入路徑錯誤地丟棄了 /v3。)
我們可以更新在 hello.go 中使用的 quote.Hello(),改為使用 quoteV3.HelloV3():
現在僅使用依賴項 rsc.io/quote 的一個版本,不再需要給導入的依賴項定義別名 quoteV3,因此我們可以修改為:
讓我們重新運行 go test,以確保一切正常運行:
我們已經刪除了我們使用的依賴項 rsc.io/quote, 但它仍然出現在 go list -m ?all 和我們的 go.mod 文件中:
為什么?因為構建單個包(如 go build 或 go test)可以輕松地判斷何時缺少依賴項并需要添加,但何時可以安全地刪除依賴項,只有在檢查了模塊中的所有包以及這些包的所有可能的生成標記組合后,才能刪除依賴項。普通構建命令不加載此信息,因此無法安全地刪除依賴項。
但是,我們可以使用 go mod tidy 命令清理這些未使用的依賴項:
Go modules 是 Go 未來版本中使用的依賴項管理系統。模塊功能現在在所有支持的 Go 版本中可用(即從 Go 1.11 起的版本)。
本文介紹了 Go modules 這些基本操作:
- go mod init 創建一個新模塊, 并初始化描述它的 go.mod 文件。
- go build、go test 和其他包構建命令根據 go.mod 文件需要添加新的依賴項。
- go list -m all 打印當前模塊的所有依賴項列表。
- go get 更新依賴項所需的版本(或添加新的依賴項)。
- go mod tidy 刪除未使用的依賴項。
建議大家開始在本地開發中使用 module,并將 go.mod 和 go.sum 文件添加到項目中的版本控制。
原文鏈接:https://mp.weixin.qq.com/s/Ce56XkzkYhLu-T0zOEjkzw
如有侵權,請聯系刪除