【问题标题】:Organize local code in packages using Go modules使用 Go 模块在包中组织本地代码
【发布时间】:2019-08-21 21:31:26
【问题描述】:

$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时:

  • 如果我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 代码(文件 go.mod)?

【问题讨论】:

  • 如果你想在同一个项目中开发多个模块,你将不得不等待Go 1.18/1.19,以及Go workspace mode的概念。

标签: go go-modules go-packages


【解决方案1】:

模块结构

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

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

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

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

Russ Cox 在#26664 发表评论:

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

模块 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
    └── pkg1.go

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

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

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

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

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

【讨论】:

  • 这是我在该主题上看到的最好的解释,谢谢
  • 以防 TLDR。答案的重点是:When importing another package with modules, you always use the full path including the module path. This is true even when importing another package in the same module.
  • 为什么每个人都必须继续使用github.com???问题说本地文件/代码...但 github.com 名称仍然出现..github.com 支付给你们所有人???
  • 如果您自己托管所有内容,包括 GIT,并且想将模块拆分为单独的存储库,我写了一些 notes
  • 它是一种类型吗? pkg2 会有 pkg​​2.go 对吗? @典型182
【解决方案2】:

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

go.mod:

module myprojectname

或(首选方式,请参阅@typical182 的answer below 了解详情)

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.modgo.summain.go 文件吗?
  • go.modgo.sum - 是的。 main.go 是您程序的入口点。如果您有单个入口点,只需将其放在根目录中,否则检查 github.com/golang-standards/project-layout 中的 cmd 目录 README.md 文件
  • repo 中提供的布局还有路要走吗?不要去模块改变它?
  • Go 模块不定义项目布局结构,您可以使用您选择的任何布局的 go 模块。您可以在此处阅读有关 go 模块的更多信息github.com/golang/go/wiki/Modules#modules
  • go.mod 中将您的模块定义为module myprojectname(而不是module github.com/myname/myproject)有点危险,并且很可能会在稍后以一种令您惊讶的方式失败。几乎总是最好使用完整路径,包括您打算最终发布它的主机名,或者选择作为主机名,就好像它有一天会发布一样(除非您正在做一些一次性的事情,比如从 go mod init tempmod 开始的快速测试和您打算很快删除该模块)。 answer 中有更多详细信息。
猜你喜欢
  • 2022-12-22
  • 1970-01-01
  • 2019-02-01
  • 1970-01-01
  • 2018-01-08
  • 2019-10-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多