首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

go语言依赖管理工具:dep

用go语言开发任何项目的时候,一般都会用到第三方的开源项目。也就是说我们开发的项目会依赖于一些第三方的项目,这样自然就需要管理这些依赖项。而目前针对go语言最流行的依赖管理工具就是dep。dep是一个开源的项目,地址如下:

https://github.com/golang/dep

首先要明白为什么需要依赖管理。依赖管理的目的就是要锁定依赖项目的特定版本,从而使我们自己的项目每次build时具有确定性。否则,如果不对依赖项目进行控制,每次build都基于依赖项目master分支的最新代码,那么就有可能出现兼容性问题,或者将依赖项目的最新BUG引入到我们自己的项目中。

Gopkg.toml和Gopkg.lock是dep产生的两个重要的配置文件。当首次在项目的根目录执行命令“dep init”的时候,就会自动生成这两个文件。Gopkg.toml里面定义了我们自己的项目的依赖需求,例如下面两项分别定义了依赖golang/protobuf的1.0.0版本,以及gorilla/muxde的1.6.1版本。

[[constraint]]

name = "github.com/golang/protobuf"

version = "1.0.0"

[[constraint]]

name = "github.com/gorilla/mux"

version = "1.6.1"

而Gopkg.lock里面则精确定义了依赖项目的信息。上面两项在Gopkg.lock中对应的定义如下。不仅定义了具体的commit ID,还列出了具体依赖的每个package。

[[projects]]

name = "github.com/golang/protobuf"

packages = [

"proto",

"protoc-gen-go/descriptor",

"ptypes/any",

"ptypes/timestamp"

]

version = "v1.0.0"

[[projects]]

name = "github.com/gorilla/mux"

packages = ["."]

revision = "53c1911da2b537f792e7cafcb446b05ffe33b996"

version = "v1.6.1"

一般情况下Gopkg.toml里面只定义直接依赖项,而Gopkg.lock里面除了包含Gopkg.toml中的所有项之外,还包含传递依赖项。比如我们的项目依赖项目A, 而项目A又依赖B、C,那么只有A会包含在Gopkg.toml中,而A、B、C都会定义在Gopkg.lock中。所以Gopkg.lock定义了所有依赖的项目的详细信息(commit ID和packages),使得每次build我们自己的项目时,始终基于确定不变的依赖项。

日常工作中使用dep时,其实主要就是和命令“dep ensure”打交道。理解了dep的工作机制,也就明白了“dep ensure”的执行流程。一张图就可以解释清楚:

该图片来自如下网址:

https://golang.github.io/dep/docs/ensure-mechanics.html

根据dep官网的说法,dep主要包含两个功能,分别是solving功能和vendoring功能。solving就是根据我们项目源代码中的import指令和Gopkg.toml定义的规则,来生成Gopkg.lock。通常,当我们需要升级某个依赖项目的版本时,只需要修改Gopkg.toml,然后执行"dep ensure -update xxxx",就可以自动更新Gopkg.lock。一般情况下,都不要试图手动修改Gopkg.lock!

vendoring功能则是根据Gopkg.lock中的信息,将每一个依赖项目的特定版本的源代码拷贝到vendor子目录下面。在执行"go build .."时会自动优先从vendor目录中寻找依赖项的源代码。

有些人倾向于将vendor目录也提交到repository中。这么做的优缺点很明显,优点是使我们的项目完全独立,不受上游项目的任何变更的影响,而且大多数情况下也不用执行"dep ensure"来根据Gopkg.lock来同步vendor。缺点就是使我们的项目的repository变得更加庞大,而且每次Gopkg.lock更新之后,也必须同步更新vendor目录。哪种是best practice?官方的回答是“It's up to you”,就是由你自己决定:)。

上面给出的Gopkg.toml的例子中,制定了依赖项目的版本。其实在Gopkg.toml同样可以通过revision指定具体的commit ID,例如:

[[constraint]]

name = "github.com/prometheus/client_golang"

revision = "c51dc758d4bb30acacbef9eaa2b774969a135086"

通常在依赖的项目很少打tag或者压根就没有tag,而我们又需要使用它的新版本时,就可以直接在Gopkg.toml中指定commit ID。

还有一个值得探讨的问题是,如何控制传递依赖项目的版本?首次执行“dep init"命令时,传递依赖项只会自动生成在Gopkg.lock中。那么如何升级这些传递依赖的项目呢?原则上来说,传递依赖的问题应该不是我们应该操心的,而应该由我们直接依赖的项目来解决。例如我们的项目依赖A,而A依赖B。那么对B的版本控制应该由A解决,而不是我们来解决。但如果A压根就没有管理依赖呢?这时我们只有两种方式来升级B的版本:

1、将B通过override方式定义在Gopkg.toml中。以后每次只须修改Gopkg.toml,然后执行“dep ensure -update xxx"即可。但是官网明确警告要谨慎使用override,因为它还具有解决冲突的作用,可能会屏蔽掉本该引起我们注意的版本冲突问题。

注:override使用方法和constraint完全一样。但dep对二者的处理则有一定的区别。请参考:

https://golang.github.io/dep/docs/Gopkg.toml.html

2、直接修改Gopkg.lock文件。这种方式虽然简单,但显然违反了前面所说的“尽量不要手动修改Gopkg.lock”的规则。

到底该怎么办?It's up to you:)。

最后,分享一个零君在实际工作中遇到的一个坑。最近零君在Gopkg.toml中更新了一个依赖项目的版本。因为该项目虽然一直有更新,但却很久没有发布新的version了,所以零君要在Gopkg.toml中对该项目指定一个比较新的commit ID。但是在执行"dep ensure -update xxx"时,提示“reference isn't a tree”。原因就是$/pkg/dep中该项目对应的repository比较陈旧,没有同步最新的修改,而我在Gopkg.toml中指定的commit ID没有包含在repository中,自然就会报错。当手动同步repository之后,问题就解决了。其实这应该算dep的一个BUG,执行“dep ensure -update xxx”时,应该在找不到commit ID时自动尝试同步repository。

总的来说,dep简单易用,很容易上手。而且官网提供的文档也不多,花个半天、最多一天就能阅读完。所以有兴趣的同学,可以在线阅读官方文档:

https://golang.github.io/dep/docs/introduction.html

或者

https://github.com/golang/dep/tree/master/docs

--END--

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180524G1XDDT00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券