go mod 介绍
参考文档:
go mod 使用 https://juejin.im/post/5c8e503a6fb9a070d878184a
Go Modules https://github.com/golang/go/wiki/Modules
go modules
是 golang 1.11 新加的特性。现在1.12 已经发布了,是时候用起来了。Modules官方定义为:
模块是相关Go包的集合。modules是源代码交换和版本控制的单元。 go命令直接支持使用modules,包括记录和解析对其他模块的依赖性。modules替换旧的基于GOPATH的方法来指定在给定构建中使用哪些源文件。
如何使用 Modules ?
准备:
- 把 golang 升级到 1.11(现在1.12 已经发布了,建议使用1.12)
- 设置 GO111MODULE
GO111MODULE的三个值:off
, on
和auto(默认值)
。
-
GO111MODULE=off
,go命令行将不会支持module功能,寻找依赖包的方式将会沿用旧版本那种通过vendor目录或者GOPATH模式来查找。 -
GO111MODULE=on
,go命令行会使用modules,而一点也不会去GOPATH目录下查找。 -
GO111MODULE=auto
,默认值,go命令行将会根据当前目录来决定是否启用module功能。这种情况下可以分为两种情形:- 当前目录在GOPATH/src之外且该目录包含go.mod文件
- 当前文件在包含go.mod文件的目录下面。
当modules 功能启用时,依赖包的存放位置变更为$GOPATH/pkg
,允许同一个package多个版本并存,且多个项目可以共享缓存的 module。
go mod 命令
golang 提供了 go mod
命令来管理包。
go mod 有以下命令:
命令 | 说明 |
---|---|
download | download modules to local cache(下载依赖包) |
edit | edit go.mod from tools or scripts(编辑go.mod |
graph | print module requirement graph (打印模块依赖图) |
init | initialize new module in current directory(在当前目录初始化mod) |
tidy | add missing and remove unused modules(拉取缺少的模块,移除不用的模块) |
vendor | make vendored copy of dependencies(将依赖复制到vendor下) |
verify | verify dependencies have expected content (验证依赖是否正确) |
why | explain why packages or modules are needed(解释为什么需要依赖) |
go mod 使用
示例一:创建一个新项目
在GOPATH 目录之外
新建一个目录,并使用go mod init
初始化生成go.mod
文件
命令:
➜ ~ mkdir hello
➜ ~ cd hello
➜ hello go mod init hello
go: creating new go.mod: module hello
➜ hello ls
go.mod
➜ hello cat go.mod
module hello
go 1.19
go.mod文件一旦创建后,它的内容将会被go toolchain全面掌控。go toolchain会在各类命令执行时,比如go get、go build、go mod等修改和维护go.mod文件。
go.mod 提供了module
, require
、replace
和exclude
四个命令:
- module 语句指定包的名字(路径)
- require 语句指定的依赖项模块
- replace 语句可以替换依赖项模块
- exclude 语句可以忽略依赖项模块
获取依赖
新建一个 server.go 文件,写入以下代码:
package main
import (
"net/http"
"github.com/labstack/echo"
)
func main() {
e := echo.New()
e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World!")
})
e.Logger.Fatal(e.Start(":1323"))
}
获取依赖方法
- 方法1:
go mod tidy
- 方法2:
go get github.com/labstack/echo
执行 go run server.go
运行代码会发现 go mod 会自动查找依赖自动下载:
go get
方式输出:
go get github.com/labstack/echo
输出:
go: downloading github.com/labstack/echo v3.3.10+incompatible
go: downloading github.com/labstack/gommon v0.4.0
go: downloading golang.org/x/crypto v0.10.0
go: downloading github.com/mattn/go-isatty v0.0.14
go: downloading github.com/mattn/go-colorable v0.1.11
go: downloading github.com/valyala/fasttemplate v1.2.1
go: downloading golang.org/x/net v0.10.0
go: downloading golang.org/x/sys v0.9.0
go: downloading github.com/valyala/bytebufferpool v1.0.0
go: downloading golang.org/x/text v0.10.0
go: added github.com/labstack/echo v3.3.10+incompatible
go: added github.com/labstack/gommon v0.4.0
go: added github.com/mattn/go-colorable v0.1.11
go: added github.com/mattn/go-isatty v0.0.14
go: added github.com/valyala/bytebufferpool v1.0.0
go: added github.com/valyala/fasttemplate v1.2.1
go: added golang.org/x/crypto v0.10.0
go: added golang.org/x/net v0.10.0
go: added golang.org/x/sys v0.9.0
go: added golang.org/x/text v0.10.0
依赖会被写入go.mod
并生成go.sum
文件。
go.mod
module hello
go 1.19
require (
github.com/labstack/echo v3.3.10+incompatible // indirect
github.com/labstack/gommon v0.4.0 // indirect
github.com/mattn/go-colorable v0.1.11 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.1 // indirect
golang.org/x/crypto v0.10.0 // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/sys v0.9.0 // indirect
golang.org/x/text v0.10.0 // indirect
)
go module 安装 package 的原則是: 先拉最新的 release tag,若无tag则拉最新的commit.
go 会自动生成一个 go.sum 文件来记录 dependency tree:
cat go.sum
输出:
github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
github.com/labstack/gommon v0.2.8 h1:JvRqmeZcfrHC5u6uVleB4NxxNbzx6gpbJiQknDbKQu0=
github.com/labstack/gommon v0.2.8/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4=
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
... 省略很多行
可以使用命令 go list -m -u all
来检查可以升级的package,使用go get -u <need-upgrade-package>
升级后会将新的依赖版本更新到go.mod
也可以使用 go get -u
升级所有依赖.
go get 升级
- 运行 go get -u 将会升级到最新的次要版本或者修订版本(x.y.z, z是修订版本号, y是次要版本号)
- 运行 go get -u=patch 将会升级到最新的修订版本
- 运行 go get package@version 将会升级到指定的版本号version
- 运行go get如果有版本的更改,那么go.mod文件也会更改
示例二:改造现有项目(helloword)
代码结构
项目目录为:
tree
.
├── api
│ └── apis.go
└── server.go
1 directory, 2 files
server.go 源码为:
package main
import (
api "./api" // 这里使用的是相对路径
"github.com/labstack/echo"
)
func main() {
e := echo.New()
e.GET("/", api.HelloWorld)
e.Logger.Fatal(e.Start(":1323"))
}
api/apis.go 源码为:
package api
import (
"net/http"
"github.com/labstack/echo"
)
func HelloWorld(c echo.Context) error {
return c.JSON(http.StatusOK, "hello world")
}
go mod 获取依赖
生成go.mod文件
mod init hello
输出:
go: creating new go.mod: module hello
go: to add module requirements and sums:
go mod tidy
获取依赖:
go mod tidy
报下面错误:
go: downloading github.com/pmezard/go-difflib v1.0.0
go: downloading gopkg.in/yaml.v3 v3.0.1
hello imports
./api: "./api" is relative, but relative import paths are not supported in module mode
那为什么会抛出这个错误呢?
这是因为 server.go 中使用 internal package 的方法跟以前已经不同了,由于 go.mod会扫描同工作目录下所有 package 并且
变更引入方法
,必须将hello
当成路径的前缀,也就是需要写成import hello/api
,以往 GOPATH/dep 模式允许的import ./api
已经失效,详情可以查看这个issue(https://github.com/golang/go/issues/26645
).
server.go 更改后代码:
package main
import (
api "helloworld/api" // 这是更新后的引入方法
"github.com/labstack/echo"
)
func main() {
e := echo.New()
e.GET("/", api.HelloWorld)
e.Logger.Fatal(e.Start(":1323"))
}
小坑
一个小坑:开始在golang1.11 下使用go mod 遇到过 go build github.com/valyala/fasttemplate: module requires go 1.12 这种错误,遇到类似这种需要升级到1.12 的问题,直接升级golang1.12 就好了。幸亏是在1.12 发布后才尝试的go mod.
到这里就和新创建一个项目没什么区别了。
获取依赖方法
使用replace替换无法直接获取的package
由于某些已知的原因,并不是所有的package都能成功下载,比如:golang.org
下的包。
modules 可以通过在 go.mod 文件中使用 replace 指令替换成github上对应的库,比如:
replace (
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a => github.com/golang/crypto v0.0.0-20190313024323-a1f597ede03a
)
或者
replace golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a => github.com/golang/crypto v0.0.0-20190313024323-a1f597ede03a
报错解决
执行go mod tidy
报错: hash/maphash: malformed module path "hash/maphash": missing dot in first path element
参考文档: https://github.com/golang/go/issues/40067
报错信息:
WL_Baby/src/github.com/wanglu119# go mod tidy
输出:
github.com/wanglu119/me-deps/db/orm imports
xorm.io/xorm imports
xorm.io/xorm/core tested by
xorm.io/xorm/core.test imports
modernc.org/sqlite imports
modernc.org/ccgo/v3/lib imports
hash/maphash: malformed module path "hash/maphash": missing dot in first path element
解决方法:
将go版本由1.13.1升级到1.14.1。
go mod vendor
报错: should not have @version error
参考文档: https://stackoverflow.com/questions/72009364/go-mod-vendor-should-not-have-version-error
报错信息:
$ go mod vendor
bitbucket.org/jigspace/jigpipeline/lib/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew: import path "bitbucket.org/jigspace/jigpipeline/lib/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew" should not have @version
bitbucket.org/jigspace/jigpipeline/lib/go/pkg/mod/github.com/dustin/go-humanize@v1.0.0: import path "bitbucket.org/jigspace/jigpipeline/lib/go/pkg/mod/github.com/dustin/go-humanize@v1.0.0" should not have @version
bitbucket.org/jigspace/jigpipeline/lib/go/pkg/mod/github.com/dustin/go-humanize@v1.0.0/english: import path "bitbucket.org/jigspace/jigpipeline/lib/go/pkg/mod/github.com/dustin/go-humanize@v1.0.0/english" should not have @version
In my case, the error occur after upgrade a old project to go 1.19
.
Here is what I did to fix it:
- First delete
pkg/
dir of project, if any. - Then run cmd:
go clean -modcache go mod tidy