53

main.go$GOPATH. _

我没有导入任何需要包含的外部依赖go.mod项,我只是想在本地组织这个 Go 模块的源代码。

文件main.go

package main

// this import does not work
import "./stuff"

func main() {
    stuff.PrintBaz()
}

该文件./stuff/bar.go(假装是本地包):

package stuff

import "log"

type Bar struct {
    Baz int
}

func PrintBaz() {
    baz := Bar{42}
    log.Printf("Bar struct: %v", baz)
}

文件go.mod(命令go mod init foo):

module foo

go 1.12

执行时go run main.go

  • 如果我import "./stuff",那么我看到了build command-line-arguments: cannot find module for path _/home/<PATH_TO>/fooprj/stuff
  • 如果我import "stuff",那么我看到了build command-line-arguments: cannot load stuff: cannot find module providing package stuff
  • 如果我import stuff "./stuff"使用包别名,那么我会再次看到:build command-line-arguments: cannot find module for path _/home/<PATH_TO>/fooprj/stuff.

我找不到使本地包与 go 模块一起使用的方法。

  • 上面的代码有什么问题?
  • 如何将本地包导入到使用 Go 模块(文件go.mod)定义的项目中的其他 Go 代码中?
4

2 回答 2

79

模块结构

最常见和最简单的方法是:

  • go.mod每个存储库使用一个,并且
  • 将单个go.mod文件放在存储库根目录中,然后
  • module使用存储库名称作为在行中声明的模块路径go.mod
    • (如果您使用的是自定义导入路径me.io/mymod而不是使用基于 VCS 主机的导入路径,那么您将使用自定义导入路径而不是您的存储库名称go.mod)。

例如,如果您的 repo 是,那么您将在 repo 根目录中github.com/my/repo放置一个,第一行为. 这可以通过'ing 到 repo root 并运行来创建。go.modmodule github.com/my/repocdgo mod init github.com/my/repo

遵循这一点可以帮助您在使用模块的过程中保持快乐,并避免多重微妙之处。

Russ Cox 在#26664中评论:

对于除高级用户之外的所有用户,您可能希望采用通常的约定,即一个 repo = 一个模块。repo可以包含多个模块,这对于代码存储选项的长期发展很重要,但几乎可以肯定默认情况下您不想这样做。

在模块 wiki 的“多模块存储库” FAQ 部分中有更多关于多模块存储库的信息。任何考虑放弃上述建议的人都应该完整阅读该部分中的这 6 个左右的常见问题解答。

在模块中排列包

一旦你设置好你的go.mod,你可以将你的包安排在目录中,但是你认为在go.mod包含go.mod. 关于如何在包中安排代码的三篇好文章:

这些文章是模块引入之前的经典,但其中的理念仍然适用于如何在模块中安排包。

在同一模块中导入其他包

在导入另一个带有模块的包时,您始终使用包含模块路径的完整路径。即使在同一模块中导入另一个包也是如此。例如,如果一个模块在其go.modas module中声明了它的身份github.com/my/repo,并且你有这个组织:

repo/
├── go.mod      <<<<< Note go.mod is located in repo root
├── pkg1
│   └── pkg1.go
└── pkg2
    └── pkg2.go

然后pkg1将其对等包导入为import "github.com/my/repo/pkg2". 请注意,您不能使用相对导入路径,例如import "../pkg2"import "./subpkg"。(这是 OP 上面提到的一部分import "./stuff")。

模块 vs. 存储库 vs. 包 vs. 导入路径

Go 模块是相关 Go 包的集合,它们作为一个单元一起进行版本控制。模块记录精确的依赖需求并创建可重现的构建。

总结存储库、模块和包之间的关系:

  • 一个存储库包含一个或多个 Go模块(通常恰好是存储库根目录中的一个模块)。
  • 每个模块包含一个或多个 Go
  • 每个包都包含一个或多个 Go源文件,它们都位于一个目录中。
  • 去源代码:
    • 用声明声明自己的包package foo
    • 自动访问同一包中的其他 Go 源代码。
    • 通过导入语句中提供的导入路径从另一个包导入代码,例如import "github.com/my/repo/pkg1". 导入路径始终以该包的模块路径开始,无论该包是在同一个模块中还是在不同的模块中。
于 2019-08-01T17:50:46.757 回答
52

首先,您必须为您的项目选择一个名称并将其写入 go.mod 文件。此名称属于项目的根目录。您创建的每个新包都必须位于其自己的子目录中,并且其名称应与目录名称匹配。

去.mod:

module myprojectname

或(首选方式,有关详细信息,请参阅下面的@典型182 的答案

module github.com/myname/myproject

然后导入项目的包,例如:

import myprojectname/stuff

或者

import github.com/myname/myproject/stuff

包文件stuff应位于项目stuff目录中。您可以随意命名这些文件。

也可以创建更深层次的项目结构。例如,您决定将源代码文件与其他文件(如应用配置、docker 文件、静态文件等)分开。让我们移动stuff目录,里面pkg的每个go文件pkg/stuff仍然有stuff包名。要导入东西包,只需编写:

import myprojectname/pkg/stuff

没有什么能阻止您在层次结构中创建更多级别,例如github.com/myuser/myproject/pkg/db/provider/postgresql

  • github.com/myuser/myproject- 项目名。
  • postgresql- 包裹名字。
  • pkg/db/provider/postgresql- 相对于项目根目录的包路径。

您可以在此处阅读有关 go 模块的更多信息:https ://github.com/golang/go/wiki/Modules

查看此存储库以获取有关项目组织中使用的各种模式的有用信息:https ://github.com/golang-standards/project-layout如果您进入pkg目录,您会发现哪些开源项目pkg在其结构中使用目录。

于 2019-03-31T16:21:07.343 回答