01
介绍
在 Golang 语言中,Golang 程序是由 Golang Package 组成的,go build 的过程实际上就是编译 Golang Package。本文我们介绍 Golang 构建模式主要演进的三个阶段,分别是 GOPATH、 引入 vendor 机制的 GOPATH 和 Go Module。
GOPATH
Golang 初期版本中就原生内置了 GOPATH 的构建模式,Golang 程序在编译时,Golang 编译器会在 GOPATH 环境变量配置的本地路径下,查找 Golang 程序依赖的三方包,如果依赖包不存在,go build 命令将返回错误「无法找到包 XXX」,此时,我们需要使用 go get 命令手动将 Golang 程序依赖的三方包下载到 GOPATH 环境变量配置的本地路径下,然后再尝试执行 go build 命令。
go get 命令虽然方便,它可以将依赖包以及依赖包的依赖包自动下载到 GOPATH 环境变量配置的本地路径下,但是读者朋友们需要注意的是,go get 命令下载的依赖包是当前依赖包的最新版本,如果我们对依赖包的版本有要求,就不能使用 go get 命令。
比如在多人开发的 Golang 项目中,新加入成员将 Golang 项目下载到本地,使用 go get 命令下载依赖包时,正好赶上依赖包的版本更新了,此时,新成员使用 go build 命令构建 Golang 程序,Golang 程序也将使用最新版本的三方依赖包。如果三方依赖包存在 bug 或不向下兼容,将直接影响 Golang 程序的稳定性。
03
Vendor
Golang 官方为了解决 go get 命令会下载最新版本依赖包的问题,在 Golang v1.5 版本中引入 vendor 机制。
所谓 vendor 机制,就是在 Golang 项目的目录中,创建一个目录名为 vendor 的目录,将 Golang 项目的所有依赖包缓存到该目录中。
Golang 程序在编译时,Golang 编译器会优先在 vendor 目录中查找 Golang 程序依赖的三方包,而不是在 GOPATH 环境变量配置的本地路径下查找。
我们只需将 vendor 目录一起提交到代码仓库中,新成员在下载 Golang 项目后,构建项目就不会改变三方依赖包的版本。
读者朋友们需要注意的是,vendor 机制也引入了新的问题,比如想要使用 vendor 机制,Golang 项目必须在 GOPATH 环境变量配置的本地路径下的 src 目录中。
随着项目不断迭代,依赖的三方包也会越来越多,vendor 目录会变得越来越大,将 vendor 目录提交到代码仓库,不仅会影响下载代码的速度,还会影响 Code Review。
此外,vendor 目录中的三方依赖包,也需要我们手动管理,比如手动记录依赖三方包的版本号,手动下载三方依赖包等。
Golang 社区为了解决 vendor 机制引入的问题,推出一些比较流行的解决三方包依赖管理的工具,比如 dep、gb、glide 等三方包依赖管理工具,但是这些社区推出的三方包依赖管理工具都有各自的问题。
04
Modules
Golang 官方在总结 Golang 社区推出的各种三方包依赖管理工具遇到的问题的基础上,在 Golang v1.11 版本中,推出 Go Module 构建模式。
关于 Go Module 构建模式,官方 blog 有相关系列文章介绍,在之前的公众号文章中,也有官方 blog 发表的 Go Module 相关文章的译文。感兴趣的读者朋友可以按需翻阅一下。
Go Module 构建模式可以将 Golang 项目代码放在任意目录,无需同 vendor 机制一样,必须将 Golang 项目代码放在 GOPATH 环境变量配置的本地目录下的 src 目录中。
因为 Golang 官方为了同时支持 GOPATH 构建模式和 Go Module 构建模式,在 Golang v1.11 版本中,Go Module 构建模式默认是「关闭」,除非手动开启 Go Module 构建模式,如果将 Go Module 构建模式设置为「自动」,而 Golang 项目在 GOPATH 环境变量配置的目录中的 src 目录下,go build 命令优先使用 GOPATH 构建模式。
在 Golang v.13 版本中,Go Module 构建模式默认是「自动」,不管 Golang 项目在不在 GOPATH 环境变量配置的本地目录中的 src 目录下,只要项目根目录中包含 go.mod 文件,就启用 Go Module 构建模式,否则启用 GOPATH 构建模式。
所以在 Golang v1.13 版本之前,如果想要使用 Go Module 构建模式,那么 Golang 项目代码不可以放在 $GOPATH/src
目录中。
在 Golang v1.16 版本开始,Golang 已经默认开启 Go Module 构建模式,未来 Golang 官方还会考虑彻底移除 GOPATH 构建模式,我建议读者朋友们现在开始将 GOPATH 构建模式的老项目迁移到 Go Module 构建模式,并且在新项目中直接使用 Go Module 构建模式。
Golang 项目使用 Go Module 构建模式,那么还需要使用 vendor 机制吗?答案是需要,因为 vendor 机制可以将 Golang 项目依赖的三方包缓存到 vendor 目录,这样在无法访问网络的环境下,我们可以在 vendor 机制下使用 Go Module 构建模式,或者在构建性能敏感的环境中使用 Go Module 构建模式,比如在使用内部 CI 工具构建 Golang 程序时。
在 Go Module 构建模式下,vendor 机制无需像在 GOPATH 构建模式下,需要我们手动管理三方依赖包的版本和下载,Golang 提供了 go mod vendor
命令,帮助我们创建和管理 vendor 目录。
在我们想要基于 vendor 目录缓存的三方依赖包构建 Golang 程序,而不是基于本地缓存的 Go Module 构建 Golang 程序时,可以在 go build 命令后面加上 -mod=vendor 参数。并且在 Golang v1.14 及以后的版本中,如果 Golang 项目根目录下存在 vendor 目录,go build 命令会默认优先基于 vendor 目录缓存的三方依赖包构建 Golang 程序,除非我们在 go build 命令后面加上 -mod=mod 参数。
05
总结
本文我们介绍了 Golang 的构建模式的演进过程,Golang 官方先后推出三种构建模式,分别是 GOPATH 构建模式,引入 vendor 机制的 GOPATH 构建模式和 Go Module 构建模式。
介绍了为什么要引进 vendor 机制,它解决了什么问题,同时它又带来了什么问题。官方基于社区推出的三方依赖包版本管理工具的基础上推出 Go Module 构建模式,是怎么解决 GOPATH 构建模式中 vendor 机制引入的问题的。
介绍了 vendor 机制在 GOPATH 构建模式和在 Go Module 构建模式中使用的区别是什么,为什么在 Go Module 构建模式中仍然需要使用 vendor 机制,如何在 Go Module 构建模式中使用 vendor 机制。
推荐阅读:
参考资料:
https://go.googlesource.com/proposal/+/master/design/25719-go15vendor.md
https://pkg.go.dev/cmd/go#hdr-GOPATH_and_Modules