构建 Go 项目时“package XXX is not in GOROOT”

Mic*_*hum 53 go goland

当我从这个项目中休息时,出现了一个奇怪的问题。启动 Goland 后,我在尝试运行我的项目时遇到了很多错误。

构建我的一个包时的具体错误是: start.go: package project/game is not in GOROOT (C:\Go\src\project\game)

我在下面有一个文件夹结构 C:\Users\username

go
|-src
   |-project
        |-game
            |-entity
                 |-whatever.go
            |-game_stuff.go
        |-server
Run Code Online (Sandbox Code Playgroud)

我的环境变量是这样的:

GOROOT=C:\Go 
GOPATH=C:\Users\ketchup\go 
Run Code Online (Sandbox Code Playgroud)

对于每个模块(项目/游戏/实体、项目/游戏、项目/服务器),我做了一个git mod init.

在构建时,Goland 将尝试运行:

C:\Go\bin\go.exe build -o C:\Users\ketchup\AppData\Local\Temp\___go_build_project_server.exe project/server
Run Code Online (Sandbox Code Playgroud)

并返回错误。

谁能帮我解决这个问题?有点迷失,因为我上次打开 Goland 时运行良好。甚至不确定要看什么方向 - 我对 Go 还很陌生,我不确定要看什么文档:\谢谢大家!

Sau*_*abh 125

在 Go 的较新版本(1.13 后)中,您不需要设置 、 等环境GOPATH变量GOBIN

\n

您还需要go.mod在项目根目录中有一个文件。这将使该目录成为 Go 模块。这也是 的所在地.git/。这意味着go.mod每个存储库只需要一个。在项目根目录中你可以做go mod init remote-repo.com/username/repository

\n

我在 macOS 上使用 Homebrew 安装了 Go,所以也是GOROOT如此/opt/homebrew/Cellar/go/1.17.5/libexec。该位置包含 Go 的标准库和运行时。

\n

test并且run命令以格式运行go COMMAND package_path/xxx。如果不指定 package_path./并仅运行go COMMAND xxx,编译器会假设模块xxx位于 GOROOT 中,并抛出错误,package xxx is not in GOROOT (path/to/GOROOT/src/xxx)因为它不存在。

\n

此行为是预期的,因为我们正在使用的包不是 Go SDK 的一部分,即不在GOROOT. 我们正在使用的包将最终出现在 go 工作区或当前工作目录中。运行go install编译并放入可执行二进制文件$GOBIN(又名$GOPATH/bin- 这里$GOPATH是 Go 工作区)。go build从包内部运行会编译并将可执行文件放入该目录中。

\n

您还没有列出包内的文件server/以及哪个文件具有主要功能,因此我将模拟计算器的 3 个工作流程,每个工作流程都展示了更多的复杂性。最后一个工作流程与您的目录结构类似。

\n

目录结构

\n

版本1:

\n
    \n
  • 开始使用包

    \n
  • \n
  • 基本功能

    \n
  • \n
\n
calculatorv1\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 go.mod                      <- go mod init github.com/yourname/calculatorv1\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 basic/\n    \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 add.go\n    \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 add_test.go\n    \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 main.go\n    \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 multiply.go\n    \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 multiply_test.go\n
Run Code Online (Sandbox Code Playgroud)\n

版本2:

\n
    \n
  • 更多功能

    \n
  • \n
  • 多个套餐

    \n
  • \n
\n
calculatorv2\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 go.mod                      <- go mod init github.com/yourname/calculatorv2\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 main.go\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 basic/\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 add.go\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 add_test.go\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 multiply.go\n\xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 multiply_test.go\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80 advanced/\n     \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 square.go\n     \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 square_test.go\n
Run Code Online (Sandbox Code Playgroud)\n

版本3:

\n
    \n
  • 更多功能

    \n
  • \n
  • 嵌套包

    \n
  • \n
\n
calculatorv3\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 go.mod                      <- go mod init github.com/yourname/calculatorv3\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 main.go\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 basic/\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 add.go\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 add_test.go\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 multiply.go\n\xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 multiply_test.go\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80 advanced/\n     \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 square.go\n     \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 square_test.go\n     \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 scientific/\n         \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 declog.go\n         \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 declog_test.go\n
Run Code Online (Sandbox Code Playgroud)\n
\n

工作流程

\n

注意:根据您使用的版本,替换xxxbasicadvanced、 或。advanced/scientific

\n
    \n
  • calculatorv1使用以下命令初始化项目目录中的 Go 模块( 、calculatorv2、 或之一calculatorv3go mod init

    \n
  • \n
  • 运行测试

    \n

    go test -v ./... (从项目根目录开始,递归执行所有测试套件)

    \n

    或者

    \n

    go test -v ./xxx (从项目根目录,运行包“xxx”中的测试套件)

    \n

    或者

    \n
    cd xxx/\ngo test -v            # (from inside the package)\n
    Run Code Online (Sandbox Code Playgroud)\n
  • \n
  • 编译并执行包

    \n

    go run ./... (从项目根目录,递归运行.go除测试之外的所有文件)

    \n

    或者

    \n

    go run ./xxx (从项目根目录运行.go“xxx”包中除测试之外的所有文件)

    \n

    或者

    \n
    cd xxx\ngo run .              # (from inside the package)\n
    Run Code Online (Sandbox Code Playgroud)\n

    注意:只有主包中的文件才是可执行的,即具有声明的文件package main。这意味着它go run ./xxx仅适用于版本 1,不适用于版本 2 和 3。因此,对于版本 2 和 3,请运行go run main.go

    \n
  • \n
\n
\n

代码

\n

很容易填写不完整的位

\n

版本1

\n

添加去

\n
package main\n\nfunc addition(x int, y int) int {\n    return x + y\n}\n
Run Code Online (Sandbox Code Playgroud)\n

add_test.go

\n
package main\n\nimport "testing"\n\nfunc TestAdd(t *testing.T) {\n\n    t.Run("adding two positive numbers", func(t *testing.T) {\n        sum := addition(2, 2)\n        expected := 4\n        \n        if sum != expected {\n            t.Errorf("Expected %d; but got %d", expected, sum)\n        }\n    })\n    \n    t.Run("adding two negative numbers", func(t *testing.T) {\n        sum := addition(-3, -4)\n        expected := -7\n\n        if sum != expected {\n            t.Errorf("Expected %d; but got %d", expected, sum)\n        }\n    })\n\n    t.Run("adding one positive and one negative integer", func(t *testing.T) {\n        sum := addition(1, -3)\n        expected := -2\n\n        if sum != expected {\n            t.Errorf("Expected %d; but got %d", expected, sum)\n        }\n    })\n    \n}\n
Run Code Online (Sandbox Code Playgroud)\n

主程序

\n
package main\n\nimport "fmt"\n\nfunc main() {\n    var num1 int = 1\n    var num2 int = 2\n    \n    sum := addition(num1, num2)\n    product := multiplication(num1, num2)\n\n    fmt.Printf("The sum of %d and %d is %d\\n", num1, num2, sum)\n    fmt.Printf("The multiplication of %d and %d is %d\\n", num1, num2, product)\n}\n
Run Code Online (Sandbox Code Playgroud)\n

版本2

\n

主程序

\n
package main\n\nimport (\n    "fmt"\n    "github.com/yourname/calculatorv2/basic"\n    "github.com/yourname/calculatorv2/advanced"\n)\n\nfunc main() {\n    var num1 int = 1\n    var num2 int = 2\n    \n    product := basic.Multiplication(num1, num2)\n    square := advanced.Square(num2)\n\n    fmt.Printf("The product of %d and %d is %d\\n", num1, num2, product)\n    fmt.Printf("The square of %d is %d\\n", num2, square)\n}\n
Run Code Online (Sandbox Code Playgroud)\n

乘法.go

\n
package basic\n\nfunc Multiplication(x int, y int) int {\n    return x * y\n}\n
Run Code Online (Sandbox Code Playgroud)\n

乘法测试.go

\n
package basic\n\nimport "testing"\n\nfunc TestMultiply(t *testing.T) {\n\n    t.Run("multiplying two positive numbers", func(t *testing.T) {\n        sum := Multiplication(2, 2)\n        expected := 4\n        \n        if sum != expected {\n            t.Errorf("Expected %d; but got %d", expected, sum)\n        }\n    })\n    \n    t.Run("multiplying two negative numbers", func(t *testing.T) {\n        sum := Multiplication(-3, -4)\n        expected := 12\n\n        if sum != expected {\n            t.Errorf("Expected %d; but got %d", expected, sum)\n        }\n    })\n\n    t.Run("multiplying one positive and one negative integer", func(t *testing.T) {\n        sum := Multiplication(1, -3)\n        expected := -3\n\n        if sum != expected {\n            t.Errorf("Expected %d; but got %d", expected, sum)\n        }\n    })\n    \n}\n
Run Code Online (Sandbox Code Playgroud)\n

广场GO

\n
package advanced\n\nfunc Square(x int) int {\n    return x * x\n}\n
Run Code Online (Sandbox Code Playgroud)\n

版本3

\n

主程序

\n
package main\n\nimport (\n    "fmt"\n    "github.com/yourname/calculatorv3/basic"\n    "github.com/yourname/calculatorv3/advanced"\n    "github.com/yourname/calculatorv3/advanced/scientific"\n)\n\nfunc main() {\n    var num1 int = 1\n    var num2 int = 2\n    var num3 float64 = 2\n    \n    product := basic.Multiplication(num1, num2)\n    square := advanced.Square(num2)\n    decimallog := scientific.DecimalLog(num3)\n\n    fmt.Printf("The product of %d and %d is %d\\n", num1, num2, product)\n    fmt.Printf("The square of %d is %d\\n", num2, square)\n    fmt.Printf("The decimal log (base 10) of %f is %f\\n", num3, decimallog)\n}\n
Run Code Online (Sandbox Code Playgroud)\n

广场GO

\n
package advanced\n\nfunc Square(x int) int {\n    return x * x\n}\n
Run Code Online (Sandbox Code Playgroud)\n

declog.go

\n
package scientific\n\nimport "math"\n\nfunc DecimalLog(x float64) float64 {\n    return math.Log10(x)\n}\n
Run Code Online (Sandbox Code Playgroud)\n

declog_test.go

\n
package scientific\n\nimport "testing"\n\nfunc TestDecimalLog(t *testing.T) {\n\n    t.Run("adding two positive numbers", func(t *testing.T) {\n        sum := DecimalLog(100)\n        expected := 2.0\n        \n        if sum != expected {\n            t.Errorf("Expected %f; but got %f", expected, sum)\n        }\n    })\n    \n    t.Run("adding two negative numbers", func(t *testing.T) {\n        sum := DecimalLog(10)\n        expected := 1.0\n\n        if sum != expected {\n            t.Errorf("Expected %f; but got %f", expected, sum)\n        }\n    })\n}\n
Run Code Online (Sandbox Code Playgroud)\n

  • 这是一个如此彻底和有用的答案!谢谢你!如果可以的话我会投两次票 (19认同)

Mic*_*hum 28

一个非常愚蠢的结论(主要是我的部分),但我的问题来自go mod init在每个文件夹中都完成了。从我所做的每个文件夹中删除go.mod和删除后,我可以毫无问题地构建(通过终端)go.depgo mod init

此外,我在 GoLand 中的包没有被检测到,因为我在设置中启用了 Go 模块。我禁用了它,GoLand 能够索引外部包和我自己的包。


小智 18

您可能将 GO111MODULE 设置为“on”,这将在 go mod 中使用。关闭 GO111MODULE 可能会解决此问题。

go env -w GO111MODULE=off
Run Code Online (Sandbox Code Playgroud)

  • 这不是一个解决方案。它引入了不同的问题。请添加说明该解决方案适合哪种情况。 (4认同)

end*_*ell 11

我不喜欢每个人都给出使用 github 模块命名约定的答案,就好像你必须以这种方式命名你的模块,或者将它们放在 github 上以便在本地环境中使用,所以这是我对 \xe2\x80 的答案\x94 希望 \xe2\x80\x94 帮助澄清一些时刻,我也对 Go 中的包和模块的命名感到困惑(我将故意为文件和文件夹使用不寻常的名称,这样你就可以看到你没有以特定方式命名文件以使 Go 项目正常工作(但您必须以特定方式构建它们)):

\n

因此,Golang 项目在概念上具有:

\n

(对于 Go 项目来说这是可选的)

\n
    \n
  • “存储库”\xe2\x80\x94 版本控制系统中存储项目源代码的位置(通常是一些远程存储库)。
  • \n
\n

(这是编译 Go 项目所必需的)

\n
    \n
  • “模块”\xe2\x80\x94 这个名称,从概念上来说,在 Go 项目中,用于指代整个程序/库(程序/库作为一个整体通常存储在某个远程存储库中)。

    \n
  • \n
  • “Packages” \xe2\x80\x94 在 Go 项目中,此名称应用于存储“包文件”的文字文件夹\xe2\x80\x94 这是在模块内组织代码的一种方式。

    \n
  • \n
\n

实际上,上面的概念意味着如果您想创建一个多包项目,为了编译它,您需要执行以下操作:

\n

(一步一步的例子:)

\n

-- 创建一个文件夹作为项目的根文件夹并使用cd进入该文件夹:

\n
$ mkdir some_amazing_project && cd some_amazing_project\n
Run Code Online (Sandbox Code Playgroud)\n

-- 当根目录下有一个有效的 go.mod 文件时,Go 代码的集合就成为一个模块,因此使用以下命令来生成它:

\n
$ go mod init my_amazing_module_lib_program\n
Run Code Online (Sandbox Code Playgroud)\n

(go.mod 文件的内部如下所示):

\n
module my_amazing_module_lib_program // this is your unique module identifier and you use this name as a relative root for your package names when you use "import"\n\ngo 1.19\n
Run Code Online (Sandbox Code Playgroud)\n

-- 现在创建一个 Go 程序的入口点,它是一个 go 文件,放置在根文件夹(go.mod 文件所在的位置)中,但您也可以将其放置在此模块内的任何子文件夹中(根文件夹),但不能更深(否则您将收到“ no Go files in <absolute_path>/some_amazing_project/some_subfolder/another_folder ”错误):

\n
$ touch some_main_file.go\n
Run Code Online (Sandbox Code Playgroud)\n
\n

(^^ 你可以随意命名这个文件,但约定是“main.go”)

\n
\n

-- 在该主文件内部,您必须指定该为主文件,并且该文件中必须有一个也称为main 的函数:

\n
// some_main_file.go\n\npackage main\n\nimport "fmt"\n\nfunc main() {\n    fmt.Println("hello there")\n}\n
Run Code Online (Sandbox Code Playgroud)\n

(这是迄今为止您的项目结构):

\n
some_amazing_project/\n    |- go.mod\n    |- some_main_file.go\n
Run Code Online (Sandbox Code Playgroud)\n

(现在,假设您想在此模块/程序/库中创建一个包。您需要执行以下操作):

\n

-- 在此模块中创建一个新文件夹,将其命名为“amazing_package”并在其中创建任何 .go 文件(文件名并不重要,因为仅包含这些文件的文件夹的名称用于引用来自其他 .go 文件的包):

\n
$ mkdir amazing_package && cd amazing_package && touch whatever_file.go\n
Run Code Online (Sandbox Code Playgroud)\n

-- 在whatever_file.go内部,您使用package关键字来指定该包的名称(仅当指定要从其他.go 文件中调用的函数名称时,才会使用该包名称 \xe2\x80\x94稍后会看到):

\n
// whatever_file.go\n\npackage wowow\n\nimport "fmt"\n\nfunc PrintWowow() {\n    fmt.Println("this is from wowow package")\n}\n
Run Code Online (Sandbox Code Playgroud)\n
\n

^^ 以大写字母开头的变量/常量/函数/类型/等(Go 中的任何“标识符”)将被导出,并可导入到其他包中。

\n
\n

(为了举例,让我们在此包中创建另一个文件):

\n
$ touch another_cool_file.go\n
Run Code Online (Sandbox Code Playgroud)\n

-- 在another_cool_file.go内部,您使用与在whatever_file.go中使用的相同的package关键字来指定该包的名称(因为这些文件在概念上位于同一个“包”中,这意味着实际上它们存储在同一文件夹中(如果将文件存储在同一文件夹中时使用不同的package关键字名称,则在编译期间将收到错误):

\n
// another_cool_file.go\n\npackage wowow\n\nimport "fmt"\n\nvar Some_var = 42 // this will also be automatically exported\n\nfunc PrintAnotherWow() {\n    fmt.Println("this is also from wowow package, but from a different file")\n}\n
Run Code Online (Sandbox Code Playgroud)\n

(这是迄今为止您的项目结构):

\n
some_amazing_project/\n    |- go.mod\n    |- some_main_file.go\n    |- amazing_package/\n            |- whatever_file.go\n            |- another_cool_file.go\n
Run Code Online (Sandbox Code Playgroud)\n

-- 现在,要从整个模块内的任何其他 .go 文件引用“PrintWowow”和“PrintAnotherWow”函数,您可以使用go.mod文件中的唯一模块标识符以及相对于该标识符(指定包的位置)的路径要导入的文件夹(在本例中我将使用 some_main_file.go 我们像这样重写它):

\n
// some_main_file.go\n\npackage main\n\nimport (\n    "fmt"\n    "my_amazing_module_lib_program/amazing_package"\n)\n\nfunc main() {\n    fmt.Println("hello there")\n\n    // now you use the name of the package specified by the "package" keyword inside of those files from *amazing_package* folder\n    wowow.PrintWowow()\n    wowow.PrintAnotherWow()\n    fmt.Println("and this is Some_var from the same imported package:", wowow.Some_var)\n}\n
Run Code Online (Sandbox Code Playgroud)\n

这是在my_amazing_module_lib_programgo build . && go run . (根)文件夹内运行的结果:

\n
hello there\nthis is from wowow package\nthis is also from wowow package, but from a different file\nand this is Some_var from the same imported package: 42\n
Run Code Online (Sandbox Code Playgroud)\n

  • 写得很棒!感谢您演示了如何在不受 Github 影响的情况下做到这一点以及模块系统如何发挥作用。 (5认同)
  • 感谢您在没有大量货物崇拜的情况下布置了这个。 (2认同)

Ram*_*zis 10

对于在停止使用 GoLand 后确实希望模块与 GoLand 一起工作的任何人,请确保在首选项中选中“启用 Go 模块集成”,如下所示:

启用 Go 模块集成


Jan*_*lek 6

所以看起来如果你运行 go mod init 'xxx' xxx 是你项目的核心名称。在那里主要包的完整名称是“xxx/main”,所以如果你的项目根文件夹是这样的:

root -> go mod init xxx
 |- main.go -> package "main"
 |- tools
      |- helper.go -> package "tools"
Run Code Online (Sandbox Code Playgroud)

如果你想从 main.go 导入工具包,你需要导入这个“xxx/tools”