使用 Go 模块在包中组织本地代码

Organize local code in packages using Go modules

$GOPATH.

之外使用 Go 模块(go 版本 >= 1.11)时,我找不到将 main.go 中的一些代码提取到本地包中的方法

我没有导入任何需要包含在 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:

我找不到让本地包与 go 模块一起工作的方法。

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

go.mod:

module myprojectname

或(首选方式,详情请参阅@thepudd 的

module github.com/myname/myproject

然后像这样导入项目的包:

import myprojectname/stuff

import github.com/myname/myproject/stuff

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

还可以创建更深层次的项目结构。例如,您决定将源代码文件与其他文件(如应用程序配置、docker 文件、静态文件等)分开。让我们将 stuff 目录移动到 pkg 中,pkg/stuff 中的每个 go 文件仍然具有 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他们结构中的目录。

模块结构

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

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

例如,如果您的存储库是 github.com/my/repo,那么您将在存储库根目录中放置一个 go.mod,第一行显示为 module github.com/my/repo。这可以通过 cd'ing 到 repo root 和 运行 go mod init github.com/my/repo.

创建

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

Russ Cox 在 #26664 中评论:

For all but power users, you probably want to adopt the usual convention that one repo = one module. It's important for long-term evolution of code storage options that a repo can contain multiple modules, but it's almost certainly not something you want to do by default.

在模块 wiki 的 "Multi-module Repositories" 常见问题解答部分中有更多关于多模块存储库的内容。任何考虑改变上述建议的人都应该完整阅读该部分中的 6 个左右的常见问题解答。

在模块内安排包

设置 go.mod 后,您可以在包含 go.mod 的目录下的目录中,以及带有 go.mod。关于如何在包中安排代码的三篇好文章:

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

在同一模块中导入其他包

当导入另一个包含模块的包时,您总是使用包括模块路径在内的完整路径。即使在同一个模块中导入另一个包也是如此。例如,如果一个模块在其 go.mod 中将其身份声明为模块 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" 命中的部分内容)。

模块与存储库与包与导入路径

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

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

  • 一个 repository 包含一个或多个 Go modules(通常只有一个模块在 repository root 中)。
  • 每个模块包含一个或多个 Go packages.
  • 每个包包含一个或多个 Go 源文件,它们都位于一个单个目录.
  • Go源码:
    • package foo 语句声明它自己的包。
    • 自动访问同一包中的其他 Go 源代码。
    • 通过 导入路径 从另一个包导入代码,该路径在导入语句中提供,例如 import "github.com/my/repo/pkg1"。导入路径始终以该包的模块路径开头,无论该包是在同一模块还是不同模块中。