1. 程式人生 > 實用技巧 >go mod 包依賴自動管理模組

go mod 包依賴自動管理模組

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(預設值),具體含義如下:

  1. off: GOPATH mode,查詢vendor和GOPATH目錄
  2. on:module-aware mode,使用 go module,忽略GOPATH目錄
  3. auto:如果當前目錄不在$GOPATH並且當前目錄(或者父目錄)下有go.mod檔案,則使用GO111MODULE, 否則仍舊使用 GOPATH mode。

修改GO111MODULE 的值的語句是:setGO111MODULE=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.1go: 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:

  1. set GO111MODULE=on //windows

  2. export GO111MODULE=on //linux

3.2$GOPATH/go.mod exists but should not

GO 1.11或之後模組遇到這個問題:

$GOPATH/go.mod exists but should not

開啟模組支援後(setGO111MODULE=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 initredisTest,成功建立時返回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 initdbredis(此處我寫的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 buildredisTest.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
 
)