Post

GVM 使用详解

GVM 使用详解

GVM 使用详解

1. GVM 简介

GVM (Go Version Manager) 是 Go 语言的版本管理工具,类似于 Ruby 的 rvm、Node.js 的 nvm。它允许在同一台机器上安装和切换多个 Go 版本,方便开发者在不同项目间使用不同版本的 Go。

核心特性

  • 安装/卸载 Go 版本
  • 管理 GOPATHs (使用 pkgset)
  • 列出可用/已安装的 Go 版本
  • 支持二进制和源码安装
  • 缓存 Go 源码以便多版本复用
  • 自动加载项目的 .go-version 文件

2. 安装 GVM

2.1 系统要求

Debian/Ubuntu

1
sudo apt-get install curl git mercurial make binutils bison gcc build-essential

RedHat/CentOS

1
sudo yum install curl git make bison gcc glibc-devel

macOS

1
2
3
xcode-select --install
brew update
brew install mercurial

2.2 安装 GVM

1
bash < <(curl -s -S -L https://raw.githubusercontent.com/moovweb/gvm/master/binscripts/gvm-installer)

如果使用 zsh,将 bash 替换为 zsh

1
zsh < <(curl -s -S -L https://raw.githubusercontent.com/moovweb/gvm/master/binscripts/gvm-installer)

2.3 加载 GVM

安装完成后,重启终端或手动加载:

1
source ~/.gvm/scripts/gvm

3. 安装 Go 版本

3.1 基本安装

1
2
gvm install go1.4
gvm use go1.4 --default

3.2 安装选项

1
gvm install [version] [options]
选项说明
-s, --source=SOURCE从指定源安装 Go
-n, --name=NAME覆盖默认版本名称
-pb, --with-protobuf安装 Go protocol buffers
-b, --with-build-tools安装构建工具包
-B, --binary仅从二进制文件安装
--prefer-binary尝试二进制安装,失败则回退到源码
-h, --help显示帮助信息

3.3 编译 Go 1.5+ 的注意事项

Go 1.5+ 移除了 C 编译器,改用 Go 编写的工具链。这造成了一个自举(bootstrapping)问题,编译 Go 1.5+ 需要先有一个可用的 Go 安装。

1
2
3
4
5
6
7
8
9
# 先安装 Go 1.4 (使用二进制包)
gvm install go1.4 -B
gvm use go1.4

# 设置引导环境变量
export GOROOT_BOOTSTRAP=$GOROOT

# 编译安装 Go 1.7
gvm install go1.7

3.4 安装 Go 1.20+

Go 1.20+ 需要 go1.17.3+:

1
2
3
4
5
6
7
8
gvm install go1.4 -B
gvm use go1.4
export GOROOT_BOOTSTRAP=$GOROOT
gvm install go1.17.13
gvm use go1.17.13
export GOROOT_BOOTSTRAP=$GOROOT
gvm install go1.20
gvm use go1.20

4. 使用 Go 版本

4.1 切换版本

1
2
3
4
5
# 临时切换
gvm use go1.20

# 设置为默认版本
gvm use go1.20 --default

4.2 自动切换(.go-version 文件)

GVM 的 cd 命令会自动检测当前目录下的 .go-version 文件,并切换到指定版本。

在项目根目录创建 .go-version

1
echo "go1.20" > /path/to/project/.go-version

当进入该目录时,GVM 会自动切换到 go1.20。

4.3 列出版本

1
2
3
4
5
6
7
8
# 列出已安装的版本(当前版本带 => 前缀)
gvm list

# 列出所有可下载的版本
gvm listall

# 列出所有可下载版本(包括 weekly 版本)
gvm listall --all

5. 管理 Pkgset(GOPATH)

GVM 允许为每个 Go 版本创建独立的工作空间(pkgset),实现项目隔离。

5.1 创建 Pkgset

1
gvm pkgset create myproject

5.2 使用 Pkgset

1
gvm pkgset use myproject

5.3 列出 Pkgset

1
gvm pkgset list

5.4 删除 Pkgset

1
gvm pkgset delete myproject

5.5 本地 Pkgset

使用 --local 选项可以在当前目录下创建本地 pkgset(保存在 .gvm_local):

1
gvm pkgset use --local

5.6 链接项目到 GOPATH

1
gvm pkgset link myproject /path/to/project

6. 其他常用命令

6.1 查看当前版本

1
gvm version

6.2 查看所有版本

1
gvm list

6.3 卸载版本

1
gvm uninstall go1.19

6.4 完全卸载 GVM

1
gvm implode

如果上述命令无效,手动删除:

1
rm -rf ~/.gvm

6.5 查看 GOROOT 差异

1
gvm diff

7. 环境变量

GVM 提供以下环境变量:

变量说明
GOROOTGo 安装目录
GOPATHGo 工作目录
GVM_ROOTGVM 安装目录 (默认: ~/.gvm)
GVM_OVERLAY_PREFIX用于依赖管理的隔离目录

GVM_OVERLAY_PREFIX

用于隔离本地依赖,避免污染系统环境。可用于编译和安装 C/C++ 依赖:

1
2
3
./configure --prefix=${GVM_OVERLAY_PREFIX}
make
make install

相关环境变量:

  • PATH: 包含 ${GVM_OVERLAY_PREFIX}/bin
  • LD_LIBRARY_PATH (Linux/FreeBSD): 包含 ${GVM_OVERLAY_PREFIX}/lib
  • DYLD_LIBRARY_PATH (macOS): 包含 ${GVM_OVERLAY_PREFIX}/lib
  • PKG_CONFIG_PATH: 包含 ${GVM_OVERLAY_PREFIX}/lib/pkgconfig

8. 最佳实践

8.1 项目版本管理

在每个项目根目录创建 .go-version 文件:

1
echo "go1.20" > .go-version

8.2 项目 GOPATH 隔离

使用 pkgset 隔离不同项目的依赖:

1
2
3
# 为项目创建独立的 pkgset
gvm pkgset create myproject
gvm pkgset use myproject

8.3 本地 Pkgset

使用 --local 选项使 pkgset 跟随项目仓库:

1
gvm pkgset use --local

9. 故障排除

9.1 GVM 状态异常

如果升级后 GVM 状态异常(尤其是从 0.0.8 以下版本升级):

1
rm -rf ~/.gvm

然后重新安装 GVM。

9.2 cd 别名问题(commit 8cda612)

某些 shell 配置(如 oh-my-bash)会为 cd 命令创建 alias,这会导致 GVM 的自定义 cd 函数失效。

问题表现

  • 进入有 .go-version 的目录时,Go 版本没有切换

解决方案: GVM 在启动时会检测 cd 的状态(函数/alias/builtin),如果是 alias,会将其保存到 __gvm_oldcd,然后 unalias cd,确保 GVM 的 cd 函数能正常工作。

相关代码(commit 8cda612570d801654b81e38ca895bda784939edf):

1
2
3
4
5
6
7
8
9
10
11
if __gvm_is_function cd; then
    # cd 是函数
    eval "$(echo "__gvm_oldcd()"; declare -f cd | sed '1 s/{/\n{/')"
elif [[ "$(builtin type cd)" == *"aliased to"* ]]; then
    # cd 是 alias,保存原始行为并移除 alias
    eval "__gvm_oldcd() { $(alias cd | sed "s/^alias cd=\['\\"\]//; s/\['\\"\]$//") \"\$@\"; }"
    unalias cd
elif [[ "$(builtin type cd)" == "cd is a shell builtin" ]]; then
    # cd 是 builtin
    eval "__gvm_oldcd() { builtin cd \$*; return \$?; }"
fi

9.3 编译失败

如果编译 Go 版本时失败,尝试使用二进制安装:

1
gvm install go1.20 -B

10. 相关链接

This post is licensed under CC BY 4.0 by the author.