Go的包管理工具(三):Go Modules

在前面的文章,咱们先是介绍了Go 的几种包管理方式,而后具体介绍了一种包管理的工具: glide。随着 Go 1.11 的发布,官方的包管理工具 Go Modules 变得流行起来。在发布不久的 Go 1.12 版本中,加强了对 Go Modules 的支持。本文将会介绍如何在项目中安装和使用 Go Moduleshtml

安装和激活 Modules 的支持

前置条件

如本文开头所说,从 Go 1.11 版本才支持 Go Modules。因此,默认 Go 的版本为 >= 1.11。git

$ go version
go version go1.12 darwin/amd64
复制代码

笔者安装了最新的 1.12 版本。github

激活使用

安装后,咱们能够经过如下两种方式之一激活模块支持:golang

  • $GOPATH/src 以外的目录中调用 go 命令,且当前目录或其任何父目录中使用有效的 go.mod 文件,而且环境变量 GO111MODULE 未设置(或显式设置为auto)。
  • 在环境变量集上设置 GO111MODULE = on 后,调用go命令。

如何定义模块

为当前的项目建立一个 go.mod 文件。缓存

当项目不在 GOPATH 中,直接执行:bash

go mod init
复制代码

不然,会出现以下的错误:微信

go: modules disabled inside GOPATH/src by GO111MODULE=auto; see 'go help modules'
复制代码

所以,咱们须要手动激活 Modules:ide

$ export GO111MODULE=on 
复制代码

而后才能执行 go mod init。这会将任何现有的dep Gopkg.lock 文件或其余九种支持的依赖关系转换,添加 require 语句以匹配现有配置。工具

go mod init一般可以使用辅助数据(例如VCS元数据)来自动肯定相应的模块路径,可是若是 go mod init 代表它不能自动肯定模块路径,或者若是你须要以其余方式覆盖 path,你能够提供模块路径做为 go mod init 的可选参数,例如:测试

$ go mod init modtest
复制代码

构建模块

从模块的根目录执行时,./... 模式匹配当前模块中的全部包。 go build 将根据须要自动添加缺失或未转换的依赖项,以知足此特定构建调用的导入:

$ go build ./...
复制代码

测试模块

$ go test ./...
复制代码

按配置测试模块,以确保它适用于所选版本。还能够运行模块的测试以及全部直接和间接依赖项的测试以检查不兼容性:

$ go test all
复制代码

实战

建立项目

建立项目并进入根目录:

$ mkdir src/hello
$ cd src/hello
复制代码

初始化

$ go mod init github.com/keets2012/hello
go: creating new go.mod: module github.com/keets2012/hello
复制代码

go mod 初始化,并命名包名为 github.com/keets2012/hello。能够看到,一块儿建立了 go.mod 文件。

实现一个简单的方法

$ cat <<EOF > hello.go
package main

import (
    "fmt"
    "rsc.io/quote"
)

func main() {
    fmt.Println(quote.Hello())
}
EOF
复制代码

咱们建立了一个 hello.go 的文件,并输出调用的方法结果。

构建执行

$ go build   # 构建可执行文件
$ ./hello  # 执行

Hello, world.  # 输出结果
复制代码

执行构建以后,获得可执行文件,咱们执行了获得结果。 go.mod 文件已更新为包含依赖项的显式版本,其中 v1.5.2semver 标记:

$ cat go.mod

module github.com/keets2012/hello

require rsc.io/quote v1.5.2
复制代码

升级和降级依赖

应该使用 go get 来完成平常升级和降级依赖项,这将自动更新 go.mod 文件。 或者能够直接编辑 go.mod

此外,像 go buildgo test 或甚至 go list 这样的命令会根据须要自动添加新的依赖项以知足导入。

要查看全部直接和间接依赖项的可用 minor 和 patch 程序升级:

go list -u -m all
复制代码

要升级到当前模块的全部直接和间接依赖关系的最新版本:

  • 运行 go get -u 以使用最新的次要版本或补丁版本
  • go -u = patch使用最新的补丁版本

要升级或降级到更具体的版本,go get 容许经过在 package 参数中添加@version 后缀或“模块查询”来覆盖版本选择,例如 go get foo@v1.6.2go get foo @ e3702bed2,或者 go foo @'<v1.6.2'

semver

在上一小节,咱们提到了 semver。golang 官方推荐的最佳实践叫作 semver,这是一个简称,写全了就是Semantic Versioning,也就是语义化版本。

语义化的定义

通俗地说,就是一种清晰可读的,明确反应版本信息的版本格式,更具体的规范在这里。

如规范所言,形如 vX.Y.Z 的形式显然比一串 hash 更直观,因此 golang 的开发者才会把目光集中于此。

为什么使用语义化版本

semver 简化版本指定的做用是显而易见的,然而仅此一条理由显然有点缺少说服力,经过semver对版本进行严格的约束,能够最大程度地保证向后兼容以及避免 “breaking changes”,而这些都是 golang 所追求的。二者一拍即合,因此 go modules 提供了语义化版本的支持。

若是你使用和发布的包没有版本 tag 或者处于 1.x 版本,那么你可能体会不到什么区别,由于 go mod 所支持的格式从始至终是遵循 semver 的,主要的区别体如今 v2.0.0 以及更高版本的包上。

“若是旧软件包和新软件包具备相同的导入路径,则新软件包必须向后兼容旧软件包。” - go modules wiki

相同名字的对象应该向后兼容,然而按照语义化版本的约定,当出现 v2.0.0 的时候必定表示发生了重大变化,极可能没法保证向后兼容,这时候应该如何处理呢?

答案很简单,咱们为包的导入路径的末尾附加版本信息便可,例如:

module my-module/v2

require (
  some/pkg/v2 v2.0.0
  some/pkg/v2/mod1 v2.0.0
  my/pkg/v3 v3.0.1
)
复制代码

格式总结为 pkgpath/vN,其中 N 是大于 1 的主要版本号。在代码里导入时也须要附带上这个版本信息,如import "some/pkg/v2"。这样包的导入路径发生了变化,也不用担忧名称相同的对象须要向后兼容的限制了,由于 golang 认为不一样的导入路径意味着不一样的包。固然还有意外的状况:

  • 当使用gopkg.in格式时可使用等价的require gopkg.in/some/pkg.v2 v2.0.0
  • 在版本信息后加上 +incompatible 就能够不须要指定 /vN ,例如:require some/pkg v2.0.0+incompatible

除此之外的状况若是直接使用 v2+ 版本将会致使 go mod 报错。

v2+ 版本的包容许和其余不一样大版本的包同时存在(前提是添加了/vN),它们将被当作不一样的包来处理。

另外 /vN 并不会影响你的仓库,不须要建立一个v2对应的仓库,这只是 go modules 添加的一种附加信息而已。

固然若是你不想遵循这一规范或者须要兼容现有代码,那么指定 +incompatible 会是一个合理的选择。不过 go modules 不推荐这种行为。

使用 vendor 目录

若是你不喜欢 go mod 的缓存方式,你可使用 go mod vendor 回到 godep 或 govendor 使用的 vendor 目录进行包管理的方式。

固然这个命令并不能让你从godep之类的工具迁移到 go modules,它只是单纯地把 go.sum 中的全部依赖下载到 vendor 目录里,若是你用它迁移 godep 你会发现 vendor 目录里的包会和 godep 指定的产生至关大的差别,因此请务必不要这样作。

使用 go build -mod=vendor 来构建项目,由于在 go modules 模式下 go build 是屏蔽 vendor 机制的,因此须要特定参数从新开启 vendor 机制:

go build -mod=vendor
./hello
hello world!
复制代码

构建成功。当发布时也只须要和使用 godep 同样将 vendor 目录带上便可。

总结

本文主要介绍了 go modules的一些特性和使用方法, go modules 是官方的包管理工具,Go 语言经过引入 module 的概念进而引入了 Go tool 的另一种工做模式 module-aware mode 。在新的工做模式下,module 支持包依赖的版本化管理。

新的工做模式也带来了一些问题,在大陆地区咱们没法直接经过 go get 命令获取到一些第三方包,这其中最多见的就是 golang.org/x 下面的各类优秀的包。一旦工做在模块下,go build 将再也不关心 GOPATH 或是 vendor 下的包,而是到 GOPATH/pkg/mod 查询是否有cache,若是没有,则会去下载某个版本的 module,而对于某些包的 module,在大陆地区每每会失败。咱们将在下篇文章介绍 go module 的 proxy 配置实现。

推荐阅读

Go的包管理工具

订阅最新文章,欢迎关注个人公众号

微信公众号

参考

  1. Modules docs
  2. 再探go modules:使用与细节
相关文章
相关标签/搜索