《go专家编程》中的go版本管理相关
indirect 间接依赖 优化。
require module //indirect 表示间接依赖
一般而言,被添加注释的包肯定是间接依赖的包,而没有添加// indirect
注释的包则是直接依赖的包,即明确的出现在某个import
语句中。
然而,这里需要着重强调的是:并不是所有的间接依赖都会出现在 go.mod
文件中。
间接依赖出现在go.mod
文件的情况,可能符合下面所列场景的一种或多种:
- 直接依赖未启用 Go module
- 直接依赖go.mod 文件中缺失部分依赖
一、直接依赖未启用 Go module
如下图所示,Module A 依赖 B,但是 B 还未切换成 Module,也即没有go.mod
文件,此时,当使用go mod tidy
命令更新A的go.mod
文件时,B的两个依赖B1和B2将会被添加到A的go.mod
文件中(前提是A之前没有依赖B1和B2),并且B1 和B2还会被添加// indirect
的注释。
此时Module A的go.mod
文件中require部分将会变成:
require (
B vx.x.x
B1 vx.x.x // indirect
B2 vx.x.x // indirect
)
依赖B及B的依赖B1和B2都会出现在go.mod
文件中。
二、直接依赖 go.mod 文件不完整
如上面所述,如果依赖B没有go.mod
文件,则Module A 将会把B的所有依赖记录到A 的go.mod
文件中。即便B拥有go.mod
,如果go.mod
文件不完整的话,Module A依然会记录部分B的依赖到go.mod
文件中。
如下图所示,Module B虽然提供了go.mod
文件中,但go.mod
文件中只添加了依赖B1,那么此时A在引用B时,则会在A的go.mod
文件中添加B2作为间接依赖,B1则不会出现在A的go.mod
文件中。
此时Module A的go.mod
文件中require部分将会变成:
require (
B vx.x.x
B2 vx.x.x // indirect
)
由于B1已经包含进B的go.mod
文件中,A的go.mod
文件则不必再记录,只会记录缺失的B2。
三、 如何处理间接依赖
综上所述间接依赖出现在go.mod
中,可以一定程度上说明依赖有瑕疵,要么是其不支持Go module,要么是其go.mod
文件不完整。
由于Go 语言从v1.11版本才推出module的特性,众多开源软件迁移到go module还需要一段时间,在过渡期必然会出现间接依赖,但随着时间的推进,在go.mod
中出现// indirect
的机率会越来越低。
出现间接依赖可能意味着你在使用过时的软件,如果有精力的话还是推荐尽快消除间接依赖。可以通过使用依赖的新版本或者替换依赖的方式消除间接依赖。
3.1 案例1
版本:dev/4a:1d80b9aa7a
github.com/allegro/bigcache/v2 v2.2.5 // indirect
github.com/allegro/bigcache/v3 v3.0.0
go mod why -m module 检查module版本依赖
[root@VM-219-215-centos]# go mod why -m github.com/allegro/bigcache/v2
# github.com/allegro/bigcache/v2
wwlocal_gateway/wwlgwauthsvr/cache
github.com/allegro/bigcache/v3
github.com/allegro/bigcache/v3.test
github.com/allegro/bigcache/v2
思考:是v3版本内部依赖,在代码内全局搜不到v2相关信息,所以可能是v3版本自身问题,于是想到升级v3版本看是否有bug修复,或者回退到v2版本。两种方式均有效。于是升级到v3.0.1最新版本。升级信息验证猜想:
并综合对比了v2和v3两个版本,并未发现有明显不兼容的差异,项目升级到v3自测也顺利通过。
查看
3.2 案例2
版本:dev/4a:1d80b9aa7a
github.com/inconshreveable/mousetrap v1.0.0 // indirect
分析:
[root@VM-219-215-centos]# go mod why -m github.com/inconshreveable/mousetrap
# github.com/inconshreveable/mousetrap
wwlocal_gateway/wwlgwlocal_proxy_pc
github.com/spf13/cobra
github.com/inconshreveable/mousetrap
思考:
cobra库在go.mod当中的版本如下:
github.com/spf13/cobra v0.0.3
版本过低且非正式版v1 ,猜测库版本有更新,更新到最新版本可以解决目前的问题,于是
[root@VM-219-215-centos]# go get -u github.com/spf13/cobra
go get: upgraded github.com/fsnotify/fsnotify v1.4.9 => v1.5.1
go get: upgraded github.com/json-iterator/go v1.1.11 => v1.1.12
go get: upgraded github.com/spf13/cast v1.3.1 => v1.4.1
go get: upgraded github.com/spf13/cobra v0.0.3 => v1.3.0
go get: upgraded github.com/spf13/viper v1.8.0 => v1.10.0
问题得到解决。
延伸:go版本号规则
一个module的版本号规则必须遵循语义化规范(https://semver.org/),版本号必须使用格式
v(major).(minor).(patch)
,比如v0.1.0
、v1.2.3
或v1.5.0-rc.1
。语义化版本(Semantic Versioning)已成为事实上的标准,几乎知名的开源项目都遵循该规范,更详细的信息请前往https://semver.org/ 查看,在此只提炼一些要点。
版本格式
v(major).(minor).(patch)
中major指的是大版本,minor指小版本,patch指补丁版本。
- major: 当发生不兼容的改动时才可以增加major版本;比如
v2.x.y
与v1.x.y
是不兼容的;- minor: 当有新增特性时才可以增加该版本,比如
v1.17.0
是在v1.16.0
基础上加了新的特性,同时兼容v1.16.0
;- patch: 当有bug修复时才可以 增加该版本,比如
v1.17.1
修复了v1.17.0
上的bug,没有新特性增加;
正常项目依赖库升级的时候,库应当遵循以上原则,且gomod 只会升级minor及patch版本,major只有在明确修改项目中源码import xxx/v2
,才会更新module。
例如上面的github.com/allegro/bigcache/v3
,在代码中是有明确版本标识的:
如果没有版本标识的,gomod会默认v1或者v0(module license问题)。
3.3 案例3
github.com/hpcloud/tail v1.0.0
gopkg.in/fsnotify.v1 v1.4.7 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
两个库都被tail库依赖,果然tail库符合indirect情况,没有支持gomod。所以这种就有两个解决方案,1是维持,2是改库。根据实际综合取舍。
3.4 案例4
github.com/natefinch/lumberjack
log 库。
3.5 引入的库 版本号 与 库所依赖的库 版本号 不对
修改到一致, go mod tidy 即可解决
...