Go 1.11开始对模块进行支持,主要目的就是使用模块来管理依赖。本文介绍使用modules的一些基本操作以及在Go 1.16版本中的变化。
在Go 1.16中,GO111MODULE默认是开启状态。在Go 1.16版本以下,请确保你已经设置了GO111MODULE=on状态。
一个module就是一组包的集合,即go.mod文件所在目录下定义的所有的包都属于这个模块。go.mod文件定义了模块的路径path,这个路径是用于import的路径以及编译时该模块依赖于其他模块的需求。该模块依赖的模块通过模块路径 语义化的版本号的格式添加到go.mod中。
本文介绍以下module的操作:
- 在项目下创建新的module
- 添加依赖
- 升级依赖 升级依赖的次版本 升级依赖到主版本
- 移除不用的依赖
01
—
创建module
你可以在$GOPATH/src之外创建新的项目
go mod init modulename 命令可以创建一个空的go.mod文件。如下:
代码语言:javascript复制module example.com/demo
go.mod文件在项目根目录下创建一次即可,如果该根目录下再有子目录,那么子目录下就不再需要重复创建go.mod文件。因为所有子目录中的包都同属于该模块。该模块中的包在被导入的时候,import的路径使用 module / package的模式导入即可。
官网有消息称在Go 1.17版本将不再对GOPATH模式支持。所有,如果有基于非MODULE模式编译的老项目,建议尽快迁移到MODULE模式下。
02
—
添加依赖
通过在程序文件中import对应的包,在go 1.16之前的版本中,运行go命令(如go run, go build , go test)时, go会通过以下规则自动解析并下载包:
1、添加特定版本的包:要import的包在go.mod文件中有对应的的require描述,则按对应描述的版本下载。
2、添加最新版本的包:要import的包 在go.mod中没有require描述,则按最新版本下载该包,同时将该包加入到go.mod中。
在go 1.16及其后续版本中,运行go命令(go run, go build,go test)时,如果import的依赖在go.mod文件中没有对应的require,则不会再自动下载并修改go.mod和go.sum文件,而会提示错误,并要求手动执行 go get命令下载对应的包。 原因是自动修复的方式不是在任何场景下都适用:如果导入的包在没有提供任何依赖的情况自动添加新依赖,则可能会引起公共依赖包的升级等。
通过运行 go get ./ ... 可以自动查找并下载所有的包。添加完包后,可以通过使用 go list -m all 查看当前模块所依赖的包列表。
在go.mod所在根目录下,除了维护go.mod文件外,还有一个go.sum文件。go.sum文件是对导入的依赖包的特定版本的hash校验值,作用就是确保将来下载的依赖包版本和第一次下载到的依赖版的版本号相同,以防止在将来有版本号升级后 程序不兼容的问题。所以,go.mod和go.sum文件都需要被加入版本管理中。go.sum内容如下:
代码语言:javascript复制$ cat go.sum
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZO...
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:Nq...
rsc.io/quote v1.5.2 h1:w5fcysjrx7yqtD/aO QwRjYZOKnaM9Uh2b40tElTs3...
rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPX...
rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD gZwtXXI RODJ2Wc4O7MPEh/Q...
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9...
$
私有包源码下载的安全约束 -- GOVCS环境变量
go command下载依赖包的来源有两个:分别是**代理网站**(如proxy.golang.org)和**版本控制仓库**(例如github.com等源码仓库管理站)。尤其是当依赖包是私有包,未在代理网站上传时,从版本控制仓库下载源代码尤为重要。但这也涉及到一个安全问题:恶意服务器可能会利用版本控制工具中的bug来运行错误代码。因此,Golang通过GOVCS环境变量的配置来确保安全。格式如下:
代码语言:javascript复制GOVCS=<module prefix>:<tool name>,[<module prefix>:<tool name>,...]
例如:GOVCS=github.com:git,evil.com:off,*:git|hg
代表可以使用git命令下载包路径是github.com的依赖包。任何版本控制命令都不可以下载evil.com路径的包。其余的任何路径的包都可以使用git或hg命令下载。
03
—
升级依赖
在go module中,使用语义化的版本号来标记所依赖的包的版本。一个语义化的版本号有三部分组成:主版本,次版本和补丁版本号。例如v0.1.2, 代表主版本号是0,次版本号是1,补丁版本号是2 。
另外,以下版本号格式也都是合法的:
代码语言:javascript复制1. gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7
2. gopkg.in/vmihailenco/msgpack.v2 v2.9.1
3. gopkg.in/yaml.v2 <=v2.2.1
4. github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e
5. latest
次版本升级
- 运行go get -u 将会升级到最新的次要版本或者修订版本(x.y.z, z是修订版本号, y是次要版本号)
- 运行 go get -u=patch 将会升级到最新的修订版本
主版本升级
Go module规定包的不同主要版本号需要使用不同的module路径。例如一个包example.com/demo 的后续主版本应该使用以下路径 example.com/demo/vX, 即v2版本的路径是example.com/demo/v2, v3,v4...依次类推。
所以,要升级依赖的主版本则通过以下方式:
- 在程序文件中import包的特定版本。例如 import demoV2 “example.com/demo/v2”
- 使用go get package@version
04
—
移除依赖
如果我们不再使用一个依赖包,则我们从代码文件的import中移除。但在go.mod文件的require中并不会自动移除。需要使用go get tidy命令将其从go.mod文件中移除。
因为go.mod的作用是在构建包的时候(例如go build或go test),告诉编译器需要哪些包,哪些包是缺失的,而不是告诉编译器什么时候该删除包。所以,想要安全的移除包需使用 命令 go get tidy
05
—
总结
本文主要介绍了使用go module的流程:
- go mod init 创建一个新模块,初始化go.mod文件
- go 1.16版本之前,go build, go test以及其他包构建命令 自动查找并下载依赖包并将依赖添加到go.mod中,go 1.16及其以后,go command不再自动下载不在go.mod中的依赖,而是需要通过go get 命令手动下载
- go list -m all 打印出当前模块直接依赖的列表
- go get 升级依赖或添加新依赖。其中go get只能升级次版本。主版本的升级需要按主版本规则import后,再 go get package@version
- go mod tidy 从go.mod中移除不需要的依赖
- go 1.17计划移除GOPATH模式,建议尽快将非module-aware模式的老项目迁移到module-aware模式