前方有坑:小心 go.mod 中的 go directive

幸运草 2020年4月13日18:32:48前端框架评论阅读模式

在我的工作环境中,我通常通过变更GOROOT的方式来使用不同的Go版本。但在进行Go 1.14新增的overlapping interface的实验时,我遇到了一个问题。本文记录的就是这个问题的发现和解决过程,以备自己备忘,也希望能给广大Gopher带来启发。

1. 问题现象

在我进行overlapping interface试验时,我使用go 1.14编译器编译下面的代码:

// https://github.com/bigwhite/experiments/blob/master/go1.14-examples/overlapping_interface.go
package foo
type I interface {    f()    String() string}type J interface {    g()    String() string}
type IJ interface {    I    J}

但出乎意料的是我得到了如下的结果:

$go build overlapping_interface.go# command-line-arguments./overlapping_interface.go:14:2: duplicate method String

我一脸懵逼啊!我靠!Go 1.14的新增的overlapping interface机制居然不好用!之后我的第一反应就是检查是不是我的当前窗口中GOROOT设置有误,结果:GOROOT设置完全没错!

于是我又切换到一个Ubuntu 18.04环境下,同样用Go 1.14版本(for linux)编译上述代码,这回编译很顺利,没有报出像MacOS那样的错误。

接下来进入胡思乱想状态:) 难道是Go team在打包go1.14.darwin-amd64.tar.gz是忘记了这个功能?.... .....

2. 问题分析过程

我首先到go项目的issue列表查是否有人遇到与我相同的问题,居然没找到,基本肯定是我自己环境的问题了。于是提了一个issue(https://github.com/golang/go/issues/37728),看看有谁能帮忙分析一下原因。

不久,Go核心开发团队的Keith Randall提供了一些信息和诊断思路。首先他在他自己的Mac环境无法重现该问题,并也怀疑是不是我的安装方式和环境变量设置的问题。

于是,我尝试了更换一种Go 1.14的安装方式来重新安装Go 1.14 for MacOS:

$go get golang.org/dl/go1.14go: finding golang.org/dl latestgo: downloading golang.org/dl v0.0.0-20200302224518-306f3096cb2fgo: extracting golang.org/dl v0.0.0-20200302224518-306f3096cb2f
$go1.14 downloadDownloaded   0.0% (    14448 / 124931758 bytes) ...Downloaded   0.3% (   391280 / 124931758 bytes) ...... ...Downloaded  92.5% (115554516 / 124931758 bytes) ...Downloaded 100.0% (124931758 / 124931758 bytes)Unpacking /Users/tonybai/sdk/go1.14/go1.14.darwin-amd64.tar.gz ...Success. You may now run 'go1.14'
$go1.14 envGO111MODULE="on"GOARCH="amd64"GOBIN=""GOCACHE="/Users/tonybai/Library/Caches/go-build"GOENV="/Users/tonybai/Library/Application Support/go/env"GOEXE=""GOFLAGS=""GOHOSTARCH="amd64"GOHOSTOS="darwin"GOINSECURE=""GONOPROXY=""GONOSUMDB=""GOOS="darwin"GOPATH="/Users/tonybai/Go"GOPRIVATE=""GOPROXY="https://goproxy.cn,direct"GOROOT="/Users/tonybai/sdk/go1.14"GOSUMDB="off"GOTMPDIR=""GOTOOLDIR="/Users/tonybai/sdk/go1.14/pkg/tool/darwin_amd64"GCCGO="gccgo"AR="ar"CC="clang"CXX="clang++"CGO_ENABLED="1"GOMOD="/dev/null"CGO_CFLAGS="-g -O2"CGO_CPPFLAGS=""CGO_CXXFLAGS="-g -O2"CGO_FFLAGS="-g -O2"CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/cz/sbj5kg2d3m3c6j650z0qfm800000gn/T/go-build236598550=/tmp/go-build -gno-record-gcc-switches -fno-common"

我使用renew后的go1.14再来编译最初那段包含了overlapping interface的代码:

➜  /Users/tonybai/Go/src/github.com/bigwhite/experiments/go1.14-examples git:(master) $go1.14 versiongo version go1.14 darwin/amd64➜  /Users/tonybai/Go/src/github.com/bigwhite/experiments/go1.14-examples git:(master) $go1.14 build overlapping_interface.go# command-line-arguments./overlapping_interface.go:14:2: duplicate method String

问题依旧!

为了给Keith Randall提供更多有用信息,我在build时传入-x -v参数:

➜  /Users/tonybai/Go/src/github.com/bigwhite/experiments/go1.14-examples git:(master) $go1.14 build -x -v overlapping_interface.goWORK=/var/folders/cz/sbj5kg2d3m3c6j650z0qfm800000gn/T/go-build369480074command-line-argumentsmkdir -p $WORK/b001/cat >$WORK/b001/importcfg << 'EOF' # internal# import configEOFcd /Users/tonybai/Go/src/github.com/bigwhite/experiments/go1.14-examples/Users/tonybai/sdk/go1.14/pkg/tool/darwin_amd64/compile -o $WORK/b001/_pkg_.a -trimpath "$WORK/b001=>" -p command-line-arguments -lang=go1.12 -complete -buildid 00YuEePKlhV_qKTnJcVK/00YuEePKlhV_qKTnJcVK -goversion go1.14 -D _/Users/tonybai/Go/src/github.com/bigwhite/experiments/go1.14-examples -importcfg $WORK/b001/importcfg -pack -c=4 ./overlapping_interface.go# command-line-arguments./overlapping_interface.go:14:2: duplicate method String

一段时间后,眼尖的Keith Randall发现了蛛丝马迹:-lang=go 1.12。在我提供的上面build日志中,"-lang=1.12"被传给了Go 1.14 compiler,于是虽然贵为go 1.14版本编译器,但它也只能按照go 1.12版本的语法规范对我的代码进行检查和编译,这就是“罪魁祸首”,没跑了!

3. 问题解决

但是"-lang=1.12"怎么就凭空出现传给了Go 1.14编译器了呢?我查了所有编译器相关的环境变量,也没看到哪里设置了-lang=1.12。正当我处于迷茫状态时,突然go.mod这个文件名浮现在我的脑海中:go.mod中除了module名、一堆require、replace语句之外,还有go 1.12引入的go指示信息(directive)! 顿悟!!!

上面所有的go build执行都是在我本地的/Users/tonybai/go/src/github.com/bigwhite/experiments/go1.14-examples下执行的。而github.com/bigwhite/experiments这个repo的顶层go.mod文件内容如下:

module github.com/bigwhite/experiments
go 1.12

在Go 1.12的release note中,我们看到这样一段说明:

The go directive in a go.mod file now indicates the version of the language used by the files within that module.
go.mod文件中的go指示器指示该当前module中文件使用的Go语言版本。

当前,我的go.mod中go指示器指示的版本为go 1.12,那么即便是使用Go 1.14版本编译器编译该module下的文件,使用的也是go 1.12的语法。

解决方法:升级go.mod中的go指示器版本为go 1.14。升级后问题迎刃而解。

4. 小结

目前仅仅在使用比go.mod中go指示器版本低的go编译器对module下源文件进行编译,在报错的时候才会给出版本不匹配的提示。但是如果使用比go指示器版本高的编译器编译,即便出错,也没有警告提示。因此,在升级go版本时,要小心维护go.mod中的go directive,使其与你使用的go compiler版本匹配。

特别声明:以上文章内容仅代表作者本人观点,不代表变化吧观点或立场。如有关于作品内容、版权或其它问题请于作品发表后的30日内与变化吧联系。

  • 赞助本站
  • 微信扫一扫
  • weinxin
  • 加入Q群
  • QQ扫一扫
  • weinxin
幸运草
Go语言接口规则 前端框架

Go语言接口规则

Go语言接口规则 接口是一个或多个方法签名的集合。任何类型的方法集中只要拥有该接口对应的全部方法签名。就表示它 "实现" 了该接口,无须在该类型上显式声明实现了哪个接口。对应方法,是指有相同名称、参数...
Go语言中处理 HTTP 服务器 前端框架

Go语言中处理 HTTP 服务器

1 概述 包 net/http 提供了HTTP服务器端和客户端的实现。本文说明关于服务器端的部分。 快速开始: package main import (   "log"   "net/http" )...

发表评论