GoLang 新版包管理 -- go module 的使用

2019-11-25 18:48:22   最后更新: 2019-11-25 20:13:59   访问数量:560




上一篇文章中,我们介绍了 GoLang 中包的使用与包管理机制

GoLang 包的使用与管理

 

从 GoLang 1.11 版本开始,官方推出了一个崭新的包管理工具 -- go module,随着 GoLang 1.13 版本的发布,go module 默认开启,官方开始强推 go module,使用 go module 作为包管理工具已经成为了官方倡导的趋势

本文,我们就来详细介绍一下 go module 的使用

 

 

模块

go module 的设计思想是改变原有的基于仓库管理 golang 包的模式,而是通过更加灵活的“模块”来进行包管理,每个包含 go.mod 文件的目录都是一个模块,从而实现:

  1. 一个仓库包含一个或者多个模块
  2. 一个模块包含一个或多个包
  3. 一个包包含该目录下的所有 go 源码文件

 

go.mod

正如上面所说,go.mod 文件是 go module 模式的关键,他定义了模块的依赖关系

go.mod 拥有四个指令 `module`, `require`, `replace`, `exclude`:

  • module -- 用来定义当前模块名称
  • require -- 描述依赖信息
  • replace -- 用于用指定依赖(后者)去代替前者,比如用 github 的仓库项目替代 goalng.org
  • exclude -- 用来忽略某些特定版本

 

下面是一个 go.mod 的例子:

module github.com/hilaily/test require ( github.com/gopkg/go-client v0.0.0-20170930090034-2628b1bfb590 // indirect github.com/gin/ginex v1.3.0 github.com/gopkg/consul v1.0.4 ) replace ( github.com/test/a v0.0.0-20190603160528-e2879d2603b8 => github.com/test/a v0.0.0-20190704135338-bf0b0a45db46 github.com/test/a v0.0.0-20190611023130-5ee11b162b30 => github.com/test/a v0.0.0-20190704135338-bf0b0a45db46 )

 

 

go module 的版本通过仓库的 tag 来确定

在 $GOPATH/pkg/mod 目录下,通过分层的目录结构实现包的管理,在最深一层,通过包最后一段名称@version 来实现包版本的管理

事实上,在实际的开发中,master 上的重要版本都打上 tag 是一个好习惯

如果没有 tag,go 会使用 v0.0.0-<time>-<commit id> 的形式记录版本

 

Semantic Versioning 2.0.0 协议

GoLang 模块版本号遵循 Semantic Versioning 2.0.0 协议:

  • 所有版本号都以 v 开头 -- v<major>.<minor>.<patch>
  • major -- 主版本号,当与之前版本不兼容时提升
  • minor -- 次版本号,发布向下兼容的新 feature 时提升
  • patch -- 修订版本号,发布 bug fix 时提升

 

测试版与正式版之间的兼容性

需要注意的是,v0.x.x、v1.x.x 与 v2 以上版本具有不同的特性:

  1. v0.x.x -- 测试版本,不要求小版本间需要保证兼容性
  2. v1.x.x -- 首个正式版本,从 v0 升级到 v1 版本引用路径无需变更
  3. 当主版本号大于等于 v2 时,这个模块的引用路径末尾必须加上 vN

 

v2 以上版本引入路径的变化

关于第三点,下面是不同场景下包引入路径的变化:

  • 在 go.mod 文件中:  module github.com/my/mod/v2
  • 在 require 的时候: require github.com/my/mod/v2 v2.0.0
  • 在 import 的时候:  import "github.com/my/mod/v2/mypkg"

 

如果将一个包从 v1.x.x 提升到 v2 以上版本,所有引入路径都需要修改,这是一个工作量很大且十分容易出错的工作,可以使用开源工具来实现包版本的变更:

https://github.com/marwan-at-work/mod

 

+incompatible

有些包在 go.mod 中,版本号显示的是:

v3.2.1+incompatible

 

+incompatible 表示该依赖打了 tag,但这个仓库不是一个 go module

 

开启 go module

通过设置环境变量 $GO111MODULE=on 可以打开 go module,默认是 auto,指的是如果项目不在 $GOPATH 下,则开启 go module

从 GoLang 1.13 开始,默认的 auto 意味着,只要当前目录具有 go.mod,那么无论项目在哪里,都开启 go module

 

相关环境变量

代理

开启 go module 以后,通过 $GOPROXY 环境变量可以设置代理服务器,例如:

export GOPROXY="https://goproxy.cn,direct"

 

不使用代理的仓库

GOPRIVATE 与 GONOPROXY 两个环境变量是一样的, 他们定义了哪些仓库是不需要经过代理的,例如:

export GOPRIVATE="*.corp.example.com,rsc.io/private"

 

初始化

对于新项目或已有的非 go module 管理的项目,可以执行下面的命令来进行初始化:

go mod init <packagename>

 

packagename 是模块的引入路径,例如 github.com/pkg/test 等

初始化完成后,在当前目录会自动生成 go.mod 文件,里面只有一行文本:

module github.com/pkg/test

 

 

添加依赖

手动添加

你可以参考上文,在 go.mod 中手动添加依赖的模块

通过下面的命令,可以将 go.mod 中声明的包下载到本地:

go mod download

 

go get 获取

也可以通过执行 go get 命令获取依赖:

go get github.com/pkg/xxx@v1.0.1

 

@v1.0.1 指定了版本,如果不指定则会自动寻找 v1 版本以下的最新版本

同时,你也可以指定 @latest 来要求 golang 自动更新到最新版本

你也可以换成指定的 commit_id:

  • gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7
  • gopkg.in/vmihailenco/msgpack.v2 v2.9.1
  • gopkg.in/yaml.v2 <=v2.2.1
  • github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e
  • go get github.com/pkg/xxx@latest

 

例如:

go get -u github.com/pkg/xxx@v1.2.1

 

自动拉取依赖

完成代码的编写后,执行 go build 或 go test 命令,相应工具会自动拉取依赖

 

依赖整理

在编写代码的过程中,往往会在 go.mod 文件中添加过多的依赖,golang 也提供了相应工具来整理依赖,增加丢失的依赖,删除无用依赖:

go mod tidy

 

升级依赖

通过命令 go get 增加 -u 参数,可以将指定包升级到指定版本:

  • 运行 go get -u 将会升级到最新的次要版本或者修订版本(x.y.z, z是修订版本号, y是次要版本号)
  • 运行 go get -u=patch 将会升级到最新的修订版本
  • 运行 go get package@version 将会升级到指定的版本号version

 

他会把 go.mod 的 latest 版本换成实际的最新的版本,并且会生成一个 go.sum 记录每个依赖库的版本和哈希值

 

依赖查询

go list all

通过 go list all 命令可以列出项目依赖的所有包

 

go mod why

go module 支持使用 go mod why 的模式查询项目为什么需要某个依赖,例如:

go mod why -m github.com/pkg/test

 

会输出从当前项目到 github.com/pkg/test 这个模块的最短依赖链

 

go mod graph

go mod graph 输出结果有两列,前者是一个库,后者是它的依赖

通过这个输出,我们可以绘制整个项目依赖的有向无环图

 

欢迎关注微信公众号,以技术为主,涉及历史、人文等多领域的学习与感悟,每周三到七篇推文,只有全部原创,只有干货没有鸡汤

 

 

https://github.com/golang/go/wiki/Modules#is-it-possible-to-add-a-module-to-a-multi-module-repository

https://semver.org/

https://colobu.com/2018/08/27/learn-go-module/

https://roberto.selbach.ca/intro-to-go-modules/

https://colobu.com/2019/09/23/review-go-module-again/

 






技术贴      module      package      golang      version      包管理      依赖管理     


京ICP备15018585号