正在或即将被使用的Go依赖包管理方法:Go Modules,Go 1.13的标准特性
公眾號(hào)原文地址:https://mp.weixin.qq.com/s/SGGV3tWEg5AAJ7I_FcK0cg
目錄
-
目錄
-
說(shuō)明
-
初始化
-
依賴(lài)包的默認(rèn)導(dǎo)入
-
依賴(lài)包的特定版本導(dǎo)入
-
查看已添加依賴(lài)
-
依賴(lài)包的存放管理
-
依賴(lài)包的版本切換
-
刪除未使用依賴(lài)包
-
引用項(xiàng)目中的 package
-
引用不同版本的父子目錄
-
實(shí)例演示
-
需要注意的坑
-
IDE 與 Go Modules
-
IntelliJ IDEA/Goland
-
vim
-
參考
說(shuō)明
Go 的依賴(lài)包管理一直是個(gè)問(wèn)題,先后出現(xiàn)了 godep、glide、dep 等一系列工具,vendor 機(jī)制使依賴(lài)包的管理方便了很多,但依然沒(méi)有統(tǒng)一的管理工具,不同的項(xiàng)目各用各的方法。
另外使用 vendor 后,每個(gè)項(xiàng)目都完整拷貝一份依賴(lài)包,既不方便管理又浪費(fèi)了本地空間。
此外,Go 項(xiàng)目中的 import 指令后面的 package 路徑與項(xiàng)目代碼的存放路徑相關(guān),項(xiàng)目目錄不能隨意移動(dòng),必須安分守己地趴在 $GOPATH/src 中,否則 import 會(huì)找不到項(xiàng)目中的 package,雖然可以通過(guò)在容器中編譯或者為每個(gè)項(xiàng)目準(zhǔn)備一套 Go 環(huán)境的方式解決,但是麻煩且有額外開(kāi)銷(xiāo)。
Go1.11 和 Go1.12 引入的 Go Modules 機(jī)制,提供了統(tǒng)一的依賴(lài)包管理工具 go mod,依賴(lài)包統(tǒng)一收集在 $GOPATH/pkg/mod 中進(jìn)行集中管理,并且將 import 路徑與項(xiàng)目代碼的實(shí)際存放路徑解耦,使 package 定義導(dǎo)入更加靈活。
Go Modules 將成為 Go1.13 默認(rèn)的依賴(lài)包管理方法,在 Go1.11 和 Go1.12 中, Go Modules 只能在 $GOPATH 外部使用,Using Go Modules 中有詳細(xì)介紹。
很多開(kāi)源項(xiàng)目已經(jīng)改用 Go Modules 了,瀏覽代碼的時(shí)候會(huì)發(fā)現(xiàn),很多項(xiàng)目的 master 分支中增加了 go.mod 和 go.sum 文件。
Go Modules 的主要功能就四個(gè):添加依賴(lài)、更新依賴(lài)、刪除依賴(lài),以及多版本依賴(lài)。
初始化
Go Modules 的初始化命令為?go mod init <ROOTPATH>,ROOTPATH 是項(xiàng)目的 import 路徑。
在 $GOPATH 外部創(chuàng)建一個(gè)目錄,然后初始化,項(xiàng)目的路徑設(shè)置為?exampe.com/hello:
$ mkdir go-modules-example$ cd go-modules-example$ go mod init example.com/hello # 該項(xiàng)目代碼的引用路徑是 example.com/hellogo: creating new go.mod: module example.com/hello引用該項(xiàng)目中的 package 時(shí)使用前綴?example.com/hello。
項(xiàng)目下生成一個(gè) go.mod 文件,里面記錄了 module 路徑和 go 的版本,剛創(chuàng)建時(shí)這個(gè)文件中沒(méi)有依賴(lài)信息:
$ cat go.modmodule example.com/hellogo 1.12對(duì)于 Go1.11 和 Go1.12,如果在 $GOPATH 中執(zhí)行 go mod 會(huì)遇到下面的錯(cuò)誤:
$ go mod init example.com/hellogo: modules disabled inside GOPATH/src by GO111MODULE=auto; see 'go help modules'依賴(lài)包的默認(rèn)導(dǎo)入
在 go-modules-example 中創(chuàng)建一個(gè) main.go,簡(jiǎn)單寫(xiě)幾行代碼,引入 “github.com/lijiaocn/golib/version”:
// Create: 2019/05/05 16:53:00 Change: 2019/05/05 16:56:53// FileName: main.go// Copyright (C) 2019 lijiaocn <lijiaocn@foxmail.com>//// Distributed under terms of the GPL license.package mainimport ( "github.com/lijiaocn/golib/version")func main() { version.Show()}用下面的 Makefile 編譯( Makefile 純粹為了方便,直接用 go build 也可以):
# Makefile編譯或者用 go test 運(yùn)行測(cè)試代碼時(shí),默認(rèn)將 import 引入的 package 的最新版本寫(xiě)入 go.mod 和 go.sum:
$ makego build -ldflags "-X github.com/lijiaocn/golib/version.VERSION=1.0.0 -X github.com/lijiaocn/golib/version.COMPILE=2019-05-05/09:55:04"go: finding github.com/lijiaocn/golib v0.0.1go: downloading github.com/lijiaocn/golib v0.0.1go: extracting github.com/lijiaocn/golib v0.0.1go.mod 中寫(xiě)入依賴(lài)關(guān)系:
$ cat go.modmodule example.com/hellogo 1.12require github.com/lijiaocn/golib v0.0.1go.sum 中記錄的完整依賴(lài):
$ cat go.sumgithub.com/lijiaocn/golib v0.0.1 h1:bC8xWHei7xTa8x65ShiPBNjVYXoxt6EDmnSUaGgRUW8=github.com/lijiaocn/golib v0.0.1/go.mod h1:BUO0RF2eDlol519GuXLQtlku8pdUim0h+f6wvX/AsNk=依賴(lài)包的特定版本導(dǎo)入
在使用 go modules 的項(xiàng)目目錄中,用 go get 下載的代碼包自動(dòng)作為依賴(lài)包添加,例如:
$ go get github.com/lijiaocn/codes-go/01-02-hellogo: finding github.com/lijiaocn/codes-go/01-02-hello latestgo: finding github.com/lijiaocn/codes-go latestgo: downloading github.com/lijiaocn/codes-go v0.0.0-20180220071929-9290fe35de7ego: extracting github.com/lijiaocn/codes-go v0.0.0-20180220071929-9290fe35de7ego.mod 中增加了一行記錄,新增的依賴(lài)被標(biāo)注為?indirect,意思是還沒(méi)有被使用:
$ cat go.modmodule example.com/hellogo 1.12require ( github.com/lijiaocn/codes-go v0.0.0-20180220071929-9290fe35de7e // indirect github.com/lijiaocn/golib v0.0.1)在用 go get 添加依賴(lài)的時(shí)候,可以用?@v1.1?樣式的后綴指定依賴(lài)的版本,例如:
$ go get github.com/lijiaocn/glib@v0.0.2查看已添加依賴(lài)
go list?命令列出當(dāng)前項(xiàng)目的依賴(lài)包以及代碼版本:
$ go list -m allexample.com/hellogithub.com/lijiaocn/codes-go v0.0.0-20180220071929-9290fe35de7egithub.com/lijiaocn/golib v0.0.1依賴(lài)包的存放管理
依賴(lài)包既不在?GOPATH/pkg/mod` 目錄中:
$ ls $GOPATH/pkg/mod/github.com/lijiaocn/codes-go@v0.0.0-20180220071929-9290fe35de7e golib@v0.0.1$ ls $GOPATH/pkg/mod/github.com/lijiaocn/golib@v0.0.1config container generator terminal version virtio如上所示,目錄名中包含版本信息,例如 golib@v0.0.1。
$GOPATH/pkg/mod/cache/download/?中有原始代碼的緩存,避免重復(fù)下載:
$ ls $GOPATH/pkg/mod/cache/download/github.com/lijiaocncodes-go golib$ ls $GOPATH/pkg/mod/cache/download/github.com/lijiaocn/golib/@vlist list.lock v0.0.1.info v0.0.1.lock v0.0.1.mod v0.0.1.zip v0.0.1.ziphash依賴(lài)包的版本切換
依賴(lài)代碼的版本更新很簡(jiǎn)單,直接用 go get 獲取指定版本的依賴(lài)代碼即可,例如將 lijiaocn/glib 更新到 v0.0.2:
$ go get github.com/lijiaocn/glib@v0.0.2go: finding github.com/lijiaocn/golib v0.0.2go: downloading github.com/lijiaocn/golib v0.0.2go: extracting github.com/lijiaocn/golib v0.0.2可以看到依賴(lài)的代碼版本發(fā)生了變化:
$ go list -m allexample.com/hellogithub.com/lijiaocn/codes-go v0.0.0-20180220071929-9290fe35de7egithub.com/lijiaocn/golib v0.0.2刪除未使用依賴(lài)
不需要的依賴(lài)必須手動(dòng)清除,執(zhí)行?go mod tidy,清除所有未使用的依賴(lài):
$ go mod tidy $ go list -m allexample.com/hellogithub.com/lijiaocn/golib v0.0.2引用項(xiàng)目中的 package
在項(xiàng)目中創(chuàng)建一個(gè)名為 display 的 package:
$ tree displaydisplay└── display.go導(dǎo)入時(shí)使用 go mod 初始化時(shí)定義的前綴,example.com/hello/display:
import ( "example.com/hello/display" "github.com/lijiaocn/golib/version" )引用當(dāng)前項(xiàng)目中的 package 時(shí),import 使用的路徑和項(xiàng)目所在的路徑徹底解耦,但是要注意,如果提供給外部項(xiàng)目使用,需要確保 go get 能夠從 example.com 獲得 /hello/display。
引用不同版本的父子目錄
Using Go Modules 中有一節(jié)是?Adding a dependency on a new major version,示例中引入了 v1.5.2 版本的 rsc.io/quote,和 v3.1.0 版本的 rsc.io/quote/v3,這兩個(gè) package 是父子目錄,版本不相同:
package helloimport ( "rsc.io/quote" quoteV3 "rsc.io/quote/v3")func Hello() string { return quote.Hello()}func Proverb() string { return quoteV3.Concurrency()} ? rsc.io tree quotequote├── LICENSE├── README.md├── buggy│ └── buggy_test.go├── go.mod├── go.sum├── quote.go├── quote_test.go└── v3 ├── go.mod ├── go.sum └── quote.go2 directories, 10 files注意,v3 是一個(gè)真實(shí)存在的子目錄,必須是用 go modules 管理的,rsc.io/quote 和 rsc.io/quote/v3 是父子目錄,但它們是完全獨(dú)立的 package。
引用 1.5.2 版本的 rsc.io/quote 和 v3.1.0 版本的 rsc.io/quote/v3 :
$ go get rsc.io/quote@v1.5.2 ...$ go get rsc.io/quote/v3@v3.1.0 ...可以看到兩個(gè)版本同時(shí)存在:
$ go list -m rsc.io/q...rsc.io/quote v1.5.2rsc.io/quote/v3 v3.1.0實(shí)例演示
實(shí)現(xiàn)一個(gè)用 go modules 管理的 package: github.com/introclass/go_mod_example_pkg
image在另一個(gè)使用 go modules 的項(xiàng)目中引用 v1.0.1 版本:github.com/introclass/go-mod-example
$ go get github.com/introclass/go_mod_example_pkg@v1.0.1go: finding github.com/introclass/go_mod_example_pkg v1.0.1go: downloading github.com/introclass/go_mod_example_pkg v1.0.1go: extracting github.com/introclass/go_mod_example_pkg v1.0.1查看依賴(lài)的代碼,顯示依賴(lài)的是 v1.0.1:
$ go list -m allexample.com/hellogithub.com/introclass/go_mod_example_pkg v1.0.1github.com/lijiaocn/golib v2.0.1+incompatible在 main 函數(shù)中使用導(dǎo)入的依賴(lài)包:
package mainimport ( "example.com/hello/display" pkg "github.com/introclass/go_mod_example_pkg" "github.com/lijiaocn/golib/version")func main() { version.Show() display.Display("display print\n") pkg.Vesrion()}編譯執(zhí)行,輸出的v1.0.1:
$ ./helloversion: compile at: golib v2display printv1.0.1將依賴(lài)包切換到版本 2.0.1:
$ go get github.com/introclass/go_mod_example_pkg@v2.0.1go: finding github.com/introclass/go_mod_example_pkg v2.0.1重新編譯執(zhí)行,輸出的版本是 v2.0.1:
$ ./helloversion: compile at: golib v2display printv2.0.1引用依賴(lài)包 v3.0.1 版本的 v3 子目錄(事實(shí)上是一個(gè)獨(dú)立的 pacakge ):
$ go get github.com/introclass/go_mod_example_pkg/v3@v3.0.1go: finding github.com/introclass/go_mod_example_pkg/v3 v3.0.1go: downloading github.com/introclass/go_mod_example_pkg/v3 v3.0.1go: extracting github.com/introclass/go_mod_example_pkg/v3 v3.0.1修改 main 函數(shù),引用 v3:
package mainimport ( "example.com/hello/display" pkg "github.com/introclass/go_mod_example_pkg" pkgv3 "github.com/introclass/go_mod_example_pkg/v3" "github.com/lijiaocn/golib/version")func main() { version.Show() display.Display("display print\n") pkg.Vesrion() pkgv3.Vesrion()}重新編譯執(zhí)行,分別輸出 v2.0.1 和 v3.0.1 in v3:
$ ./helloversion: compile at: golib v2display printv2.0.1v3.0.1 in v3需要注意的坑
1、引用不同版本的父子目錄,被引用的父子目錄必須是用 go mod 管理的 package,非 go mod 管理的代碼不行;
2、go mod 會(huì)在本地緩存代碼,如果被引用的代碼的版本號(hào)不變,但是代碼變了(在做實(shí)驗(yàn)或者代碼版本管理比較亂的時(shí)候,可能會(huì)出現(xiàn)的這種情況),清除本地緩存(?GOPATH/pkg/mod/ 依賴(lài)代碼 )后,才能重新拉取最新的代碼(可能會(huì)有其它的更新緩存的方法);
3、如果被外部項(xiàng)目引用,go.mod 中設(shè)置的 package 路徑需要與代碼的獲取地址相同,項(xiàng)目?jī)?nèi)部引用沒(méi)有該限制,github.com/introclass/go-mod-example 的 go.mod 中標(biāo)注的是 example.com/hello,代碼獲取地址 github.com/intraoclass/go-mode-example 與 example.com/hello 不一致,在另一個(gè)項(xiàng)目中用 github 地址加載時(shí)會(huì)失敗:
$ go get github.com/introclass/go-mod-examplego: finding github.com/introclass/go-mod-example latestgo: github.com/introclass/go-mod-exampleIDE 與 Go Modules 項(xiàng)目
IntelliJ IDEA/Goland
在 IntelliJ IDEA 或者 Goland 中(需要是最新的2019.1版本)導(dǎo)入使用 Go Module 的項(xiàng)目的時(shí)候,要選擇?Go Module(vgo),否則 IDE 找不到 import 導(dǎo)入的代碼,create-a-project-with-vgo-integration 有更多介紹:
imageIntelliJ IDEA/Goland 左側(cè)編碼顯示的依賴(lài)代碼(帶有版本號(hào)或者 commit id):
imagevim
vim插件 vim-go 從 v1.19 開(kāi)始支持 go.mod,但是代碼跳轉(zhuǎn)等還不支持。
cmd/go: track tools/tooling updates to support modules 列出了一些工具對(duì) go module 的支持情況。
參考
Using Go Modules
Where is the module cache in golang?
create-a-project-with-vgo-integration
cmd/go: track tools/tooling updates to support modules
上一篇:源代碼閱讀方法(附Go語(yǔ)言項(xiàng)目的代碼閱讀技巧)
關(guān)注后加作者微信
image公眾號(hào)原文地址:https://mp.weixin.qq.com/s/SGGV3tWEg5AAJ7I_FcK0cg
轉(zhuǎn)載于:https://www.cnblogs.com/lijiaocn/p/11178642.html
總結(jié)
以上是生活随笔為你收集整理的正在或即将被使用的Go依赖包管理方法:Go Modules,Go 1.13的标准特性的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 开博序
- 下一篇: celery 学习笔记 01-介绍