前提
对于Go的版本管理主要用过 glide,下面介绍 Go 1.11 之后官方支持的版本管理工具 mod。
关于 mod 官方给出了三个命令 go help mod
、go help modules
、go help module-get
帮助了解使用。
设置 GO111MODULE
可以用环境变量 GO111MODULE
开启或关闭模块支持,它有三个可选值:off、on、auto,默认值是 auto。
GO111MODULE=off
无模块支持,go 会从 GOPATH 和 vendor 文件夹寻找包。GO111MODULE=on
模块支持,go 会忽略 GOPATH 和 vendor 文件夹,只根据 go.mod 下载依赖。GO111MODULE=auto
在 $GOPATH/src 外面且根目录有 go.mod 文件时,开启模块支持。
在使用模块的时候,GOPATH 是无意义的,不过它还是会把下载的依赖储存在 $GOPATH/pkg/mod 中,也会把 go install 的结果放在 $GOPATH/bin 中。
Go Mod 命令
download download modules to local cache (下载依赖的module到本地cache)) edit edit go.mod from tools or scripts (编辑go.mod文件) graph print module requirement graph (打印模块依赖图)) init initialize new module in current directory (再当前文件夹下初始化一个新的module, 创建go.mod文件)) tidy add missing and remove unused modules (增加丢失的module,去掉未用的module) vendor make vendored copy of dependencies (将依赖复制到vendor下) verify verify dependencies have expected content (校验依赖) why explain why packages or modules are needed (解释为什么需要依赖)
Go Mod 使用
创建 go.mod 文件
在一个新的项目中,需要执行go mod init
来初始化创建文件go.mod
,go.mod
中会列出所有依赖包的路径和版本。
module github.com/xfstart07/watcher require ( github.com/apex/log v1.0.0 github.com/fatih/color v1.7.0 // indirect github.com/fsnotify/fsnotify v1.4.7 github.com/go-ini/ini v1.38.2 github.com/go-kit/kit v0.7.0 github.com/go-logfmt/logfmt v0.3.0 // indirect )
indirect
表示这个库是间接引用进来的。
go mod vendor
命令可以在项目中创建 vendor
文件夹将依赖包拷贝过来。
go mod download
命令用于将依赖包缓存到本地Cache起来。
显示所有Import库信息
go list -m -json all
-json
JSON格式显示all
显示全部库
Mod Cache 路径
默认在$GOPATH/pkg
下面:
$GOPATH/pkg/mod
我们来看看一个项目下载下来的文件形式:
➜ mod ls -lh cache/download/github.com/go-kit/kit/@v/ total 3016 -rw-r--r-- 1 a1 staff 7B Sep 29 15:37 list -rw------- 1 a1 staff 50B Sep 29 15:37 v0.7.0.info -rw------- 1 a1 staff 29B Sep 29 15:37 v0.7.0.mod -rw-r--r-- 1 a1 staff 1.5M Sep 29 15:37 v0.7.0.zip -rw-r--r-- 1 a1 staff 47B Sep 29 15:37 v0.7.0.ziphash
可以看出项目库会对每个版本创建一个文件夹,文件夹下有对于版本的信息。
天坑来了:go mod 不能下载google包 ?
对于全世界绝大多数Gophers来说,Go module的引入带来的都是满满的幸福感,但是对于位于中国大陆地区的Gopher来说,在这种幸福感袭来的同时,也夹带了一丝“无奈”。其原因在于module-aware mode下,go tool默认不再使用传统GOPATH下或top vendor下面的包了,而是在GOPATH/pkg/mod(go 1.11中是这个位置,也许以后版本这个位置会变动)下面寻找Go module的local cache。
由于众所周知的原因,在大陆地区我们无法直接通过go get命令或git clone获取到一些第三方包,这其中最常见的就是golang.org/x下面的各种优秀的包。但是在传统的GOPATH mode下,我们可以先从golang.org/x/xxx的mirror站点github.com/golang/xxx上git clone这些包,然后将其重命名为golang.org/x/xxx。这样也能勉强通过开发者本地的编译。又或将这些包放入vendor目录并提交到repo中,也能实现正确的构建。
但是go module引入后,一旦工作在module-aware mode下,go build将不care GOPATH下或是vendor下的包,而是到GOPATH/pkg/mod查询是否有module的cache,如果没有,则会去下载某个版本的module,而对于golang.org/x/xxx下面的module,在大陆地区往往会get失败。
有朋友可能会说,可以继续通过其他mirror站点下载再改名啊?理论上是可行的。但是现实中,这样做很繁琐。我们先来看看go module的专用本地缓存目录结构:
➜ /Users/tony/go/pkg/mod $tree -L 7
.
├── cache
│ └── download
│ └── golang.org
│ └── x
│ └── text
│ └── @v
│ ├── list
│ ├── v0.1.0.info
│ ├── v0.1.0.mod
│ ├── v0.1.0.zip
│ ├── v0.1.0.ziphash
│ ├── v0.3.0.info
│ ├── v0.3.0.mod
│ ├── v0.3.0.zip
│ └── v0.3.0.ziphash
└── golang.org
└── x
├── text@v0.1.0
└── text@v0.3.0
我们看到mod下的结构是经过精心设计的。cache/download下面存储了每个module的“元信息”以及每个module不同version的zip包。比如在这里,我们看到了golang.org/x/text这个module的v0.1.0和v0.3.0两个版本的元信息和对应的源码zip;同时mod下还直接存有text module的两个版本v0.1.0和v0.3.0的源码。
如果我们还像GOPATH mode下那种通过“mirror站下载再改名”的方式来满足go build的需求,那么我们需要手工分别制作某个module的不同版本的元信息以及源码目录,制作元信息时还要了解每个文件(比如:xx.info、xxx.mod等)的内容的生成机制,这样的方法的“体验”并不好。
填坑: Go module proxy
那么问题来了:大陆Gopher如何能在go module开启的状态下享受go module带来的福利呢? “解铃还须系铃人”!答案就在go 1.11中。Go 1.11在引入go module的同时,还引入了Go module proxy(go help goproxy)的概念。
go get命令默认情况下,无论是在gopath mode还是module-aware mode,都是直接从vcs服务(比如github、gitlab等)下载module的。但是Go 1.11中,我们可以通过设置GOPROXY环境变量来做一些改变:让Go命令从其他地方下载module。比如:
export GOPROXY=https://goproxy.io
一旦如上面设置生效后,后续go命令会通过go module download protocol与proxy交互下载特定版本的module。聪明的小伙伴们一定想到了。如果我们在某个国外VPS上搭建一个go module proxy server的实现,我们将可以通过该proxy下载到类似golang.org/x下面的module。与此同时,一些诸如从github.com上get package慢等次要的问题可能也被一并fix掉了。
显然Go官方加入go proxy的初衷并非为了解决中国大陆地区的下载qiang外包的烦恼的。但不可否认的是,GOPROXY让gopher在versioned go的基础上,对module和package的获取行为上增加了一层控制和干预能力。