1. 版本选择机制
使用go get <pkg>
来获取某个依赖,如果没有特别指定依赖的版本号,go get
会自动选择一个最优版本,并且如果本地有go.mod
文件的话,还会自动更新go.mod文件.
事实上除了go get
,go build
和go mod tidy
也会自动帮我们选择依赖的版本。这些命令选择依赖版本时都遵循一些规则,本节我们就开始介绍Go module涉及到的版本选择机制。
2.依赖包版本约定
2.1 Go module 之前版本兼容性
在Go v1.11
(开始引入Go module
的版本)之前,Go 语言建议: 依赖包需要保持向后兼容,这包括可导出的函数、变量、类型、常量等不可以随便删除。
以函数为例,如果需要修改函数的入参,可以增加新的函数而不是直接修改原有的函数。
如果确实需要做一些打破兼容性的修改,建议创建新的包。
比如仓库github.com/RainbowMango/xxx
中包含一个package A,此时该仓库只有一个package:
github.com/RainbowMango/xxx/A
那么其他项目引用该依赖时的import 路径为:
import "github.com/RainbowMango/xxx/A"
如果该依赖包需要引入一个不兼容的特性,可以在该仓库中增加一个新的package A1,此时该仓库包含两个包:
github.com/RainbowMango/xxx/A
github.com/RainbowMango/xxx/A1
那么其他项目在升级依赖包版本后不需要修改原有的代码可以继续使用package A,如果需要使用新的package A1,只需要将import 路径修改为import "github.com/RainbowMango/xxx/A1"
并做相应的适配即可。
2.2 Go module 之后版本兼容性
从Go v1.11版本开始,随着Go module特性的引入,依赖包的兼容性要求有了进一步的延伸,Go module开始关心依赖包版本管理系统(如Git)中的版本号。尽管如此,兼容性要求的核心内容没有改变:
- 如果新
package
和旧的package
拥有相同的import 路径,那么新package必须兼容旧的package; - 如果新的package不能兼容旧的package,那么新的package需要更换import路径;
在前面的介绍中,我们知道Go module 的go.mod中记录的module名字决定了import路径。例如,要引用module module github.com/renhongcai/indirect
中的内容时,其import路径需要为import github.com/renhongcai/indirect
。
在Go module时代,module版本号要遵循语义化版本规范,即版本号格式为v<major>.<minor>.<patch>
,如v1.2.3
。当有不兼容的改变时,需要增加major版本号,如v2.1.0。
Go module规定,如果major版本号大于1,则major版本号需要显式地标记在module名字中,如module github.com/my/mod/v2
。这样做的好处是Go module 会把module github.com/my/mod/v2
和 module github.com/my/mod
视做两个module,他们甚至可以被同时引用。
另外,如果module的版本为v0.x.x
或v1.x.x
则都不需要在module名字中体现版本号。
3. 版本选择机制
Go 的多个命令行工具都有自动选择依赖版本的能力,如go build 和go test,当在源代码中增加了新的import,这些命令将会自动选择一个最优的版本,并更新go.mod文件。
需要特别说明的是,如果go.mod文件中已标记了某个依赖包的版本号,则这些命令不会主动更新go.mod中的版本号。所谓自动更新版本号只在go.mod中缺失某些依赖或者依赖不匹配时才会发生。
3.1 最新版本选择
当在源代码中新增加了一个import,比如:
import "github.com/RainbowMango/M"
如果go.mod的require指令中并没有包含github.com/RainbowMango/M
这个依赖,那么go build 或go test
命令则会去github.com/RainbowMango/M
仓库寻找最新的符合语义化版本规范的版本,比如v1.2.3,并在go.mod文件中增加一条require依赖:
require github.com/RainbowMango/M v1.2.3
这里,由于import路径里没有类似于v2或更高的版本号,所以版本选择时只会选择v1.x.x的版本,不会去选择v2.x.x或更高的版本。
3.2 最小版本选择
有时记录在go.mod文件中的依赖包版本会随着引入其他依赖包而发生变化。
如下图所示: M最新版本是v1.5.0
Module A 依赖 Module M的v1.0.0版本,但之后 Module A 引入了 Module D,而Module D 依赖 Module M的v1.1.1版本,此时,由于依赖的传递,Module A也会选择v1.1.1版本。
需要注意的是,此时会自动选择最小可用的版本,而不是最新的tag(v1.5.0)版本。
请发表评论