Go module 再回顾

我曾经写了一篇跳出Go module的泥潭, 记录了使用go module的一些坑,随着Go 1.13版本的发布,go module的功能再不断的演化(或者不委婉的说在不断的修补),go module很多功能也发生了变化,这篇文章记录了新版本(go 1.13)下module的使用方法。

不客气地的说,go module的实现不是一个很成功的功能的添加,首先go module的引入就引入了很大的争议,包括dep等多个go依赖工具的作者颇有微词,其次go module的功能并没有很好的设计就开始引入到go的发布版本中,导致Go多个版本module功能在不停的修修补补,第三,虽然go module也一些官方的文档,但是文档并不详细,也没有很好的列出各个版本对相应工具的影响。另外,go module多个版本的实现导致一些工具比如go等不向下兼容,刚入的新的Go开发者感觉不到兼容的变化,但是老的go开发者就很痛苦了,因为执行相同的命令产生了不同的"执行语义"。

如著名的Go布道者 Bill Kennedy 的吐槽:


当然,go module设计初衷还是好的,而且go生态圈也应该有一个强势的库依赖工具避免分片化。

即使你对go module有一件,我的观点是如果你不能改变它,那么就去接收它,所以让我们来了解一下go module对现有的功能的影响。

环境变量

首先,GO111MODULE环境变量并没有被移除,它的默认值也没有发生了改变,依然是在auto, 但是auto的含义却发生了变化 :(。
在Go 1.13中auto意味着当前目录或者在目录中包含go.mod文件夹的话,即使当前目前在$GOPATH/src下,也会开启module模式。

很显然,Go开发者要强推go module功能了,但是如果你不了解的这种变化的化,很可能变得很茫然,因为初次使用go module可能会重新下载很多的依赖库。

相应的,这也对常用的IDE产生影响,幸运地是,主流的IDE都支持了go module功能,不用担心引入的库找不到的问题。

GOPROXY环境变量设置代理服务器,可以设置多个代理服务器以及direct,已经有很多的代理服务器了,不用担心墙的问题,比如https://goproxy.cn,direct

GOSUMDB可以用来校验你下载的库的哈希是否和官方的哈希值是否一样,避免被proxy给修改了,万一proxy给你的下载库加上挖矿代码就惨了,毫无疑问也被墙了,即使专为中国区设置的域名/服务器也被墙。你可以使用goproxy.cn作为GOSUMDB服务器,或者心大使用GOSUMDB=off进行禁用。

GOPRIVATE用来设置不使用代理的仓库,比如一些公司内部的仓库等等,如GOPRIVATE=*.corp.example.com,rsc.io/private

作为和以前的兼容,还保留着GONOPROXY,功能和GOPRIVATE一样。

go env -w可以为这些值设置为全局变量。有些人不建议你这么做,因为可能你在你的开发环境中编译好好的,但是在服务器或者docker中项目却无法编译,所以建议将这些环境变量写到配置文件如Makefile中。

go help module-auth了解GOSUMDBGONOSUMDB变量。

go help goproxy了解proxy的通讯协议。

go help modules了解module的功能。

go help module-get了解go get命令的变化。而go help gopath-get显示先前的基于GOPATH的go get功能。

go help module-private了解私有库的设置。

go mod 命令

go help mod显示go mod命令的子命令:

  • download:下载依赖库到本地缓存中,当前在$GOPAH/pkg/mod
  • edit: 命令行中编辑go.mod
  • graph: 显示依赖库的关系,包括间接依赖库都会显示,它以行的方式显示,如果能以tree方式显示更好。
  • init: 初始化当前文件夹为module方式的go项目,会生成go.mod
  • tidy: 增加新的依赖,移除不使用的依赖库
  • vendor: 将依赖库放入到vendor文件夹下,如果将来使用module方式,这个命令是不会使用的
  • verify: 校验下载到本地缓存中的依赖库自下载后未被修改
  • why: 解释为什么需要一个依赖库(肯定是有代码使用它了, go help mod why可以看它的详细帮助)

一般新的项目,无论文件夹在哪里,首先go mod init初始化它,然后go mod tidy自动将引入的库加入到go.mod文件中。

go action 工具

老司机比较不适应的使用 go xxxx工具参数和语义的改变。

如何更新go.mod文件中的库的版本到最新版本?如何值更新当前项目?如何更新本地库和依赖库?

go get

当前go get的命令的格式是go get [-d] [-t] [-u] [-v] [-insecure] [build flags] [packages]

首先 go get会寻找依赖库的版本,它会寻找库的最新的tag未release的版本(tagged release version), 如 v0.4.5v1.2.3, 如果没有,则寻找pre版,如v0.0.1-pre1,如果都没有,寻找最新的commit版。

当然你也可以指定下载某个库的某个特定的版本,如go get golang.org/x/text@v0.3.0go get golang.org/x/text@master

-t是也下载 test 文件中的依赖。

-u指示下载的库使用最新的minorpatch最新版。'go get -u A'下载最新的A, 以及A依赖的 B v1.3.1 (而不是 B v1.2.3)。假如B依赖C,而C并没有提供任何A所需的包,那么C不会更新。

-u=patch更新pacth release。'go get -u=patch A@latest'更新A到最新版,依赖的B更新到v1.2.4(而不是 V1.3.1)。
go get -u=patch A把A更新到最新的patch版,而不是minor版。

go get xxxxx/...可以批量安装工具。 go get golang.org/x/perf/cmd/...会安装perf的所有的命令行工具到$GOPATH/bin中。

go get相当于go get .,它应用于当前文件夹对应的包,go get -ugo get -u=patch同样的处理。

没有-u的时候,go get并不会比go install多做什么,go get -d也不会比go list多做什么。

go list

go list列出所依赖的库,格式如下:
go list [-f format] [-json] [-m] [list flags] [build flags] [packages]

-f指定要显示的字段, -json指定显示的格式为json格式,否则为每行一条记录的方式。

比如:

1
2
3
4
5
6
7
8
9
10
11
12
# go list ./...
github.com/smallnest/rpcx/client
github.com/smallnest/rpcx/codec
github.com/smallnest/rpcx/errors
github.com/smallnest/rpcx/log
github.com/smallnest/rpcx/protocol
github.com/smallnest/rpcx/server
github.com/smallnest/rpcx/serverplugin
github.com/smallnest/rpcx/share
github.com/smallnest/rpcx/tool/xgen
github.com/smallnest/rpcx/tool/xgen/parser
github.com/smallnest/rpcx/util

或者 go list -f '\{\{.ImportPath\}\}' ./...go list -json ./...

-deps还会列出它们的依赖。

它还有其它一些参数,比如-e-compiled-test, 还有-m参数。

-m列出所依赖的module,而不是package,比如go list -m all

-u可以列出可升级的库的信息,如go list -m -u -json all

-versions显示库的可用的版本列表。

all

all 现在是go action中一个特殊的语义,代表main module和它的依赖(The special pattern "all" specifies all the active modules, first the main module and then dependencies sorted by module path.

...代表代表匹配这个模式的所有活动的module(A pattern containing "..." specifies the active modules whose module paths match the pattern.)

go get allgo list allgo get -u allgo get ./...go get -u ./...都是合法的。

go保留了四个四个特殊的名字:

  • main: 可执行程序的顶层包
  • all: GOPATH所有的包; module模式下指main module和它的依赖。
  • std: 标准库的包
  • cmd: go仓库里的命令行工具和它们的内部库

你可以使用go list stdgo list cmd看看效果。

...代表匹配任何字符串,包括空格和斜杠,但是不会匹配vendor文件夹。

乱。

参考资料

  1. https://golang.org/doc/go1.13#modules
  2. https://blog.golang.org/module-mirror-launch
  3. https://blog.golang.org/migrating-to-go-modules
  4. https://blog.golang.org/using-go-modules
  5. https://blog.golang.org/modules2019