解决编译时cannot find package "xxx" in any of yyy
或package xxx is not in GOROOT (yyy)
或"./xxx" is relative, but relative import paths are not supported in module mode
问题。
问题
golang对包的相对引用处理十分蛋疼,这就导致了第三方包无法直接放在项目目录下进行引用、编译。
假定此时我们现在go目录结构如下。
./test/main.go
./test/hellowrold/helloworld.go
其中main.go
//main.go
package main
import (
"helloworld"
)
func main() {
helloworld.HelloWorld()
}
helloworld.go
//helloworld.go
package helloworld
import "fmt"
func HelloWorld() {
fmt.Println("Hello, World!")
}
此时在test目录下编译go build main.go
就会得到如下报错
main.go:5:2: package helloworld is not in GOROOT (/usr/local/go/src/helloworld)
这时,你可能会说,导入应该为import "test/helloworld"
。
那么修改main.go
如下
//main.go
package main
import (
"test/helloworld"
)
func main() {
helloworld.HelloWorld()
}
在进行编译,会发现问题依旧。
main.go:5:2: package test/helloworld is not in GOROOT (/usr/local/go/src/test/helloworld)
解决方案
通过报错可知,我们处理的包不在GOROOT
目录下,因此import无法找到该包。
所以,我们只要把helloworld
这个目录移动到GOROOT
也就是/usr/local/go/src/
下,问题就可以解决。
但是这个解决方案无法在打包代码的时候,把相关依赖一起打包。
因此我们需要先了解一下go的依赖管理。
依赖管理
Go 语言中用于管理依赖关系和组织项目的方式主要有两种:Go Module(Go 模块)和 GOPATH(Go 路径)。从兼容性上来说,Go Module 支持 Go 1.11 及更高版本,是 Go 官方推荐的依赖管理方式。GOPATH 模式是 Go 1.10 之前的默认方式,虽然仍然可用,但已不再是推荐的方式。
- GOROOT:
GOROOT
是 Go 的根目录,它指定了 Go 语言的安装路径。- 默认情况下,Go 会根据您的操作系统自动设置
GOROOT
,通常是指向 Go 的安装目录。例如,在 Windows 上,它可能是C:\Program Files\Go
。 GOROOT
用于定位 Go 的标准库(std),标准工具和其他 Go 相关的文件。- 一般情况下,不需要手动更改
GOROOT
,除非您需要自定义 Go 的安装位置。 - GOROOT/src下是go本身的包。
- go mod模式下,gopath/pkg/mod/找不到的包也会从root/src路径查找
- go path模式下,gopath/src/里面不存在的包也会从root/src路径查找
- GOPATH:
GOPATH
是 Go 的工作目录,它用于存储您的 Go 项目、源码和依赖包。- 默认情况下,
GOPATH
位于您的用户主目录下,例如$HOME/go
(Linux/macOS)或%USERPROFILE%\go
(Windows)。 - 在
GOPATH
下,通常有三个子目录:src
:存放您的 Go 项目的源码。pkg
:存放已编译的包(二进制文件)。bin
:存放 Go 可执行文件(例如,通过go install
安装的命令)。
- 项目的导入路径通常基于
GOPATH
。例如,如果您的GOPATH
是$HOME/go
,则项目的导入路径可能是import "example.com/myproject"
。 GOPATH
的设置可以包含多个路径,用冒号(Linux/macOS)或分号(Windows)分隔。这些路径可以包含多个工作空间,每个工作空间用于不同的项目。- go mod模式,第三方依赖包会在此目录下GOPATH/pkg/mod下
- go path模式,第三方依赖包会在此目录下GOPATH/pkg/src下
GO111MODULE
GO111MODULE
是一个用于控制 Go 模块行为的环境变量。它有三个可能的值:
off
:这个值会禁用 Go 模块,Go 将继续使用传统的 GOPATH 模式来管理依赖关系。这意味着项目的依赖将不会被保存在go.mod
文件中,而是存放在 GOPATH 中。on
:这个值启用 Go 模块,Go 将使用go.mod
文件来管理项目的依赖。这是 Go 1.11 版本之后的默认行为,它提供了更好的依赖管理和版本控制。auto
:这是默认值,Go 将尝试自动检测是否应该启用 Go 模块。通常,当项目目录包含go.mod
文件时,Go 会自动启用模块支持。
GO111MODULE
的设置可以影响您的项目的依赖管理方式。尤其是在升级 Go 版本或迁移项目时,您可能需要注意这个环境变量的设置。
总之,GO111MODULE
是一个用于控制 Go 模块行为的配置选项。在大多数情况下,启用 Go 模块是推荐的,因为它提供了更好的依赖管理和版本控制,但根据项目的需求,您可以根据需要配置它。
最终解决
通过了解go的依赖管理可知,我们只需要将相关helloworld包移动到src目录下,
./test/main.go
./test/src/hellowrold/helloworld.go
# CurrentDir = ./test/
# import "helloworld"
修改GOPATH环境变量,重新编译即可。
# Linux
export GOPATH=`pwd`
GO111MODULE=off go build main.go
# Windows
set GOPATH=CurrentDir
set GO111MODULE=off
go build main.go