使用Go模块组织软件包中的本地代码

TPP*_*PPZ 8 go go-packages go-modules

main.go当在之外使用Go模块(go版本> = 1.11)时,找不到在本地包中分解出某些代码的方法$GOPATH

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

文件main.go

package main

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

func main() {
    stuff.PrintBaz()
}
Run Code Online (Sandbox Code Playgroud)

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

package stuff

import "log"

type Bar struct {
    Baz int
}

func PrintBaz() {
    baz := Bar{42}
    log.Printf("Bar struct: %v", baz)
}
Run Code Online (Sandbox Code Playgroud)

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

module foo

go 1.12
Run Code Online (Sandbox Code Playgroud)

执行时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代码?

Vad*_*man 12

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

go.mod:

module myprojectname
Run Code Online (Sandbox Code Playgroud)

或(首选方式,请参阅@ typical182的评论以获取详细信息)

module github.com/myname/myproject
Run Code Online (Sandbox Code Playgroud)

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

import myprojectname/stuff
Run Code Online (Sandbox Code Playgroud)

要么

import github.com/myname/myproject/stuff
Run Code Online (Sandbox Code Playgroud)

软件包文件stuff应位于项目stuff目录中。您可以根据需要命名这些文件。

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

import myprojectname/pkg/stuff
Run Code Online (Sandbox Code Playgroud)

没有什么能阻止您在层次结构中创建更多级别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”中将模块定义为“module myprojectname”(而不是“module github.com/myname/myproject”)有点危险,并且很可能会在以后以令您惊讶的方式失败。使用完整路径(包括您最终打算发布它的主机名)几乎总是更好,或者选择主机名,就好像有一天它将发布一样(除非您正在做一些一次性的事情,例如启动“go mod init”的快速测试tempmod` 并且您打算尽快删除该模块)。此[答案](/sf/answers/4012014611/)中有更多详细信息。 (2认同)

typ*_*182 10

模块结构

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

  • go.mod每个存储库使用一个,
  • 将单个go.mod文件放在存储库根目录中,然后
  • 将存储库名称用作模块module行中声明的模块路径。go.mod
    • (如果您使用的是虚荣网址,那么您将使用虚荣网址而不是中的存储库名称go.mod)。

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

遵循这一点可以帮助您在使用模块时走上幸福的道路,并且可以避免多个微妙之处。

拉斯·考克斯(Russ Cox)在#26664中评论:

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

在模块Wiki上的“多模块存储库”常见问题解答部分中,有关多模块存储库的更多信息。考虑改写以上建议的任何人都应完整阅读该部分中的那6条左右的FAQ。

在模块内安排包装

设置好之后go.mod,您可以将软件包安排在目录中,但是您会看到适合放在包含的目录下的目录go.mod以及带有的目录中go.mod。关于如何在程序包中排列代码的三篇好文章:

这些文章是在引入模块之前的经典文章,但是其中的思想仍然适用于如何在模块中排列软件包。

在同一模块中导入其他软件包

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

repo/
??? go.mod      <<<<< Note go.mod is located in repo root
??? pkg1
?   ??? pkg1.go
??? pkg2
    ??? pkg1.go
Run Code Online (Sandbox Code Playgroud)

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

模块,存储库,软件包,导入路径

Go模块是一组相关的Go软件包的集合,这些版本一起作为一个单元进行了版本控制。模块记录精确的依赖要求并创建可复制的构建。

总结存储库,模块和软件包之间的关系:

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

  • 这是我在该主题上看到的最好的解释,谢谢 (6认同)
  • 万一TLDR。答案的关键点是:`当导入另一个带有模块的包时,您始终使用包括模块路径的完整路径。即使在同一模块中导入另一个包也是如此。 (3认同)