首页 > temp > 简明python教程 >
-
Go Modules 终极入门(2)
fmt.Println(mquote.GetHello())
}
然后在项目根目录执行 go get github.com/eddycjy/mquote
命令,如下:
$ go get github.com/eddycjy/mquote
go: finding github.com/eddycjy/mquote latest
go: downloading github.com/eddycjy/mquote v0.0.0-20200220041913-e066a990ce6f
go: extracting github.com/eddycjy/mquote v0.0.0-20200220041913-e066a990ce6f
查看 go.mod 文件
在初始化项目时,会生成一个 go.mod 文件,是启用了 Go modules 项目所必须的最重要的标识,同时也是 GO111MODULE 值为 auto 时的识别标识,它描述了当前项目(也就是当前模块)的元信息,每一行都以一个动词开头。
在我们刚刚进行了初始化和简单拉取后,我们再次查看 go.mod 文件,基本内容如下:
module github.com/eddycjy/module-repo
go 1.13
require (
github.com/eddycjy/mquote v0.0.0-20200220041913-e066a990ce6f
)
为了更进一步的讲解,我们模拟引用如下:
module github.com/eddycjy/module-repo
go 1.13
require (
example.com/apple v0.1.2
example.com/banana v1.2.3
example.com/banana/v2 v2.3.4
example.com/pear // indirect
example.com/strawberry // incompatible
)
exclude example.com/banana v1.2.4
replace example.com/apple v0.1.2 => example.com/fried v0.1.0
replace example.com/banana => example.com/fish
-
module:用于定义当前项目的模块路径。 -
go:用于标识当前模块的 Go 语言版本,值为初始化模块时的版本,目前来看还只是个标识作用。 -
require:用于设置一个特定的模块版本。 -
exclude:用于从使用中排除一个特定的模块版本。 -
replace:用于将一个模块版本替换为另外一个模块版本。
另外你会发现 example.com/pear
的后面会有一个 indirect 标识,indirect 标识表示该模块为间接依赖,也就是在当前应用程序中的 import 语句中,并没有发现这个模块的明确引用,有可能是你先手动 go get
拉取下来的,也有可能是你所依赖的模块所依赖的,情况有好几种。
查看 go.sum 文件
在第一次拉取模块依赖后,会发现多出了一个 go.sum 文件,其详细罗列了当前项目直接或间接依赖的所有模块版本,并写明了那些模块版本的 SHA-256 哈希值以备 Go 在今后的操作中保证项目所依赖的那些模块版本不会被篡改。
github.com/eddycjy/mquote v0.0.1 h1:4QHXKo7J8a6J/k8UA6CiHhswJQs0sm2foAQQUq8GFHM=
github.com/eddycjy/mquote v0.0.1/go.mod h1:ZtlkDs7Mriynl7wsDQ4cU23okEtVYqHwl7F1eDh4qPg=
github.com/eddycjy/mquote/module/tour v0.0.1 h1:cc+pgV0LnR8Fhou0zNHughT7IbSnLvfUZ+X3fvshrv8=
github.com/eddycjy/mquote/module/tour v0.0.1/go.mod h1:8uL1FOiQJZ4/1hzqQ5mv4Sm7nJcwYu41F3nZmkiWx5I=
...
我们可以看到一个模块路径可能有如下两种:
github.com/eddycjy/mquote v0.0.1 h1:4QHXKo7J8a6J/k8UA6CiHhswJQs0sm2foAQQUq8GFHM=
github.com/eddycjy/mquote v0.0.1/go.mod h1:ZtlkDs7Mriynl7wsDQ4cU23okEtVYqHwl7F1eDh4qPg=
h1 hash 是 Go modules 将目标模块版本的 zip 文件开包后,针对所有包内文件依次进行 hash,然后再把它们的 hash 结果按照固定格式和算法组成总的 hash 值。
而 h1 hash 和 go.mod hash 两者,要不就是同时存在,要不就是只存在 go.mod hash。那什么情况下会不存在 h1 hash 呢,就是当 Go 认为肯定用不到某个模块版本的时候就会省略它的 h1 hash,就会出现不存在 h1 hash,只存在 go.mod hash 的情况。
查看全局缓存
我们刚刚成功的将 github.com/eddycjy/mquote
模块拉取了下来,其拉取的结果缓存在 $GOPATH/pkg/mod
和 $GOPATH/pkg/sumdb
目录下,而在mod
目录下会以 github.com/foo/bar
的格式进行存放,如下:
mod
├── cache
├── github.com
├── golang.org
├── google.golang.org
├── gopkg.in
...
需要注意的是同一个模块版本的数据只缓存一份,所有其它模块共享使用。如果你希望清理所有已缓存的模块版本数据,可以执行 go clean -modcache
命令。
Go Modules 下的 go get 行为
在拉取项目依赖时,你会发现拉取的过程总共分为了三大步,分别是 finding(发现)、downloading(下载)以及 extracting(提取), 并且在拉取信息上一共分为了三段内容:
需要注意的是,所拉取版本的 commit 时间是以UTC时区为准,而并非本地时区,同时我们会发现我们 go get
命令所拉取到的版本是 v0.0.0,这是因为我们是直接执行 go get -u
获取的,并没有指定任何的版本信息,由 Go modules 自行按照内部规则进行选择。
go get 的拉取行为
刚刚我们用 go get
命令拉取了新的依赖,那么 go get
又提供了哪些功能呢,常用的拉取命令如下:
命令 | 作用 |
---|---|
go get | 拉取依赖,会进行指定性拉取(更新),并不会更新所依赖的其它模块。 |
go get -u | 更新现有的依赖,会强制更新它所依赖的其它全部模块,不包括自身。 |
go get -u -t ./... | 更新所有直接依赖和间接依赖的模块版本,包括单元测试中用到的。 |
那么我想选择具体版本应当如何执行呢,如下:
命令 | 作用 |
---|---|
go get golang.org/x/text@latest | 拉取最新的版本,若存在tag,则优先使用。 |
go get golang.org/x/text@master | 拉取 master 分支的最新 commit。 |
go get golang.org/x/text@v0.3.2 | 拉取 tag 为 v0.3.2 的 commit。 |
go get golang.org/x/text@342b2e | 拉取 hash 为 342b231 的 commit,最终会被转换为 v0.3.2。 |
go get 的版本选择
我们回顾一下我们拉取的 go get github.com/eddycjy/mquote
,其结果是 v0.0.0-20200220041913-e066a990ce6f
,对照着上面所提到的 go get
行为来看,你可能还会有一些疑惑,那就是在 go get
没有指定任何版本的情况下,它的版本选择规则是怎么样的,也就是为什么 go get
拉取的是 v0.0.0
,它什么时候会拉取正常带版本号的 tags 呢。实际上这需要区分两种情况,如下:
-
所拉取的模块有发布 tags: -
如果只有单个模块,那么就取主版本号最大的那个tag。 -
如果有多个模块,则推算相应的模块路径,取主版本号最大的那个tag(子模块的tag的模块路径会有前缀要求)
-
-
所拉取的模块没有发布过 tags: -
默认取主分支最新一次 commit 的 commithash。
-
没有发布过 tags
那么为什么会拉取的是 v0.0.0
呢,是因为 github.com/eddycjy/mquote
没有发布任何的tag,如下: