Go 1.16 中包含着大量的 Modules 相关的更新,详细内容可直接查看其 Release Note[2]。整体而言,包含以下要点:

  • GO111MODULE 默认为 on ,如果要恢复到之前的行为,则需要将 GO111MODULE 设置为 auto ,这样差不多意味着 GOPATH 模式要逐步淡出人们的视野了;
  • go install 命令可以接受一个版本后缀了,(例如,go install sigs.k8s.io/kind@v0.9.0),并且它是在模块感知的模式下运行,可忽略当前目录或上层目录的 go.mod 文件。这对于在不影响主模块依赖的情况下,安装二进制很方便;
  • 在将来,go install 被设计为“用于构建和安装二进制文件”, go get 则被设计为 “用于编辑 go.mod 变更依赖”,并且使用时,应该与 -d 参数共用,在将来版本中 -d 可能会默认启用;
  • go build 和 go test 默认情况下不再修改 go.mod 和 go.sum。可通过 go mod tidy ,go get 或者手动完成;

总结而言,关于 go install 和 go get 必须要注意的是:

  • 基本上 go install @ 是用于命令的全局安装:例如:go install sigs.k8s.io/kind@v0.9.0;
  • go get 安装二进制的功能,后续版本将会删除(执行目录如果没有go.mod,则安装二进制;如果有go.mod,则修改go.mod,执行go mod tidy);
  • go get 主要被设计为修改 go.mod 追加依赖之类的,但还存在类似 go mod tidy 之类的命令,所以使用频率可能不会很高;

1.16 中已解决的工具安装问题

到目前为止,Go 一直使用 go get 命令,将我们需要的工具安装到 $GOPATH/bin 目录下,但这种方式存在一个很严重的问题。go get 由于具备更改 go.mod 文件的能力,因此我们 必须要避免执行 go get 命令时,让它接触到我们的 go.mod 文件 ,否则它会将我们安装的工具作为一个依赖,而不是安装一个binary工具。
目前的解决方案通常是:

  1. $ go get sigs.k8s.io/kind@v0.9.0
  2. // 自 1.16 开始,我们可以直接使用下面的方式:
  3. $ go install sigs.k8s.io/kind@v0.9.0

非常的简单直观。需要注意的是 go install @ 是从 1.16 开始增加的,无论你当前是否在一个模块下,此命令都会在 $GOPATH/bin 下安装指定版本的工具。此外由于 Go 1.16 中 GO111MODULE 默认是打开的,go install 不会修改 go.mod 之类的文件,不会造成任何意外。
注意:@version 只能安装主软件包。非主程序包不受此格式约束。

关于不带 @version 的 go install

在模块外,不带 @version 是无法安装的,会有如下错误:

  1. $ go install -v sigs.k8s.io/kind
  2. go install: version is required when current directory is not in a module
  3. Try 'go install sigs.k8s.io/kind@latest' to install the latest version

如果你在模块目录中,并且你不带 @version 执行安装的话,只能安装 go.mod 中已经包含的版本。并且不能安装未出现在 go.mod 中的包。

  1. # 创建一个模块
  2. devhg mkdir kind-install
  3. devhg cd kind-install
  4. kind-install go mod init
  5. go: creating new go.mod: module github.com/devhg/kind-install
  6. kind-install cat go.mod
  7. module github.com/devhg/kind-install
  8. go 1.17
  9. # 执行安装 不带 @version 无法安装
  10. kind-install go install -v sigs.k8s.io/kind
  11. no required module provides package sigs.k8s.io/kind; to add it:
  12. go get sigs.k8s.io/kind
  13. # 提示需要先添加依赖。
  14. # 用 go get -d 下载添加依赖
  15. kind-install go get -d sigs.k8s.io/kind
  16. # 可以看到已经被添加到了模块依赖中了
  17. kind-install cat go.mod
  18. module github.com/devhg/kind-install
  19. go 1.17
  20. require (
  21. sigs.k8s.io/kind v0.11.1 // indirect
  22. )
  23. # 再次使用不带 @version 进行安装,安装成功
  24. kind-install go install -v sigs.k8s.io/kind
  25. kind-install which kind
  26. /Users/devhg/GoWork/bin/kind
  27. kind-install

关于 go get 和 go.mod

go get 将二进制安装相关的功能都转移到了 go install, 仅作为用于编辑 go.mod 文件的命令存在。在后续版本(计划是 Go 1.17)中删掉 go get 安装二进制的功能,接下来 go get 的行为就等同于我们现在执行 go get -d 命令了,仅需下载源码,并将依赖添加至 go.mod 即可。

在 Go 1.16 中,另一个行为变更是 go build 和 go test 不会自动编辑 go.mod 了,基于以上信息,Go 1.16 中将进行如下处理:

  • 通过在代码中修改 import 语句,来修改 go.mod:
    • go get 可用于添加新模块;
    • go mod tidy 删除掉无用的模块;
  • 将未导入的模块写入 go.mod:
    • go get [@];
    • go mod tidy 也可以;
    • 手动编辑;

go get新的规则

新的go get所支持的版本选择则是这一方案的进一步扩展,看几条规则:

  • go get会自动下载并安装package,然后更新到go.mod中
  • 可以使用go get package[@version]来安装指定版本的package,不指定version时默认行为和go get package@latest一样
  • version可以是vx.y.z这种形式或者直接使用commit的checksum,也可以是master或者latest
  • 当version是latest时,也就是默认行为,对于有tags的package,会选取最新的tag,对于没有tags的package,则选取最新的commit
  • 当version是master时,不管package有没有打tag,都会选择master分支的最新commit
  • 可以在version前使用>,>=,<,<=,表示选取的版本不得超过/低于version,在这个范围内的符合latest条件的版本
  • go get -u可以更新package到latest版本
  • go get -u=patch将只更新小版本,例如从v1.2.4到v1.2.5
  • 当想要修改package的版本时,只需要go get package@指定的version即可

如果我们想用v1.9的gorm:
go get github.com/jinzhu/gorm@v1.9
很遗憾,版本选择是从大版本到小版本的顺序,如果有v1.9和v1.9.1,那么当你指定v1.9时会自动选取小版本号最高的版本,除非除了v1.9之外没有其他的v1.9.z的tag存在,在这里就是v1.9.1。

总结

Go 1.16 中 go install 和 go get 方面有些不兼容的变更,但是 1.16 中模块更加简洁,减少了使用时的心智负担,我还是很期待这个版本的。

https://zhuanlan.zhihu.com/p/338011682
https://www.cnblogs.com/apocelipes/p/9537659.html