从Go 1.16 开始, module-aware 成为了默认模式,这表示 GO111MODULE 默认值为on. 换句话说就是,即使GO111MODULE不设置,也是使用 module-aware模式。 其实go mod从Go1.11就开始加入, Go1.13有重大变化,直到Go1.16成为默认,完全取代GOPATH模式。历史变化,见下表:
GO111MODULE | < Go 1.13 | Go 1.13 | Go 1.16 |
---|---|---|---|
on | 任何路径下都开启module模式 | 任何路径下都开启module模式 | (默认)任何路径下都开启module模式 |
auto | (默认)源码位置不在$GOPATH/src 目录下,并且包含go.mod 文件,则使用module 模式,否则使用GOPATH模式。 | (默认)当前目录或者父目录包含go.mod文件时,开启module模式,无论源码是否在$GOPATH/src目录外面 | 当前目录或者父目录包含go.mod文件时,开启module模式,无论源码是否在$GOPATH/src目录外面 |
off | GOPATH模式 | GOPATH模式 | GOPATH模式 |
本文基于Go1.16.6 版本, 我们从一个示例展开,该示例调用了semver 包,对semver语义化版本感兴趣的读者可以参考 https://semver.org/lang/zh-CN/ 本文略过。
package main
import (
"fmt"
"github.com/blang/semver"
)
func main() {
inRange, err := semver.ParseRange(">=4.3.0-0 <4.3.0-201912111117")
if err != nil {
panic(err)
}
version := semver.MustParse("4.3.0-201912060615")
fmt.Printf("within range: %t\n", inRange(version))
}
直接运行,报错,显示找不到semver包,没有go.mod 文件。
mac:blog jianzhang$ go run main.go
main.go:6:2: no required module provides package github.com/blang/semver: go.mod file not found in current directory or any parent directory; see 'go help modules'
mac:blog jianzhang$ go build main.go
main.go:6:2: no required module provides package github.com/blang/semver: go.mod file not found in current directory or any parent directory; see 'go help modules'
然后,我们使用go mod init 来添加go.mod 文件
mac:blog jianzhang$ pwd
/Users/jianzhang/blog
mac:blog jianzhang$ ls
main.go
mac:blog jianzhang$ go mod init blog
go: creating new go.mod: module blog
go: to add module requirements and sums:
go mod tidy
mac:blog jianzhang$ ls
go.mod main.go
然后运行,go run/build 竟然出错了,这表示这两个指令已经不再自动拉取依赖包到go.mod文件中了。
mac:blog jianzhang$ go run main.go
main.go:6:2: no required module provides package github.com/blang/semver; to add it:
go get github.com/blang/semver
mac:blog jianzhang$ go build main.go
main.go:6:2: no required module provides package github.com/blang/semver; to add it:
go get github.com/blang/semver
根据提示,我们可以使用 go mod tidy 或者 go get xxx 来获取所需的依赖包。 运行go mod tidy可以看到,所需的依赖包已经被下载到go.mod 文件中了。同时生成了一个go.sum文件, 该文件包含了依赖包的哈希值,保证你的项目依赖的模块不会遇到恶意代码、随机异常等原因导致的异常变化。该文件自动生成,不需要人为干预,略过。
mac:blog jianzhang$ go mod tidy
go: finding module for package github.com/blang/semver
go: found github.com/blang/semver in github.com/blang/semver v3.5.1+incompatible
mac:blog jianzhang$ ls
go.mod go.sum main.go
mac:blog jianzhang$ cat go.sum
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
我们来看下 go.mod 文件, 默认拉取最新版本,版本格式遵循semver语法,这个v3.5.1 表示的是该依赖包的tag version, 如果依赖包中并没有tag 版本,那么go mod 会自动设置该依赖包的版本为v0.0.0+git-commit-date+随机数, 例如 github.com/openshift/openshift-tests v0.0.0-20210916082130-4fca21c38ee6
mac:blog jianzhang$ cat go.mod
module blog
go 1.16
require github.com/blang/semver v3.5.1+incompatible
注意:只有源码直接依赖的包才会被记录到go.mod 文件中。
那么这些依赖包存储在哪里呢?
mac:blog jianzhang$ go env |grep GOMODCACHE
GOMODCACHE="/Users/jianzhang/pkg/mod"
这些依赖包会被存储到 GOMODCACHE, 而这个路径是根据GOPATH自动生成的。可以看到这个GOMODCACHE路径就是 $GOPATH/pkg/mod, 如果该路径不存在,会被自动创建。
mac:blog jianzhang$ go env |grep GOPATH
GOPATH="/Users/jianzhang"
我们来看下 该路径下存储的包:
mac:blog jianzhang$ ls /Users/jianzhang/pkg/mod
cache github.com
mac:blog jianzhang$ tree /Users/jianzhang/pkg/mod
/Users/jianzhang/pkg/mod
├── cache
│ ├── download
│ │ ├── github.com
│ │ │ └── blang
│ │ │ └── semver
│ │ │ └── @v
│ │ │ ├── list
│ │ │ ├── list.lock
│ │ │ ├── v1.1.0.lock
│ │ │ ├── v1.1.0.zip
│ │ │ ├── v1.1.0.ziphash
│ │ │ ├── v3.5.1+incompatible.info
│ │ │ ├── v3.5.1+incompatible.lock
│ │ │ ├── v3.5.1+incompatible.mod
│ │ │ ├── v3.5.1+incompatible.zip
│ │ │ └── v3.5.1+incompatible.ziphash
│ │ └── sumdb
│ │ └── sum.golang.org
│ │ ├── lookup
│ │ │ └── github.com
│ │ │ └── blang
│ │ │ ├── semver@v1.1.0
│ │ │ └── semver@v3.5.1+incompatible
│ │ └── tile
│ │ └── 8
│ │ ├── 0
│ │ │ ├── 005
│ │ │ ├── 268
│ │ │ └── x027
│ │ │ ├── 639
│ │ │ └── 643.p
│ │ │ └── 51
│ │ ├── 1
│ │ │ ├── 000
│ │ │ ├── 001
│ │ │ └── 107.p
│ │ │ └── 251
│ │ └── 2
│ │ └── 000.p
│ │ └── 107
│ └── lock
└── github.com
└── blang
├── semver@v1.1.0
│ ├── LICENSE
│ ├── README.md
│ ├── examples
│ │ └── main.go
│ ├── semver.go
│ ├── semver_test.go
│ ├── sql.go
│ └── sql_test.go
└── semver@v3.5.1+incompatible
├── LICENSE
├── README.md
├── examples
│ └── main.go
├── json.go
├── json_test.go
├── package.json
├── range.go
├── range_test.go
├── semver.go
├── semver_test.go
├── sort.go
├── sort_test.go
├── sql.go
└── sql_test.go
26 directories, 42 files
好的,恭喜读到这里的朋友,你们已经掌握了go mod的基本用法了,我们总结下:
Vendoring
一直以来,Goalng 核心团队追求“可重复性构建”,就是让不同用户拿到相同的代码,使用相同的依赖包,构建出相同的成果。按理说,module-aware模式已经实现了这一愿景,但为什么还要保留vendor呢?这其实是来自于社区的反馈:https://groups.google.com/g/golang-dev/c/FTMScX1fsYk/m/uEUSjBAHAwAJ ,主要原因如下:
这也是为什么目前很多项目仍然在使用 Vendor(go build -mod=vendor) 的原因。当使用Vendor 时,建议把vendor 目录提交到代码库中,虽然这会使代码库文件增多,体积变大,依赖包变动频繁,但如果不添加的话,就无法实现上面提到的这些优势,也就没有意义了。
那么在CI/CD 中,使用vendor 时,如何加快编译呢?敬请期待下一篇。提前祝大家国庆节假期愉快,不妨停下脚步,虚度时光~