Hi,你们好。html
我是明哥,在本身学习 Golang 的这段时间里,我写了详细的学习笔记放在个人我的微信公众号 《Go编程时光》,对于 Go 语言,我也算是个初学者,所以写的东西应该会比较适合刚接触的同窗,若是你也是刚学习 Go 语言,不防关注一下,一块儿学习,一块儿成长。linux
在线博客:golang.iswbm.com Github:github.com/iswbm/GolangCodingTimeandroid
在之前,Go 语言的的包依赖管理一直都被你们所诟病,Go官方也在一直在努力为开发者提供更方便易用的包管理方案,从最初的 GOPATH 到 GO VENDOR,再到最新的 GO Modules,虽然走了很多的弯路,但最终仍是拿出了 Go Modules 这样像样的解决方案。git
目前最主流的包依赖管理方式是使用官方推荐的 Go Modules ,这不前段时间 Go 1.14 版本发布,官方正式放话,强烈推荐你使用 Go Modules,而且有自信能够用于生产中。github
本文会大篇幅的讲解 Go Modules 的使用,可是在那以前,我仍然会简要介绍一下前两个解决方案 GOPATH 和 go vendor 究竟是怎么回事?我认为这是有必要的,由于只有了解它的发展历程,才能知道 Go Modules 的到来是有多么的不容易,多么的意义非凡。golang
GOPATH 应该不少人都很眼熟了,以前在配置环境的时候,都配置过吧?算法
你能够将其理解为工做目录,在这个工做目录下,一般有以下的目录结构shell
每一个目录存放的文件,都不相同编程
.a
文件将你的包或者别人的包所有放在 $GOPATH/src
目录下进行管理的方式,咱们称之为 GOPATH 模式。json
在这个模式下,使用 go install 时,生成的可执行文件会放在 $GOPATH/bin
下
若是你安装的是一个库,则会生成 .a
文件到 $GOPATH/pkg
下对应的平台目录中(由 GOOS 和 GOARCH 组合而成),生成 .a
为后缀的文件。
GOOS,表示的是目标操做系统,有 darwin(Mac),linux,windows,android,netbsd,openbsd,solaris,plan9 等
而 GOARCH,表示目标架构,常见的有 arm,amd64 等
这两个都是 go env 里的变量,你能够经过 go env 变量名
进行查看
至此,你可能不会以为上面的方案会产生什么样的问题,直到你开始真正使用 GOPATH 去开发程序,就不得不开始面临各类各样的问题,其中最严重的就是版本管理问题,由于 GOPATH 根本没有版本的概念。
如下几点是你使用 GOPATH 必定会碰到的问题:
为了解决 GOPATH 方案下不一样项目下没法使用多个版本库的问题,Go v1.5 开始支持 vendor 。
之前使用 GOPATH 的时候,全部的项目都共享一个 GOPATH,须要导入依赖的时候,都来这里找,正所谓一山不容二虎,在 GOPATH 模式下只能有一个版本的第三方库。
解决的思路就是,在每一个项目下都建立一个 vendor 目录,每一个项目所需的依赖都只会下载到本身vendor目录下,项目之间的依赖包互不影响。在编译时,v1.5 的 Go 在你设置了开启 GO15VENDOREXPERIMENT=1
(注:这个变量在 v1.6 版本默认为1,可是在 v1.7 后,已去掉该环境变量,默认开启 vendor
特性,无需你手动设置)后,会提高 vendor 目录的依赖包搜索路径的优先级(相较于 GOPATH)。
其搜索包的优先级顺序,由高到低是这样的
虽然这个方案解决了一些问题,可是解决得并不完美。
若是多个项目用到了同一个包的同一个版本,这个包会存在于该机器上的不一样目录下,不只对磁盘空间是一种浪费,并且无法对第三方包进行集中式的管理(分散在各个角落)。
而且若是要分享开源你的项目,你须要将你的全部的依赖包悉数上传,别人使用的时候,除了你的项目源码外,还有全部的依赖包所有下载下来,才能保证别人使用的时候,不会由于版本问题致使项目不能如你预期那样正常运行。
这些看似不是问题的问题,会给咱们的开发使用过程变得很是难受,虽然我是初学者,还未使用过 go vendor,但能有很明显的预感,这个方案照样会另我崩溃。
go modules 在 v1.11 版本正式推出,在最新发布的 v1.14 版本中,官方正式发话,称其已经足够成熟,能够应用于生产上。
从 v1.11 开始,go env
多了个环境变量: GO111MODULE
,这里的 111,其实就是 v1.11 的象征标志, go 里好像很喜欢这样的命名方式,好比当初 vendor 出现的时候,也多了个 GO15VENDOREXPERIMENT
环境变量,其中 15,表示的vendor 是在 v1.5 时才诞生的。
GO111MODULE
是一个开关,经过它能够开启或关闭 go mod 模式。
它有三个可选值:off
、on
、auto
,默认值是auto
。
GO111MODULE=off
禁用模块支持,编译时会从GOPATH
和vendor
文件夹中查找包。GO111MODULE=on
启用模块支持,编译时会忽略GOPATH
和vendor
文件夹,只根据 go.mod
下载依赖。GO111MODULE=auto
,当项目在$GOPATH/src
外且项目根目录有go.mod
文件时,自动开启模块支持。go mod 出现后, GOPATH(确定没人使用了) 和 GOVENDOR 将会且正在被逐步淘汰,可是若你的项目仍然要使用那些即将过期的包依赖管理方案,请注意将 GO111MODULE 置为 off。
具体怎么设置呢?可使用 go env 的命令,如我要开启 go mod ,就使用这条命令
$ go env -w GO111MODULE="on"复制代码
接下来,来演示一下 go modules 是如何来管理包依赖的。
go mod 再也不依靠 $GOPATH,使得它能够脱离 GOPATH 来建立项目,因而咱们在家目录下建立一个 go_test 的目录,用来建立个人项目,详细操做以下:
接下来,进入项目目录,执行以下命令进行 go modules 的初始化
接下来很重要的一点,咱们要看看 go install 把下载的包安装到哪里了?
上面咱们观察到,在使用 go modules 模式后,项目目录下会多生成两个文件也就是 go.mod
和 go.sum
。
这两个文件是 go modules 的核心所在,这里不得很差好介绍一下。
go.mod 的内容比较容易理解
在实际应用上,你会看见更复杂的 go.mod 文件,好比下面这样
module github.com/BingmingWong/module-test
go 1.14
require (
example.com/apple v0.1.2
example.com/banana v1.2.3
example.com/banana/v2 v2.3.4
example.com/pear // indirect
example.com/strawberry // incompatible
)
exclude example.com/banana v1.2.4
replace(
golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac => github.com/golang/crypto v0.0.0-20180820150726-614d502a4dac
golang.org/x/net v0.0.0-20180821023952-922f4815f713 => github.com/golang/net v0.0.0-20180826012351-8a410e7b638d
golang.org/x/text v0.3.0 => github.com/golang/text v0.3.0
)复制代码
主要是多出了两个 flag:
exclude
: 忽略指定版本的依赖包replace
:因为在国内访问golang.org/x的各个包都须要fan墙,你能够在go.mod中使用replace替换成github上对应的库。反观 go.sum 文件,就比较复杂了,密密麻麻的。
能够看到,内容虽然多,可是也不难理解
每一行都是由 模块路径
,模块版本
,哈希检验值
组成,其中哈希检验值是用来保证当前缓存的模块不会被篡改。hash 是以h1:
开头的字符串,表示生成checksum的算法是初版的hash算法(sha256)。
值得注意的是,为何有的包只有一行
<module> <version>/go.mod <hash>复制代码
而有的包却有两行呢
<module> <version> <hash>
<module> <version>/go.mod <hash>复制代码
那些有两行的包,区别就在于 hash 值不一行,一个是 h1:hash
,一个是 go.mod h1:hash
而 h1:hash
和 go.mod h1:hash
二者,要不就是同时存在,要不就是只存在 go.mod h1:hash
。那什么状况下会不存在 h1:hash
呢,就是当 Go 认为确定用不到某个模块版本的时候就会省略它的h1 hash
,就会出现不存在 h1 hash
,只存在 go.mod h1:hash
的状况。[引用自 3]
go.mod 和 go.sum 是 go modules 版本管理的指导性文件,所以 go.mod 和 go.sum 文件都应该提交到你的 Git 仓库中去,避免其余人使用你写项目时,从新生成的go.mod 和 go.sum 与你开发的基准版本的不一致。
go mod init
:初始化go mod, 生成go.mod文件,后可接参数指定 module 名,上面已经演示过。
go mod download
:手动触发下载依赖包到本地cache(默认为$GOPATH/pkg/mod
目录)
go mod graph
: 打印项目的模块依赖结构
go mod tidy
:添加缺乏的包,且删除无用的包
go mod verify
:校验模块是否被篡改过
go mod why
: 查看为何须要依赖
go mod vendor
:导出项目全部依赖到vendor下
go mod edit
:编辑go.mod文件,接 -fmt 参数格式化 go.mod 文件,接 -require=golang.org/x/text 添加依赖,接 -droprequire=golang.org/x/text 删除依赖,详情可参考 go help mod edit
go list -m -json all
:以 json 的方式打印依赖详情如何给项目添加依赖(写进 go.mod)呢?
有两种方法:
若是让我用一段话来评价 GOPATH 和 go vendor,我会说
GOPATH 作为 Golang 的第一个包管理模式,只能保证你能用,但不保证好用,而 go vendor 解决了 GOPATH 忽视包版的本管理,保证好用,可是还不够好用,直到 go mod 的推出后,才使 Golang 包的依赖管理有了一个能让 Gopher 都统一比较满意的方案,达到了能用且好用的标准。
若是是刚开始学习 Golang ,那么 GOPATH 和 go vendor 能够作适当了解,没必要深刻研究,除非你要接手的项目因为一些历史缘由仍然在使用 go vender 械管理,除此以外,任何 Gopher 应该今后刻就投入 go modules 的怀抱。
以上是我在这几天的学习总结,但愿对还未入门阶段的你,有所帮助。另外,本篇文章若有写得不对的,请后台批评指正,以避免误导其余朋友,很是感谢。
系列导读
24. 超详细解读 Go Modules 前世此生及入门使用