你还记得 Go 语言中的 GOPATH 吗?
起因是有个读者朋友在新的工作中要维护一个 Go 语言版本比较老的项目,而用的恰恰是 GOPATH 模式,而他从开始接触 Go 语言已经在用 go mod 模式了,因此,对于 GOPATH 很陌生也很不适。 也借此机会,我也整理了下 Go 语言构建模式的演化方式。
Go 程序是由 Go 包组合而成的,Go 程序的构建过程就是确定包版本、编译包以及将编译后得到的目标文件链接在一起的过程。 Go 语言构建模式经过三个迭代和演化的过程:
- GOPATH
- Vendor 机制
- Go Module
GOPATH 模式
Go 最初就内置了 GOPATH 的构建模式,Go 编译器可以在本地 GOPATH 环境变量配置的路径下,搜寻 Go 程序依赖的第三方包。如果存在,就使用这个本地包进行编译;否则就会报编译错误。
如果没有显示设置 GOPATH 环境变量,Go 会将 GOPATH 设置为默认值,在 *nix 操作系统下,它的默认值是 $HOME/go。
在这里,我们需要注意下,GOPATH 是支持设置多个路径的,如果设置为多个路径,Go编译器在编译 Go 程序时,就会在这多个路径下搜索第三方依赖包是否存在。
那如何解决第三方依赖包没有在本地呢?
这里就会用到了 go get 命令了。比如我们需要下载某个第三方的依赖包就可以执行以下命令:
$ go get github.com/example/example
虽然,go get 解决了第三方依赖包的问题,但是在 GOPATH 模式下,go get 下载的包只是当时各依赖包的最新发布版本。
而由于依赖包的持续迭代,依赖版本的随意变化,就有可能导致开发者在不同时间获取和编译同一个Go包时,得到不同的结果。所以这也是 GOPATH 所被诟病的地方。
Vendor 机制
Go 在1.5版本中引入了 Vendor 机制,但是需要手动设置环境变量 GO15VENDOREXPERIMENT=1,在版本1.6中则默认为1,1.7版本以后,该环境变量被移除,默认开启 Vendor 机制。
Vendor 机制本质上就是在Go 项目下有个vendor的特定目录,将项目的所有依赖包缓存起来。如果使用 Vendor 机制管理第三方依赖包,需要将 vendor 一并提交到代码仓库中。
使用 Vendor 机制后,Go 编译器在寻找第三方包的优先顺序为:
- 当前项目下的 vendor 目录
- 向上级目录查找,直到查到 src 目录下的 vendor 目录
- 在 GOROOT 目录下查找
- 在 GOPATH 目录下查找
这里还是需要注意一点,开启 Vendor 机制,Go 项目必须位于 GOPATH 环境变量配置的某个路径下的src目录下。否则,Go 编译器是不会去该目录下查找的。
虽然 vendor 在一定程度上解决了第三方包依赖版本的问题,但是在体验上却比较差,vendor 目录下的第三方依赖包,不仅占用代码仓库空间,减慢仓库下载和更新速度,在一定程度上也会干扰代码评审,对实施代码统计等开发者效能工具也有比较大的影响。
Go Module
Go 核心团队基于社区实践的经验和教训,在Go 1.11 版本开始,推出了官方的解决方案: Go Moudle。
在 Go Module 模式下,通常一个代码仓库对应一个 Go Module。在项目的顶层目录下会放置一个 go.mod 文件,每个 go.mod 文件会定义唯一一个 module。
基于当前项目创建一个 Go Module,通常有以下几个步骤:
- 第一步,通过
go mod init创建 go.mod 文件,将当前项目变为一个 Go Module; - 第二步:通过
go mod tidy命令自动更新当前 module 的依赖信息; - 第三步:执行
go build,执行新的 module 的构建。
使用 Go Module,可以在任意路径下创建项目,从而也摆脱了 GOPATH 环境变量配置的路径。
go mod tidy 命令会扫描 Go 源码,并自动找出项目依赖的外部库,下载这些依赖并更新本地的 go.mod 文件,执行完 go mod tidy 后,当前项目还会多出一个新的文件 go.sum,这个文件记载了特定版本库内容的哈希值。
这是 Go Module 的一个安全举措,它的意义在于将来某个外部库再次被下载时,会对比 go.sum 文件中对应的哈希值,只有哈希值对比一致才是合法的。
从 GOPATH 到 Vendor 机制,再到现在的 Go Module 机制,开发者对于 Go 项目的构建也越来越方便。
Go Module 机制的出现,其实也可以算是对 GOPATH 的告别了,相对于 GOPATH 的固定项目目录,Go Module 随处可见项目的机制也更加符合广大开发者胃口。
现在 Go 核心团队也正在考虑在后续版本彻底移除 GOPATH 构建模式,而从那时 Go Module 构建模式也将成为官方唯一的标准构建模式了。
所以,从现在开始,我们也可以彻底拥抱 Go Module 模式了。