Go Modules 介紹與基本操作
Go 自 1.11 以來(lái),包含對(duì) Module 版本的支持。初始原型 vgo 于 2018 年 2 月宣布。2018 年 7 月,Module 版本進(jìn)入 Go 代碼倉(cāng)庫(kù)主分支。
Go 1.11 和 1.12 包含對(duì) Go Modules 的初步支持,Go 的新依賴項(xiàng)管理系統(tǒng)使依賴關(guān)系版本信息更加明確且更易于管理。

Module 是存儲(chǔ)在文件樹中的 Go 包的集合,其根目錄有 go.mod 文件。go.mod 文件定義了 Module 的模塊路徑,該路徑也是用于根目錄的導(dǎo)入路徑,以及其依賴項(xiàng)要求,這些依賴項(xiàng)要求是成功構(gòu)建所需的其他模塊。每個(gè)依賴項(xiàng)要求都編寫為模塊路徑和特定的語(yǔ)義版本。
自 Go 1.11 起,可以顯式啟用模塊模式(通過設(shè)置 GO111MODULE=on),go 命令允許在當(dāng)前目錄或任何父目錄具有 go.mod 文件時(shí)使用模塊模式,前提是該目錄位于 $GOPATH/src 之外。($GOPATH/src 內(nèi)部,為了兼容性,go 命令仍以舊的 GOPATH 模式運(yùn)行,即使找到 go.mod 文件。
在 Go 1.13,無(wú)需顯式設(shè)置啟用模塊模式,只需設(shè)置 GO111MODULE=auto,如果發(fā)現(xiàn)任何 go.mod,即使在 GOPATH 內(nèi)部,也表示啟用模塊模式。(在 Go 1.13 之前,GO111MODULE=auto 永遠(yuǎn)不會(huì)在 GOPATH 內(nèi)啟用模塊模式)。
自 Go 1.14 以來(lái),模塊支持被視為可供生產(chǎn)環(huán)境使用,鼓勵(lì)所有用戶從其他依賴管理系統(tǒng)遷移到 Module。并且改回需顯式設(shè)置啟用模塊模式(通過設(shè)置 GO111MODULE=on),如果不存在 go.mod 文件,大多數(shù)模塊命令的功能更有限。
在 Go 1.15,可以通過 GOMODCACHE 環(huán)境變量設(shè)置模塊緩存的位置。GOMODCACHE 的默認(rèn)值是 GOPATH[0]/pkg/mod,可以在此更改模塊緩存的位置。
本文將學(xué)習(xí)使用模塊開發(fā) Go 代碼時(shí)出現(xiàn)的一系列基本操作,示例假定在 Linux 系統(tǒng)中 gopher 用戶的家目錄。
在 $GOPATH/src 外的任何位置,創(chuàng)建一個(gè)新的空目錄,進(jìn)入新創(chuàng)建的目錄,創(chuàng)建一個(gè)新的源碼文件:hello.go。

再創(chuàng)建一個(gè)測(cè)試源碼文件:hello_test.go。

此時(shí),目錄包含包,但不包含模塊,因?yàn)闆]有 go.mod 文件。如果我們?cè)?/home/gopher/hello 目錄去運(yùn)行測(cè)試命令 go test,我們將看到:

因?yàn)槲覀冊(cè)?$GOPATH 之外,并且在任何模塊之外的目錄運(yùn)行,go 命令不知道當(dāng)前目錄的導(dǎo)入路徑,根據(jù)目錄名稱組成一個(gè)假的導(dǎo)入路徑:_/home/gopher/hello。
讓我們使用 go mod init 使當(dāng)前目錄成為模塊的根目錄,然后重試測(cè)試:

祝賀!您已經(jīng)編寫并測(cè)試了第一個(gè)模塊。go mod init 命令寫了一個(gè) go.mod 文件:

go.mod 文件僅出現(xiàn)在模塊的根目錄中。子目錄中的包具有導(dǎo)入路徑,包括模塊路徑和子目錄路徑。例如,如果我們創(chuàng)建了一個(gè)子目錄 world,我們不需要(也不想)運(yùn)行 go mod init。包將自動(dòng)識(shí)別為該模塊 example.com/hello 的一部分,導(dǎo)入路徑 example.com/hello/world。
Go modules 的主要目的是改進(jìn)使用其他開發(fā)人員編寫的代碼(即添加依賴項(xiàng))的體驗(yàn)。
讓我們更新我們的 hello.go 導(dǎo)入 rsc.io/quote 并用它來(lái)實(shí)現(xiàn)函數(shù) Hello:

現(xiàn)在讓我們?cè)俅芜\(yùn)行 go test:

go test 命令使用 go.mod 文件中列出的特定依賴項(xiàng)模塊版本解析導(dǎo)入。當(dāng)它遇到 go.mod 文件中任何模塊未提供的包的導(dǎo)入時(shí),go 命令會(huì)自動(dòng)通過「最新版本」來(lái)備份包含該包的模塊并將其添加到 go.mod?!缸钚隆苟x為最新的標(biāo)記穩(wěn)定(非預(yù)發(fā)行)版本,或者最新的標(biāo)記預(yù)發(fā)行版本,或者最新的未標(biāo)記版本。
在我們的示例中,go test 導(dǎo)入 rsc.io/quote 模塊的最新版本 rsc.io/quote v1.5.2。
它還下載了兩個(gè)間接依賴項(xiàng),
rsc.io/sampler 和 golang.org/x/text。
僅將直接依賴項(xiàng)記錄在 go.mod 文件中:

再次運(yùn)行 go test 命令,不會(huì)重復(fù)下載檢索工作,因?yàn)?go.mod 現(xiàn)在是最新的,下載的模塊在本地緩存目錄中($GOPATH[0]/pkg/mod):

請(qǐng)注意,雖然 go 命令使添加新的依賴項(xiàng)變得快速而簡(jiǎn)單,但它并非沒有成本。您的模塊現(xiàn)在實(shí)際上依賴于關(guān)鍵領(lǐng)域(如正確性、安全性和正確許可等)中的新依賴關(guān)系。
正如我們上面看到的,添加一個(gè)直接依賴關(guān)系通常也會(huì)帶來(lái)其他間接依賴關(guān)系。命令 go list -m all 列出了當(dāng)前模塊及其所有依賴項(xiàng):

在 go 列表輸出中,當(dāng)前模塊(也稱為主模塊)始終是第一行,后跟按模塊路徑排序的依賴項(xiàng)。
golang.org/x/text 版本 v0.0.0-20170915032832-14c0d48ead0c 是偽版本的示例,這是特定未標(biāo)記提交的命令版本語(yǔ)法。
除了 go.mod 之外,go 命令還維護(hù)一個(gè)名為 go.sum 的文件,其中包含特定模塊版本內(nèi)容的預(yù)期加密哈希:

go 命令使用 go.sum 文件來(lái)確保這些模塊的未來(lái)下載檢索與第一次下載相同的位,以確保項(xiàng)目所依賴的模塊不會(huì)意外更改,無(wú)論是出于惡意、意外還是其他原因。go. mod 和 go. sum 都應(yīng)簽入版本控制。
使用 Go modules,版本使用語(yǔ)義版本標(biāo)記進(jìn)行引用。語(yǔ)義版本由三個(gè)部分組成:主要版本、次要版本和修補(bǔ)程序版本。例如,對(duì)于 v0.1.2,主要版本為 0,次要版本為 1,修補(bǔ)程序版本為 2。讓我們演練幾個(gè)次要版本升級(jí)。
從 go list -m all的輸出中,我們可以看到我們使用的是未標(biāo)記的 golang.org/x/text。讓我們升級(jí)到最新的標(biāo)記版本,并測(cè)試一切仍然有效:

運(yùn)行成功,讓我們?cè)倏纯?go list -m all 和 go.mod 文件:

golang.org/x/text 已升級(jí)到最新的標(biāo)記版本 (v0.3.0)。go.mod 文件已更新,指定 v0.3.0。注釋「indirect」指示依賴項(xiàng)不直接由此模塊使用,僅由其他模塊依賴項(xiàng)間接使用。
現(xiàn)在,讓我們嘗試升級(jí) rsc.io/sampler 版本。通過運(yùn)行 go get 和運(yùn)行 go test:

go test 運(yùn)行失敗表明最新版本的 rsc.io/sampler 與我們的用法不兼容。讓我們列出該模塊的可用標(biāo)記版本:

我們一直在使用v1.3.0,v1.99.99 明顯不兼容。也許我們可以嘗試使用 v1.3.1 代替:

請(qǐng)注意在 go get 中的顯式內(nèi)容??@v1.3.1 指定 Module 版本。通常,傳遞給 go get 的每個(gè)參數(shù)都可以獲取顯式版本,默認(rèn)值為@latest,解析為前面定義的最新版本。
讓我們?cè)谖覀兊陌刑砑右粋€(gè)新函數(shù):func Proverb 返回 Go 并發(fā)原語(yǔ),通過調(diào)用 quote.Concurrency,由模塊 rsc.io/quote/v3 提供。首先我們更新 hello.go 添加新函數(shù):

然后,我們添加一個(gè) hello_test.go:

然后,我們可以測(cè)試我們的代碼:

請(qǐng)注意,我們的模塊現(xiàn)在依賴于 rsc.io/quote 和 rsc.io/quote/v3:

Go modules 的每個(gè)不同主要版本(v1、v2 等)使用不同的模塊路徑:從 v2 開始,路徑必須以主要版本結(jié)束。
在示例中,rsc.io/quote的 v3 版本不再 rsc.io/quote:而是由模塊路徑 rsc.io/quote/v3。
此約定稱為語(yǔ)義導(dǎo)入版本控制,它為不兼容的包(具有不同主要版本的包)提供不同的名稱。
相比之下,rsc.io/quote 的 v1.6.0 應(yīng)與 v1.5.2 向后兼容,因此它重用 rsc.io/quote。(在上一節(jié)中,rsc.io/sampler v1.99.99 應(yīng)與 rsc.io/sampler v1.3.0 向后兼容,但模塊行為的錯(cuò)誤或不正確的客戶端假設(shè)都可能發(fā)生。
go 命令允許生成最多包含任何特定模塊路徑的一個(gè)版本,這意味著最多包含每個(gè)主要版本的一個(gè)版本:一個(gè) rsc.io/quote、一個(gè) rsc.io/quote/v2、rsc.io/quote/v3,等等。這為模塊作者提供了關(guān)于單個(gè)模塊路徑可能重復(fù)的清晰規(guī)則:程序不可能同時(shí)使用 rsc.io/quote v1.5.2 和 rsc.io/quote v1.6.0 構(gòu)建。同時(shí),允許模塊的不同主要版本(因?yàn)樗鼈兙哂胁煌穆窂剑┦鼓K使用者能夠逐步升級(jí)到新的主要版本。
在此示例中,我們希望使用 rsc/quote/v3 v3.1.0 的 quote.Concurrency,但尚未準(zhǔn)備好遷移我們使用 rsc.io/quote v1.5.2。我們暫且通過起別名的方式使用。在大型程序或代碼庫(kù)中,增量遷移的能力尤為重要。
讓我們完成從同時(shí)使用 rsc.io/quote 和 rsc.io/quote/v3 到僅使用 rsc.io/quote/v3 的依賴項(xiàng)升級(jí)。由于主要版本更改,我們預(yù)期某些 API 可能已被刪除、重命名或以其他方式以不兼容的方式更改。
運(yùn)行 go doc rsc.io/quote/v3 命令,閱讀文檔,?我們可以看到, Hello() 已成為 Hellov3():

(輸出中還有一個(gè)已知錯(cuò)誤,顯示的導(dǎo)入路徑錯(cuò)誤地丟棄了 /v3。)
我們可以更新在 hello.go 中使用的 quote.Hello(),改為使用 quoteV3.HelloV3():

現(xiàn)在僅使用依賴項(xiàng) rsc.io/quote 的一個(gè)版本,不再需要給導(dǎo)入的依賴項(xiàng)定義別名 quoteV3,因此我們可以修改為:

讓我們重新運(yùn)行 go test,以確保一切正常運(yùn)行:

我們已經(jīng)刪除了我們使用的依賴項(xiàng) rsc.io/quote, 但它仍然出現(xiàn)在 go list -m ?all 和我們的 go.mod 文件中:

為什么?因?yàn)闃?gòu)建單個(gè)包(如 go build 或 go test)可以輕松地判斷何時(shí)缺少依賴項(xiàng)并需要添加,但何時(shí)可以安全地刪除依賴項(xiàng),只有在檢查了模塊中的所有包以及這些包的所有可能的生成標(biāo)記組合后,才能刪除依賴項(xiàng)。普通構(gòu)建命令不加載此信息,因此無(wú)法安全地刪除依賴項(xiàng)。
但是,我們可以使用 go mod tidy 命令清理這些未使用的依賴項(xiàng):

Go modules 是 Go 未來(lái)版本中使用的依賴項(xiàng)管理系統(tǒng)。模塊功能現(xiàn)在在所有支持的 Go 版本中可用(即從 Go 1.11 起的版本)。
本文介紹了 Go modules 這些基本操作:
- go mod init 創(chuàng)建一個(gè)新模塊, 并初始化描述它的 go.mod 文件。
- go build、go test 和其他包構(gòu)建命令根據(jù) go.mod 文件需要添加新的依賴項(xiàng)。
- go list -m all 打印當(dāng)前模塊的所有依賴項(xiàng)列表。
- go get 更新依賴項(xiàng)所需的版本(或添加新的依賴項(xiàng))。
- go mod tidy 刪除未使用的依賴項(xiàng)。
建議大家開始在本地開發(fā)中使用 module,并將 go.mod 和 go.sum 文件添加到項(xiàng)目中的版本控制。
原文鏈接:https://mp.weixin.qq.com/s/Ce56XkzkYhLu-T0zOEjkzw
如有侵權(quán),請(qǐng)聯(lián)系刪除