go mod入門

背景介紹

在Go 1.11版本開始,go支持go modules-(go官方的依賴管理系統),使得依賴版本信息的管理更加簡單方便。同時官方給了對應blog,使用戶能夠快速上手go mod,blog鏈接可參考Using Go Modules

在go 1.11中,使用go mod的modules的路徑需要在非GOPATH/src(GOPATH/src路徑下(如果在GOPATH/src路徑下,則go會使用老的方式,即使存在go.mod文件)。而在Go 1.13中,go mod將成爲默認方式。

快速上手

創建一個新的module

在非$GOPATH/src路徑下,創建一個hello目錄,並在目錄中定義hello.go文件

package hello

func Hello() string {
    return "Hello, world."
}

同時定義對應的單測文件。

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)
    }
}

定義當前路徑爲一個module,使用go mod init,在當前路徑下會生成一個go.mod文件。

$ export GO111MODULE=on
$ go mod init github.com/hello
go: creating new go.mod: module github.com/hello 

執行go test,發現打印即爲剛定義的module,github.com/hello。

$ go test
PASS
ok  	github.com/hello	0.675s

查看go.mod文件,發現go.mod文件中定義module的name和當前的go版本。

$ cat go.mod
module github.com/hello

go 1.13

添加依賴

更新hello.go,並導入包rsc.io/quote。

package hello

import "rsc.io/quote"

func Hello() string {
    return quote.Hello()
}

再次運行go 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: 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
go: finding rsc.io/sampler v1.3.0
go: finding golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
PASS
ok  	github.com/hello	0.718s

go命令通過go.mod中定義的module版本來解析導入的包。當遇到導入的包未定義在go.mod文件中,go會自動將這個module的latest版本導入go.mod文件中。查看go.mod文件可以發現,已經導入了包rsc.io/quote v1.5.2。

module github.com/hello

go 1.13

require rsc.io/quote v1.5.2

從上面的命令來看,添加一個依賴的同時經常會引入另一個依賴,go list -m all命令列出了當前的module以及對應所有依賴。可以看到,在 go list的輸出中,當前的module(github.com/hello是第一行)

$ go list -m all
github.com/hello
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
rsc.io/quote v1.5.2
rsc.io/sampler v1.3.0

在目錄下除了go.mod文件,還會生成一個go.sum文件,go.sum文件中包含了特定module版本的加密哈希值,用來確保在以後的下載過程中下載的文件內容和第一次是一致的,也就是保證版本的一致性,因此go.mod和go.sum文件需要添加到版本控制中。

golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZOaTkIIMiVjBQcw93ERBE4m30iBm00nkL0i8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
rsc.io/quote v1.5.2 h1:w5fcysjrx7yqtD/aO+QwRjYZOKnaM9Uh2b40tElTs3Y=
rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0=
rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

更新依賴

在Go module中,版本可分爲三個部分,分別爲major,minor和patch。舉個例子,對於版本v0.1.2,major version是0,minor version是1,patch version是2.

從go list -m all中看出,當前使用的包golang.org/x/test沒有對應的tag,因此將這個包升級到最新版

$ go list -m all
github.com/hello
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
rsc.io/quote v1.5.2
rsc.io/sampler v1.3.1

執行go get升級包,可以發現包已經升級到最新版。

$go get golang.org/x/test
$ go list -m all
github.com/hello
golang.org/x/text v0.3.2
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e
rsc.io/quote v1.5.2
rsc.io/sampler v1.3.1

查看go.sum文件,可以看到當前使用的包就是最新的v0.3.2版本。

$ cat go.mod
module github.com/hello

go 1.13

require (
	golang.org/x/text v0.3.2 // indirect
	rsc.io/quote v1.5.2
	rsc.io/sampler v1.3.1 // indirect
)

更新另一個包,rsc.io/sampler,同樣使用go get方法進行升級,並運行go test。

$ go get rsc.io/sampler
go: finding rsc.io/sampler v1.99.99
go: downloading rsc.io/sampler v1.99.99
go: extracting rsc.io/sampler v1.99.99
$ go test
--- FAIL: TestHello (0.00s)
    hello_test.go:8: Hello() = "99 bottles of beer on the wall, 99 bottles of beer, ...", want "Hello, world."
FAIL
exit status 1
FAIL	github.com/hello	0.633s

從結果中看到最新的包rsc.io/sampler並不兼容當前的用法,使用go list查看可用的版本。從上面的go.mod文件中可以看到,當前使用的版本是v1.3.1,而1.99.99這個版本並不適合。因此,使用版本v1.3.1來代替

$ 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/[email protected]

添加一個新的major版本的依賴

修改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()
}

並添加新的單測

func TestProverb(t *testing.T) {
    want := "Concurrency is not parallelism."
    if got := Proverb(); got != want {
        t.Errorf("Proverb() = %q, want %q", got, want)
    }
}

測試新的代碼,並打印當前的module

$ 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  	github.com/hello	0.463s
$ go list -m rsc.io/q...
rsc.io/quote v1.5.2
rsc.io/quote/v3 v3.1.0

更新新的major版本依賴

在本節中,將當前的包從rsc.io/quote轉換到rsc.io.quote/v3版本,因爲當前的主版本改變了,因此當前包中的一些APIs也會被修改。通過go doc命令,發現Hello()已經變成了HelloV3()

$ go doc rsc.io/quote/v3
package quote // import "rsc.io/quote/v3"

Package quote collects pithy sayings.

func Concurrency() string
func GlassV3() string
func GoV3() string
func HelloV3() string
func OptV3() string

修改hello.go文件,不需要對包進行重命名,可以直接引用包。

package hello

import "rsc.io/quote/v3"

func Hello() string {
    return quote.HelloV3()
}

func Proverb() string {
    return quote.Concurrency()
}

執行go test,執行結果正常。

$ go test
PASS
ok  	github.com/hello	1.161s

移除未使用的依賴

在上面的例子中,已經移除了包rsc.io/quote,通過go list和go.mod文件,可以發現這個包並沒有在依賴關係中刪除。

$ go list -m all
github.com/hello
golang.org/x/text v0.3.2
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e
rsc.io/quote v1.5.2
rsc.io/quote/v3 v3.1.0
rsc.io/sampler v1.3.1

$ cat go.mod
module github.com/hello

go 1.13

require (
	golang.org/x/text v0.3.2 // indirect
	rsc.io/quote v1.5.2
	rsc.io/quote/v3 v3.1.0
	rsc.io/sampler v1.3.1 // indirect
)

從結果中看出,雖然在代碼中已經移除了對於包rsc.io/quote的依賴,但是在go list和go.mod文件中依然保留。go build和go test可以用來添加缺失的依賴,但並不能用於安全地移除未使用的包。

命令go mod tidy可以用來清除這些未使用的依賴,從結果中可以看到包rsc.io/quote已經被移除。

$ go mod tidy

$ go list -m all
github.com/hello
golang.org/x/text v0.3.2
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e
rsc.io/quote/v3 v3.1.0
rsc.io/sampler v1.3.1

$ cat go.mod
module github.com/hello

go 1.13

require (
	golang.org/x/text v0.3.2 // indirect
	rsc.io/quote/v3 v3.1.0
	rsc.io/sampler v1.3.1 // indirect
)

結論

從目前來看,go modules將會是go未來的版本依賴管理趨勢。

  • go mod init,用來創建一個新的module,並初始化go.mod文件。
  • go build,go test用來添加新的依賴到go.mod文件中。
  • go list -m all打印當前module的依賴。
  • go get 改變當前依賴的版本或添加一個新的依賴。
  • go mod tidy用來移除未使用的依賴。

對於go mod的具體用法可以,go mod的輸出:

Usage:

	go mod <command> [arguments]

The commands are:

	download    download modules to local cache
	edit        edit go.mod from tools or scripts
	graph       print module requirement graph
	init        initialize new module in current directory
	tidy        add missing and remove unused modules
	vendor      make vendored copy of dependencies
	verify      verify dependencies have expected content
	why         explain why packages or modules are needed
  • download 用來下載依賴的module到$GOPATH/pkg/mod路徑下
  • edit 用來編譯go.mod文件
  • graph 用來打印當前module的依賴圖
  • init 初始化一個新的module並生成go.mod。
  • tidy 添加丟失的module,並移除未使用的module
  • vendor 將依賴複製到vendor下
  • verify 驗證依賴
  • why 解釋爲什麼該包或module被需要
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章