转载请注明出处:www.huamo.online
字节杭州 求贤若渴:
Modules
概念
一个module
是相关Go package的一个集合。modules
是源码交互和版本控制的单元。go
命令直接支持使用模块,包括记录和解析对其他模块的依赖。modules
替代了以前基于GOPATH
的方式来指定在构建时使用哪些源文件。
Go 1.11开始初步支持modules
这一崭新概念,它统一支持包依赖的版本和路径,版本依赖信息变得轻量而且明确,构建也变得更加可靠和可复现。
module作为一个集合存放在一个以go.mod
文件为根的文件树中。go.mod
文件定义了模块的module path
,这个路径同时也是相对于根目录($GOPATH/src
)的import path
。还定义了这个模块的依赖需求
,即为了完成构建需要用到的其它模块。每一个依赖需求都写作一个模块路径加上一个特定的语义化版本
从Go 1.11开始,如果一个工程目录在$GOPATH/src
外面,且当前目录或任何父目录有go.mod
文件,那么go
命令就会启用modules
(如果工程在$GOPATH/src
内部,为了兼容性,go
命令依然使用旧的基于GOPATH
构建模式,即使找到了go.mod
文件)。从Go 1.13开始,module
模式会作为所有开发的默认构建模式。
module
的初步支持
Go 1.11初步支持modules
,包含了一个新的可以感知module
的go get
命令。开发组会一直保持更新,直到官方宣布正式支持以后,然后就会移除掉GOPATH
构建模式以及老的go get
命令。
最快最简单的使用新版module
构建项目的方式是:将工程目录移到$GOPATH/src
外面,并在目录下创建一个go.mod
文件,然后在目录下运行go
命令。
为了更细粒度的控制,Go 1.11提供了一个临时的环境变量GO111MODULE
。可以设置为3个值:off, on, auto(默认值)
。如果GO111MODULE=off
,那么go
命令永不使用module
支持,而是像以前一样查看vendor
文件夹和$GOPATH
目录来寻找依赖(这被称为GOPATH
模式)。如果GO111MODULE=on
,那么go
命令就会要求使用modules
特性,而永不再考虑$GOPATH
(这被称为module-aware
模式)。如果GO111MODULE=auto
或未设置,go
命令则会依据当前目录来启用或关闭module
特性。仅当工程目录在$GOPATH/src
外面且本身有go.mod
文件或父目录有这个文件,才启用module
支持。
在module-aware
模式下,构建时$GOPATH
不再定义import的含义,但它会存储下载好的依赖(存在$GOPATH/pkg/mod
),以及安装的可执行命令(一般存在$GOPATH/bin,除非系统设置了$GOBIN)
创建一个新的module
- 将工程目录创建在
$GOPATH/src
外面,正常编写代码文件和单元测试代码文件
1 | $ pwd |
- 将工程目录作为
module
的根目录
1 | $ go mod init gopher/hello |
go.mod
文件只会出现在module
的根目录中。子目录中package
的import路径是module
路径加上子目录路径即可,且子目录不需要再运行go mod init
命令。例如hello/
有个子目录world
,那么它的import路径就是gopher/hello/world
。
添加一个依赖
创造Go modules
的主要目的就是为了提高使用其他代码的体验(即添加依赖)
当遇到import
了一个go.mod
中没有提供的package
,go
命令就会自动寻找包含这个package
的module
并将其添加到go.mod
中,使用latest
版本。所谓的latest
规则为:
最新,打了tag标志稳定的版本(非预发行版本),即
x.y.z
如果没有,则选最新,打了tag的预发行版本,即
x.y.z-abc1
如果还没有,则选最新,没有打tag的版本。
只有直接引用的依赖才记录在go.mod
文件中。但是使用go get
升级了的依赖,即使是非直接引用,也会记录在go.mod
中,下文会说
检查当前模块和它的所有依赖:
1 | $ go list -m all |
还可以使用go list -m rsc.io...
进行模糊搜索
依赖的显示是根据
module
路径排序
其中golang.org/x/text
的是go
命令为没有打tag的提交生成的伪版本
go
命令还维护了一个go.sum
文件,包含了每个依赖的加密哈希校验和。以此确保以后的下载依赖和初次下载的内容完全一致。
go.mod
和go.sum
都需要检入到版本控制中。
升级依赖
可以使用go get -v golang.org/x/text
升级这个依赖,这里没有指定升级到的版本,就会默认升级到@latest
。完成之后,go.mod
就会新增记录,go.sum
也会随之更新。即使是非直接依赖,此时也会记录在go.mod
中
也可以使用go get -v rsc.io/[email protected]
指定升级版本。
1 | $ go get -v rsc.io/[email protected] |
增加一个新的主版本依赖
就像以太坊中遇到的那样,whisper
有v5和v6两个版本,分别对应两个目录,现在我终于明白了为什么会这样分开冗余代码,因为主版本的变更一般都是对外接口不再向后兼容,但是对于一个已经存在的module
,别人有可能还在依赖旧版本代码,所以只能以主版本号为文件夹名开辟新的module path
,这种惯例称为语义化导入版本
所以,在go import中,可以同时依赖一个模块的多个主版本,例如:
1 | $ vim hell.go |
这样就可以既使用部分新特性,又能和老版本进行兼容。
移除未使用的依赖
go build
或者go test
这种命令可以快速发现缺少了什么,但并不能安全的告知什么可以移除,因为移除一个依赖需要在检查完这个module
下所有的包之后才能做,包括这些包的所有可能编译参数组合都检查通过才行。
go mod tidy
可以完成这个使命
Go包版本提议
介绍
8年前,Go开发组推出了goinstall
(最后演化为go get
),其去中心化,类似URL的导入路径对Go开发者颇为熟悉。当goinstall
发布之后,人们最先询问的问题便是如何纳入版本信息。Go开发组承认:We admitted we didn’t know。长期以来,他们都认为包版本的问题最好是通过一个插件工具来解决,他们也鼓励社区创造出一个这样的工具。于是很多工具用了很多不同方式来解决这个问题,到了2016中间的时候,市面上已经出现了太多解决方案,开发组认为需要选用一个单一的,官方的工具结束这个局面。
参考链接
https://golang.org/cmd/go/#hdr-Modules__module_versions__and_more
https://golang.org/cmd/go/#hdr-Module_downloading_and_verification
转载请注明出处:www.huamo.online