本文使用「署名 4.0 国际 (CC BY 4.0)」许可协议,欢迎转载、或重新修改使用,但需要注明来源。 [署名 4.0 国际 (CC BY 4.0)](https://creativecommons.org/licenses/by/4.0/deed.zh) 本文作者: 苏洋 创建时间: 2022年05月12日 统计字数: 5384字 阅读时间: 11分钟阅读 本文链接: https://soulteary.com/2022/05/12/better-golang-usage-on-m1-mac.html ----- # M1 芯片 Mac 上更好的 Golang 使用方案 本篇文章,将分享如何在苹果 M1 Mac 设备上,来进行高效、可靠的 Golang 开发环境的安装和管理。 ## 写在前面 如果你是一个 Golang 的用户,那么你大概率会遇到管理和维护 Golang 版本的诉求,如果你恰好同时需要开发调试两个不同版本的项目,在不考虑强制跳版本的情况下,你或许就需要使用“Golang 版本管理工具”来帮助你减轻负担了。 去年年末,我曾在一篇文章中分享过三种关于[《Golang 多版本管理》](https://soulteary.com/2021/12/15/golang-multi-version-management.html)的方案。 相比较官方方案 `golang/dl` 或者社区方案 `voidint/g` 这两个更适合安装或管理单一版本的方案之外,我更推荐使用的方案是 `gvm` 。 它除了能够完成 golang 开发环境的快速安装之外,还能够保障多个版本的 golang 共存,不同版本的软件依赖包都保持正常工作。并且,它的实现和社区大名鼎鼎的 `nvm-sh/nvm` 、`shyiko/jabba` 是一致的,都是由 BASH 编写,和所需要管理的 Runtime 语言无关,能够更稳定的完成“管理工作”。 但可惜的是,**它和 M1 设备存在兼容性问题,它并不能够很好的运行,甚至可以说,完全无法运行。** 暂且不说我是如何解决问题的,让我们先来看看,怎么能够在数秒、几分钟内完成 Golang 开发环境的安装和切换吧。 ## 安装和使用 Golang 版本管理工具:`gvm` 想要使用 `gvm`,需要先完成一次“绿色安装”(下载)。 ### 如何安装 `gvm` `gvm` 的安装非常简单,只需要“一句话”: ```bash curl -sSL https://github.com/soulteary/gvm/raw/master/binscripts/gvm-installer | bash ``` 当命令执行完毕之后,我们可以看到类似下面的日志输出: ```bash Cloning from https://github.com/soulteary/gvm.git to /Users/soulteary/.gvm No existing Go versions detected Installed GVM v1.0.24 Please restart your terminal session or to get started right away run `source /Users/soulteary/.gvm/scripts/gvm` ``` 接下来,我们选择重新打开命令行终端,或者是选择执行上一步提示的 `source /Users/soulteary/.gvm/scripts/gvm` 命令都可以让安装的 `gvm` 命令生效。 ### 如何使用 `gvm` 安装 Golang 开发环境 当 `gvm` 安装完毕之后,假设我们要安装最新的 `golang 1.18.2`,只需要执行下面的命令即可: ```bash gvm install go1.18.2 -B ``` 稍等片刻,当命令行中提示 `Installing go1.18.2 from binary source` 的时候,指定 golang 开发环境就安装完毕啦,是不是很简单! 除了“下载”之外,还需要执行下面的命令,将我们刚刚下载的版本在当前的命令行环境中 “激活”: ```bash gvm use go1.18.2 ``` 如果我们希望刚刚下载的 golang 版本全局生效,并且作为默认的使用版本,可以在命令行后面添加 `--default` 参数: ```bash gvm use go1.18.2 --default ``` 不论使用哪一种方式,当命令执行完毕之后,我们将得到提示:`Now using version go1.18.2`。接着,我们就正常可以使用 `go` 了,比如执行 `go version`: ```bash go1.18.2 darwin/arm64 ``` ### 如何使用 `gvm` 进行多版本切换 想使用不同版本的 golang 的方法和上面其实并没有什么不同。依旧是先使用 `install` 命令,安装我们想要的版本: ```bash gvm install go1.18.1 -B ``` 当看到 `Installing go1.18.1 from binary source` 提示之后,我们接着使用 `use` 命令,激活这个版本就好了: ```bash gvm gvm use go1.18.1 #或者 gvm gvm use go1.18.1 --default ``` 当命令执行完毕,我们会得到熟悉的 `Now using version go1.18.1` 提示。 ### 如何使用 `gvm` 进行编译安装 有的同学因为一些诉求,可能不想下载预编译好的内容,而是希望进行本地编译。 那么可以在使用 `install` 命令下载 golang 的时候,去掉 `-B` 参数: ```bash gvm install go1.18 ``` 当命令执行完毕之后,我们会看到带有编译过程的日志: ```bash Downloading Go source... Installing go1.18... * Compiling... go1.18 successfully installed! ``` 接着就是和上文中提到的一样,根据自己需求使用 `use` 来激活命令了。 ### 如何卸载 `gvm` 或进行重置 先聊聊软件的卸载,在前文提到过 `gvm` 是一个“绿色软件”,卸载方式十分简单,将 `~/.gvm` 目录删除,然后清理掉前文中我们在 `~/.zshrc` 或 `~/.bashrc` 中添加的内容即可。 如果你希望进行软件重置,我推荐你在“卸载之后”,再次进行安装即可。 ### 如何解决某个版本下载不顺利的问题 在上一篇内容中,我们[曾提到过](https://soulteary.com/2021/12/15/golang-multi-version-management.html#%E5%9F%BA%E4%BA%8E-bash-%E7%9A%84%E7%89%88%E6%9C%AC%E7%AE%A1%E7%90%86%E5%B7%A5%E5%85%B7gvm)如何解决这个问题。原理在此就不过多赘述了,感兴趣的同学自行翻阅即可。 简单来说,如果遇到下载二进制文件出现问题,可以尝试进行缓存清理: ```bash rm -rf ~/.gvm/archive/ ``` 好了,关于 `gvm` 的使用部分就讲完了。 聊聊为了解决“兼容性”问题,我做了哪些事情。 ## 解决 `gvm` 兼容性问题 如果你有使用过 `gvm`,会发现上文中的安装仓库并不是官方仓库,而是我的修改版本 [soulteary/gvm](https://github.com/soulteary/gvm)。那么在这里修改版本里,我到底做了哪些事情呢? 如果我们使用官方命令进行 `gvm` 安装,大概率会碰到几个问题导致不能安装,当我们使用 `install` 命令进行安装的时候,默认的版本会告诉我们发生了下面的错误: ```bash # gvm install go1.18.2 -B Installing go1.18 from binary source ERROR: Binary Go unavailable for this platform ``` 要解决这个问题,我们需要调整 `scripts/install` 相关程序实现: ```bash download_binary() { mkdir -p $GO_INSTALL_ROOT >> "${GVM_ROOT}/logs/go-${GO_NAME}-download-binary" 2>&1 if [ "$(uname)" == "Darwin" ]; then GVM_OS="darwin" osx_major_version="$(sw_vers -productVersion | cut -d "." -f 2)" if [ "${osx_major_version}" -ge 8 ]; then GVM_OS_VERSION="-osx10.8" elif [ "${osx_major_version}" -ge 6 ]; then GVM_OS_VERSION="-osx10.6" else display_error "Binary Go unavailable for this platform" rm -rf $GO_INSTALL_ROOT rm -f $GO_BINARY_PATH exit 1 fi else GVM_OS="linux" fi if [ "$(uname -m)" == "x86_64" ]; then GVM_ARCH="amd64" elif [ "$(uname -m)" == "ppc64le" ]; then GVM_ARCH="ppc64le" elif [ "$(uname -m)" == "aarch64" ]; then GVM_ARCH="arm64" else GVM_ARCH="386" fi ... ``` 上面这段程序中,其实存在两个问题。不过,我们先来解决上文提到的第一个问题:`Binary Go unavailable for this platform`。 默认的程序缺少了针对 M1 设备的判断: ```bash if [ "$(uname -m)" == "x86_64" ]; then GVM_ARCH="amd64" elif [ "$(uname -m)" == "ppc64le" ]; then GVM_ARCH="ppc64le" elif [ "$(uname -m)" == "aarch64" ]; then GVM_ARCH="arm64" else GVM_ARCH="386" fi ``` 先通过执行 `uname -m` ,得到 M1 设备的架构名称:`arm64`,接着完善上面的程序: ```bash if [ "$(uname -m)" == "x86_64" ]; then GVM_ARCH="amd64" elif [ "$(uname -m)" == "ppc64le" ]; then GVM_ARCH="ppc64le" elif [ "$(uname -m)" == "aarch64" ]; then GVM_ARCH="arm64" elif [ "$(uname -m)" == "arm64" ]; then GVM_ARCH="arm64" else GVM_ARCH="386" fi ``` 更新完毕程序之后,我们再次执行 `install` 命令,会得到一个新的错误: ```bash # gvm install go1.18 -B Installing go1.18.2 from binary source ERROR: Failed to download binary go ``` 再次审查上面的逻辑,可以定位到大概率出错的位置: ```bash osx_major_version="$(sw_vers -productVersion | cut -d "." -f 2)" if [ "${osx_major_version}" -ge 8 ]; then GVM_OS_VERSION="-osx10.8" elif [ "${osx_major_version}" -ge 6 ]; then GVM_OS_VERSION="-osx10.6" else display_error "Binary Go unavailable for this platform" rm -rf $GO_INSTALL_ROOT rm -f $GO_BINARY_PATH exit 1 fi ``` 推测大概率是 `sw_vers` 命令的输出结果产生了变化,导致整段程序结果和预期有偏差,我们尝试执行一下相关的命令,进行下验证: ```bash # sw_vers ProductName: macOS ProductVersion: 12.3.1 BuildVersion: 21E258 # sw_vers -productVersion 12.3.1 # sw_vers -productVersion | cut -d "." -f 2 3 ``` 经过上面的日志输出,我们可以确认之前的猜想是正确的,语义化版本的格式是 `{major}.{minor}.{revision}`,虽然预期中的结果是 `{major}`,但上面的命令执行后,我们得到的结果却是:`{minor}` 。 要解决这个问题也很简单,我们可以将 `cut -d "." -f 2` 调整为 `cut -d "." -f 1` 来解决在 macOS 11.6 ~ macOS 12 以上的系统版本中,原始程序获取版本出错的问题。 除此之外,我还调整了一些文档、安装、Golang 默认镜像等细节,如果感兴趣的话,可以浏览这里的变更记录。[https://github.com/soulteary/gvm/releases/tag/1.0.24](https://github.com/soulteary/gvm/releases/tag/1.0.24) ## 最后 在今年3月份苹果发布会之后,苹果官方所有设备都告别了 x86 处理器,完成了“去英特尔化”,自此所有设备都换上了自研的 M1 芯片。 这件事对于苹果而言不见得是一件坏事,但是对于开发者而言,可能是一件麻烦事:因为再也无法在官方渠道购买到非 ARM 架构的设备了,但是开发者生态相关的开源项目,其实有不少,都存在上文中提到的兼容性问题,**亟待改进**。 希望我的这篇文章,可以帮助你节约大量不必要的折腾时间,更安心的在 Mac 设备上使用 Golang 进行愉快的开发。 --EOF