hello云胜

技术与生活

0%

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
2
go mod edit -require=github.com/sirupsen/logrus@v1.7.0
go mod tidy