go module
go.mod 示例:
1 | module api.local |
- module:用于定义当前项目的模块路径。
- go:用于设置预期的 Go 版本。
- require:用于设置一个特定的模块版本。
- exclude:用于从使用中排除一个特定的模块版本。
- replace:用于将一个模块版本替换为另外一个模块版本。
indirect 表示什么意思
https://my.oschina.net/renhc/blog/3162751
在执行命令 go mod tidy
时,Go module 会自动整理 go.mod 文件
,如果有必要会在部分依赖包的后面增加 // 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 部分将会变成:
1 | require ( |
依赖 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 部分将会变成:
1 | require ( |
由于 B1 已经包含进 B 的 go.mod
文件中,A 的 go.mod
文件则不必再记录,只会记录缺失的 B2。
总结
为什么要记录间接依赖
在上面的例子中,如果某个依赖 B 没有 go.mod
文件,在 A 的 go.mod
文件中已经记录了依赖 B 及其版本号,为什么还要增加间接依赖呢?
我们知道 Go module 需要精确地记录软件的依赖情况,虽然此处记录了依赖 B 的版本号,但 B 的依赖情况没有记录下来,所以如果 B 的 go.mod
文件缺失了(或没有)这个信息,则需要在 A 的 go.mod
文件中记录下来。此时间接依赖的版本号将会跟据 Go module 的版本选择机制确定一个最优版本。
如何处理间接依赖
综上所述间接依赖出现在 go.mod
中,可以一定程度上说明依赖有瑕疵,要么是其不支持 Go module,要么是其 go.mod
文件不完整。
由于 Go 语言从 v1.11 版本才推出 module 的特性,众多开源软件迁移到 go module 还需要一段时间,在过渡期必然会出现间接依赖,但随着时间的推进,在 go.mod
中出现 // indirect
的机率会越来越低。
出现间接依赖可能意味着你在使用过时的软件,如果有精力的话还是推荐尽快消除间接依赖。可以通过使用依赖的新版本或者替换依赖的方式消除间接依赖。
如何查找间接依赖来源
Go module 提供了 go mod why
命令来解释为什么会依赖某个软件包,若要查看 go.mod
中某个间接依赖是被哪个依赖引入的,可以使用命令 go mod why -m <pkg>
来查看。
另外,命令 go mod why -m all
则可以分析所有依赖的依赖链。