经历了 Go 的 workspace,然后又有 Vendor,Go 终于有了更加好的包依赖解决方案 Modules 机制。
快速入门
即使从 go1.5 引入了 vendor 机制和配合官方的 dep 工具,也依然不是一个便捷的解放方案。
不过现在 go modules 随着 golang1.11 的发布而和我们见面了,这是官方提倡的新的包管理,乃至项目管理机制。
因此项目结构可以像下面一样的:
1 | $GOPATH |
从项目结构来看,就是 Go 的 workspace 不再像以前规定得那么死。
在 go1.11 中,Modules 还只是一个实验性功能,
所以 go 提供了一个环境变量 “GO111MODULE”,默认值为 auto,
如果当前目录里有 go.mod 文件,就使用 go modules,否则使用旧的 $GOPATH 和 vendor 机制,
从 go1.12 开始,应该会强制使用 go modules 不再使用 $GOPATH 和 vendor 机制。
试试看吧:
1 | ...\ProjectZ> go mod init test |
1 | $GOPATH |
接下来,我们手动创建一个 main.go 文件,内容是:
1 | package main |
运行看看:
1 | ...\ProjectZ> go run main.go |
这个时候会下载相应的依赖包,如果你遇到恶心的墙网络问题,那么可以添加环境变量:
- 变量名(n):GOPROXY 变量值(v):https://goproxy.io
详情,可以浏览 https://goproxy.io
First, you will need to enable the Go Modules feature and configure Go to use the proxy.
墙内阿里的代理:https://mirrors.aliyun.com/goproxy
因为我们的文件夹中有 go.mod
,所以可以不用添加环境变量: GO111MODULE = on
1 | $GOPATH |
看看 go.mod
文件的内容:
1 | module test |
看看 go.sum
文件的内容:
1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= |
以前我们可以在控制台任何目录下用 go get
,那么会下载到 src
目录中。
1 | > go get github.com/gin-gonic/gin |
假设我们要切换到 gin@1.1.4 版本1
2>go get github.com/gin-gonic/gin@1.1.4
go: cannot use path@version syntax in GOPATH mode
必须切换到有 go.mod
文件的目录。
1 | ...\ProjectZ> go get github.com/gin-gonic/gin@1.1.4 |
1 | $GOPATH |
同时留意 go.mod
和 go.sum
文件内容的变化。
除了用 go get
来切换版本,还可以通过编辑 go.mod
的文件内容,然后
1 | ...\ProjectZ> go mod tidy |
也会下载相应的版本和该版本需要的依赖包。
下面是可能用到的功能:
go list -m all
—— 查看将在生成中用于所有直接和间接依赖关系的最终版本go list -u -m all
—— 查看所有直接和间接依赖项的可用次要和修补程序升级go get -u
orgo get -u=patch
—— 更新所有直接和间接依赖关系, 以进行最新的次要或修补程序升级 (忽略预发布)go build ./...
orgo test ./...
——go mod download
—— 下载依赖的module到本地cachego mod edit
—— 编辑go.mod文件go mod graph
—— 打印模块依赖图go mod init
—— 再当前文件夹下初始化一个新的module, 创建go.mod文件go mod tidy
—— 增加丢失的module,去掉未用的modulego mod vendor
—— 将依赖复制到vendor下go mod verify
—— 校验依赖go mod why
—— 解释为什么需要依赖
最后你会发现怎么 pkg
文件夹中没有生成 xxx.a 包的文件呢?
可以用 go env
查看,有一个叫 GOCACHE
的变量
小结
可以看出 $GOPATH
从以前存放引用包和项目的地方,变成只存放包,项目单独出来了,
而依然有 vendor
是有时候我们需要将引用包一起放到 svn 上。
概念
Modules
module (模块)是将一组相关的 Go packages(包),当作一个单元进行版本控制。
module (模块)精准地记录着必要的依赖关系,并且可用于构建项目。
通常,一个版本控制存储库只在在存储库根目录中定义的一个模块。
(单个存储库中支持多个模块, 但通常会比每个存储库的单个模块需要更多的工作)
总结一下 repositories(存储库), modules(模块), 和 packages(包) 之间的关系:
- 一个
repository(存储库)
包含 一个或以上 的 Go 模块 - 每个
module(模块)
包含 一个或以上 的 Go 包 - 每个
package(包)
包含 一个或以上 的 Go 源码文件
1 | repo // 存储库——项目文件夹 |
模块必须进行语义版本控制,规则是 v(major[主版本号]).(minor[次版本号]).(patch[补丁版本号])
,
如 v0.1.0, v1.2.3, or v1.5.0-rc.1,开头的 v
是必须的。
如果是使用 Git,则标记发布的提交及其版本。公共和私有模块存储库和代理正在变得可用。
go.mod
A module is defined by a tree of Go source files with a go.mod file in the tree’s root directory.
Module source code may be located outside of GOPATH.
Module 源码可以位于 GOPATH
目录之外。
通常,每个存储库会有一个 go.mod 位于 存储库 的根目录,但 go.mod 可以位于其它位置。
以下是一个 go.mod 文件定义 github.com/my/thing 模块的示例:
1 | module github.com/my/thing |
有四个指令:module(模块)
, require(依赖)
, replace(代替)
, exclude(排除)
。
模块通过模块指令在其 go. mod
中声明其标识, 该指令提供模块路径。
模块中所有包的导入(import)路径将模块路径共享为通用前缀。
模块路径和从 go. mod
到包目录的相对路径共同确定了包的导入(import)路径。
例如,如果你创建一个叫 github.com/my/repo
的模块,
它包含了两个包,它们的导入(import)路径分别是 github.com/my/repo/foo
和 github.com/my/repo/bar
,
那么通常在 go.mod
文件中的第一行声明你的模块路径,如 module github.com/my/repo
,相应在硬盘上的文件结构如下:
1 | repo/ |
在 Go 源码中,包的导入是使用模块路径的完整路径。
例如,如果一个模块在它的go.mod声明了 module example.com/my/module
,那么使用者则:
1 | import "example.com/my/module/mypkg" |
导入的包 mypkg
来自模块 example.com/my/module
。
exclude(排除)
和 replace(代替)
指令仅作用于当前主模块中。
如果 exclude(排除)
和 replace(代替)
在其他模块中,那么构建(build)主模块时,会忽略它们。
因此, exclude(排除)
和 replace(代替)
语句允许主模块完全控制自己的生成, 而不完全受依赖关系的控制。
具体详情,请看 官方例子
版本选择
如果你在你的源码中添加了一行新的 import
,那么在 go.mod
的 require
指令处中不会立刻覆盖。
大部分 go 命令行,如 go build
和 go test
会自动查找准确的模块,
并添加它的最高版本作为直接依赖项添加到你的模块 go.mod
文件中 require
指令处。
如果你的模块依赖 模块A 和 模块B,
其中 模块A 有 require D v1.0.0
,而 模块B 有 require D v1.1.1
,
那么根据最小版本选择 算法,在构建(build)你的模块的时候会选择 D v1.1.1
。
如何准备发布
运行 go mod tidy 删减无关的依赖 和 确保你的 go.mod 刷新所有可能用于构建 tags/OS/架构组合。
运行 go test all 测试模块, 以验证当前选定的包版本是否兼容。
确保你的 go.mod 和 go.sum 一起提交到版本管理系统中(svn,git)
结束语
本来想尝试翻译官方文档,但发现自己战五渣英语水平好吃力,所以选择只翻译重点的。
关于怎么将之前的版本依赖迁移到 Module 机制上 和 FAQ,请看 go modules 官方文档