Go 1.11和1.12已经基本上支持Go Modules,Go的新依赖管理系统,更好的管理依赖的版本信息。
本文使用版本Go1.13.
在Go Modules之前,每个项目都必须设置一个GOPATH,并将第三方包安装在各自的目录下,多个项目之间不能共用第三方包。
| 1 | GO111MODULE | 
新建一个module
在$GOPATH/src目录之外新建一个目录hello,并新建一个文件server.go
整个目录结构如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21➜  gopro mkdir hello
➜  gopro cd hello
➜  hello vim hello.go
➜  hello cat hello.go
package hello
func Hello() string {
    return "Hello, world."
}
➜  hello cat hello_test.go 
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)
    }
}
到目前为止这个目录包含package,但不是一个module,因为不包含go.mod文件。
执行go test,将会提示如下错误(使用的go版本为1.13)1
2➜  hello go test
go: cannot find main module; see 'go help modules'
使用go mod init把目录转换成为一个module1
2
3
4
5
6
7
8
9➜  hello go mod init example.com/hello
go: creating new go.mod: module example.com/hello
➜  hello go test
PASS
ok  	example.com/hello	0.002s
➜  hello cat go.mod 
module example.com/hello  //定义了 module path,是导入路径的根目录
go 1.13go.mod文件一般只出现于module的根目录。如果你在module下面新建了一个子目录world,world会自动被认为是example.com/hello的一部分,导入路径为example.com/hello/world。(go.mod所在的目录被认为是一个module的根目录,导入路径为example.com/hello加入对应的package名称)
添加依赖
| 1 | ➜ hello cat hello.go | 
执行go test命令,该命令会自动下载所需要的包。并将所需的依赖写入到go.mod1
2
3
4
5
6
7
8
9
10
11
12➜  hello 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  	example.com/hello	0.003s
| 1 | ➜ hello cat go.mod | 
go命令还维护了一个文件go.sum,里面定义了go.mod包中所需的依赖项。1
2
3
4
5
6
7➜  hello cat 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=
更新依赖
| 1 | ➜ hello go get golang.org/x/text | 
此时查看依赖项,golang.org/x/text已经被升级到v0.3.31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16➜  hello go list -m all
go: finding golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e
example.com/hello
golang.org/x/text v0.3.3
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e
rsc.io/quote v1.5.2
rsc.io/sampler v1.3.0
➜  hello cat  go.mod
module example.com/hello
go 1.13
require (
	golang.org/x/text v0.3.3 // indirect
	rsc.io/quote v1.5.2
)
尝试升级rsc.io/sampler1
2
3
4
5
6
7
8
9
10➜  hello 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
➜  hello 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	example.com/hello	0.002srsc.io/sampler包是更新成功了,但go test命令执行失败。
查看rsc.io/sampler版本信息1
2➜  hello 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
导致出错的原因可能是,rsc.io/sampler@v1.99.99与原来版本的包不兼容所致。我们重新安装rsc.io/sampler@v1.3.1然后再进行测试。1
2
3
4
5
6
7➜  hello go get rsc.io/sampler@v1.3.1
go: finding rsc.io/sampler v1.3.1
go: downloading rsc.io/sampler v1.3.1
go: extracting rsc.io/sampler v1.3.1
➜  hello go test
PASS
ok  	example.com/hello	0.003s
添加对新主版本的依赖
修改hello.go的内容如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15➜  hello cat 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()
}
修改hello_test.go的内容如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18package hello
import "testing"
func TestHello(t *testing.T) {
    want := "Hello, world."
    if got := Hello(); got != want {
        t.Errorf("Hello() = %q, want %q", got, want)
    }
}
func TestProverb(t *testing.T) {
    want := "Concurrency is not parallelism."
    if got := Proverb(); got != want {
        t.Errorf("Proverb() = %q, want %q", got, want)
    }
}
执行go test命令1
2
3
4
5
6➜  hello 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  	example.com/hello	0.003s
查看rsc.io/quote,发现存在不同版本的两个包。1
2
3
4➜  hello go list -m rsc.io/q...
rsc.io/quote v1.5.2
rsc.io/quote/v3 v3.1.0
升级新主版本的依赖
因为一些原因我们需要将quote升级到V3版本。
修改hello.go的内容如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14➜  hello cat hello.go
package hello
import (
    quoteV3 "rsc.io/quote/v3"
)
func Hello() string {
    return quoteV3.HelloV3()
}
func Proverb() string {
    return quoteV3.Concurrency()
}
执行go test命令1
2
3➜  hello go test
PASS
ok  	example.com/hello	0.003s
移除没有使用的依赖
在上一步中,我们的代码已经没有使用rsc.io/quote/v1版本,但rsc.io/quote v1.5.2还是存在于依赖中。1
2
3
4
5
6
7➜  hello go list -m all
example.com/hello
golang.org/x/text v0.3.3
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
因为构建单个软件包(例如使用go build或go test)可以轻松判断出什么时候缺少什么东西和需要添加什么东西,但是不能确定什么时候可以安全地删除东西。 仅在检查模块中的所有软件包以及这些软件包的所有可能的构建标记组合之后,才能删除依赖项。 普通的build命令不会加载此信息,因此它不能安全地删除依赖项。
使用go mod tidy命令清除未使用的依赖。1
2
3
4
5
6
7➜  hello go mod tidy
➜  hello go list -m all
example.com/hello
golang.org/x/text v0.3.3
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e
rsc.io/quote/v3 v3.1.0
rsc.io/sampler v1.3.1
go mod相关命令:
| COMMAND | DESC | 
|---|---|
| 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 | 
Ref:
1.https://blog.golang.org/using-go-modules
2.https://juejin.im/post/5c8e503a6fb9a070d878184a