多版本 Go 管理策略

多版本 Go 管理策略

本文是 [Managing Multiple Go Versions with Go] (https://lakefs.io/blog/managing-multiple-go-versions-with-go/)的中文翻译版本,内容有删减

作为 Go 编程语言的用户,我发现在一个项目中启用运行多个版本非常有用。如果你已经尝试过或考虑过这个功能,那太好了!在本文中,我将介绍启用多个 Go 版本的时间和方法。最后,我们将讨论为什么这种方法如此强大。

什么时候需要多版本的Go (When Do We Need Multiple Go Versions?)

默认情况下,安装 Go 只意味着你可以运行一个 go 命令来构建和测试你的项目。这对于入门很简单,但也可能有局限性。

更灵活的设置是通过 go1.17 或 go1.18 命令启用在同一环境中运行多个版本。另一种替代方法是设置终端的 PATH 环境变量以指向特定的 Go 版本的 SDK。

以下是我发现可能会有有多个go版本的几种情况:

项目需求不同 —— 在多个项目之间切换时,通常需要为每个项目使用不同的 Go 版本。

创建特定的测试环境 —— 在测试向后兼容性或确保修复漏洞的成功时,重要的是控制运行时版本。

保持最前沿 —— 当测试最新 Go 发布中仅可用的新功能或包的行为时

必备条件(Prerequisites)

本指南假定您已经知道如何使用 Go 构建和运行程序。具体来说,这意味着您已经安装了 Go 和 Git,并且它们在您的路径中可用。

Go – https://golang.org/doc/install

Git – https://git-scm.com/

Modules – Using Go Modules

如何使用多个 Go 版本(How to Work With Multiple Go Versions)

我们可以使用 go install 命令来下载并安装单个 Go 版本。

运行 go install golang.org/dl/go@latest 命令将下载并安装特定 Go 版本的包装 Go 命令。

请注意,如果您运行的 Go 版本低于 Go 1.16,请使用 go get 命令替代所有以下示例中的 go install @latest 命令。

通过使用 Go 包装器,我们可以下载特定版本的 Go,并为此版本运行 Go 工具链。

Go v1.18的例子

$ go install golang.org/dl/go1.18@latest

$ go1.18 download

使用包装器 go1.18,我们可以使用 Go v1.18 进行构建和测试。例如:

$ go1.18 mod init hello

go: creating new go.mod: module hello

$ echo 'package main; import "fmt"; func main() { fmt.Println("Hello, World") }' > hello.go

$ go1.18 build

$ ./hello

Hello, World

另一个选项是通过设置 GOROOT 并将其添加到我们的路径中来使用刚刚下载的版本:

$ export GOROOT=$(go1.18 env GOROOT)

$ export PATH=${GOROOT}/bin;${PATH}

默认的下载目录是您的 /sdk 文件夹。要删除特定版本,只需删除特定版本目录即可。

还有一个特殊标记的版本,称为 gotip,代表来自开发者的最新版本的 Go:

$ go install golang.org/dl/gotip@latest

$ gotip download

请注意,gotip 下载并构建当前的开发版本。它还可以接受其他参数 - 分支名称或变更列表 (CL),并使用它来获取特定的 Go 版本。

内部原理 (How It Works Under the Hood)

https://go.googlesource.com/dl 实现了这个功能。它包括:

每个 Go 版本的小型应用程序 - 例如:go1.18/main.go

实现 Go 包装器功能的内部包:internal/version/version.go

一个辅助应用程序,用于生成 Go 包装器代码:internal/genv/main.go

对于每个Go版本的 main.go,Go的包装器是

package main

import "golang.org/dl/internal/version"

func main() {

version.Run("go1.18")

}

正如你所看到的,这里没有太多的操作。我们只需调用要提供的版本的公共代码

A packaged version’s Run function performs the two main tasks (based on command line arguments):

Run 函数执行两个主要任务(基于命令行参数):

Download - go download

Wrapper - go

下载(Download)

显式指定版本(例如 go1.18)会从 https://dl.google.com/go 下载特定版本、操作系统和平台的 Go SDK 压缩包,并解压到你的 /sdk 文件夹下。

接下来,它会通过从下载服务器获取 checksum 文件,使用 sha256 验证压缩包的完整性。最后,它会解压压缩包并创建一个名为 ..unpacked-success 的空标记文件。

Gotip 版本 - 当使用 gotip download 时,我们只有最新的或特定分支。

gotip: usage: gotip download [CL number | branch name]

Gotip download 命令将使用 git 从源码仓库获取源代码文件,然后执行清理操作并运行相关的构建脚本以构建 Go。

与解压特定的发行版不同,我们没有标记文件来标记解压成功。相反,gotip 始终尝试获取任何更新并运行构建过程。

包装器(Wrapper)

包装功能首先通过验证相关的Go文件夹下是否有有效的标记文件(.unpacked-success)来验证我们是否安装了有效的SDK。

如果标记文件缺失,将会给出错误提示信息,指导你运行一个下载请求来进行安装,如:go: not downloaded. Run 'go download' to install to /sdk/go。下面这段代码(来自 internal/version/version.go)格式化了要执行的命令,基于我们要执行的 go 版本,渲染了带有正确路径的环境变量 GOROOT(并将其添加到 PATH 中)。

func runGo(root string) {

gobin := filepath.Join(root, "bin", "go"+exe())

cmd := exec.Command(gobin, os.Args[1:]...)

cmd.Stdin = os.Stdin

cmd.Stdout = os.Stdout

cmd.Stderr = os.Stderr

newPath := filepath.Join(root, "bin")

if p := os.Getenv("PATH"); p != "" {

newPath += string(filepath.ListSeparator) + p

}

cmd.Env = dedupEnv(caseInsensitiveEnv, append(os.Environ(), "GOROOT="+root, "PATH="+newPath))

handleSignals()

if err := cmd.Run(); err != nil {

os.Exit(1)

}

os.Exit(0)

}

为了为每个版本创建一个Go包装器主程序,使用了一个帮助命令——genv。

它接受我们想要构建Go包装器的go版本作为输入。

首先,它运行go list -m -json并解析输出:

$ go list -m -json

{

"Path": "golang.org/dl",

"Main": true,

"Dir": "/dl",

"GoMod": "/dl/go.mod",

"GoVersion": "1.11"

}

然后它将Path与golang.org/dl进行匹配,并使用Dir作为包装器代码的目标目录(/main.go)。

最后,它使用可用的Go模板来渲染上面所述的Go包装器代码。

总结(Summary )

运行多个 Go 版本的 Go 解决方案非常简单而优雅——它使用与 Go 中的其他工具相同的工具,并提供一种以最小化的编码方式访问不同版本的 Go SDK 的方法。

相关推荐

为什么打开手机需要很长时间? 加快速度的一切
365bet体育在线投注

为什么打开手机需要很长时间? 加快速度的一切

07-29 👁️ 2357
成人动画
下载旧版本彩票365软件

成人动画

07-19 👁️ 8384
一图看懂:口罩戴多久后需要更换?如何选择?
365bet体育在线投注

一图看懂:口罩戴多久后需要更换?如何选择?

07-10 👁️ 9710