你还记得 GOPATH 吗?回顾下Go 语言构建模式的演化历程

615 阅读4分钟

你还记得 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 模式了。