以前写过一篇关于go管理依赖包工具 dep的文章,当时认为dep将会成为官方依赖工具,现在看来是自己图样图斯内幕破了,正如官方一直提到dep是“official experiment”官方实验项目的那样,随着go modules 在go1.11版推出,go1.12版功能不断改进,再到go1.13版完善优化,正式扶正。预计dep将来也只能定格在“official experiment”了。
Go Modules有哪些特点:
Go Modules
是官方正式推出的包依赖管理项目,由Russ Cox (即Go 现在的掌舵人)推动,dep是“official
experiment”仅此而已。Go modules
出现的目的之一就是为了解决GOPATH
的问题,也就相当于是抛弃GOPATH
了。以前项目必须在$GOPATH/src
里进行,现在Go
允许在$GOPATH/src
外的任何目录下使用go.mod
创建项目。当然现在- 随着模块一起推出的还有模块代理协议
(Module proxy protocol)
,通过这个协议我们可以实现Go
模块代理(Go module proxy)
,也就是依赖镜像。 Tag
必须遵循语义化版本控制,如果没有将忽略Tag
,然后根据你的Commit
时间和哈希值再为你生成一个假定的符合语义化版本控制的版本号。Go modules
还默认认为,只要你的主版本号不变,那这个模块版本肯定就不包含Breaking
changes
,因为语义化版本控制就是这么规定的啊。Global Caching
这个主要是针对Go modules
的全局缓存数据说明,如下:- 同一个模块版本的数据只缓存一份,所有其他模块共享使用。
- 目前所有模块版本数据均缓存在
$GOPATH/pkg/mod
和$GOPATH/pkg/sum
下,未来或将移至
$GOCACHE/mod
和$GOCACHE/sum
下( 可能会在当$GOPATH
被淘汰后)。 - 可以使用
go clean -modcache
清理所有已缓存的模块版本数据。
另外在 Go1.11
之后 GOCACHE
已经不允许设置为 off
了,我想着这也是为了模块数据缓存移动位置做准备,因此大家应该尽快做好适配。
如果你的版本是go1.12
或更早版本,这里建议升级到go1.13
,来体验一把go modules
,看它能给你带来哪些方面身心的愉悦。
本文将介绍使用Go Modules
相关操作
#修改 GOBIN 路径(可选)
go env -w GOBIN=$HOME/bin
#打开 Go modules
go env -w GO111MODULE=on
#设置 GOPROXY
go env -w GOPROXY=https://goproxy.cn,direct
go env -w: Go1.13 新增了 go env -w 用于写入环境变量,而写入的地方是 os.UserConfigDir 所返回的路径,需要注意的是 go env -w 不会覆写。需要指出,它不会覆盖系统环境变量。
GO111MODULE:
这个环境变量主要是 Go modules
的开关,主要有以下参数:
auto
:只在项目包含了go.mod
文件时启用Go modules
,在Go 1.13
中仍然是默认值,详见
:golang.org/issue/31857。on
:无脑启用Go modules
,推荐设置,未来版本中的默认值,让GOPATH
从此成为历史。off
:禁用Go modules
。
GOPROXY:
这个环境变量主要是用于设置 Go 模块代理,它的值是一个以英文逗号 “,” 分割的 Go module proxy 列表,默认是proxy.golang.org,国内访问不了。这里要感谢盛傲飞和七牛云为中国乃至全世界的 Go 语言开发者提供免费、可靠的、持续在线的且经过CDN加速Go module proxy(goproxy.cn)。
其实值列表中的 “direct” 为特殊指示符,用于指示 Go 回源到模块版本的源地址去抓取(比如 GitHub 等),当值列表中上一个 Go module proxy 返回 404 或 410 错误时,Go 自动尝试列表中的下一个,遇见 “direct” 时回源,遇见 EOF 时终止并抛出类似 “invalid version: unknown revision…” 的错误。
GOBIN
GOBIN是设置的编译后的存放路径,这部分也可以不设置。
3、创建你的项目
这里我们在$GOPATH/src
外,创建 /var/www/demo
实例
mkdir /var/www/demo
cd /var/www/demo
新建main.go
package main
import (
"github.com/gin-gonic/gin"
"fmt"
)
func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
fmt.Println("hello world!")
c.JSON(200, gin.H{
"message": "pong",
})
})
r.Run() // listen and serve on 0.0.0.0:8080
}
4、在/var/www/demo根目录下
#生成go.mod文件
go mod init demo
打开go.mod
文件,内容
module demo
go 1.13
go.mod
是启用了 Go moduels
的项目所必须的最重要的文件,它描述了当前项目(也就是当前模块)的元信息,每一行都以一个动词开头,目前有以下 5 个动词:
- module:用于定义当前项目的模块路径。
- go:用于设置预期的 Go 版本。
- require:用于设置一个特定的模块版本。
- exclude:用于从使用中排除一个特定的模块版本。
- replace:用于将一个模块版本替换为另外一个模块版本。
这里的填写格式基本为包引用路径+版本号,另外比较特殊的是 go $version
,目前从 Go1.13
的代码里来看,还只是个标识作用,暂时未知未来是否有更大的作用。
5、在/var/www/demo根目录下,执行 go build
go build
完成后项目
├── demo
├── go.mod
├── go.sum
└── main.go
项目中增加了go.sum
、demo
文件
go.sum
文件内容
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 h1:t8FVkw33L+wilf2QiWkw0UV77qRpcH/JHPKGpKa2E8g=
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod
github.com/gin-gonic/gin v1.4.0 h1:3tMoCCfM7ppqsR0ptz/wi1impNpT7/9wQtMZ8lr1mCQ=
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
...
go.sum
类似于比如 dep
的 Gopkg.lock
的一类文件,它详细罗列了当前项目直接或间接依赖的所有模块版本,并写明了那些模块版本的 SHA-256
哈希值以备 Go
在今后的操作中保证项目所依赖的那些模块版本不会被篡改。
我们可以看到一个模块路径可能有如下两种:
github.com/gin-gonic/gin v1.4.0 h1:3tMoCCfM7ppqsR0ptz/wi1impNpT7/9wQtMZ8lr1mCQ=
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
前者为 Go modules 打包整个模块包文件 zip 后再进行 hash 值,而后者为针对 go.mod 的 hash 值。他们两者,要不就是同时存在,要不就是只存在 go.mod hash。
那什么情况下会不存在 zip hash 呢,就是当 Go 认为肯定用不到某个模块版本的时候就会省略它的 zip hash,就会出现不存在 zip hash,只存在 go.mod hash 的情况。
go.mod文件内容发生了变化,增加了
require github.com/gin-gonic/gin v1.4.0
默认使用最新版本的package
。
更换依赖版本
查看gin所有历史版本
go list -m -versions github.com/gin-gonic/gin
github.com/gin-gonic/gin v1.1.1 v1.1.2 v1.1.3 v1.1.4 v1.3.0 v1.4.0
如果想更换依赖版本,比如v1.3.0,怎么办?
只需执行如下命令
go mod edit -require="github.com/gin-gonic/gin@v1.3.0"
go mod tidy #更新现有依赖
@后跟版本号,这个时候go.mod
已经修改好了
require github.com/gin-gonic/gin v1.3.0
查看所有项目依赖的包
go list -m all
github.com/davecgh/go-spew v1.1.0
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3
github.com/gin-gonic/gin v1.4.0
github.com/golang/protobuf v1.3.1
github.com/json-iterator/go v1.1.6
github.com/mattn/go-isatty v0.0.7
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
github.com/modern-go/reflect2 v1.0.1
github.com/pmezard/go-difflib v1.0.0
github.com/stretchr/objx v0.1.0
github.com/stretchr/testify v1.3.0
github.com/ugorji/go v1.1.4
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223
golang.org/x/text v0.3.0
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405
gopkg.in/go-playground/assert.v1 v1.2.1
gopkg.in/go-playground/validator.v8 v8.18.2
gopkg.in/yaml.v2 v2.2.2
快速迁移项目至 Go Modules
- 在你项目的根目录下执行
go mod init 项目名
(项目名可不加),以生成go.mod
文件。 - 执行
go mod tidy
更新整理现有的依赖,删除未使用的依赖。
注意
使用go.mod
管理依赖会对go get命令产生一定影响,
- 用
go help module-get
和go help gopath-get
分别去了解Go modules
启用和未启用两种状态下的go get
的行为 - 用
go get
拉取新的依赖- 拉取最新的版本(优先择取 tag):
go get golang.org/x/text@latest
- 拉取 master 分支的最新 commit:
go get golang.org/x/text@master
- 拉取 tag 为 v0.3.2 的 commit:
go get golang.org/x/text@v0.3.2
- 拉取 hash 为 342b231 的 commit,最终会被转换为 v0.3.2:
go get golang.org/x/text@342b2e
- 用
go get -u
更新现有的依赖
- 拉取最新的版本(优先择取 tag):
- 使用go的命令进行依赖包的安装之后,安装的路径仍旧为之前设置的
GOPATH
。
go mod 相关命令
go mod download
下载 go.mod 文件中指明的所有依赖
go mod tidy
整理现有的依赖,删除未使用的依赖。
go mod graph
查看现有的依赖结构
go mod init
生成 go.mod
文件 (Go 1.13 中唯一一个可以生成 go.mod
文件的子命令)
go mod edit
编辑 go.mod
文件
go mod vendor
导出现有的所有依赖 (事实上 Go modules
正在淡化 Vendor
的概念)
go mod verify
校验一个模块是否被篡改过
go clean -modcache
清理所有已缓存的模块版本数据。
go mod
查看所有 go mod
的使用命令。