解决编译时cannot find package "xxx" in any of yyypackage 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 之前的默认方式,虽然仍然可用,但已不再是推荐的方式。

  1. 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路径查找
  2. 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 模块行为的环境变量。它有三个可能的值:

  1. off:这个值会禁用 Go 模块,Go 将继续使用传统的 GOPATH 模式来管理依赖关系。这意味着项目的依赖将不会被保存在 go.mod 文件中,而是存放在 GOPATH 中。
  2. on:这个值启用 Go 模块,Go 将使用 go.mod 文件来管理项目的依赖。这是 Go 1.11 版本之后的默认行为,它提供了更好的依赖管理和版本控制。
  3. 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