写的挺清楚的,备份一下 转载于:https://www.studygolang.com/articles/10523 vendor一下
当开始真正用go去做项目的时候,不可避免的就会遇到依赖包的问题。go的依赖包管理与java+maven的依赖管理不太一样,我们从GOPATH开始逐渐理解其思想,然后再对比下目前常用的依赖包管理工具。
GOPATH和GOROOT
初学者很容易会被这两个环境变量给搞晕。
GOROOT并不是必须要设置的。
GOROOT不是必须要设置的。参见Installing to a custom location,默认go会安装在/usr/local/go下,但也允许自定义安装位置,GOROOT的目的就是告知go当前的安装位置,编译的时候从GOROOT去找SDK的system libariry。
例如我用的是ubuntu 16.04,默认go的版本是1.6,如果我想升级为更新的版本,就需要自定义安装,所以我设置了GOROOT:export GOROOT=$HOME/go1.7.4
。
GOPATH必须要设置,但并不是固定不变的
GOPATH的目的是为了告知go,需要代码的时候,去哪里查找。注意这里的代码,包括本项目和引用外部项目的代码。GOPATH可以随着项目的不同而重新设置。
GOPATH下会有3个目录:src,bin,pkg。
本项目内部的依赖
以kubernetes为例。kubernetes/cmd/kubectl/kubectl.go
中引用了app包中的kubectl.go,代码是这样写的:
import ( "os" "k8s.io/kubernetes/cmd/kubectl/app" )
那么go在编译的时候怎么查找这个包呢?
这就是GOPATH发挥作用的时候了。go编译时会去$GOPATH/src/目录去查找需要的代码,因此只要上面app/kubectl.go在$GOPATH/src/k8s.io/kubernetes/cmd/kubectl/
里面,go编译的时候就能找到,那么自然的,kubernetes/cmd/kubectl/kubectl.go
也需要放到$GOPATH/src/k8s.io/
里去。最终$GOPATH里的代码结构是这样的:
├── src │ ├── k8s.io │ │ └── kubernetes │ │ ├── cmd │ │ │ ├── kubectl │ │ │ │ ├── app │ │ │ │ │ ├── BUILD │ │ │ │ │ └── kubectl.go │ │ │ │ ├── BUILD │ │ │ │ ├── kubectl.go │ │ │ │ └── OWNERS
管理外部的依赖包
不可避免的我们会使用外部的依赖包包。go在这方面做的非常飘逸。go没有像java使用maven来管理依赖包、包版本,而是直接使用GOPATH来管理外部依赖。
使用GOPATH来管理外部依赖
go允许import不同代码库的代码,例如github.com,k8s.io,golang.org等等;对于需要import的代码,可以使用 go get 命令取下来放到GOPATH对应的目录中去。例如go get github.com/silenceshell/hcache
,会下载到$GOPATH/src/github.com/silenceshell/hcache
中去,当其他项目在import github.com/silenceshell/hcache
的时候也就能找到对应的代码了。
看到这里也就明白了,对于go来说,其实并不care你的代码是内部还是外部的,总之都在GOPATH里,任何import包的路径都是从GOPATH开始的;唯一的区别,就是内部依赖的包是开发者自己写的,外部依赖的包是go get下来的。
vendor
依赖GOPATH来解决go import有个很严重的问题:如果项目依赖的包做了修改,或者干脆删掉了,会影响我的项目。因此在1.5版本以前,为了规避这个问题,通常会将当前使用的依赖包拷贝出来。
为了能让项目继续使用这些依赖包,有这么几个办法:
- 将依赖包拷贝到项目源码树中,然后修改import
- 将依赖包拷贝到项目源码树中,然后修改GOPATH
- 在某个文件中记录依赖包的版本,然后将GOPATH中的依赖包更新到对应的版本(因为依赖包实际是个git库,可以切换版本)
go作为一个现代化的语言,居然要用这么复杂不直观而又不标准的方法来管理依赖,难怪在早期会有很多人非常不看好go的前景。
为了解决这个问题,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,会编译不过:
@H_403_74@./controller.go:107: undefined: "k8s.io/kubernetes/pkg/client/unversioned".Client