前言
mod 是 modules 的简称,Go 1.11 和 Go 1.12 早已支持 modules。
在 Go 1.11 中 $GOPATH/src 目录下,即使有 go.mod 那也是工作在 GOPATH 模式下;从 Go 1.13 开始默认为 module 模式。
modules 使用 semantic version tags 格式为:major.minor.patch(如:v0.1.2), major 为大版本号,minor 为小版本号,patch 为补丁号。
依赖包后面的 // indirect 注释表明这个包是间接被这个 module 所依赖。
在 module 目录下使用 go get xxx 会使 xxx 依赖包升至最新版本(默认为:go get xxx@latest),如果 xxx 是个间接依赖包被其它包所依赖时,并且这些包所需要的 xxx 版本为老版本并且新老版本不兼容的话,会使得编译不通过。
可以使用 go get [email protected] 获取特定的依赖包。
module 允许同时依赖于某个包的不同主版本,但是特定主版本最多只能有一个。例如:可以同时依赖 xxx 的 v1 和 v2两个版本,但不能同时依赖 v1 版本中的 v1.0.0 和 v1.0.1。
当依赖多个主版本时使用 semantic import versioning 进行指定。例如:rsc.io/quote 表示 v1 版,rsc.io/quote/v2 表示 v2 版。
allowing different major versions of a module (because they have different paths) gives module consumers the ability to upgrade to a new major version incrementally
可以看出之所以可以这样是因为不同的主版本的路径必须是不同的,例如:rsc.io/quote 和 rsc.io/quote/v2 是两个路径。
这样引入了一条规则:
If an old package and a new package have the same import path,
the new package must be backwards compatible with the old package.
所以对于 github/user/xxx 的开发者来说,当他想开发第二个版本时,必须把 v2 版的代码提交到 github/user/xxx/v2 路径下(两个路径下都是完整的 module,分别有各自的 go.mod ),这样使用者就可以有选择的使用。
官方说使用版本后缀的方式管理包,是 go 有别于其他工具的地方,这样可以解决菱形依赖问题。
$ pwd
/.../xxx
$ ls
README.md one.go two.go ...
$ cat go.mod
module github.com/hengzi52125/xxx
go 1.13
require (
...
)
$
$
$ mkdir v2
$ cp *.go v2/
$ cp go.mod v2/go.mod
$ go mod edit -module github.com/hengzi52125/xxx/v2 v2/go.mod
依赖包会被下载到 $GOPATH/pkg/mod/ 目录下,并带有版本标记(例如:$GOPATH/pkg/mod/github.com/nfnt/[email protected]),当下次另外的 module 用到同样的包时不会重复下载。
对于未被 tag 的包(例如上面的 nfnt/resize 包,只有个 master 分支,而且没有 Tag ),go 命令会自动生成个 preudo version 版本号, 如:v0.0.0-yyyymmddhhmmss-abcdef 时间为 commit UTC时间,abcdef 为 commit hash。
go.sum 文件列出依赖包的源码的 SHA-256 哈希值,主要用于验证从 GOPROXY 镜像站点上下载下来的代码有没有被做篡改,保证安全。
所以 GOSUMDB 没有镜像站点,鉴于国内网络只能关闭该检查功能,即 GOSUMDB="off"
使用 go mod init xxx 会自动创建 go.mod 文件,当下载依赖后会自动创建 go.sum 文件。所以这两个文件一般不需要手动创建。
GOPROXY 和 GOSUMDB
GOPROXY 协议流程:
设置国内镜像站点 https://goproxy.cn :
go env -w GOPROXY="https://goproxy.cn,https://proxy.golang.org,direct"
GOSUMDB 协议流程:
关闭 GOSUMDB:
go env -w GOSUMDB="off"
go.mod 文件
module:指定本 module 的名字(路径)
如果不是第一个主版本,如: v0 或 v1,则要在后面加上版本后缀
module github.com/hengzi52125/xxx/v2
require:指定依赖
多个依赖可以放在一个 "()" 里,就像 import 那样
require (
dependency1
dependency2
)
replace:替换依赖
当某些原因导致 require 中的依赖下载不下来时,或者想导入本地的包时,可以使用 replace
replace (
golang.org/x/image vx.y.z => github.com/golang/image vx.y.z
a-package vx.y.z => /Users/...
)
exclude:排除依赖
举个栗子:假设在代码中 import github.com/user/xxx, 对于这个 xxx 它现在最新的版本是 v1.1.1 , 但是你不想用 v1.1.1, 于是就可以把它放入 exclude 中
exclude github.com/user/xxx v1.1.1
使用
创建 module
go mod init github.com/hengzi52125/xxx
导入依赖
正常编写代码,当执行 go test/run/build 等命令时会自动导入最新的依赖包(“Latest” is defined as the latest tagged stable (non-prerelease) version, or else the latest tagged prerelease version, or else the latest untagged version.)
go get [email protected] 可以导入特定版本的依赖,@ 后面可以跟 git 的 branch、tag、commit hash。如果使用的是分支,如:go get foo@master 那么只会下载一次,即下次执行 go get foo@master 时不会再去下载,直接用本地的,即使 master 上代码有更新。
go mod download : 下载依赖包
查看依赖
go list -m all :会列出当前的 module 和它所有的依赖(包括间接依赖)
go list -m -versions xxx :列出 xxx 的所有版本
go doc xxx/v3 :查看文档
go mod graph :打印模块依赖图
清除无用的依赖
go mod tidy :清除未被使用的依赖,或导入所需依赖(导入最新的版本)
升降级依赖
go list -u -m all :升级并查看所有依赖(go list -u 只能搭配 -m 才能使用)
go get -u xxx :升级 minor 或 patch 版本号
go get -u=patch xxx :只升级 patch 版本号
go get xxx@' 格式化 go.mod preudo-version
go mod edit -fmt :如果手动修改了 go.mod 变得格式不美观了,可以用其格式化参考链接
Go Modules: v2 and Beyond
Publishing Go Modules
Module Mirror and Checksum Database Launched
Using Go Modules