正在或即将被使用的Go依赖包管理方法:Go Modules,Go 1.13的标准特性
公眾號原文地址:https://mp.weixin.qq.com/s/SGGV3tWEg5AAJ7I_FcK0cg
目錄
-
目錄
-
說明
-
初始化
-
依賴包的默認導入
-
依賴包的特定版本導入
-
查看已添加依賴
-
依賴包的存放管理
-
依賴包的版本切換
-
刪除未使用依賴包
-
引用項目中的 package
-
引用不同版本的父子目錄
-
實例演示
-
需要注意的坑
-
IDE 與 Go Modules
-
IntelliJ IDEA/Goland
-
vim
-
參考
說明
Go 的依賴包管理一直是個問題,先后出現了 godep、glide、dep 等一系列工具,vendor 機制使依賴包的管理方便了很多,但依然沒有統一的管理工具,不同的項目各用各的方法。
另外使用 vendor 后,每個項目都完整拷貝一份依賴包,既不方便管理又浪費了本地空間。
此外,Go 項目中的 import 指令后面的 package 路徑與項目代碼的存放路徑相關,項目目錄不能隨意移動,必須安分守己地趴在 $GOPATH/src 中,否則 import 會找不到項目中的 package,雖然可以通過在容器中編譯或者為每個項目準備一套 Go 環境的方式解決,但是麻煩且有額外開銷。
Go1.11 和 Go1.12 引入的 Go Modules 機制,提供了統一的依賴包管理工具 go mod,依賴包統一收集在 $GOPATH/pkg/mod 中進行集中管理,并且將 import 路徑與項目代碼的實際存放路徑解耦,使 package 定義導入更加靈活。
Go Modules 將成為 Go1.13 默認的依賴包管理方法,在 Go1.11 和 Go1.12 中, Go Modules 只能在 $GOPATH 外部使用,Using Go Modules 中有詳細介紹。
很多開源項目已經改用 Go Modules 了,瀏覽代碼的時候會發現,很多項目的 master 分支中增加了 go.mod 和 go.sum 文件。
Go Modules 的主要功能就四個:添加依賴、更新依賴、刪除依賴,以及多版本依賴。
初始化
Go Modules 的初始化命令為?go mod init <ROOTPATH>,ROOTPATH 是項目的 import 路徑。
在 $GOPATH 外部創建一個目錄,然后初始化,項目的路徑設置為?exampe.com/hello:
$ mkdir go-modules-example$ cd go-modules-example$ go mod init example.com/hello # 該項目代碼的引用路徑是 example.com/hellogo: creating new go.mod: module example.com/hello引用該項目中的 package 時使用前綴?example.com/hello。
項目下生成一個 go.mod 文件,里面記錄了 module 路徑和 go 的版本,剛創建時這個文件中沒有依賴信息:
$ cat go.modmodule example.com/hellogo 1.12對于 Go1.11 和 Go1.12,如果在 $GOPATH 中執行 go mod 會遇到下面的錯誤:
$ go mod init example.com/hellogo: modules disabled inside GOPATH/src by GO111MODULE=auto; see 'go help modules'依賴包的默認導入
在 go-modules-example 中創建一個 main.go,簡單寫幾行代碼,引入 “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 運行測試代碼時,默認將 import 引入的 package 的最新版本寫入 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 中寫入依賴關系:
$ cat go.modmodule example.com/hellogo 1.12require github.com/lijiaocn/golib v0.0.1go.sum 中記錄的完整依賴:
$ cat go.sumgithub.com/lijiaocn/golib v0.0.1 h1:bC8xWHei7xTa8x65ShiPBNjVYXoxt6EDmnSUaGgRUW8=github.com/lijiaocn/golib v0.0.1/go.mod h1:BUO0RF2eDlol519GuXLQtlku8pdUim0h+f6wvX/AsNk=依賴包的特定版本導入
在使用 go modules 的項目目錄中,用 go get 下載的代碼包自動作為依賴包添加,例如:
$ 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 中增加了一行記錄,新增的依賴被標注為?indirect,意思是還沒有被使用:
$ 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 添加依賴的時候,可以用?@v1.1?樣式的后綴指定依賴的版本,例如:
$ go get github.com/lijiaocn/glib@v0.0.2查看已添加依賴
go list?命令列出當前項目的依賴包以及代碼版本:
$ go list -m allexample.com/hellogithub.com/lijiaocn/codes-go v0.0.0-20180220071929-9290fe35de7egithub.com/lijiaocn/golib v0.0.1依賴包的存放管理
依賴包既不在?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/?中有原始代碼的緩存,避免重復下載:
$ 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依賴包的版本切換
依賴代碼的版本更新很簡單,直接用 go get 獲取指定版本的依賴代碼即可,例如將 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可以看到依賴的代碼版本發生了變化:
$ go list -m allexample.com/hellogithub.com/lijiaocn/codes-go v0.0.0-20180220071929-9290fe35de7egithub.com/lijiaocn/golib v0.0.2刪除未使用依賴
不需要的依賴必須手動清除,執行?go mod tidy,清除所有未使用的依賴:
$ go mod tidy $ go list -m allexample.com/hellogithub.com/lijiaocn/golib v0.0.2引用項目中的 package
在項目中創建一個名為 display 的 package:
$ tree displaydisplay└── display.go導入時使用 go mod 初始化時定義的前綴,example.com/hello/display:
import ( "example.com/hello/display" "github.com/lijiaocn/golib/version" )引用當前項目中的 package 時,import 使用的路徑和項目所在的路徑徹底解耦,但是要注意,如果提供給外部項目使用,需要確保 go get 能夠從 example.com 獲得 /hello/display。
引用不同版本的父子目錄
Using Go Modules 中有一節是?Adding a dependency on a new major version,示例中引入了 v1.5.2 版本的 rsc.io/quote,和 v3.1.0 版本的 rsc.io/quote/v3,這兩個 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 是一個真實存在的子目錄,必須是用 go modules 管理的,rsc.io/quote 和 rsc.io/quote/v3 是父子目錄,但它們是完全獨立的 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 ...可以看到兩個版本同時存在:
$ go list -m rsc.io/q...rsc.io/quote v1.5.2rsc.io/quote/v3 v3.1.0實例演示
實現一個用 go modules 管理的 package: github.com/introclass/go_mod_example_pkg
image在另一個使用 go modules 的項目中引用 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查看依賴的代碼,顯示依賴的是 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 函數中使用導入的依賴包:
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()}編譯執行,輸出的v1.0.1:
$ ./helloversion: compile at: golib v2display printv1.0.1將依賴包切換到版本 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重新編譯執行,輸出的版本是 v2.0.1:
$ ./helloversion: compile at: golib v2display printv2.0.1引用依賴包 v3.0.1 版本的 v3 子目錄(事實上是一個獨立的 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 函數,引用 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()}重新編譯執行,分別輸出 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 會在本地緩存代碼,如果被引用的代碼的版本號不變,但是代碼變了(在做實驗或者代碼版本管理比較亂的時候,可能會出現的這種情況),清除本地緩存(?GOPATH/pkg/mod/ 依賴代碼 )后,才能重新拉取最新的代碼(可能會有其它的更新緩存的方法);
3、如果被外部項目引用,go.mod 中設置的 package 路徑需要與代碼的獲取地址相同,項目內部引用沒有該限制,github.com/introclass/go-mod-example 的 go.mod 中標注的是 example.com/hello,代碼獲取地址 github.com/intraoclass/go-mode-example 與 example.com/hello 不一致,在另一個項目中用 github 地址加載時會失敗:
$ go get github.com/introclass/go-mod-examplego: finding github.com/introclass/go-mod-example latestgo: github.com/introclass/go-mod-exampleIDE 與 Go Modules 項目
IntelliJ IDEA/Goland
在 IntelliJ IDEA 或者 Goland 中(需要是最新的2019.1版本)導入使用 Go Module 的項目的時候,要選擇?Go Module(vgo),否則 IDE 找不到 import 導入的代碼,create-a-project-with-vgo-integration 有更多介紹:
imageIntelliJ IDEA/Goland 左側編碼顯示的依賴代碼(帶有版本號或者 commit id):
imagevim
vim插件 vim-go 從 v1.19 開始支持 go.mod,但是代碼跳轉等還不支持。
cmd/go: track tools/tooling updates to support modules 列出了一些工具對 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語言項目的代碼閱讀技巧)
關注后加作者微信
image公眾號原文地址:https://mp.weixin.qq.com/s/SGGV3tWEg5AAJ7I_FcK0cg
轉載于:https://www.cnblogs.com/lijiaocn/p/11178642.html
總結
以上是生活随笔為你收集整理的正在或即将被使用的Go依赖包管理方法:Go Modules,Go 1.13的标准特性的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 开博序
- 下一篇: celery 学习笔记 01-介绍