You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Go 引用的包,可以分为两类:内部包和外部包,对于内部包,因为是项目内部自己写的,管理起来比较简单;但是,对于外部包,因为是开源的,一般由第三方维护管理,可能会版本不一致,出现非预期的情况,导致编译错误,或者出现程序 BUG,有经验的程序员应该都知道,这种由于第三方包引起的问题,定位和解决往往都是很烦人的。所以,目前所谓的依赖包管理工具,都是针对外部包的管理。
大部分编程语言都是将项目代码目录作为单独的工作目录(workspace),但 Go 只有一个工作目录 ——
$GOPATH,因为 Go 自身提供了很多工具,固定的工作目录,便于这些工具的运行。因此,需要强调一点是,我们应该在 $GOPATH/src 下,创建自己的项目目录。因为,项目构建时,就是从这个路径下开始的(即作为环境路径)。
其实,对于 Go 本身来说,是不区分内部包和外部包的,因为 go get 下载的包,也都放在 $GOPATH/src 目录下,因此,当我们 import 依赖包时,只要直接路径指定就行了,类似 github.com/xx/x。又因为 $GOPATH 目录下可能创建了多个项目,因此下载在这里的依赖包,也可能会被多个项目引用,随时可能会被其他项目更新修改。
实际上对于内部包,我们在引用时可以使用相对路径(相对 import 代码的路径,例如: ./a/b 或者 ../x/y 等),这可能是 go 命令的潜规则,这种方式相对较灵活而且直观,项目存放位置也可以很随意。所以对于简单项目,个人是比较喜欢使用这种方式的。
但是,这会影响 dep 生成依赖关系,导致 dep 误以为没有依赖的外部包,非常奇怪的问题。
其实,这两个坑都是因为我之前比较随意,没有按规范,将项目放在 $GOPATH/src 导致的。
Go Modules
dep 还没有应用推广,在 Go 1.11 中,又推出了全新的依赖包管理机制 Go Modules,原来叫 vgo ,在 Go 1.11 被采纳合并到了主分支,正式被发布,不出意外的话,后续版本应该都会将这个当作包依赖管理的官方解决方案。
模块为 GOPATH 提供了替代方案,用来为项目定位依赖和管理版本化。如果 go 命令在 $GOPATH/src 之外的目录中运行,并且该目录中有一个模块的话,那么模块功能就会启用,否则 go 将会使用 GOPATH(Go 1.11)。与 dep 不同,模块是集成在 go 命令的,可以使用 go mod init 创建。
小结
Go 作为比较新的语言,包依赖管理方式也在持续变动,主要分为三个阶段: 纯依赖包源码拷贝 --> 基于 vendor --> Go Modules。
每次变动都使管理变的更先进,这对于 Go 和 Gopher 都是有益的,特别是 Go Modules 据称使用了其它语言包管理工具不同的理念算法,看起来比较复杂。因为刚出来,有较多问题需要讨论,后续比较成熟了再研究,可能要等到 Go 1.12 版本吧。
所以,如果不想成为第一个吃螃蟹的人,目前,还是使用 dep 作为管理工具比较稳妥。
The text was updated successfully, but these errors were encountered:
Go 依赖管理
Go 引用的包,可以分为两类:内部包和外部包,对于内部包,因为是项目内部自己写的,管理起来比较简单;但是,对于外部包,因为是开源的,一般由第三方维护管理,可能会版本不一致,出现非预期的情况,导致编译错误,或者出现程序 BUG,有经验的程序员应该都知道,这种由于第三方包引起的问题,定位和解决往往都是很烦人的。所以,目前所谓的依赖包管理工具,都是针对外部包的管理。
大部分编程语言都是将项目代码目录作为单独的工作目录(workspace),但 Go 只有一个工作目录 ——
$GOPATH,因为 Go 自身提供了很多工具,固定的工作目录,便于这些工具的运行。因此,需要强调一点是,我们应该在
$GOPATH/src
下,创建自己的项目目录。因为,项目构建时,就是从这个路径下开始的(即作为环境路径)。其实,对于 Go 本身来说,是不区分内部包和外部包的,因为
go get
下载的包,也都放在$GOPATH/src
目录下,因此,当我们import
依赖包时,只要直接路径指定就行了,类似github.com/xx/x
。又因为 $GOPATH 目录下可能创建了多个项目,因此下载在这里的依赖包,也可能会被多个项目引用,随时可能会被其他项目更新修改。所以,在最开始的时候(Go 1.5 之前),go 项目包的依赖管理是很简陋的,为了保证外部包的安全,就必须要把外部包拷贝到项目内部,变成“内部包”。此时,包的查找顺序是: $GOROOT --> $GOPATH.
在 Go 1.5 时,为了解决这个问题,引入了
vendor
这一特殊目录,该目录在项目内创建,这就相当于,每个项目有了自己独立的GOPATH
,此时,包的查找顺序是:离引用代码最近的vendor
--> $GOROOT --> $GOPATH.项目内的每个目录下都能创建自己的
vendor
目录,引用时,使用的是最近一层的vendor
。但是,最好只在根目录创建vendor
,便于管理,也避免同样的包,出现在同一项目内的多个vendor
目录下。基于
vendor
能够实现真正意义的依赖包管理,而告别简陋的拷贝操作。内部包管理
内部包管理其实很简单,但是有一个特性需要说明,那就是
internal
目录,位于internal
目录下的包,只能被同父目录下的包引用,否则会编译报错。 这里需要说明的是,父目录的父目录也算,即位于项目根目录下internal
里面的包,是可以被项目内所有包引用的。与
vendor
类似,项目内也可以有多个internal
目录,但是建议在项目根目录创建一个就够了。internal
目录的目的是保护该目录下的包被其他项目所引用,例如某个包还不太成熟,不想被其他项目使用。很明显,这只是一种简单的“警告”机制。外部包管理
基于
vendor
实现的依赖包管理工具,比较流行的有dep
,Godep
,Glide
,Govendor
等,实现原理大同小异。官网这里对这几个工具进行了分类说明,建议使用dep
作为项目的依赖管理工具,原因主要有两点:dep
是官方推出的管理工具,虽然目前还是处于official experiment.
状态,但是已经可以作为正式工具使用了;Godep
已经是 Gopher 比较常用的依赖工具了,但是可以看到,其官网已经声明后面只会进行维护性的开发工作,建议大家使用dep
或其他工具代替:Please use dep or another tool instead.
dep 使用
dep 的使用,官方有较详细的说明文档:dep introduction.
这里根据自己的理解,做些说明。
主要有两个安装方式:
$GOPATH/bin
目录下:当然你也可以使用源码安装,但是,你最好不要这么干。安装完成后,执行
dep -h
查看帮助信息。dep init
首先进入需要 dep 管理依赖包的项目,以 dep-test 项目为例。执行命令
dep init
完成后,在项目根目录下会创建文件
Gopkg.lock
,Gopkg.toml
和vendor
目录,vendor
里面就是依赖的包源码了,前两个文件保存内保存了依赖包的规则类型信息,依赖包版本信息以及依赖关系描述,后面还会对 dep 实现原理进行简单说明。dep ensure
自动下载项目所有引用的依赖包dep ensure add
下载指定路径的引用包,当有新的包引用时,尽量使用这种方式,而不是偷懒直接用dep ensure
,因为这个指令会将引用包版本信息自动写入文件Gopkg.toml
,否则只会更新文件Gopkg.lock
;dep ensure -update
更新依赖包版本,可以指定包路径,否则会更新所有依赖包;dep ensure -vendor-only
只会根据当前的Gopkg.lock
文件内包信息下载依赖包,而不会根据项目引用,自动更新Gopkg.lock
;dep ensure --no-vendor
会更新Gopkg.lock
,但是不会更新vendor
;dep check
检查vendor
里面的包源码是否与 dep 记录的信息一致,如果不一致,则使用dep ensure
更新;实际应用中,提交代码时,我们可以将
vendor
目录一起提交,此时 其他开发者应该先使用dep check
检验。但是,一般
vendor
目录都比较大,这时可以只提交Gopkg.toml
和Gopkg.lock
文件,开发者在本地使用dep ensure -vendor-only
自行下载依赖包。实现模型
实现模型也有详细说明:Models and Mechanisms,
dep
使用four state system
模型,这是一种经典的包管理模型。这四个模块分别是:Project source code
当前项目代码,即你的项目代码A manifest
依赖清单,这里就是指Gopkg.toml
文件,最初由dep init
生成,主要由用户编辑,来实现个性化需求。它包含几种类型的规则声明来管理 dep 的行为,可以实现灵活的下载依赖包,例如可以指定特定区间版本的依赖包。A lock
依赖描述文件,这里是指Gopkg.lock
文件,里面详细记录了依赖包的地址,hash 值,版本等基本信息,根据描述能够复现完整的依赖图。这个文件完全是由dep
根据Gopkg.toml
和项目引用自动生成的。Source code of the dependences themselves
依赖包自身源码,这是就是指vendor
目录下的代码。dep
主要就是围绕对这四个模块进行输入输出管理实现。流程大概如下:dep
根据项目引用代码和Gopkg.toml
文件内的规则信息,计算自动生成Gopkg.lock
文件,此时,依赖的包信息就确定了,然后根据这些信息将对应的包下载到vendor
目录内。踩过的坑
对于 go 这种必须将项目放在
$GOPATH/src
的约定,实际应用中可能并不灵活,于是有两个技巧:$GOPATH/src
下创建一个软链接,指向项目目录;但是,当使用软链接这种方式的时候,
vendor
目录会失效,变成普通的目录了,因为vendor
目录只有位于$GOPATH
路径下,Go 才会把它当作特殊目录处理。而符号链接只是创建了指向文件名的符号,实际的文件位置依然没变。实际上对于内部包,我们在引用时可以使用相对路径(相对
import
代码的路径,例如:./a/b
或者../x/y
等),这可能是 go 命令的潜规则,这种方式相对较灵活而且直观,项目存放位置也可以很随意。所以对于简单项目,个人是比较喜欢使用这种方式的。但是,这会影响
dep
生成依赖关系,导致dep
误以为没有依赖的外部包,非常奇怪的问题。其实,这两个坑都是因为我之前比较随意,没有按规范,将项目放在
$GOPATH/src
导致的。Go Modules
dep
还没有应用推广,在 Go 1.11 中,又推出了全新的依赖包管理机制Go Modules
,原来叫vgo
,在 Go 1.11 被采纳合并到了主分支,正式被发布,不出意外的话,后续版本应该都会将这个当作包依赖管理的官方解决方案。模块为
GOPATH
提供了替代方案,用来为项目定位依赖和管理版本化。如果 go 命令在$GOPATH/src
之外的目录中运行,并且该目录中有一个模块的话,那么模块功能就会启用,否则 go 将会使用 GOPATH(Go 1.11)。与dep
不同,模块是集成在 go 命令的,可以使用go mod init
创建。小结
Go 作为比较新的语言,包依赖管理方式也在持续变动,主要分为三个阶段:
纯依赖包源码拷贝 --> 基于 vendor --> Go Modules
。每次变动都使管理变的更先进,这对于 Go 和 Gopher 都是有益的,特别是
Go Modules
据称使用了其它语言包管理工具不同的理念算法,看起来比较复杂。因为刚出来,有较多问题需要讨论,后续比较成熟了再研究,可能要等到 Go 1.12 版本吧。所以,如果不想成为第一个吃螃蟹的人,目前,还是使用
dep
作为管理工具比较稳妥。The text was updated successfully, but these errors were encountered: