Go依赖管理
Go 程序由 Go 包组合而成的,Go 程序的构建过程就是确定包版本、编译包以及将编译后 得到的目标文件链接在一起的过程。
Go Module
go 1.11版本引入。
一个 Go Module 的顶 层目录下会放置一个 go.mod 文件,每个 go.mod 文件会定义唯一一个 module,也就是 说 Go Module 与 go.mod 是一一对应的。
go.mod 文件所在的顶层目录也被称为 module 的根目录,module 根目录以及它子目录 下的所有 Go 包均归属于这个 Go Module,这个 module 也被称为 main module。
由 go mod tidy 下载的依赖 module 会被放置在本地的 module 缓存路径下,默认值为 $GOPATH[0]/pkg/mod,Go 1.15 及以后版本可以通过 GOMODCACHE 环境变量,自 定义本地 module 的缓存路径。
包依赖管理
go为了解决包依赖管理问题,采取了语义导入版本 (Semantic Import Versioning),以及和其他主流 语言不同的最小版本选择 (Minimal Version Selection) 等机制
语义导入版本机制
我们看 go.mod 的 require 段中依赖的版本号,都符合 vX.Y.Z 的格 式。在 Go Module 构建模式下,一个符合 Go Module 要求的版本号,就是这样由前缀 v 和一个 满足规范的版本号组成。
x:主版本。不同主版本是互相不兼容的
y:次版本。同一主版本。大的次版本号向后兼容
z:补丁版本。不影响兼容性
而且,Go Module 规定:如果同一个包的新旧版本是兼容的,那么它们的包导入路径应该 是相同的。
那么有一个问题,如果遇到主版本升级了,怎么办?
Go Module 创新性地给出了一个方法:将包主版本号引入到包导入路径中。
比如,以前是1.x.y版本:
1 | import "github.com/sirupsen/logrus" |
现在升级到2.x版本
1 | import "github.com/sirupsen/logrus/v2" |
这就是 Go 的“语义导入版本”机制,也就是说通过在包导入路径中引入主版本号的方 式,来区别同一个包的不兼容版本,这样一来我们甚至可以同时依赖一个包的两个不兼容 版本
另外,为什么v1不需要写呢?
因为,这是约定,当依赖的主版 本号为 0 或 1 的时候,我们在 Go 源码中导入依赖包,不需要在包的导入路径上增加版本 号
最小版本选择原则
myproject 有两个直接依赖 A 和 B,A 和 B 有一个共同的依赖包 C,但 A 依 赖 C 的 v1.1.0 版本,而 B 依赖的是 C 的 v1.3.0 版本,并且此时 C 包的最新发布版为 C v1.7.0。这个时候,Go 命令是如何为 myproject 选出间接依赖包 C 的版本呢?选出的究 竟是 v1.7.0、v1.1.0 还是 v1.3.0 呢?
按照上面提到的,版本兼容性,选择1.7.0版本应该是可以保障向后兼容的。
但是。go选择了最小版本匹配原则。会使用1.3.0的版本。
指定版本
如果go自动为我们确认的版本有问题。我们可以指定版本。
go list列出所有版本
1 | go list -m -versions github.com/sirupsen/logrus |
指定版本
1 | go mod edit -require=github.com/sirupsen/logrus@v1.7.0 |