1 介紹
1.1、go mod是什么
go mod 是Golang 1.11 版本引入的官方包(package)依賴管理工具,用於解決之前沒有地方記錄依賴包具體版本的問題,方便依賴包的管理。
之前Golang 主要依靠vendor和GOPATH來管理依賴庫,vendor相對主流,但現在官方更提倡go mod。
1.2、go mod初始化及使用
下載官方包1.11(及其以上版本將會自動支持gomod) 默認GO111MODULE=auto(auto是指如果在gopath下不啟用mod)
Golang 提供一個環境變量 GO111MODULE 來設置是否使用mod,它有3個可選值,分別是off, on, auto(默認值),具體含義如下:
- off: GOPATH mode,查找vendor和GOPATH目錄
- on:module-aware mode,使用 go module,忽略GOPATH目錄
- auto:如果當前目錄不在$GOPATH 並且 當前目錄(或者父目錄)下有go.mod文件,則使用 GO111MODULE, 否則仍舊使用 GOPATH mode。
修改 GO111MODULE 的值的語句是:set GO111MODULE=on 。
在使用模塊的時候, GOPATH
是無意義的,不過它還是會把下載的依賴儲存在 GOPATH/src/mod
中,也會把 go install
的結果放在 GOPATH/bin
(如果 GOBIN
不存在的話)
go mod download
下載模塊到本地緩存,緩存路徑是$GOPATH/pkg/mod/cache
go mod edit
是提供了命令版編輯go.mod
的功能,例如go mod edit -fmt go.mod
會格式化go.mod
go mod graph
把模塊之間的依賴圖顯示出來go mod init
初始化模塊(例如把原本dep管理的依賴關系轉換過來)go mod tidy
增加缺失的包,移除沒用的包go mod vendor
把依賴拷貝到vendor/
目錄下go mod verify
確認依賴關系go mod why
解釋為什么需要包和模塊
注意有幾個坑的地方:
-
go mod
命令在$GOPATH
里默認是執行不了的,因為GO111MODULE
的默認值是auto
。默認在$GOPATH
里是不會執行, 如果一定要強制執行,就設置環境變量為on
。 -
go mod init
在沒有接module名字的時候是執行不了的,會報錯go: cannot determine module path for source directory
。可以這樣執行:
$ go mod init github.com/jiajunhuang/hello
否則就要在 main.go
里加上導入聲明,例如:
$ cat main.go package main func main() { println("Hello world") } $ go mod init go: cannot determine module path for source directory /Users/jiajun/hello (outside GOPATH, no import comments) $ vim go.mod $ cat go.mod module github.com/jiajunhuang/hello $ go mod init go mod init: go.mod already exists $ rm go.mod $ vim main.go $ cat main.go package main // import "github.com/jiajunhuang/hello" func main() { println("Hello world") } $ go mod init go: creating new go.mod: module github.com/jiajunhuang/hello $ ls go.mod main.go $ cat go.mod module github.com/jiajunhuang/hello
當然,如果在已有代碼的倉庫里執行是不存在這個問題的。
2 命令
2.1 指定module的根目錄並生成go.mod文件
go mod init example.com/hello
2.2 下載並添加依賴到go.mod文件中
go build, go test
2.3 查看module下的所有依賴
go list -m all
2.4 更新穩定版依賴
go get rsc.io/sampler
2.5 更新為指定版本依賴
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 go get rsc.io/sampler@v1.3.1
2.6 清理無用的依賴
go mod tidy
2.7 將依賴復制到項目路徑的vendor文件夾中
go mod vendor
2.8 忽略cache里的包,只使用vendor目錄里的依賴進行編譯
go build -mod=vendor
2.9 校驗依賴並查看是否有修改
go mod verify
3 問題
3.1 go: modules disabled inside GOPATH/src by GO111MODULE=auto; see 'go help modules'
go mod init go: modules disabled inside GOPATH/src by GO111MODULE=auto; see 'go help modules'
開啟go module:
-
set GO111MODULE=on //windows
-
export GO111MODULE=on //linux
3.2 $GOPATH/go.mod exists but should not
GO 1.11或之后模塊遇到這個問題:
$GOPATH/go.mod exists but should not
開啟模塊支持后(set GO111MODULE=on),並不能與$GOPATH共存,所以把$GOPATH從env中移出即可(unset GOPATH),可運行“unset GOPATH && make”。
4 例子
4.1 例子1
go mod初始化:在$GOPATH外建一個文件夾,把個人代碼放進去,我的測試代碼路徑:https://github.com/kevinhao8/go-mod-example。
首先main入口代碼所在文件夾創建mod
創建語句 go mod init [module name]
比如我的測試代碼 redisTest.go,創建語句就是 go mod init redisTest,成功創建時返回 go: creating new go.mod: module redisTest
此時文件夾下出現 go.mod文件,打開發現只有2行如下,並沒有記錄依賴庫。
module redisTest
go 1.12
此時需要輸入go test語句,根據需要的依賴自動生成require,也就是依賴包,此時go.mod多了如下內容(紅字是我寫的注釋,文件里面沒有)
require (
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 // indirect(有indirect注釋的代表間接依賴,沒有的代表直接依賴)
github.com/gin-gonic/gin v1.3.0
github.com/golang/protobuf v1.3.1 // indirect
github.com/mattn/go-isatty v0.0.7 // indirect
github.com/ugorji/go/codec v0.0.0-20190316192920-e2bddce071ad // indirect(這里是版本號+時間戳+hash)
gopkg.in/go-playground/validator.v8 v8.18.2 // indirect
gopkg.in/yaml.v2 v2.2.2 // indirect
)
此時會有如下失敗提示,此處為我踩得第一個坑!!!花了挺長時間才解決
build redisTest: cannot load dbredis: cannot find module providing package dbredis
dbredis是我寫的私有包,代碼是沒有問題的,為什么找不到呢?從網上查了一圈,發現私有包如果不想發布到網上,需要手動添加require ,然后replace 進行替換,將私有包指向本地module所在的絕對或相對路徑。一般用相對路徑更通用。
此時手動將go.mod改為如下,紅字為新加
require (
dbredis v0.0.0
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 // indirect
github.com/gin-gonic/gin v1.3.0
github.com/golang/protobuf v1.3.1 // indirect
github.com/mattn/go-isatty v0.0.7 // indirect
github.com/ugorji/go/codec v0.0.0-20190316192920-e2bddce071ad // indirect
gopkg.in/go-playground/validator.v8 v8.18.2 // indirect
gopkg.in/yaml.v2 v2.2.2 // indirect
)
replace dbredis v0.0.0 => ./dbredis
再度運行go test 命令,發現仍有失敗提示如下,找不到dbredis文件夾下的go.mod文件。
go: parsing dbredis\go.mod: open E:\code\go-mod-example\dbredis\go.mod: The system cannot find the file specified.
go: error loading module requirements
從網上查資料發現,這種情況下需要給私有包也生成mod,這樣整個工程的依賴才能完整。故運行如下命令:
cd dbredis
go mod init dbredis(此處我寫的mod名跟package名一致,不知道不一致行不行)
go test
三條命令依次運行通過,dbredis文件夾下的go.mod文件如下:
module dbredis
go 1.12
require github.com/go-redis/redis v6.15.2+incompatible
此時再運行如下命令:
cd ..(回到上層文件夾)
go test
運行通過,不再有報錯。運行命令 go build redisTest.go 也能夠正常生成redisTest.exe
至此通過mod來管理依賴包基本實現,我們的程序能基本脫離$GOPATH編譯。
5 go mod 和 dep 比較
go mod
支持代理,以后就可以使用私有鏡像源了~,具體請搜索GOPROXY
go mod
速度比dep
快很多go.mod
中列出了所有的依賴,這一點其實我不是很喜歡,因為當項目一大,歷史一久,只要升級其中一個依賴,很可能整個依賴 就掛了。我還是比較喜歡只要列出頂級依賴,由程序處理子依賴的情況。
一個生成的 go.mod
的示例
module github.com/my/module/v3 // 這是你的包的聲明 // require 里是依賴。需要帶上路徑和版本。 require ( github.com/some/dependency v1.2.3 github.com/another/dependency v0.1.0 github.com/additional/dependency/v4 v4.0.0 )