在前面转载了系列文章:Golang 需要避免踩的 50 个坑,总的来说阅读量都挺大。今天这篇文章,咱们一起聊聊Go的依赖包管理工具
背景每一门语言都有其依赖的生态,当我们使用Java语言的时候,使用Maven或者Gradle管理包依赖。早期的Go被很多开发者所诟病的一个问题就是依赖包的管理。Golang 1.5 release版本的发布之前,只能通过设置多个GoPATH的方式来解决这个问题,例如:我两个工程都依赖了Beego,但是A工程依赖的是Beego1.1,B工程依赖的是Beego1.7,我必须设置两个GOPATH来区分,并且在切换工程的时候GOPATH也得切换,无比痛苦。在Golang1.5 release开始支持除了GOROOT和GOPATH之外的依赖管理:vender,官方wiki推荐了多种支持这种特性的包管理工具,如:Godep,gv、gvt、govendor和官方的dep等。
环境准备Go准备
go env
set GOARCH=amd64
set GOBIN=
set GOEXE=.exe
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOOS=windows
set GOPATH=C:\Users\Administrator\go
set GORACE=
set GOROOT=E:\software\Go
set GOTOOLDIR=E:\software\Go\pkg\tool\windows_amd64
set GCCGO=gccgo
set CC=gcc
set GOGCCFLAGS=-m64 -mthreads -fmessage-length=0 -fdebug-prefix-map=C:\Users\ADMINI~1\AppData\Local\Temp\go-build004730355=/tmp/go-build -gno-record-gcc-switches
set CXX=g++
set CGO_ENABLED=1
set CGO_CFLAGS=-g -O2
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-g -O2
set CGO_FFLAGS=-g -O2
set CGO_LDFLAGS=-g -O2
set PKG_CONFIG=pkg-config
go version
go version go1.9.2 windows/amd64
GOPATH&GOROOT
GOROOT不是必须要设置的。默认GO会安装在/usr/local/go下,但是也会允许自定义安装位置,GORROT的目的就是告知Go当前的安装位置,编译的时候从GOROOT去找SDK的system libariry
GOPATH必须要设置,但是并不是固定不变的。GOPATH的目的是为了告知Go,需要代码的时候,去哪里寻找。注意这里的代码,包括本项目和引用外部项目的代码。GOPATH可以随着项目的不同而重新设置。
GOPATH下会有3个目录:src、bin、pkg。
- src目录:go编译时查找代码的地方;
- bin目录:go get这种bin工具的时候,二进制文件下载的目的地;
- pkg目录:编译生成的lib文件存储的地方。
上面小节提到,依赖的代码去$GOPATH指定的位置寻找,这部分代码可能是本项目或者外部引用的项目。下面依次介绍这两种情况。
内部依赖管理
如笔者示例route_auth.go的引入:
import (
"gwp/Chapter_2_Go_ChitChat/chitchat/data"
"net/http"
)
route_auth.go需要引用data/user.go,项目结构如下:
编译时会去$ GOPATH/src/目录去查找需要的代码,因此只要上面data/user.go在$GOPATH/src/gwp/Chapter_2_Go_ChitChat/chitchat- /data里面,go编译的时候就能找到。
外部依赖管理
对于外部依赖的管理,在最开始的时候go没有像java使用maven来管理依赖包、包版本;而是直接使用GOPATH来管理外部依赖。
github.com, k8s.io, golang.orgimportgo getGOPATHgo get github.com/globalsign/mgo$GOPATH/src/github.com/globalsign/mgoimport github.com/globalsign/mgo
goGOPATHimportGOPATHgo getGo
github.com
vendor
依赖GOPATH来解决go import存在的问题在上面小节已经列举。为了解决这个问题,go在1.5版本引入了vendor属性(默认关闭,需要设置go环境变量GO15VENDOREXPERIMENT=1),并在1.6版本中默认开启了vendor属性。
简单来说,vendor属性就是让go编译时, 优先从项目源码树根目录下的vendor目录查找代码(可以理解为切了一次GOPATH),如果vendor中有,则不再去GOPATH中去查找。
以kube-keepalived-vip为例。该项目会调用k8s.io/kubernetes的库(Client),但如果你用1.5版本的kubernetes代码来编译keepalived,会编译不过。1.5版本中代码有变化,已经没有这个Client了。这就是前面说的依赖GOPATH来解决go import所带来的问题,代码不对上了。
kube-keepalived-vip项目用vendor目录解决了这个问题:该项目把所有依赖的包都拷贝到了vendor目录下,对于需要编译该项目的人来说,只要把代码从github上clone到$ GOPATH/src以后,就可以进去go build了(注意,必须将kube-keepalived-vip项目拷贝到$ GOPATH/src目录中,否则go会无视vendor目录,仍然去$GOPATH/src中去找依赖包)。
通过如上vendor解决了部分问题,然而又引起了新的问题:
- vendor目录中依赖包没有版本信息。这样依赖包脱离了版本管理,对于升级、问题追溯,会有点困难。
- 如何方便的得到本项目依赖了哪些包,并方便的将其拷贝到vendor目录下?依靠人工实在不现实。
为了解决这些问题,开源社区在vendor基础上开发了多个管理工具,比较常用的有godep、govendor glide等,go官方发布了dep。
godep
godep时解决包依赖的管理工具,原理是扫描记录版本控制的信息,并在go命令前加壳来做到依赖管理。godep早期版本并不依赖vendor,所以对go的版本要求很松,go1.5之前的版本也可以用,只是行为上有所不同。在vendor推出之后,godep也改为使用vendor了。godep建议在golang1.6之后使用
,而且godep依赖vendor
godep的使用者众多,如docker,kubernetes, coreos等go项目很多都是使用godep来管理其依赖,当然原因可能是早期也没的工具可选。
下载安装
go get -u -v github.com/tools/godep
通过如上的命令安装,成功安装后,在$GOPATH的bin目录下会有一个godep可执行的二进制文件,后面执行的命令都是用这个,建议这个目录加入到全局环境变量中。
编译运行
因为go命令是直接到GOPATH目录下去找第三方库,且在1.6以后支持vendor方式编译,而使用godep下载的依赖库放到Godeps/workspace目录下的,但是不影响继续使用依赖GOPATH目录,所以与三方工具本身不冲突。因此使用:
godep go build main.go
godep中的go命令,就是将原先的go命令加了一层壳,执行godep go的时候,会将当前项目的workspace目录加入GOPATH变量中。
检出依赖
如果要增加新的依赖包
- 运行 go get github.com/globalsign/mgo
- 代码中 import github.com/globalsign/mgo
项目编写好了,使用GOPATH的依赖包测试ok了的时候,执行:
godep save
如上的命令将会自动扫描当前目录所属包中import的所有外部依赖库(非系统库),并将所有的依赖库下来下来到当前工程中,产生文件 Godeps/Godeps.json 文件。
godep save时godep把所有依赖包代码从GOPATH路径拷贝到Godeps目录下,并去除代码管理目录。这个用处主要是为了支撑godep go tool的一系列操作,尤其是git clone了代码库下来后,通常直接用godep go install xxx即可完成编译,一定程度上能够缓解golang比较严格的代码路径和包管理带来的烦恼。在没有 Godeps 文件的情况下,生成模组依赖目录vendor文件夹。如果是开发依赖使用三方库,需要固定使用某个版本,请完全提交Godeps和vendor文件夹。
依赖包会有更新,如何更新依赖包?可以通过如下的命令实现。
- 运行 go get -u github.com/globalsign/mgo
- 运行 godep update github.com/globalsign/mgo
拉取依赖 restore
godep restore
godep restore执行时,godep会按照Godeps/Godeps.json内列表,依次执行go get -d -v来下载对应依赖包到GOPATH路径下。
govendor
govendor是在vendor之后出来的,功能相对godep多一点,不过就核心问题的解决来说基本是一样的。该工具将项目依赖的外部包拷贝到项目下的 vendor 目录下,并通过 vendor.json 文件来记录依赖包的版本,方便用户使用相对稳定的依赖。
go get -u github.com/kardianos/govendor
如上的命令即可安装govendor,govendor生成vendor目录的时候需要2条命令:
- govendor init生成vendor/vendor.json,此时文件中只有本项目的信息
- govendor add +external更新vendor/vendor.json,并拷贝GOPATH下的代码到vendor目录中。
govendor还可以直接指定依赖包版本来获取包。
govendor的依赖包主要有以下多种类型:
使用步骤
进入项目的根目录。
# 创建 vendor 文件夹和 vendor.json 文件
govendor init
# 从 $GOPATH 中添加依赖包,会加到 vendor.json
govendor add +external
# 列出已经存在的依赖包
govendor list
# 找出使用的对应包
govendor list -v fmt
# 拉取指定版本的包
govendor fetch golang.org/x/net/context@a4bbce9fcae005b22ae5443f6af064d80a6f5a55
govendor fetch golang.org/x/net/context@v1 # Get latest v1.*.* tag or branch.
govendor fetch golang.org/x/net/context@=v1 # Get the tag or branch named "v1".
相对上面的工具来说,govendor功能更加丰富。
总结本文主要介绍了几种go依赖包管理工具,首先介绍了go的环境安装,配置对应的环境变量;其次讲到包管理的两种类型:内部依赖和外部依赖的管理。内部依赖包的管理很简单,go原生的外部依赖包管理存在很多缺陷,随后介绍了开源社区推出的godep和govendor,在vendor基础上进行了功能的完善。还有目前常用的包依赖管理工具glide和官方的dep,将会在后面的文章介绍,尽请期待。