跳转至

学习Go Module 构建模式

概要: 全面拥抱 Go Module 构建模式

创建时间: 2022.08.24 21:30:37

更新时间: 2023.08.16 22:27:21

GOPATH 模式

下面假定使用的 go 语言版本为 1.18

Go
1
2
3
4
5
6
7
package main

import "github.com/sirupsen/logrus"

func main() {
    logrus.Println("hello, gopath mode")
}
当运行命令 go build main.go 时,Go 编译器在 GOPATH 构建模式下会搜索第三方包 github.com/sirupsen/logrus
路径搜索
如果配置了如下的 GOPATH
Bash
export GOPATH=/usr/local/golang:/home/lzwang/go
那么go编译器搜索第三方包的顺序为
Bash
/usr/local/golang/src/github.com/sirupsen/logrus
/home/lzwang/go/src/github.com/sirupsen/logrus

提示

GOPATH如果没有配置,默认路径为 $HOME/go

安装缺失的包

Bash
go get github.com/sirupsen/logrus
此时 go 将把第三方包安装到第一个 GOPATH 路径下,即 /usr/local/golang/src/github.com/sirupsen/logrus

弊端

go get 下载的包只是当前时刻各个依赖包的最新主线版本,也就是说不同人在不同时刻运行此命令获得的包可能并不完全一致,这样可能导致兼容性等方面的问题。

总结

在 GOPATH 构建模式下,Go 编译器实质上并没有关注 Go 项目所依赖的第三方包的版本。

vendor 模式

Go 在 1.5 版本中引入 vendor 机制。vendor 机制本质上就是在 Go 项目的某个特定目录下,将项目的所有依赖包缓存起来,这个特定目录名就是 vendor

对于上面一节的例子,添加vendor目录后,缓存第三方包后结构如下:
image.png

弊端

如果依赖的包很多,将导致自己的项目库体积显著变大,影响拉取等操作。此外更致命的是,手动管理vendor下的依赖包版本十分头疼,包括项目依赖包的分析、版本的记录、依赖包获取和存放。

总结

十分鸡肋

Go Module 模式

从 Go 1.11 版本开始,除了 GOPATH 构建模式外,Go 又增加了一种 Go Module 构建模式。

Go Module是什么

  • 一个 Go Module 是一个 Go 包的集合,它包含了包的版本号信息。
  • 通常情况下,一个代码仓库对应一个 Go Module ,即在项目的根目录下放置 go.mod 文件,Go Module 与 go.mod 是一一对应的。
  • go.mod 文件所在的顶层目录也被称为 module 的根目录,module 根目录以及它子目录下的所有 Go 包均归属于这个 Go Module,这个 module 也被称为 main module。

Go Module使用方法

  1. go mod init "my/pkg/name" 创建 go.mod 文件
  2. go mod tidy 自动更新当前项目的依赖包信息
  3. go build my_pkg.go 构建自己的module

下面以第一节的项目为例,在 go 1.18 环境下演示使用方法。

创建 go.mod

Bash
go mod init lzwang/mylog
image.png
image.png
查看 go.mod 内容如下
Bash
1
2
3
module lzwang/mylog

go 1.18

更新 go.mod

Bash
go mod tidy
image.png
再次查看 go.mod 内容如下
Bash
1
2
3
4
5
6
7
module lzwang/mylog

go 1.18

require github.com/sirupsen/logrus v1.9.0

require golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
其中第7行是第三包 github.com/sirupsen/logrus 的依赖包

构建 module

Bash
go build make_log.go
image.png

Go Module版本机制

go语言的版本号规则于Python类似,见下图
image.png
注意

  1. 按照语义版本规范,主版本号不同的两个版本是相互不兼容的
  2. 主版本号相同时,次版本号大都是向后兼容次版本号小的版本。补丁版本号也不影响兼容性

语义导入版本机制

不同主版本号的导入方式不同,对于 0.x.y1.x.y 的包,导入时不需要带上额外版本说明,高于1时,导入方式为 import "github.com/sirupsen/logrus/v2"

最小版本选择机制

如果自己的包同时依赖 A(v1.1.2) 和 B(v1.2.4) 两个包,且 A 包依赖 C(v1.3.0),B 包也依赖 C(v1.4.0),但 C 包最新版为 v1.8.0。

Go 设计者在诸多兼容性版本间,他们不光要考虑最新最大的稳定与安全,还要尊重各个 module 的述求:A 明明说只要求 C v1.1.0,B 明明说只要求 C v1.3.0。所以 Go 会在该项目依赖项的所有版本中,选出符合项目整体要求的“最小版本”。

所以,go会选择 C(v1.4.0),因为此版本是符合项目整体要求的版本集合中的版本最小的那个。

展望

Go 核心团队已经考虑在后续版本中彻底移除 GOPATH 构建模式,Go Module 构建模式将成为 Go 语言唯一的标准构建模式。

总结

抛弃 GOPATH 和 vendor 构建模式,全面转向 Go Module 构建模式。

参考