Golang 的包管理机制
在 Go1.5 之前用 GOPATH 以及 GOROOT 这两个环境变量来管理包的位置,GOROOT 为 Go 的安装目录,以及编译过程中使用到的系统库存放位置,如 fmt。Go1.5 到 Go1.7 开始稳定到 Vendor 方式,即依赖包需要放到 $GOPATH/src/vendor 目录下,这样每个项目都有自己的 vendor 目录,但是如果依赖同样的三方包,很容易造成资源重复,Go vendor 出现了几种主流的管理工具,包括 godep、govendor、golide 等。
在 Go1.11 之前,GOPATH 是开发时的工作目录,其中包含三个子目录:
src 目录:存放 go 项目源码和依赖源码,包括使用 go get 下载的包
bin 目录:通过使用 go install 命令将 go build 编译出的二进制可执行文件存放于此
pkg 目录:go 源码包编译生成的 lib 文件存储的地方
在 Go1.11 之前,import 包时的搜索路径
GOROOT/src: 该目录保存了 Go 标准库代码(首先搜寻导入包的地方)
GOPATH/src: 该目录保存了应用自身的各个包代码和第三方依赖的代码
./vendor :vendor 方式第三方依赖包(如果支持 Vendor)
在 Unix 和类 Unix 系统上,GOPATH 默认值是 $HOME/go,Go1.11 版本后,开启 GO Modules 后,GOPATH 的作用仅仅为存放依赖的目录了。
在 Go 的 1.11 版本之前,GOPATH 是必需的,且所有的 Go 项目代码都要保存在 GOPATH/src 目录下,也就是如果想引用本地的包,你需要将包放在 $GOPATH/src 目录下才能找得到。Go 的 1.11 版本之后,GO 官方引入了 Go Modules,不仅仅方便的使用我们的依赖,而且还对依赖的版本进行了管理。
在 Go1.11 后通过 go mod vendor 和 -mod=vendor 来实现 Vendor 管理依赖方式。本来在 vgo 项目(Go Modules 前身)是要完全放弃 vendor,但是在社区反馈下还是保留了。总之就是在 Go.1.11 之后需要开启 Go Modules 条件下才能使用 Vendor,具体地感兴趣或还沿用了 Vendor 的朋友可以去了解下,不过建议以后仅使用 Go Modules 包管理方式了。
dep/govendor 机制
vendor 使用限制
使用 vendor 来管理包的项目,必须位于$GOPATH/src 下面。
vendor 目录和 json 文件
该工具将项目依赖的外部包拷贝到项目下的 vendor 目录下,并通过 vendor.json 文件来记录依赖包的版本,方便用户使用相对稳定的依赖。
vendor 机制下,如何搜索包依赖
那么查找依赖包路径的解决方案如下:
- 当前包下的 vendor 目录。
- 向上级目录查找,直到找到 src 下的 vendor 目录。
- 在 GOPATH 下面查找依赖包。
- 在 GOROOT 目录下查找
如果我们已经使用 GOPATH 去存储 packages 了,问什么还需要使用 vendor 目录呢?
这是一个很实战的问题。假如多个应用使用一个依赖包的不同版本?这个问题不只是 Go 应用,其他语言也会有这个问题。
vendor 目录允许不同的代码库拥有它自己的依赖包,并且不同于其他代码库的版本,这就很好的做到了工程的隔离。
每个项目都有各自的 vendor,每个 vendor 可以存放不同版本的依赖包。
module 机制
在 go1.11 版本中,新增了 module 管理模块功能,用来管理依赖包。要知道,在这个之前,想要对 go 语言包进行管理,只能依赖第三方库实现,比如 Vendor,GoVendor,GoDep,Dep,Glide 等等,对于初学者来说,真的是选择困难症。
开启 module 特性
要开始使用 go module 的特性, 需要先设置 GO111MODULE 环境变量。
开启 GO111MODULE。
要使用 go module,首先要设置 GO111MODULE=on,这没什么可说的,如果没设置,执行命令的时候会有提示,这个大家应该都了解了
在$GOAPTH/src 中创建项目
在$GOPATH/src 目录下创建 github.com/cnwyt/mytest 目录,mytest 为项目目录
1 | mkdir -p $GOPATH/src/github.com/cnwyt/mytest |
1 | $ export GO111MODULE=on |
当然这个 go 模块可以创建在任意位置,不强制邀请放在 GOPATH 路径下。
在$GOPATH/src 外也可以创建项目
在 GOPATH 以外的模块,创建一个 helloworld 目录,用来调用刚刚创建的 mytest 模块。
1 | $ mkdir helloworld && cd helloworld |
创建一个 main.go 文件:
1 | package main |
初始化该模块,引入 github.com/cnwyt/mytest 模块,指定版本为 latest:
1 | $ go mod init helloworld |
初始化后,会生成一个 go.mod 文件,类似 npm 里的 package.json 或者 composer 的 composer.json 的一个文件。
1 | module helloworld |
这样直接执行 go test 或者 go run main.go 会报错:
1 | $ go test |
这是为啥呢? 这是因为我们虽然创建了一个名为 github.com/cnwyt/mytest 模块,在 GOPATH 路径里也有这个模块。但是,GO 模块去 Github 去找这个模块,而不是在 GOPATH 路径里去找,所以找不到。
那该怎么办呢?
有两个解决办法:
第一个办法,很简单,就是直接将 cnwyt/mytest 模块推送的 GitHub 上。
但是,如果我要修改 cnwyt/mytest 里的代码,都得先推送到 GitHub 上,才能生效,实在太麻烦了。
那就直接使用第二个办法, 使用 go replace:
直接修改 go.mod,新增一行 replace:
1 | module helloworld |
注意版本号必须填写,可以填 v0.0.0 或者 latest.
调用第三方模块
比如项目中会用到比较流行的路由模块 gorilla/mux:
直接修改 go.mod,新增一行 require,不指定版本可以直接写 latest 获取最新版本:
1 | require github.com/gorilla/mux latest |
运行 go build 或 go test 会自动从 GitHub 下载模块,并会修改 go.mod 文件。
比如运行后会把 latest 直接修改成目前最新的版本 v1.6.2 :
1 | module helloworld |
可以看到模块 gorilla/mux 代码会下载到 $GOPATH/pkg/mod/ 模块下:
1 | $ ll /Users/wangtom/goworkspace/pkg/mod/github.com/gorilla |
不使用 vendor 和 module 机制时,可以手动下载所有依赖
在不使用 vendor 和 module 的情况下,可以使用较原始的方式,将代码中依赖的内容全部下载并编译在$GOPATH 路径下。
在项目目录下,执行如下命令:
go get -d -v ./…