内容简介:这篇文章是对 Go 官方依赖管理工具这里引用
这篇文章是对 Go 官方依赖管理工具 Go Modules
机制的一次初探,教会你如何使用 Go modules
。
一、准备
1. Go Modules 定义
这里引用 TonyBai老师 初窥Go module 一文的总结:
通常我们会在一个repo(仓库)中创建一组Go package,repo的路径比如: github.com/bigwhite/gocmpp
, 会作为go package的导入路径(import path),Go 1.11给这样的一组在同一repo下面的packages赋予了一个新的抽象概念: module,并启用一个新的文件go.mod记录module的元信息。
并且一个repo可以拥有多个module,如下:
图:single repo,single module
图:single monorepo,multiple modules
2. 准备 Go 语言环境
需要安装 Go 1.11 以上的版本,当前最新版 1.12.5
。
3. Go Modules 配置
Go modules 机制作为 1.11
才正式引入的特性,当前还有一个特性开关: GO111MODULE
,对应的值有三个: auto/on/off
,这会影响 Go 所有命令 依赖管理
的模式选择(即 GOPATH mode/module-aware mode
),这三个值的作用简要介绍如下:
1. off: GOPATH mode,和之前版本一样,默认查找vendor和GOPATH目录
2. on:module-aware mode,使用 go modules,将会忽略GOPATH目录,使用是go mod命令的缓存目录($GOPATH/pkg/mod)
3. auto:如果当前目录不在 $GOPATH 并且 当前目录(或者父目录)下有go.mod文件,则使用 GO111MODULE, 否则仍旧使用 GOPATH mode。
注意:当前版本的默认值是 auto
,从 Go 1.13
以后,将会默认设置为 on
,即 module-aware mode
。
二、实战
学习最好的方式就是实战!这里采用默认设置( GO111MODULE=auto
),将在 GOPATH
目录外创建一个新的工程( single Module
),使用 Go modules 机制,一步步教会你如何使用 Go modules。
-
创建 Module
以
Windows
为例(Linux
和Mac
同理),在我的系统中GOPATH=D:\go
,接下来就在GOPATH
之外的目录(比如D:\gotest
)创建工程:
$ mkdir testgomod $ cd testgomod
继而在当前目录写一个简单的 Go 程序 hello.go
:
package testgomod import "fmt" // Say hello func Hello(who string) string { return fmt.Sprintf("Hello, %s", who) }
程序创建完成,但这还不是一个 module
,需要使用 go mod
命令初始化成一个 module
:
$ go mod init github.com/bingohuang/testgomod go: creating new go.mod: module github.com/bingohuang/testgomod
在该目录下,会生成了一个新的文件 go.mod
:
module github.com/bingohuang/testgomod go 1.12
到此,我们的工程就是一个 go module
了,区别就在于是否有这个 go.mod
文件。
接下来可以将工程推送到远程仓库:
$ git init $ git add . $ git commit -m "init commit" $ git push -u origin master
到这步,其他人就可以使用这个 go package
,比如使用最常用的 go get
方式: go get -v github.com/bingohuang/testgomod
这会拉取 master
分支的最新代码,但实际上这么做是有隐患的,因为我们不知道代码作者是否会修改代码,造成使用上的不兼容,而且也不能很好的管理代码版本。
2. 使用 Module
有了 Go modules 机制,就可以很方便的解决以上该问题。
Go modules 的主要设计理念包括: Semantic Import Versioning 、 Minimal Version Selection 等,所以我们最好也对我们的 module
打上版本。
使用 git tag
的方式打版本,发布 1.0.0
:
$ git tag v1.0.0 $ git push --tags
这个将会在当前 commit 上,创建一个 tag v1.0.0
,并推送至远程仓库。
接下来,我们创建一个 Go 程序,使用上面我们推送的新包: github.com/bingohuang/testgomod
。
$ mkdir usegomod $ cd usegomod
package main import ( "fmt" "github.com/bingohuang/testgomod" ) func main() { fmt.Println(testgomod.Hello("bingohuang")) }
如果是以前,你通常需要通过 go get github.com/bingohuang/testgomod
来下载依赖包,不过有了 go modules
,将无需这么做,而且更加方便。
首先,同样使用 go mod
命令初始化代码工程,使其成为一个 Go Module:
go mod init github.com/bingohuang/usegomod
这样就生成一个我们熟知的文件 go.mod
,内容如下:
module github.com/bingohuang/usegomod go 1.12
接下来就是见证奇迹的时刻,使用原生 go build
命令编译该工程:
$ go build go: finding github.com/bingohuang/testgomod v1.0.0 go: downloading github.com/bingohuang/testgomod v1.0.0 go: extracting github.com/bingohuang/testgomod v1.0.0
可以看到,go 命令会自动的获取和下载远程三方包,此时再看 go.mod
文件,发现有了新的变化:
module github.com/bingohuang/usegomod go 1.12 require github.com/bingohuang/testgomod v1.0.0
并且还生成了一个新的文件: go.sum
,包含了所引用包的 hash
值,保证我们获取的是正确的版本和文件。
github.com/bingohuang/testgomod v1.0.0 h1:JdNLPaJoAvogFRBWAyyr5jrLAsKFv7axKYDOOeFUbOo= github.com/bingohuang/testgomod v1.0.0/go.mod h1:dSAc0893lV3VXfM/YX6n2s+lW3CxIXytDP//OgpmKFo=
同时,还可以使用 go list -m all
命令来列出当前的 module 信息和它的依赖包:
$ go list -m all github.com/bingohuang/usegomod github.com/bingohuang/testgomod v1.0.0
3. 更新 Module
A、小更新
假设我们需要修复点小 bug,更新这个库: github.com/bingohuang/testgomod
, git diff
如下:
+// Say hello func Hello(who string) string { - return fmt.Sprintf("Hello, %s", who) + return fmt.Sprintf("Hello, %s!", who) }
并发布新版 v1.0.1
:
$ git commit -m "update package" hello.go $ git tag v1.0.1 $ git push --tags origin master
接下来,我们该如何在使用该包的地方( github.com/bingohuang/usegomod
)做更新呢?
在此之前,先补充一个知识点:默认情况下,Go modules 是不会自动更新的,需要我们主动更新包依赖,同样还是使用 go get
,有三种更新方式:
go get -u go get -u=patch go get package@version
对我们要从 v1.0.0
更新到 v1.0.1
来说,以下几种更新方式都可行:
$ go get -u $ go get -u=patch $ go get github.com/bingohuang/testgomod@v1.0.1
运行之后, go.mod
和 go.sum
都会更新,其中 go.mod
关键变动如下:
require github.com/bingohuang/testgomod v1.0.1
B、大变动
再回到我们引用的包: github.com/bingohuang/testgomod
,此时,我们需要做一个大的变更,甚至会影响到函数签名,比如给函数添加参数和返回值:
// Say hello func Hello(who, lang string) (string, error) { switch lang { case "en": return fmt.Sprintf("Hello, %s!", who), nil cse "cn": return fmt.Sprintf("你好, %s!", who), nil default: return "", errors.New("unknown language") } }
这样的改动,将导致新的 API
不兼容之前版本,所以包的版本需要升级到 v2.0.0
。
为了给使用我们 package
的程序做好兼容,避免直接出现不兼容错误,需要在我们的 module
名称上加上一个版本路径,比如 V2
:
github.com/bingohuang/testgomod/v2
接下来还是和之前一样,提交、打tag 并 push:
$ git commit hello.go -m"change func Hello" $ git commit go.mod -m "Bump version to v2" $ git tag v2.0.0 $ git push --tags origin master
此时,对于其它使用该包的程序,比如: github.com/bingohuang/usegomod
,还是能正常运行,因为我们一直会使用 v1.0.1
,即使使用 go get -u
也不会更新到 v2.0.0
。
如果,我们就是想将 gotestmod
库升级到最新的 v2.0.0
版本,该如何做呢?
很简单,修改引入的 package
和 函数
即可:
package main import ( "fmt" "github.com/bingohuang/testgomod/v2" ) func main() { hi, err := testgomod.Hello("bingohuang", "cn") if err != nil { panic(err) } fmt.Println(hi) }
注意: github.com/bingohuang/testgomod/v2
虽然是以 v2 结尾,但是 Go 引用的包名还是 testgomod
,非常方便。
这时,我们再运行 go build 或者 go run . ,将会自动为我们拉去 v2.0.0
版本:
$ go run . go: downloading github.com/bingohuang/testgomod/v2 v2.0.0 go: extracting github.com/bingohuang/testgomod/v2 v2.0.0 你好, bingohuang!
并且 go.mod
和 go.sum
都有所更新,其中 go.mod
关键变动如下:
require ( github.com/bingohuang/testgomod v1.0.1 github.com/bingohuang/testgomod/v2 v2.0.0 )
甚至,我们还可以同时使用这两个不兼容版本,如下:
package main import ( "fmt" "github.com/bingohuang/testgomod" testgomodV2 "github.com/bingohuang/testgomod/v2" ) func main() { fmt.Println(testgomod.Hello("bingohuang")) hi, err := testgomodV2.Hello("bingohuang", "cn") if err != nil { panic(err) } fmt.Println(hi) }
这个方式可以用来做升级过渡或调试。
4. 清理 Module
回到上文,我们修改了引用 package
路径,仅使用 v2.0.0
版本的情况,其中 go.mod 的关键变动如下:
require ( github.com/bingohuang/testgomod v1.0.1 github.com/bingohuang/testgomod/v2 v2.0.0 )
可以看到,这里未被使用的 v1.0.1
版本并未自动移除,Go 给我们提供了一个命令来主动移除不用的依赖,如下:
$ go mod tidy
5. Vender 机制
Go modules 机制会忽略 vendor 目录,甚至在最初的设计中, Go team
有想彻底废除 vendor
,但 vendor
毕竟使用多年,影响很大,在社区的反馈下,当前得以保留,并且 Go modules 支持将该 module 下所有的依赖都 copy 一份到 module 根目录
的 vendor目录
下,命令同样很简单:
$ go mod vendor
这样,如果你不在 module-aware 模式下,就可以使用 vendor 目录来编译:
$ go build
即使在 module-aware
模式下,也可以通过如下命令来使用 vendor 目录构建:
$ go build -mod vendor
3. 总结:
Go Modules 机制作为官方正式推出的包依赖管理机制,必定会步入大众的舞台,成为包管理 工具 的主导工具,并且会越来越完善,在此极力推荐大家学习使用。
最后,总结几条好的工程实践:
- 通常我们在
GOPATH
目录下开发居多,在当前版本下想使用 module-aware 模式,只需打开特性开关:export GO111MODULE=auto
- 配合
GOPROXY
,可以达到更好的效果,比如无需配置个人代理了,就可以方便下载任何 Go 第三方代码。比如可以采用开源工具 athens 搭建了一个Go Proxy 服务
,简要使用方法如下:
# 开启 Go Modules 机制 $ export GO111MODULE=on # 配置 Go Proxy $ export GOPROXY=http://go_proxy_server # 进入工程目录 $ cd 工程目录 # 初始化 Go Module $ go mod init 你的module名 # 使用 go 相关命令即可,包括 go build/go run/go test/go get 等 $ go build
- JetBrain 推出的 Go IDE
Goland
也有对 Go modules 的集成支持,可以参考这篇文章: Working with Go Modules ,可能会获得一些有趣的tips
。
参考资料
以上所述就是小编给大家介绍的《Go Modules 初探》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
JS 压缩/解压工具
在线压缩/解压 JS 代码
Base64 编码/解码
Base64 编码/解码