Go语言自从推出了go mod
作为版本管理工具后,结束Go语言版本管理工具的纷争,实现了大一统。
相信有很多人都对这个版本管理的机制都有基础的概念、但并不深入。而官方把最核心的实现,都放在这一篇 https://golang.org/ref/mod 文档中。
今天,我们一起来读读这一篇文章。
新手直接阅读这篇文章的门槛有点高,我建议可以先看看下面这五篇较为通俗的官方博客,能帮助我们了解一些背景知识。
https://blog.golang.org/using-go-modules
go.mod
放在项目的根目录,抛弃原来的GOPATH
MAJOR.MINOR.PATCH
格式管理版本,详细可参考Semantic Versioning 2.0.0go.sum
保证依赖文件被完整下载(如果公司搭建私有库就会出现校验问题,需要关闭GOSUM)go mod模块名
+ 相对路径
,定义更加清晰第四点的价值很大,可读性大大提高,我们可以将一整个path来辅助命名,如 market/order,前者market可以帮助后者order的含义做一定补充
https://blog.golang.org/migrating-to-go-modules
将项目迁移到go mod,主要讲的是对接老的版本管理系统,如godeps
。
https://blog.golang.org/publishing-go-modules
版本命名规则:推荐不稳定版本用v0.x.x
开始,稳定后改成v1.x.x
。
pseudo-version 直译为假的版本,一般是直接依赖branch,而不是按规范依赖tag
https://blog.golang.org/v2-go-modules
注意主版本名为v2
之后的,项目里对应的要新增一个v2
或者更高版本号的目录。
我个人感觉升级到v2这种方式不太友好,需要我们再维护一整个目录
https://blog.golang.org/module-compatibility
...
的特性提高函数的入参扩展性Option types
模式,更详细地可以参考我的文章interface
分离调用和实现struct
明确不能对比,就用一个doNotCompare
的field
来告诉调用者type doNotCompare [0]func()
type Point struct {
doNotCompare
X int
Y int
}
第五点非常有意思,也就是要求我们在前期设计对象时,就要考虑清楚这个对象后续的特性,比如示例中的是否可以对比
进入正题,我们来一起看看go.mod
,它的定义简单示例如下:
module example.com/my/thing
go 1.12
require example.com/other/thing v1.0.2
require example.com/new/thing/v2 v2.3.4
exclude example.com/old/thing v1.2.3
replace example.com/bad/thing v1.4.5 => example.com/good/thing v1.4.5
retract [v1.9.0, v1.9.5]
IDE会帮助你格式化,记住以下关键词即可(重点为前三个)
go mod 底层实现依赖 - MVS 最小版本选择。 这个特性很有意思,后续单独来讲讲这块,一开始就不深入到细节了
在我们整理go.mod
文件时,经常能看到两个奇怪的字符indirect
和incompatible
。我们来详细地分析一下。
上面我们已经讲过,如果一个库的tag为v1以上,如v2,就必须得创建一个v2的目录。例如
require example.com/new/thing/v2 v2.3.4
这就要求我们在项目example.com/new/thing
下新建v2目录,再存放代码。但是,很多库往往只是升级个主版本号,并不会去新建目录、还需要迁移代码。为了兼容这个情况,就会引入+incompatible
。例如
require example.com/new/thing v2.3.4+incompatible
我们先聊一个简单的场景:当前项目为A,调用了项目B,B又调用了C。对A进行编译,需要B和C的相关代码。
在完全规范的项目中:
在编译A时,会在go.mod找到B的信息,所以B是require
字段;而C的信息已经被维护在B的go.mod里了,不需要在A的go.mod里维护。
而什么样的情况会发生indirect呢?它对应的是 条件2 缺失的场景
最常见的部分缺失场景是:项目虽然有go.mod,但实际编译不走Go Module,而是如vendor目录等方式
用一句话总结,A库无法根据B库的go.mod
找到C库。
相关的指令有很多,我重点分两块来说:
先是高频使用的命令:
初始化项目,保证module名称与git路径一致。
例如 go mod init github.com/example/a
常见的flags
go.mod
中的依赖,轻量级例如go get -d github.com/example/b
其中download是下载到Go Module的缓存中,而vendor是下载到vendor依赖路径。官方推荐前者。
我经常会去手动编辑go.mod
文件,然后用这个指令刷新一下依赖库
整理并更新go mod的依赖信息,保证当前的go.mod
为最新。
然后是排查依赖库问题用到的:
go list -m all
查看本项目的所有依赖库与版本go list -m -versions {module名}
查看module支持的版本号go list -m -json {module名}@{版本号}
用json格式查看指定module版本号的信息,如创建时间查看所有go mod的依赖,一般在查依赖关系时用到
查指定库是怎么被依赖的
查看指定(go文件编译的)二进制文件的版本信息
大部分人使用go.mod
的最大问题是无法下载代码库,也就是代理的设置,网上也有很多教程,我这边给三个我常用的:
公司私有库需要私有代理。
本讲的内容到这里就告一段落了,相信通过这篇文章,大家已经能应对绝大部分Go Module的场景。
下一讲,我会重点讲Go Module最核心的 Minimal version selection (MVS) 机制。