Total Pageviews

Tuesday, 13 October 2020

Golang Modules的用法

 Go Modules 是 golang 1.11 才新增加的一个特性,虽然还处于试验阶段,不过官方说明中表示后续的版本会持续保持对已发布功能的兼容,所以我们还是可以大胆地进行尝试……

快速上手

在 $GOPATH 路径之外创建一个目录,并初始化一个新的 module:

这样子就会生成一个 go.mod 文件,然后我们再写一段简单的代码:

执行 go build,生成可执行文件 hello,以及 go.sum 文件:

 查看 go.mod 和 go.sum 文件的内容,可以发现其中包含了依赖的包:

当我们执行 go build 或者 go get 命令时,项目所需要的依赖将会自动下载到 $GOPATH/pkg/mod/ 目录下(作为缓存,可被其他项目依赖使用),并更新 go.mod 和 go.sum。

当然,我们还可以指定特定的版本或者分支比如: go get foo@1.2.3 或 go get foo@master。此外,还有一些常用的命令

  • go list -m all:列出所有依赖的最终版本
  • go list -u -m all:列出可更新的依赖
  • go get -u:更新依赖
  • go build ./… 或 go test ./… :在项目根目录下执行,构建/测试 module 中 的包
  • go mod download:下载依赖到本地缓存
  • go mod tidy:增加需要的依赖,删除不需要的依赖
  • go mod vendor:把依赖复制到项目的 vendor 目录

基本概念

Modules

A module is a collection of related Go packages that are versioned together as a single unit.

  • 一个 repository 包含一个或多个 Go modules;
  • 每个 module 包含一个或多个 Go packages;
  • 每个 package 由单一目录下的一个或多个 Go 源文件 组成;

go.mod

一个 Module 由位于根目录的 go.mod 和一些 go 文件构成,module 可以放在 $GOPATH 目录之外。

如何使用

初始化

首先,需要安装 Go 1.11 以上版本,然后:

  • 在 $GOPATH/src 目录外,直接执行 go 命令(环境变量GO111MODULE无需设置,或设置为 auto);
  • 在 $GOPATH/src 目录下,设置 GO111MODULE=on,再执行 go 命令;

如果是在 $GOPATH/src 目录下,执行 go mod init 将会自动确定 module 的名字,不然就需要输入 module 名字 go mod init moduleName。

更新/回退

使用 go get 命令:

  • 执行 go get -u 更新最新版本(到 minor 或者 patch 版本)
  • 为指定的包选择指定的版本:
    • go get foo 相当于 go get foo@latest 
    • go get foo@1.2.3 
    • go get foo@e3702bed2
    • go get foo@'<1.6.2′

发布

大部分的步骤将会被未来的 go release 工具自动化执行,不过目前我们可以这么操作:

  • 执行 go mod tidy 移除不必要的依赖,因为 go build/test 并不会自动删除不再需要的依赖;
  • 执行 go test all 测试;
  • 把 go.sum 和 go.mod 加入到 vcs 中提交;

Vendor

前面我们提到了,如果启用了 Modules ($GOPATH/src 外或 GO111MODULE=on),默认的依赖都是下载到 $GOPATH/pkg/mod 目录下,我们也可以使用 go mod vendor 将依赖添加到项目根目录的 vendor 目录中。

不过,此时如果我们直接执行 go build 命令,那么使用仍然是 go.mod 描述的依赖( $GOPATH/pkg/mod )而非 vendor 中的依赖,我们需要指定 -mod=vendor 参数:

这样子 go 命令就可以忽略了 go.mod,而认为 vendor 目录包含了正确的依赖。

另外,如果指定了 -mod=readonly 参数,则 go build 过程中 go.mod 将不会被自动更新(如果 go.mod  需要被修改,那么构建就是失败)。这个参数常用于 CI 自动化构建,保证正确的依赖关系。

最后,注意 -mod 构建参数只对 go build 有效,而 go test 会将其忽略。

 

参考:

  • https://github.com/golang/go/wiki/Modules
  • https://roberto.selbach.ca/intro-to-go-modules/
  • https://www.youtube.com/watch?v=F8nrpe0XWRg&list=PLq2Nv-Sh8EbbIjQgDzapOFeVfv5bGOoPE&index=3&t=0s
--------------------------------------

跳出Go module的泥潭

说明: go module增在快速的成长中,最近每个版本中(1.11 ~ 1.13)都有很大的变动。 建议你阅读官方的wiki了解go module最新的知识: go/wiki/Modules

最新扩展阅读(go 1.13):Go module 再回顾

Go 1.11 前天已经正式发布了,这个版本包含了两个最重要的feature就是 moduleweb assembly。虽然也有一些简单的教程介绍了go module的特性,但是基本上都是hello world的例子,在实践的过程中, 很多人都在“拼命的挣扎”,包括我自己, 从一些qq群、github的issue, twitter上都可以看到大家茫然或者抱怨的语句。

虽然有三个帮助文件go help modgo help modulesgo help module-get可以了解一些go module的用法,但是感觉Go开发组对module这一特性还是没有很好的做一个全面的介绍,很多情况还得靠大家看源代码或者去猜,比如module下载的文件夹、版本格式的完整声明,module的最佳实践等,并且当前Go 1.11的实现中还有一些bug,给大家在使用的过程中带来了很大的困难。

我也在摸索中前行, 记录了摸索过程中的一些总结,希望能给还在挣扎中的Gopher一些帮助。

Introduction to Go Modules 是一篇很好的go module 入门介绍, 如果你仔细阅读了它,应该就不需要看本文了。

GO111MODULE

要使用go module,首先要设置GO111MODULE=on,这没什么可说的,如果没设置,执行命令的时候会有提示,这个大家应该都了解了。

既有项目

假设你已经有了一个go 项目, 比如在$GOPATH/github.com/smallnest/rpcx下, 你可以使用go mod init github.com/smallnest/rpcx在这个文件夹下创建一个空的go.mod (只有第一行 module github.com/smallnest/rpcx)。

然后你可以通过 go get ./...让它查找依赖,并记录在go.mod文件中(你还可以指定 -tags,这样可以把tags的依赖都查找到)。

通过go mod tidy也可以用来为go.mod增加丢失的依赖,删除不需要的依赖,但是我不确定它怎么处理tags

执行上面的命令会把go.modlatest版本换成实际的最新的版本,并且会生成一个go.sum记录每个依赖库的版本和哈希值。

新的项目

你可以在GOPATH之外创建新的项目。

go mod init packagename可以创建一个空的go.mod,然后你可以在其中增加require github.com/smallnest/rpcx latest依赖,或者像上面一样让go自动发现和维护。

go mod download可以下载所需要的依赖,但是依赖并不是下载到$GOPATH中,而是$GOPATH/pkg/mod中,多个项目可以共享缓存的module。

go mod命令

1
2
3
4
5
6
7
8
download download modules to local cache (下载依赖的module到本地cache))
edit edit go.mod from tools or scripts (编辑go.mod文件)
graph print module requirement graph (打印模块依赖图))
init initialize new module in current directory (再当前文件夹下初始化一个新的module, 创建go.mod文件))
tidy add missing and remove unused modules (增加丢失的module,去掉未用的module)
vendor make vendored copy of dependencies (将依赖复制到vendor下)
verify verify dependencies have expected content (校验依赖)
why explain why packages or modules are needed (解释为什么需要依赖)

有些命令还有bug, 比如go mod download -dir:

1
2
3
4
go mod download -dir /tmp
flag provided but not defined: -dir
usage: go mod download [-dir] [-json] [modules]
Run 'go help mod download' for details.

帮助里明明说可以设置dir,但是实际却不支持dir参数。

看这些命令的帮助已经比较容易了解命令的功能。

翻墙

在国内访问golang.org/x的各个包都需要翻墙,你可以在go.mod中使用replace替换成github上对应的库。

1
2
3
4
5
replace (
golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac => github.com/golang/crypto v0.0.0-20180820150726-614d502a4dac
golang.org/x/net v0.0.0-20180821023952-922f4815f713 => github.com/golang/net v0.0.0-20180826012351-8a410e7b638d
golang.org/x/text v0.3.0 => github.com/golang/text v0.3.0
)

依赖库中的replace对你的主go.mod不起作用,比如github.com/smallnest/rpcxgo.mod已经增加了replace,但是你的go.mod虽然requirerpcx的库,但是没有设置replace的话, go get还是会访问golang.org/x

所以如果想编译那个项目,就在哪个项目中增加replace

版本格式

下面的版本都是合法的:

1
2
3
4
5
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
latest

go get 升级

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

go mod vendor

go mod vendor 会复制modules下载到vendor中, 貌似只会下载你代码中引用的库,而不是go.mod中定义全部的module。

go module, vendor 和 Travis CI

https://arslan.io/2018/08/26/using-go-modules-with-vendor-support-on-travis-ci/

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

No comments:

Post a Comment