Go包管理之modules
Go 包管理发展进程
在 Go 语言出世之时还未为其设计包管理工具,所以在最初开发之时,包管理一直是它的一个痛点。
- 在 1.5 版本之前,所有的依赖包都是放在
GOPATH
之下的,并且没有版本控制。这会带来一些问题,如 A 和 B 项目依赖不同版本的相同包,但是包没有做到完全向前兼容,在使用过程中可能会出现奇怪的 bug 。 - 1.5 版本推出了vendor 机制,就是让每个项目的根目录下都有一个 vendor 目录,里面存放项目所依赖的包,在
go build
时会先查找 vendor 目录,之后查找GOPATH
。 - 1.9 版本推出了包管理工具
dep
,因为争议,重新开发了 modules。 - 1.11 版本推出了 modules 机制,即
go mod
。
一些比较流行的第三方包管理工具: govendor
、godep
、glide
注:因本人未使用过
go mod
之前的包管理工具所以仅做简述,若想详细了解,请百度或 Google。
Go modules 简介
Go 1.11支持 modules 模式,是官方推出的 Go 包版本依赖管理系统,Go module 通过 go.mod
文件维护一个包文件树和包对应的版本。
Go module 模式通过
GO111MODULE
环境变量来设置 module 模式是否开启,它有三个值on
、off
、auto
,在 Go 1.11 和 Go 1.12 版本,默认为GOMODULE=auto
,即在GOPATH
中 Go module 模式无效,GOPATH
之外开启有效,关闭无效。Go 1.11 和 1.12 在项目
GOPATH
中,通过设置环境变量GO111MODULE=on
来强制开启 Go module 模式,但是go.mod
文件应该存在;在GOPATH
之外,通过设置环境变量GO111MODULE=off
来强制开启GOPATH
模式,即使go.mod
文件存在。从 Go 1.13 开始 go module 模式作为所有开发的默认模式。并且 Go module 模式默认
GO111MODULE=auto
发生了改变:1.在GOPATH
下go.mod
文件存在,环境变量GO111MODULE=on
;在GOPATH
外,即使go.mod
不存在,环境变量也是GO111MODULE=on
。2.在GOPATH
下,没有go.mod
文件时,环境变量GO111MODULE=off
Go modules 模式的使用
注意:使用官网教程使用 module 模式(需要开启代理下载所需要的包)
启动 module 模式
在 GOPATH
之外创建一个空的目录,并且增加 hello.go
文件,内容如下
package hello
func Hello() string {
return "Hello, world."
}
编写测试文件,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)
}
}
结果如下:
$ go test
PASS
ok _/home/gopher/hello 0.020s
$
此时项目在 GOPATH
外运行,并且没有开启 module 模式,Go 命令在不知道当前目录的导入路径下,会根据当前目录名创建一个假目录,即 _/home/gopher/hello
。
执行 shell
创建 module 模式。
$ go mod init example.com/hello
go: creating new go.mod: module example.com/hello
$ go test
PASS
ok example.com/hello 0.020s
$
此时已经使用了 Go module 模式,并且在该目录下生成了 go.mod
文件。该文件仅出现在 module 模式的根目录下,如果在 module 模式下创建 world 子目录,不需要在子目录在执行一次操作,包会自动识别 module ,并且导入路径 example.com/hello/world
。
增加依赖
修改之前的文件,导入 rsc.io/quote
包
package hello
import "rsc.io/quote"
func Hello() string {
return quote.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: finding rsc.io/sampler v1.3.0
go: finding golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
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
PASS
ok example.com/hello 0.023s
$
go 命令使用 go.mod
文件中列出的特定依赖模块版本来解析导入。当遇到未在 go.mod
中提供的任何模块提供的软件包导入时,go 命令将自动使用最新版本查找该软件包的模块,并将其增加到 go.mod
中,已经下载过的模块会缓存到 $GOPATH/pkg/mod
目录下。
注意:Go 的这些命令会有一定内存的消耗。
通过 Go 命令下载我们直接增加的依赖以外,通常还会有这些依赖的一些间接依赖会被下载,可以通过以下命令来查看:
$ go list -m all
example.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 命令还会维持 go.sum
文件,其中保存了指定的模块版本加密哈希值。用于保证这些模块将来下载于第一次下载时检索的位相同,从而确保项目依赖的模块不会因为一些原因而被更改。所以应该将 go.mod
和 go.sum
都加入到版本控制中检查。
更新依赖
go 模块有不同的版本,通过 go 命令指定版本可进行更新。v0.1.2
表示的是:0主要版本,1次要版本,2补丁。
更新次要版本:
$ go get golang.org/x/text
go: finding golang.org/x/text v0.3.0
go: downloading golang.org/x/text v0.3.0
go: extracting golang.org/x/text v0.3.0
$ go test
PASS
ok example.com/hello 0.013s
$
查看更新后的结果:
$ go list -m all
example.com/hello
golang.org/x/text v0.3.0
rsc.io/quote v1.5.2
rsc.io/sampler v1.3.0
$ cat go.mod
module example.com/hello
go 1.12
require (
golang.org/x/text v0.3.0 // indirect
rsc.io/quote v1.5.2
)
$
以上的 indirect 注释表示模块没直接使用依赖,仅间接接收其他模块依赖。
查看模块可用的版本:
$ 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
命令显示指定版本,默认是 @latest 也就是最新版。
更新主版本,可以指定模块的主版本文件夹。即在文件中导入 rsc.io/quote/v3
(包未变还是 quote 包)。 然后通过 go 命令就下载对版本。
移出未使用的依赖:
$ go mod tidy
$ go list -m all
example.com/hello
golang.org/x/text v0.3.0
rsc.io/quote/v3 v3.1.0
rsc.io/sampler v1.3.1
$ cat go.mod
module example.com/hello
go 1.12
require (
golang.org/x/text v0.3.0 // indirect
rsc.io/quote/v3 v3.1.0
rsc.io/sampler v1.3.1 // indirect
)
Go module的坑
vscode
代码提示
若 VSCODE
打开 GOPATH
下开启了 Go modules 的项目,查找代码很慢解决方案。可以在 GOPATH
下关闭 Go modules 模式,若在 GOPATH
外打开Go modules 项目特别慢可以增加 vendor 模式来自动拷贝项目所需要的依赖 go mod vendor
来解决,也可以将其增加到 GOPATH
中,具体跟 Go 的版本相关,详情见上。
go get
命令
开启 module 模式 go get
拉取指定版本时,注意加上 -u 参数的变化。详情见此