大家好,我是煎鱼。新春快乐呀!
年前我们在《醒醒吧,未来不会有 Go2 了!》文章中讨论了 Go2 的未来,明确了未来是以 Go1.x.y 为主的 Go1 时代。
为了实现这个北极星目标,Go 团队采取的策略分别是:增强 Go1 向后兼容(在前文已分享)和 Go1 向前兼容(本文重点内容)。
本次要提到的 “向前兼容”,指的是旧版本的 Go 编译新的 Go 代码。这个方向比较少被谈论,甚至特意设计。
让我有种,还能这样搞的感觉?
Go1 向前兼容
Go 团队的大当家 Russ Cox,针对如下几个方面做了新设计和调整,输出了《Proposal: Extended forwards compatibility in Go[1]》,已经得到讨论,很大概率落地,把版本号停留在 Go1.x.y。
将会涉及的部分重点如下:
- 新增 GOTOOLCHAIN 环境变量的设置。
- 改变在工作模块(work module)中解释 go 行的方式,增加了新的工具链(toolchain)行以此实现声明。此对应的是 go.mod 文件的 go 行和toolchain 行。
- 对 go get 等命令进行联动修改,允许对 GOTOOLCHAIN 和工作模块的 go 版本进行修改。
增强工作模块的 go.mod 和 toolchain
声明 Go 版本号
我们会在 go module 生成时,在 Go 工程下生成一个 go.mod 文件。其中会包含一个 go 行,将会声明该模块应该应用的 go 版本语义是什么版本。
如下图,声明的是 go1.13:
go.mod 文件中的 go 版本声明
在该提案落地后,本地安装的 Go 工具链如果比 go 行所声明的 go 版本新时,它将会直接提供所要求的旧语义,而不会重新下载和调用一个旧版本的 Go 工具链。
但如果 go 行声明了一个较新的 Go 工具链,那么本地安装的 Go 工具链就会下载并运行较新的工具链来满足其需求。
以下是一个例子。
在例子中,我们正在运行的版本是 go1.30。但在模块中,有一个 go.mod 声明了 go 版本:
Go1.30 会下载并调用 go1.30.1 来完成命令,因为模块中要求的 go 版本比本地安装的更高。
但如果 go.mod 文件中声明的是:
Go1.30 将自己提供 go1.20rc1 语义,而不是运行 go1.20 rc1 工具链。因为本地安装的版本更新,可以通过 GODEBUG 来满足旧语义的诉求。
声明 Go 工具链版本号
可能会有同学想要运行更新版本的 Go 工具链,但 Go 语义上还是使用旧版本。
为了满足这点诉求,go.mod 文件也会支持 toolchain 行的设置,以此来支持新版本的工具链的使用。
如果 go.mod 文件中设置了 toolchain 行,将指定使用的工具链版本,go 行只指定语言语义的 Go 版本。
go.mod 文件如下:
作用是将为这个模块选择 go1.18 的语义,使用 go1.20rc1 的工具链来构建应用。
增强 Go 工具链 GOTOOLCHAIN
将会在 Go 工具链新增 GOTOOLCHAIN 环境变量的设置和使用可以使用 go env -w 设置。也可以在 go test 时做如下调整:
go build 编译时可以:
可能会有同学疑惑 GOTOOLCHAIN 的默认值哪来,有哪些值?
- 设置GOTOOLCHAIN=local:使用本地安装的 Go 工具链,不会下载不同版本的工具链。这是现在的的默认行为。
- 设置GOTOOLCHAIN=auto:使用工作模块的 go.mod 中声明的 go 版本(当它比本地安装的 Go 工具链要新时)。
GOTOOLCHAIN 环境变量的默认值取决于 Go 工具链。标准 Go 发行版默认为 GOTOOLCHAIN=auto,也就是将控制权交给 go.mod 文件。这是在实施这个提案后 99% 会看到的默认行为。
Go 工具链的一揽子东西里也比较多,例如:go get 命令,也会对 go.mod 文件中的 go 版本或 toolchain 行进行变更,以配合使用。
总结
在今天这篇文章中,我们介绍了 Go1 兼容性增强中的 “向前兼容” 部分,其中要点是:加大对 go.mod 文件中的 go 行和 toolchain 行和工具链 GOTOOLCHAIN 相关的应用。
核心目的是为了将 go 语法语义和 go 工具链的版本声明公开出来,达到隔离使用。再配合 “向后兼容” 中的 GODEBUG 的使用,让 Go 语言做兼容性时有更多更大的使用空间来实现机制上的保障。
这么一来,Go 语言在这一块会变得异常复杂,理解成本也会变高。希望大家后续在这块也不要再踩坑了。