Go 模塊--開始使用 Go Modules

Go的1.11和1.12版本包括對模塊--新的Go依賴管理系統(tǒng)的初步支持,使依賴版本信息變得明確且更易于管理。這篇博客文章介紹了開始使用模塊所需的基本操作。

模塊是存儲在根目錄有一個 go.mod文件的文件樹中的 Go 包(package)的集合满哪。go.mod文件定義了模塊的module path(也是模塊根目錄的導(dǎo)入路徑)以及模塊依賴的其他模塊的要求,滿足了依賴要求模塊才能被成功構(gòu)建起來丢胚。每個依賴模塊的要求被寫為一個模塊路徑和相應(yīng)的模塊版本翩瓜。

下面展示了一個簡單的go.mod文件

module example.com/hello

go 1.12

require rsc.io/quote v1.5.2

從Go 1.11開始受扳,當(dāng)當(dāng)前目錄或任何父目錄有g(shù)o.mod時携龟,只要該目錄位于$GOPATH/src之外,go命令就可以使用模塊勘高。 (在$ GOPATH/src內(nèi)部峡蟋,出于兼容性考慮,即使找到了go.mod华望,go命令仍然在舊的GOPATH模式下運(yùn)行蕊蝗。)從Go 1.13開始,模塊模式將是所有開發(fā)的默認(rèn)模式赖舟。

本文介紹了使用模塊開發(fā)Go代碼時出現(xiàn)的一系列常見操作:

創(chuàng)建一個新模塊蓬戚。

添加模塊的依賴項。

升級模塊的依賴項宾抓。

增加依賴項的主版本子漩。

將依賴項升級到新的主版本。

刪除未使用的依賴項石洗。

創(chuàng)建一個新模塊

在$GOPATH/src之外的某個地方創(chuàng)建一個新的空目錄,然后在新目錄下創(chuàng)建一個新的源文件hello.go:

package hello

func Hello() string {

? ? return "Hello, world."

}

同時編寫它的測試文件hello_test.go

package hello

import "testing"

func TestHello(t *testing.T) {

? ? want := "Hello, world."

? ? if got := Hello(); got != want {

? ? ? ? t.Errorf("Hello() = %q, want %q", got, want)

? ? }

}

假設(shè)我們新建的目錄為/home/gopher/hello,此時該目錄包含一個包脾还,而不是模塊性锭,因為目錄中沒有g(shù)o.mod文件。使用 go 命令運(yùn)行測試會看到:

$ go test

PASS

ok? ? ? _/home/gopher/hello? ? 0.020s

$

輸出的最后一行匯總了整個包的測試信息涉兽。因為我們工作在$GOPATH和任意模塊之外招驴,go 命令不知道當(dāng)前目錄的導(dǎo)入路徑(導(dǎo)入路徑是標(biāo)識包的唯一字符串標(biāo)識)所以根據(jù)目錄所在位置創(chuàng)建了一個假的導(dǎo)入路徑_/home/gopher/hello

讓我們使用go mod init將當(dāng)前目錄設(shè)為一個模塊的根目錄,然后再次執(zhí)行g(shù)o test:

$ go mod init example.com/hello

go: creating new go.mod: module example.com/hello

go mod init命令編寫了一個go.mod文件:

$ cat go.mod

module example.com/hello

go 1.12

$

go.mod僅出現(xiàn)在模塊的根目錄中枷畏。位于子目錄中的包的導(dǎo)入路徑將由模塊路徑加上子目錄路徑組成别厘。比如說如果我們創(chuàng)建了一個子目錄world無需(也不希望)在其中運(yùn)行g(shù)o mod init。該包將自動被識別為example.com/hello模塊的一部分矿辽,導(dǎo)入路徑為example.com/hello/world丹允。

現(xiàn)在再運(yùn)行g(shù)o test其運(yùn)行結(jié)果如下:

$ go test

PASS

ok? ? ? example.com/hello? ? 0.020s

$

現(xiàn)在輸出中的導(dǎo)入路徑變成了example.com/hello郭厌,不知不覺中就編寫并測試了我們的第一個go模塊。

添加模塊依賴

Go模塊的主要動機(jī)是改善管理使用其他開發(fā)者編寫的代碼(代碼依賴)的體驗雕蔽。 讓我們更新hello.go以導(dǎo)入rsc.io/quote并使用它來實(shí)現(xiàn)Hello 函數(shù):

package hello

import "rsc.io/quote"

func Hello() string {

? ? return quote.Hello()

}

現(xiàn)在再次運(yùn)行g(shù)o test:

$ go test

go: finding rsc.io/quote v1.5.2

go: downloading rsc.io/quote v1.5.2

go: extracting rsc.io/quote v1.5.2

go: finding rsc.io/sampler v1.3.0

go: finding golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c

go: downloading rsc.io/sampler v1.3.0

go: extracting rsc.io/sampler v1.3.0

go: downloading golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c

go: extracting golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c

PASS

ok? ? ? example.com/hello? ? 0.023s

$

go命令使用在go.mod中列出的指定的依賴模塊版本來解析導(dǎo)入折柠,當(dāng)遇到未由go.mod中的任何模塊提供的包的導(dǎo)入時,go命令將自動查找包含該軟件包的模塊批狐,使用其最新的穩(wěn)定版本扇售,并將其添加到go.mod中。 在我們的示例中嚣艇,go test將新的導(dǎo)入rsc.io/quote解析為rsc.io/quote v1.5.2模塊承冰,它還下載了rsc.io/quote使用的兩個依賴項,即rsc.io/sampler和golang.org/x/text食零。但是只有直接依賴項被記錄在go.mod文件中:

$ cat go.mod

module example.com/hello

go 1.12

require rsc.io/quote v1.5.2

$

再次運(yùn)行g(shù)o test命令不會重復(fù)上面的依賴下載工作困乒,因為go.mod現(xiàn)在是最新的,并且下載的模塊已本地緩存在$ GOPATH/pkg / mod中了贰谣。

正如我們在上面看到的娜搂,添加一個直接依賴項通常也會帶來其他間接依賴項。命令go list -m all列出當(dāng)前模塊及其所有依賴項:

$ go list -m all

example.com/hello

golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c

rsc.io/quote v1.5.2

rsc.io/sampler v1.3.0

$

在go list的輸出中吱抚,當(dāng)前模塊也被稱為主模塊百宇,總是會出現(xiàn)在第一行,后面跟隨的是根據(jù)模塊路徑排序后展示的依賴項:

除了go.mod之外秘豹,go命令還會維護(hù)一個名為go.sum的文件携御,其中包含依賴模塊版本的加密哈希值:

$ cat go.sum

golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZO...

golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:Nq...

rsc.io/quote v1.5.2 h1:w5fcysjrx7yqtD/aO+QwRjYZOKnaM9Uh2b40tElTs3...

rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPX...

rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/Q...

rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9...

$

go命令使用go.sum文件來確保這些模塊的將來的下載與第一次下載相同,以確保項目所依賴的模塊不會由于惡意既绕,意外或其他原因而意外更改啄刹。此外**go.sum并不是類似package-lock.json的包管理器鎖文件**,它是一個構(gòu)建狀態(tài)跟蹤文件岸更。它會記錄當(dāng)前模塊所有的直接和間接依賴鸵膏,以及這些依賴的校驗和,從而提供一個可以100%復(fù)現(xiàn)的構(gòu)建過程并對構(gòu)建對象提供安全性的保證怎炊。所以應(yīng)該將go.mod和go.sum都添加到版本控制中谭企。go.sum同時還會保留過去使用的包的版本信息,以便日后可能的版本回退评肆,這一點(diǎn)也與普通的鎖文件不同债查。所以go.sum并不是包管理器的鎖文件。

更新依賴

對于Go模塊瓜挽,使用語義版本標(biāo)記引用模塊版本盹廷。語義版本包括三個部分:主要,次要和補(bǔ)丁久橙。例如俄占,對于v0.1.2管怠,主要版本為0,次要版本為1缸榄,補(bǔ)丁版本為2渤弛。讓我們逐步進(jìn)行幾個次要版本升級。在下一節(jié)中甚带,我們將考慮進(jìn)行主要版本升級

從go list -m all的輸出中她肯,我們可以看到我們正在使用未標(biāo)記版本的golang.org/x/text。讓我們升級到最新的標(biāo)記版本鹰贵,并測試一切是否正常:

$ go get golang.org/x/text

go: finding golang.org/x/text v0.3.0

go: downloading golang.org/x/text v0.3.0

go: extracting golang.org/x/text v0.3.0

$ go test

PASS

ok? ? ? example.com/hello? ? 0.013s

$

測試通過了晴氨。讓我們再來看一下go list -m all的輸出和go.mod文件里的內(nèi)容:

$ go list -m all

example.com/hello

golang.org/x/text v0.3.0

rsc.io/quote v1.5.2

rsc.io/sampler v1.3.0

$ cat go.mod

module example.com/hello

go 1.12

require (

? ? golang.org/x/text v0.3.0 // indirect

? ? rsc.io/quote v1.5.2

)

$

golang.org/x/text軟件包已升級到最新的標(biāo)記版本(v0.3.0)。 go.mod文件中g(shù)olang.org/x/text也已更新為指定的v0.3.0碉输。indirect注釋指明依賴項不被當(dāng)前模塊直接使用籽前,而是由其依賴的模塊所使用的。

現(xiàn)在腊瑟,讓我們嘗試升級rsc.io/sampler到指定的版本聚假,首先列出它的可用版本:

$ go list -m -versions rsc.io/sampler

rsc.io/sampler v1.0.0 v1.2.0 v1.2.1 v1.3.0 v1.3.1 v1.99.99

$

我們將 rsc.io/sampler升級到v1.3.1

$ go get rsc.io/sampler@v1.3.1

go: finding rsc.io/sampler v1.3.1

go: downloading rsc.io/sampler v1.3.1

go: extracting rsc.io/sampler v1.3.1

$ go test

PASS

ok? ? ? example.com/hello? ? 0.022s

$

注意go get參數(shù)中的顯式@ v1.3.1块蚌。通常闰非,傳遞給get的每個參數(shù)都可以采用顯式形式。默認(rèn)值為@latest峭范,它將解析為先前定義的最新版本财松。

增加依賴的主版本

讓我們在包中添加一個新函數(shù):函數(shù)Proverb通過調(diào)用quote.Concurrency返回Go并發(fā)諺語(就是Pike說在某年 Go 開發(fā)大會上說的金句:"Concurrency is not parallelism"),這是由rsc.io/quote/v3模塊提供的纱控。首先辆毡,我們更新hello.go以添加新功能:

package hello

import (

? ? "rsc.io/quote"

? ? quoteV3 "rsc.io/quote/v3"

)

func Hello() string {

? ? return quote.Hello()

}

func Proverb() string {

? ? return quoteV3.Concurrency()

}

然后我們在hello_test.go中添加測試方法:

func TestProverb(t *testing.T) {

? ? want := "Concurrency is not parallelism."

? ? if got := Proverb(); got != want {

? ? ? ? t.Errorf("Proverb() = %q, want %q", got, want)

? ? }

}

然后我們運(yùn)行測試:

$ go test

go: finding rsc.io/quote/v3 v3.1.0

go: downloading rsc.io/quote/v3 v3.1.0

go: extracting rsc.io/quote/v3 v3.1.0

PASS

ok? ? ? example.com/hello? ? 0.024s

$

可以看到 go 命令下載安裝了rsc.io/quote/v3模塊,現(xiàn)在我們的模塊同時依賴了 rsc.io/quote和rsc.io/quote/v3:

$ go list -m rsc.io/q...

rsc.io/quote v1.5.2

rsc.io/quote/v3 v3.1.0

$

Go模塊的每個不同的主要版本(v1甜害,v2等)都使用不同的模塊路徑:從v2開始舶掖,該路徑必須以主要版本結(jié)尾。在示例中尔店,rsc.io/quote的v3版本的模塊路徑不再是rsc.io/quote眨攘,而是rsc.io/quote/v3。此約定稱為語義導(dǎo)入版本控制嚣州,它為不兼容的程序包(具有不同主要版本的程序包)提供了不同的名稱鲫售。相反,rsc.io/quote的v1.6.0應(yīng)該與v1.5.2向后兼容该肴,因此它重用了名稱rsc.io/quote情竹。

go命令要求每個主版本模塊路徑不可重復(fù),每個主要版本的至多:一個rsc.io/quote匀哄,一個rsc.io/quote/v2秦效,一個rsc.io/quote/v3雏蛮,依此類推。這為模塊作者提供了關(guān)于可能重復(fù)單個模塊路徑的明確規(guī)則:程序無法同時使用rsc.io/quote v1.5.2和rsc.io/quote v1.6.0來構(gòu)建阱州。同時底扳,允許模塊的不同主要版本(因為它們具有不同的路徑)使模塊使用者可以逐步升級到新的主要版本。在此示例中贡耽,我們想使用rsc/quote/v3 v3.1.0中的quote.Concurrency衷模,但尚未準(zhǔn)備好遷移rsc.io/quote v1.5.2的使用。在大型程序或代碼庫中蒲赂,增量遷移的能力尤其重要阱冶。

將依賴項升級到新的主版本

讓我們完成從使用rsc.io/quote兩個版本的包到僅使用rsc.io/quote/v3的轉(zhuǎn)換。由于版本的重大更改滥嘴,我們應(yīng)該期望某些API可能已以不兼容的方式被刪除木蹬,重命名或以其他方式更改。閱讀文檔若皱,我們可以看到Hello已經(jīng)變成HelloV3:

$ go doc rsc.io/quote/v3

package quote // import "rsc.io/quote"

Package quote collects pithy sayings.

func Concurrency() string

func GlassV3() string

func GoV3() string

func HelloV3() string

func OptV3() string

$

我們可以把hello.go中對qoute.Hello()的調(diào)用更新為使用quoteV3.HelloV3()镊叁,現(xiàn)在已經(jīng)不需要對 v3 版本的導(dǎo)入路徑重命名了所以我們撤銷包的重命名(注意默認(rèn)包名不會包含版本后綴)。

package hello

import "rsc.io/quote/v3"

func Hello() string {

? ? return quote.HelloV3()

}

func Proverb() string {

? ? return quote.Concurrency()

}

重新運(yùn)行測試走触,確保一切能正常工作:

$ go test

PASS

ok? ? ? example.com/hello? ? ? 0.014s

刪除未使用的依賴項

我們已經(jīng)刪除了對rsc.io/quote的所有使用晦譬,但是它仍顯示在go list -m all的輸出和go.mod文件中:

$ go list -m all

example.com/hello

golang.org/x/text v0.3.0

rsc.io/quote v1.5.2

rsc.io/quote/v3 v3.1.0

rsc.io/sampler v1.3.1

$ cat go.mod

module example.com/hello

go 1.12

require (

? ? golang.org/x/text v0.3.0 // indirect

? ? rsc.io/quote v1.5.2

? ? rsc.io/quote/v3 v3.0.0

? ? rsc.io/sampler v1.3.1 // indirect

)

$

為什么?因為構(gòu)建單個軟件包(例如使用go build或go test)可以輕松判斷出來缺少某些內(nèi)容并需要添加互广,但無法確定某些內(nèi)容是否可以安全刪除敛腌。只有在檢查模塊中的所有軟件包以及這些軟件包的所有可能的構(gòu)建標(biāo)記組合之后,才能刪除依賴項惫皱。普通的build命令不會加載此信息像樊,因此它不能安全地刪除依賴項。

go mod tidy命令會清除這些未使用的依賴項:

$ go mod tidy

$ go list -m all

example.com/hello

golang.org/x/text v0.3.0

rsc.io/quote/v3 v3.1.0

rsc.io/sampler v1.3.1

$ cat go.mod

module example.com/hello

go 1.12

require (

? ? golang.org/x/text v0.3.0 // indirect

? ? rsc.io/quote/v3 v3.1.0

? ? rsc.io/sampler v1.3.1 // indirect

)

$ go test

PASS

ok? ? ? example.com/hello? ? 0.020s

$

總結(jié)

Go模塊是Go依賴管理的未來旅敷。從 Go1.11都提供模塊功能生棍。 這篇文章介紹了使用Go模塊的這些工作流程:

go mod init 創(chuàng)建一個新模塊,初始化描述它的go.mod文件媳谁。

go buil涂滴,go test和其他程序包構(gòu)建命令根據(jù)需要向go.mod添加新的依賴項。

go list -m all打印當(dāng)前模塊的依賴關(guān)系韩脑。

go get更改所需依賴的版本(或添加新的依賴)氢妈。

go mod tidy刪除未使用的依賴項。

最后:

為了幫助大家少走彎路段多,我總結(jié)出一個Java程序員的工作2-5年成長路線圖首量。

上面都是自己整理好的!我就把資料貢獻(xiàn)出來給有需要的人!順便求一波關(guān)注加缘,哈哈~各位小伙伴關(guān)注我后私信【Java】就可以免費(fèi)領(lǐng)取噠

原文作者:?kevinyan

原文鏈接:https://juejin.im/post/5e119b196fb9a048246197d4

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末鸭叙,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子拣宏,更是在濱河造成了極大的恐慌沈贝,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件勋乾,死亡現(xiàn)場離奇詭異宋下,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)辑莫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進(jìn)店門学歧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人各吨,你說我怎么就攤上這事枝笨。” “怎么了揭蜒?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵横浑,是天一觀的道長。 經(jīng)常有香客問我屉更,道長徙融,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任偶垮,我火速辦了婚禮张咳,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘似舵。我一直安慰自己,他們只是感情好葱峡,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布砚哗。 她就那樣靜靜地躺著,像睡著了一般砰奕。 火紅的嫁衣襯著肌膚如雪蛛芥。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天军援,我揣著相機(jī)與錄音仅淑,去河邊找鬼。 笑死胸哥,一個胖子當(dāng)著我的面吹牛涯竟,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼庐船,長吁一口氣:“原來是場噩夢啊……” “哼银酬!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起筐钟,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤揩瞪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后篓冲,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體李破,經(jīng)...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年壹将,在試婚紗的時候發(fā)現(xiàn)自己被綠了喷屋。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,567評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡瞭恰,死狀恐怖屯曹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情惊畏,我是刑警寧澤恶耽,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站颜启,受9級特大地震影響偷俭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜缰盏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一涌萤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧口猜,春花似錦负溪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至须尚,卻和暖如春崖堤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背耐床。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工密幔, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人撩轰。 一個月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓胯甩,卻偏偏與公主長得像昧廷,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蜡豹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,585評論 2 359

推薦閱讀更多精彩內(nèi)容