这个代码怎么会编译不通过?Goland 新手常见问题解决:GOPATH 和 Go Modules 编译不成功

点击上方蓝色“Go语言中文网”关注我们,设个星标,每天学习Go语言

自从 Go1.11 开始,官方开始支持依赖管理,这就是 Go Modules。如果你对 Go Modules 还不熟悉,请一定要掌握,Go 1.13 默认会使用它。推荐阅读 Go 官方博文,GCTT 译文:Go Modules 的使用方法

新手学习的时候,对于 GOPATH 和 Go Modules 可能容易搞混,特别是,很多资料,依然是 GOPATH 的,而最新版本的一些 IDE 或编辑器可能默认使用 Modules,这样很容易出现一些“自己解决不了的问题”。(特别是即将发布的 Go1.13 会默认启用 Modules)

我们来看一个具体的例子,使用 Goland。(Go 版本:1.12.6)

创建项目 testgopath

如图,我们使用 GOPATH 模式。

image-20190706231208346.png

之后,创建如下目录和文件:

image-20190706233136771.png

其中,hello.go 文件代码如下:

package main
import "fmt"
func demo() {
   fmt.Println("This is demo")
}

helloworld.go 文件代码如下:

package main
import "fmt"
func main() {
   fmt.Println("Hello")
   demo()
}

(以上目录和代码来自球友咨询的问题)。

如何运行 GOPATH 项目

1)在 helloworld.go 文件右键弹出菜单,执行:Run "go build helloworld.go”,结果如下:

# command-line-arguments
src/hello/hellowrold.go:7:2: undefined: demo
Compilation finished with exit code 2

很明显,无论是 go run 还是 go build ,多个文件的项目,只提供单个文件是不行的。
2)配置运行:go build hello ,采用 Package 的运行方式,很可能结果是:

image-20190707170457999.png

can’t load package: package hello: unknown import path "hello": cannot find module providing package hello
3)go build hello,选择 Directory 的方式,没有出现错误。

image-20190707170620536.png

然而,执行 Run,我这里却报错了:

go: cannot find main module; see ‘go help modules’

从错误提示可知,启用的是 Go Modules 而不是 GOPATH。原因是我在全局配置了 GO111MODULE=on。实际上,Go1.13 开始,默认也会启用 Modules,所以这个错误会很常见。

知道原因,我们就可以通过配置环境变量,禁用 Modules 来解决:

image-20190707171035598.png

这次运行正常了!
4)如果你在 IDE 下怎么配置运行都不对时,你试试在终端执行。

GOPATH 模式下,对于 go build 来说,只要 GOPATH 正确,go build hello 总是能成功;

上面的规则,在终端验证成功了,那么我们可以肯定,IDE 不成功的原因是 GOPATH 不对。(一样要注意 GO111MODULE 这个环境变量的值)

对于 Goland IDE 来说,我们打开 Preferences,在 Go > GOPATH 下,配置 Project GOPATH:

image-20190707171218645.png

这时,我们再回到 Package 的运行模式重试,会发现也正常了。(如果不成功,一样需要加上 GO111MODULE=off)。

将项目改造为 module

将 GOPATH 项目改造为 module ,推荐阅读:迁移到 mod 只需 3 个步骤[1]。

针对上面的 testgopath,我们执行如下迁移动作:

1. cd /Users/xuxinhua/project/golang/testgopath

  1. mv src/* . && rm -rf src

    3. go mod init testgopath

这样,我们已经成功将项目改造为 module 模式了。

打开 Goland,很可能会发现多了一个 pkg 目录:

image-20190707172542398.png

原因是,module 模式会将依赖和缓存写入一个 GOPATH 目录中,一般情况下是全局的 GOPATH(通常是 $HOME/go),因为我们上面为该项目制定了 Project GOPATH,所以出现了 pkg 目录。我们可以改为 Global GOPATH,同时删除 pkg 目录。

如何运行 module 项目

和运行 GOPATH 项目一样,我们使用三种方式运行,go build helloworld.go 的 File 方式肯定不行,我们着重看 Package 和 Directory 方式。
1)Directory 方式,如下配置:

一切正常!
2)Package 方式,如下配置:

image-20190707174205533.png

这里说明两点:

  • Package path:testgopath/hello,也就是需要加上 module 的名字
  • 报错了,提示找不到包

虽然报错,我们强制执行,点击工具栏 Run,会出现弹窗:

image-20190707174405827.png

不管它,依然 Run(Continue Anyway)。会发现运行正常!这说明 Goland 误报了?

还记得我们通过 Goland 创建该项目时, 并没有选择 Go Modules(vgo) 模式。而且,如果你项目依赖了其他包,会提示各种找不到依赖包的错误。这时候,我们只需要将项目配置改为 Go Modules(vgo) 即可。

https://goproxy.cn 可能是一个更好的选择

  • 勾选 Enable Go Modules
  • 国内环境下,一般需要配置 Proxy,具体可以参考我之前的文章:Go module 模式下解决“墙”问题[2]

这样配置后,发现红叉没了,运行一切正常。

总结

无论我们使用什么编辑器(IDE),遇到问题时,我们如果知晓原理,都能够轻松解决掉;如果不知晓原理,通过瞎配置或其他方式碰巧对了,下次遇到其他问题,你依然一脸懵逼。

因此,知其然并知其所然很重要!GOPATH 和 Go Modules 的环境问题搞明白了吗?建议按照文章的步骤自己实际试试,加深理解,有问题留言沟通。

补充:随着 Modules 成为趋势,慢慢请忘掉 GOPATH 吧!

基于 Go1.13 的话,建议执行:go env -w GOPROXY=https://goproxy.cn,direct

参考资料

[1]迁移到 mod 只需 3 个步骤: https://studygolang.com/articles/17780

[2]Go module 模式下解决“墙”问题: https://studygolang.com/topics/8737

7