using go module
原文重点是:Go Module 不是简单替代 GOPATH,而是把“依赖版本”变成项目的一部分,由 go.mod 显式声明,由 go.sum 保证内容一致性。
1. Module 是什么
一个 module 是一组 Go packages 的集合。
它的根目录下有一个 go.mod 文件:
module example.com/hello
go 1.12
go.mod 主要记录两类信息:
- module path: 当前模块的路径,也就是项目根目录的 import path,如果需要 import 项目其他目录,就从这个 import path 开始。
- requirements: 构建当前模块所需的依赖模块及其版本
例如:
require rsc.io/quote v1.5.2
含义是:当前模块依赖 rsc.io/quote,版本固定为 v1.5.2。
2. 创建一个新 module
假设有一个包:
package hello
func Hello() string {
return "Hello, world."
}
初始化 module:
go mod init example.com/hello
生成:
module example.com/hello
go 1.12
此时当前目录就是 module root。
子目录不需要再执行 go mod init。例如:
example.com/hello/world
会自动被视为当前 module 下的 package。
核心点:
- 一个仓库/项目通常只有一个 go.mod,放在 module root。
- 子目录 package 的 import path = module path + 子目录路径。
3. 添加依赖
当代码中新增 import:
import "rsc.io/quote"
func Hello() string {
return quote.Hello()
}
执行:
go test
Go 会自动:
- 发现缺少 rsc.io/quote
- 查询包含该 package 的 module
- 下载合适版本
- 更新 go.mod
- 更新 go.sum
生成的 go.mod 可能是:
module example.com/hello
go 1.12
require rsc.io/quote v1.5.2
注意:go.mod 主要记录直接依赖。
从 Go 1.16 开始,自动下载行为改变了。
go build、go test 发现缺少依赖时,一般不会直接下载和修改 go.mod,而是报错并提示你执行类似命令:go get rsc.io/quote
查看完整依赖图:
go list -m all
输出示例:
example.com/hello
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
rsc.io/quote v1.5.2
rsc.io/sampler v1.3.0
第一行永远是当前 module,也叫 main module。
4. go.sum 的作用
go.sum 不是锁文件的简单等价物。
它记录的是:某个 module 版本内容的加密哈希。作用是防止依赖内容被意外篡改。
例如:
$ 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...
$
go 命令使用 go.sum 文件来确保后续下载这些模块时获取的代码与首次下载完全一致,从而避免项目依赖的模块因恶意、意外或其他原因发生意外变更。 go.mod 和 go.sum 都应纳入版本控制。
5. 升级依赖
Go Modules 使用语义化版本:
vMAJOR.MINOR.PATCH
例如:
v0.1.2
major = 0
minor = 1
patch = 2
升级到最新版本:
go get golang.org/x/text
等价于:
go get golang.org/x/text@latest
指定版本:
go get rsc.io/sampler@v1.3.1
查看某个 module 可用版本:
go list -m -versions rsc.io/sampler
重要点:
- go get 用来下载或改变依赖版本。
- 默认是 @latest。
- 但 latest 不一定适合你的项目,升级后必须跑回归测试。
6. indirect 依赖
go.mod 中可能出现:
require (
golang.org/x/text v0.3.0 // indirect
rsc.io/quote v1.5.2
)
// indirect 表示:当前 module 没有直接 import 它,它是被其他依赖间接引入的。
也就是:
- 你的代码直接依赖 A
- A 又依赖 B
那么 B 可能会以 indirect 的形式出现在 go.mod 中
7. 主版本(Major Version)升级:v2+ 必须进入 import path
这是 Go Modules 最关键的设计之一。
从 v2 开始,module path 必须带主版本号:
import "rsc.io/quote/v3"
而不是:
import "rsc.io/quote"
例如:
import (
"rsc.io/quote"
quoteV3 "rsc.io/quote/v3"
)
这意味着:
- rsc.io/quote 是 v1 系列
- rsc.io/quote/v3 是 v3 系列
它们是不同的 module path,可以同时存在。
这个设计叫:Semantic Import Versioning
核心意义是:不兼容的大版本,用不同 import path 表示。
好处是:一个项目可以渐进迁移。旧代码继续用 v1;新代码可以先引入 v3;等准备好后再逐步替换。
为什么只针对 Major Version?
因为不同 Major Version 可能出现对用户提供的 Api 的不兼容修改。而小版本的演进通常保证用户 Api 兼容.
8. 同一个 module path 只能有一个版本
Go 构建时允许:
rsc.io/quote v1.5.2
rsc.io/quote/v3 v3.1.0
因为它们的 module path 不同。
但不允许同一次构建里同时存在:
rsc.io/quote v1.5.2
rsc.io/quote v1.6.0
因为它们是同一个 module path。
核心规则:
- 同一个 module path,构建中最多只能选一个版本。
- 不同 major version,因为路径不同,所以可以并存。
9. 迁移到新 major version
假设原来:
import "rsc.io/quote"
func Hello() string {
return quote.Hello()
}
迁移到 v3:
import "rsc.io/quote/v3"
func Hello() string {
return quote.HelloV3()
}
大版本升级意味着 API 可能变了。
所以迁移步骤通常是:
- 引入新 major version
- 查看新版本文档
- 修改 import path
- 修改 API 调用
- 跑测试
- 清理旧依赖
10. 删除无用依赖
即使代码已经不再使用旧依赖,它可能仍然留在 go.mod 中。
原因是:
go build / go test 可以发现“缺少依赖”,但不能安全判断“哪些依赖可以删除”。
因为 go build / go test 只加载当前命令需要构建的 packages,因此能发现当前构建缺少依赖;
但它不会穷举整个 module 的所有 package、测试依赖、平台文件和 build tags,所以不能安全删除 go.mod 中看似无用的依赖。
清理命令:
go mod tidy
它会:
- 添加缺失依赖
- 删除无用依赖
- 整理 go.mod / go.sum
常用流程:
go mod tidy
go test ./...
11. 常用命令总结
| 命令 | 作用 |
|---|---|
go mod init <module-path> |
初始化 module,生成 go.mod |
go test / go build |
构建/测试,并在需要时自动添加缺失依赖 |
go list -m all |
查看当前 module 和完整依赖列表 |
go list -m -versions <module> |
查看某个 module 的可用版本 |
go get <module> |
添加或升级依赖到 latest |
go get <module>@<version> |
切换依赖到指定版本 |
go mod tidy |
清理无用依赖,补齐缺失依赖 |
评论