Golang 模块(Module)官方手册

 官方原文: https://github.com/golang/go/wiki/Modulesgit

 

 

 

Go 1.11包括此处建议的对版本模块的初步支持模块是Go 1.11中的实验性加入功能,并计划归入反馈并最终肯定 Go 1.14中的功能。即便某些细节可能会更改,未来的发行版也将支持使用Go 1.十一、1.12和1.13定义的模块。github

最初的原型vgo2018年2月宣布。2018年7月,版本化的模块进入了主Go存储库。golang

请经过现有问题或新问题以及经验报告提供有关模块的反馈算法

近期变更

Go 1.13中对模块进行了重大改进和更改。shell

若是使用模块,请务必仔细阅读Go 1.13发行说明模块部分数据库

三个值得注意的变化:json

  1. go工具如今默认为从https://proxy.golang.org上的公共Go模块镜像下载模块,而且还默认为针对https://sum.golang.org的公共Go校验和数据库验证下载的模块(不管源如何)vim

    • 若是您有私人代码,则极可能应该配置GOPRIVATE设置(例如go env -w GOPRIVATE=*.corp.com,github.com/secret/repo),或者配置更细粒度的变体,GONOPROXY或者GONOSUMDB支持使用频率较低的用例。有关更多详细信息,请参见文档
  2. GO111MODULE=auto若是找到任何go.mod,即便在GOPATH内部,也将启用模块模式。(在Go 1.13以前,GO111MODULE=auto永远不会在GOPATH中启用模块模式)。api

  3. go get 参数已更改:缓存

    • go get -u(不带任何参数)如今仅升级当前软件包的直接和间接依赖关系,而再也不检查整个模块
    • go get -u ./... 从模块根目录升级模块的全部直接和间接依赖关系,如今不包括测试依赖关系。
    • go get -u -t ./... 类似,但也升级了测试依赖项。
    • go get再也不受支持-m(由于go get -d因为其余更改,它会在很大程度上与重叠;您一般能够替换go get -m foogo get -d foo)。

请参阅发行说明,以获取有关这些更改和其余更改的更多详细信息。

 

 

快速开始

详细信息将在本页面的其他部分中介绍,但这是一个从头开始建立模块的简单示例。

在GOPATH以外建立目录,并可选地初始化VCS:

$ mkdir -p /tmp/scratchpad/repo
$ cd /tmp/scratchpad/repo
$ git init -q
$ git remote add origin https://github.com/my/repo

初始化一个新模块:

$ go mod init github.com/my/repo

go: creating new go.mod: module github.com/my/repo

编写代码:

$ cat <<EOF > hello.go
package main

import (
    "fmt"
    "rsc.io/quote"
)

func main() {
    fmt.Println(quote.Hello())
}
EOF

构建并运行:

$ go build -o hello
$ ./hello

Hello, world.

go.mod文件已更新为包括依赖项的显式版本,v1.5.2这里是语义化版本号标签:

$ cat go.mod

module github.com/my/repo

require rsc.io/quote v1.5.2

平常工做流程

请注意,go get上面的示例中没有要求。

典型的平常工做流程能够是:

  • .go根据须要将导入语句添加到您的代码中。
  • 标准命令(例如go build或)go test会根据须要自动添加新的依赖关系,以实现导入(更新go.mod和下载新的依赖关系)。
  • 须要时,可使用go get foo@v1.2.3go get foo@masterfoo@tip带有Mercurial)go get foo@e3702bed2,或go.mod直接编辑等命令来选择更具体的依赖关系版本

您可能会使用的其余常见功能的简要介绍:

  • go list -m all—查看将在构建中用于全部直接和间接依赖关系的最终版本(详细信息
  • go list -u -m all—查看全部直接和间接依赖项的可用次要和补丁升级(详细信息
  • go get -u ./...go get -u=patch ./...(从模块根目录)—将全部直接和间接依赖关系更新为最新的次要或补丁升级(忽略预发行版)(详细信息
  • go build ./...go test ./...(从模块根目录)—构建或测试模块中的全部软件包(详细信息
  • go mod tidy go.modOS,架构和构建标签的其余组合中修剪全部不须要的依赖项,并添加其余依赖项所需的任何依赖项(详细信息
  • replace指令或gohack—使用派生,本地副本或依赖项的确切版本(详细信息
  • go mod vendor—建立vendor目录的可选步骤(详细信息

在阅读了有关“新概念”的下四个部分以后,您将得到足够的信息来开始使用大多数项目的模块。查看上面的目录(包括此处的FAQ常见问题解答)以使本身熟悉更详细的主题列表也颇有用

新概念

这些部分对主要的新概念进行了高级介绍。有关更多详细信息和原理,请观看Russ Cox的这段40分钟的介绍性视频,其中介绍了设计背后的理念正式的建议文档或更为详细的初始vgo博客系列

模块

一个模块是一些以版本做为单元相关的包的集合。

模块记录精确的依赖要求并建立可复制的构建。

一般,版本控制存储库仅包含在存储库根目录中定义的一个模块。单个存储库中支持多个模块,可是一般,与每一个存储库中的单个模块相比,这将致使正在进行的工做更多)。

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

  • 一个存储库包含一个或多个Go模块。
  • 每一个模块包含一个或多个Go软件包。
  • 每一个软件包都在一个目录中包含一个或多个Go源文件。

模块必须根据在语义版本语义化版本号,一般在形式v(major).(minor).(patch),如 v0.1.0v1.2.3,或v1.5.0-rc.1领导v是必需的。若是使用Git,则标记发布会提交其版本。公共和私有模块存储库和代理均可以使用(请参阅下面的常见问题解答)。

go.mod

模块由Go源文件树定义,该go.mod文件在树的根目录中。模块源代码可能位于GOPATH以外。有四种指令:modulerequirereplaceexclude

这是go.mod定义模块的示例文件github.com/my/thing

module github.com/my/thing

require (
    github.com/some/dependency v1.2.3
    github.com/another/dependency/v4 v4.0.0
)

模块go.mod经过module提供模块path指令在其声明中声明其身份模块中全部软件包的导入路径将模块路径共享为公共前缀。模块路径和从go.mod到软件包目录的相对路径共同肯定了软件包的导入路径。

例如,若是要为存储库建立一个模块,该模块github.com/my/repo将包含两个带有导入路径github.com/my/repo/foo和的软件包github.com/my/repo/bar,则go.mod文件中的第一行一般会将模块路径声明为module github.com/my/repo,相应的磁盘结构能够是:

repo
|-- bar
|   `-- bar.go
|-- foo
|   `-- foo.go
`-- go.mod

在Go源代码中,将使用完整路径(包括模块路径)导入软件包。例如,若是一个模块在go.modas中声明其身份module example.com/my/module,则使用者能够执行如下操做:

import "example.com/my/module/mypkg"

mypkg将从模块导入包example.com/my/module

excludereplace指令仅在当前(“主”)模块上运行。在构建主模块时,将忽略除主模块之外的其余模块中的指令excludereplace指令。replaceexclude语句,所以,容许在本身的构建主要模块的彻底控制权,也没有受制于由依赖于彻底控制。(有关什么时候使用指令的讨论,请参见下面常见问题解答replace)。

版本选择

若是您在源代码中添加了一个还没有被requirein 覆盖的新导入,则go.mod大多数go命令(例如“ go build”和“ go test”)将自动查找适当的模块,并将该新直接依赖项的最高版本添加到您的模块go.modrequire指令。例如,若是您的新导入对应于依赖项M,其最新标记版本为v1.2.3,则模块的go.mod结尾将为require M v1.2.3,这表示模块M是容许版本> = v1.2.3(且<v2,由于v2被认为不兼容)的依赖项与v1)。

最小的版本选择算法用来选择在构建中使用的全部模块的版本。对于构建中的每一个模块,经过最小版本选择选择的版本始终是主模块中指令或其依赖项之一明确列出的版本的语义上最高的版本require

例如,若是您的模块依赖于具备A的模块A require D v1.0.0,而您的模块也依赖于具备A的模块B require D v1.1.1,则最小的版本选择将选择v1.1.1D包含在构建中(假设它是列出的最高require版本)。v1.1.1即便稍后某个v1.2.0D可用,对D的选择仍保持一致这是模块系统如何提供100%可复制构建的示例。准备就绪后,模块做者或用户能够选择升级到D的最新可用版本,或为D选择一个显式版本。

有关最小版本选择算法的简要原理和概述,请参阅官方建议书的“高保真度构建”部分,或查看更详细的vgo博客系列

要查看所选模块版本的列表(包括间接依赖关系),请使用go list -m all

另请参见下面的“如何升级和降级依赖项”部分和“如何将版本标记为不兼容?” 下面的常见问题解答。

语义导入版本控制

多年以来,官方的Go常见问题解答已在软件包版本管理中包括如下建议:

“面向公共用途的软件包应在发展过程当中尝试保持向后兼容性。Go1兼容性指南在此处是很好的参考:请勿删除导出的名称,鼓励带标签的复合文字等等。若是须要不一样的功能,请添加一个新名称,而不是更改旧名称。若是须要彻底中断,请建立一个具备新导入路径的新软件包。”

最后一句特别重要-若是破坏兼容性,则应更改软件包的导入路径。使用Go 1.11模块,该建议被正式化为导入兼容性规则

“若是旧软件包和新软件包具备相同的导入路径,则新软件包必须与旧软件包向后兼容。”

当v1或更高版本的软件包进行向后不兼容的更改时,召回语义化版本号须要对主要版本进行更改。遵循导入兼容性规则和语义化版本号的结果称为语义导入版本控制,其中主要版本包含在导入路径中-这可确保在主要版本因为兼容性中断而增长时,导入路径都会更改。

因为语义导入版本控制,选择加入Go模块的代码必须遵照如下规则

  • 跟随语义化版本号(示例VCS标签为v1.2.3)。
  • 若是模块的版本为v2或更高版本,则必须/vNgo.mod文件(例如module github.com/my/mod/v2require github.com/my/mod/v2 v2.0.1)和包导入路径(例如import "github.com/my/mod/v2/mypkg")中使用的模块路径的末尾将主要版本做为包括在内这包括go get命令中使用的路径(例如,go get github.com/my/mod/v2@v2.0.1请注意,在该示例中同时包含a /v2和a @v2.0.1。一种考虑方式是模块名称如今包括/v2,所以/v2不管什么时候使用模块名称,都包括)。
  • 若是模块的版本为v0或v1,则在模块路径或导入路径中不要包含主版本。

一般,具备不一样导入路径的软件包是不一样的软件包。例如,与math/rand是不一样的软件包crypto/rand若是不一样的导入路径是因为导入路径中出现的主要版本不一样而致使的,则也是如此。所以,与example.com/my/mod/mypkg包是一个不一样的包example.com/my/mod/v2/mypkg,二者均可以在一个单一版本中导入,这除其余优势外还有助于解决钻石依赖问题,而且还容许在替换v2方面实施v1模块,反之亦然。

有关语义导入版本控制的更多详细信息,请参见命令文档“模块兼容性和语义版本控制”部分,go有关语义版本控制的更多信息,请参见https://语义化版本号.org

到目前为止,本节的重点是已选择加入模块并导入其余模块的代码。可是,将主要版本置于v2 +模块的导入路径中可能会与Go的较早版本或还没有选择加入模块的代码产生不兼容性。为了解决这个问题,上述行为和规则有三种重要的过渡性特殊状况或例外。随着愈来愈多的程序包加入模块,这些过渡性异常将再也不重要。

三个例外:

  1. gopkg.in

    使用导入路径gopkg.in(以gopkg.in/yaml.v1开头)的现有代码gopkg.in/yaml.v2即便选择加入模块,也能够继续将这些格式用于其模块路径和导入路径。

  2. 导入非模块v2 +软件包时为“ +不兼容”

    模块能够导入还没有选择加入模块的v2 +软件包。具备有效v2 + 语义化版本号标签的非模块v2 +软件包+incompatible在导入模块的go.mod文件中记录后缀+incompatible后缀表示,即便V2 +包有一个有效的V2 + 语义化版本号标签,例如v2.0.0,使V2 +包没有主动选择的模块和假设,所以该V2 +包都没有被与语义进口版本的含义的理解产生以及如何在导入路径中使用主要版本。所以,当以模块模式运行时go该工具会将非模块v2 +软件包视为该软件包的v1版本系列的(不兼容)扩展,并假定该软件包不了解语义导入版本控制,而且+incompatible后缀表示该go工具正在这样作。

  3. 未启用模块模式时的“最小模块兼容性”

    为了帮助向后兼容,对Go版本1.9.7 +,1.10.3 +和1.11进行了更新,以使使用这些发行版构建的代码可以更轻松地正确使用v2 +模块,无需修改现有代码。此行为称为“最小模块兼容性”,而且仅在禁用该工具的完整模块模式时才生效go,例如您GO111MODULE=off在Go 1.11中进行了设置,或者正在使用Go 1.9.7+或1.10.3版本+。当依靠Go 1.9.7 +,1.10.3 +和1.11中的这种“最小模块兼容性”机制时,选择模块的软件包不会在任何导入的v2 +模块的导入路径中包含主版本。相比之下,选择在模块必须包括在导入路径主要版本导入任何V2 +模块(为了正确导入V2 +模块时的go工具在全模块模式语义进口版本的充分认识工做)。

有关发布v2 +模块所需的确切机制,请参阅下面的“发布模块(v2或更高版本)”部分。

如何使用模块

如何安装和激活模块支持

要使用模块,两个安装选项是:

安装后,您能够经过如下两种方式之一激活模块支持:

  • go$GOPATH/src以外的目录中调用命令,并go.mod在当前目录或其任何父目录中使用有效文件,而且未GO111MODULE设置(或显式设置为auto环境变量
  • 调用go带有GO111MODULE=on环境变量设置命令

如何定义模块

go.mod现有项目建立一个

  1. 导航到GOPATH以外的模块源代码树的根目录:

    $ cd <project path outside $GOPATH/src>         # e.g., cd ~/projects/hello

    请注意,在GOPATH以外,您无需进行设置GO111MODULE便可激活模块模式。

    或者,若是要在GOPATH中工做:

    $ export GO111MODULE=on                         # manually active module mode
    $ cd $GOPATH/src/<project path>                 # e.g., cd $GOPATH/src/you/hello
  2. 建立初始模块定义并将其写入go.mod文件:

    $ go mod init

    此步骤从任何现有文件或其余全部9种受支持的依赖项格式中的任何一种转换,添加require语句以匹配现有配置。dep Gopkg.lock

    go mod init一般可使用辅助数据(例如VCS元数据)来自动肯定适当的模块路径,可是若是go mod init状态不能自动肯定模块路径,或者若是您须要以其余方式覆盖该路径,则能够提供模块路径做为的可选参数go mod init,例如:

    $ go mod init github.com/my/repo

    请注意,若是您的依赖项包括v2 +模块,或者正在初始化v2 +模块,则在运行后,go mod init您可能还须要编辑go.mod.go代码,以添加/vN导入路径和模块路径,如上面“语义导入版本控制”部分所述。即便go mod init自动从dep或其余依赖项管理器转换了您的依赖项信息,这也适用(所以,在运行以后go mod init,一般go mod tidy只有在成功运行go build ./...或相似操做后才能运行,这是本节中显示的顺序)。

  3. 生成模块。从模块的根目录执行时,该./...模式将匹配当前模块中的全部软件包。 go build会根据须要自动添加缺乏或未转换的依赖项,以知足此特定构建调用的导入需求:

    $ go build ./...
  4. 按照配置测试模块,以确保它能够与所选版本一块儿使用:

    $ go test ./...
  5. (可选)运行模块的测试以及全部直接和间接依赖项的测试,以检查不兼容性:

    $ go test all

在标记发行版以前,请参见下面的“如何准备发行版”部分。

有关全部这些主题的更多信息,可在golang.org找到官方模块文档的主要入口点

如何升级和降级依赖项

平常的依赖关系升级和降级应该使用“ go get”完成,它将自动更新go.mod文件。或者,您能够go.mod直接编辑

此外,执行“执行构建”,“执行测试”甚至“执行列表”之类的go命令将根据须要自动添加新的依赖关系,以知足导入要求(更新go.mod和下载新的依赖关系)。

要查看全部直接和间接依赖项的可用次要和补丁升级,请运行go list -u -m all

要将当前模块的全部直接和间接依赖关系升级到最新版本,能够在模块根目录中运行如下命令:

  • go get -u ./...使用最新的次要版本或补丁程序版本(并添加-t以升级测试依赖项)
  • go get -u=patch ./...使用最新的补丁程序发行版(并添加-t以升级测试依赖项)

go get foo更新到的最新版本foogo get foo等效于go get foo@latest—换句话说,@latest若是未@指定版本则为默认值

在本节中,“最新”是带有语义化版本号标签的最新版本,或者若是没有语义化版本号标签则是最新的已知提交。除非存储库中没有其余语义化版本号标签,不然不会将预发布标签选择为“最新”标签(details)。

一个广泛的错误是认为go get -u foo仅获取最新版本的foo实际上,-uin go get -u foo或的go get -u foo@latest意思是得到的全部直接和间接依赖关系的最新版本foo升级时,一个共同的起点foo是否是作go get foogo get foo@latest没有-u(和后一切正常,能够考虑go get -u=patch foogo get -u=patchgo get -u foo,或go get -u)。

要升级或降级到一个更具体的版本,“去把”容许版本选择经过添加一个后缀@version或覆盖“模块查询”到包的说法,好比go get foo@v1.6.2go get foo@e3702bed2或者go get foo@'<v1.6.2'

不管是否具备语义化版本号标记,使用分支名称(例如go get foo@masterfoo@tip带有mercurial))都是获取最新提交的一种方法。

一般,不能解析为语义化版本号标签的模块查询将做为伪版本记录go.mod文件中。

有关此处的主题的更多信息,请参见命令文档“支持模块的获取”“模块查询”部分go

模块可以使用还没有加入模块的软件包,包括在其中记录任何可用的语义化版本号标签go.mod并使用这些语义化版本号标签进行升级或降级。模块还可使用尚没有任何适当语义化版本号标签的软件包(在这种状况下,它们将使用中的伪版本进行记录go.mod)。

在升级或降级任何依赖项以后,您可能想要对构建中的全部软件包(包括直接和间接依赖项)再次运行测试以检查不兼容性:

$ go test all

如何准备发布

发行模块(全部版本)

建立模块发行版的最佳实践有望做为初始模块实验的一部分出现。其中许多最终可能会由未来的“发布”工具自动化

在标记版本以前,应考虑一些当前建议的最佳作法:

  • 运行go mod tidy到可能修剪任何无关的要求(如描述在这里),并确保您的当前go.mod反映了全部可能的堆积标签/ OS /架构的组合(如描述在这里)。

    • 相反,其余命令(例如,go build而且go test不会从中删除依赖项)go.mod再也不须要,而是仅go.mod基于当前构建调用的标记/ OS /体系结构进行更新。
  • 运行go test all以测试您的模块(包括针对直接和间接依赖项运行测试),以验证当前所选软件包版本是否兼容。

    • 可能的版本组合数与模块数成指数关系,所以,一般,您不能指望依赖项已针对其依赖项的全部可能组合进行了测试。
    • 做为模块工做的一部分,go test all已被从新定义为更有用:经过一个或多个导入序列,将当前模块中的全部软件包以及它们所依赖的全部软件包包括在内,而在其中将可有可无的软件包排除在外当前模块。
  • 确保您的go.sum文件与go.mod文件一块儿提交。有关更多详细信息和原理,请参见下面常见问题解答

发行模块(v2或更高版本)

若是要发布v2或更高版本的模块,请首先查看上面“语义导入版本控制”部分中的讨论,其中包括为什么在v2 +模块的模块路径和导入路径中包含主要版本以及Go版本1.9的方式.7+和1.10.3+已更新,以简化该过渡。

请注意,若是您是第一次v2.0.0采用模块,是为了在采用模块以前针对已存在标签或更高版本的预先存在的存储库或软件包集进行采用,那么建议的最佳实践是在首次采用模块时增长主版本。例如,若是您是的做者foo,而且foo存储库的最新标记v2.2.2,而且foo还没有采用模块,则最佳作法是v3.0.0将第一个版本的foo采用采用模块(所以将第一个版本的footo做为)。包含一个go.mod文件)。在这种状况下,增长主要版本可为的使用者提供更大的清晰度foo,从而容许在v2系列的其余非模块补丁或次要发行版上使用foo若是须要,并提供了一个基于模块的消费者一个强烈的信号foo,不一样的主要版本,若是你作的结果import "foo"和相应的require foo v2.2.2+incompatible,与import "foo/v3"和相应require foo/v3 v3.0.0(请注意,关于递增主要版本时,首先采用模块不会这个建议并不适用于预先存在的回购或包,其最新版本v0.xx或v1.xx)。

有两种替代机制能够发布v2或更高版本的模块。请注意,使用这两种技术,当模块做者推送新标签时,新模块版本就能够供消费者使用。以建立v3.0.0发行版为例,两个选项是:

  1. Major分支:更新go.mod文件以/v3module指令的模块路径末尾包含a (例如module github.com/my/module/v3)。更新模块中的import语句以也使用/v3(例如import "github.com/my/module/v3/mypkg")。用标记发布v3.0.0

    • Go版本1.9.7 +,1.10.3 +和1.11可以正确使用和构建使用此方法建立的v2 +模块,而无需更新还没有选择模块的使用者代码(如“语义导入”中所述)版本”部分)。
    • 社区工具github.com/marwan-at-work/mod可帮助实现此过程的自动化。有关概述,请参见下面的存储库社区工具常见问题解答
    • 为避免与此方法混淆,请考虑将v3.*.*模块提交放在单独的v3分支上。
    • 注:建立的一个新的分支不是必需的。相反,若是您之前是在master上发布的,而且但愿v3.0.0在master上进行标记,那么这是一个可行的选择。(可是,要知道,在引入一个不兼容的API的变化master可能会致使谁发出非模块用户的问题go get -u给出的go工具是不知道的语义化版本号以前去1.11或当模块模式在Go 1.11+未启用)。
    • 诸如dep当前之类的现有依赖关系管理解决方案在使用这种方式建立的v2 +模块时可能会遇到问题。参见例如dep#1962
  2. 主要子目录:建立一个新的v3子目录(例如my/module/v3),而后go.mod在该子目录中放置一个新文件。模块路径必须以结尾/v3将代码复制或移动到v3子目录中。更新模块中的import语句以也使用/v3(例如import "github.com/my/module/v3/mypkg")。用标记发布v3.0.0

    • 这提供了更大的向后兼容性。特别是,低于1.9.7和1.10.3的Go版本也可以正确使用和构建使用此方法建立的v2 +模块。
    • 这里一种更复杂的方法能够利用类型别名(在Go 1.9中引入)并在驻留在不一样子目录中的主要版本之间转发填充。这能够提供额外的兼容性,并容许以另外一个主要版本的形式实现一个主要版本,可是对于模块做者而言,这将须要更多的工做。正在进行自动化的工具是goforward在此处查看更多详细信息和基本原理,以及可正常运行的goforward
    • 预先存在的依赖项管理解决方案,例如dep应该可以使用以这种方式建立的v2 +模块。

有关这些替代方案的更深刻讨论,请参见https://research.swtch.com/vgo-module

发布发行

能够经过将标签推送到包含模块源代码的资源库中来发布新的模块版本。标签是经过串联两个字符串造成的:前缀版本

版本是该发行的语义导入版本。应该按照语义导入版本控制规则进行选择

所述前缀指示其中模块的存储库中定义的。若是模块是在存储库的根目录中定义的,则前缀为空,而标记仅为版本。可是,在多模块存储库中,前缀区分不一样模块的版本。前缀是存储库中定义模块的目录。若是存储库遵循上述主要子目录模式,则前缀不包括主要版本后缀。

例如,假设咱们有一个模块example.com/repo/sub/v2,而且咱们要发布version v2.1.6存储库根目录与相对应example.com/repo,而且模块sub/v2/go.mod在存储库内定义此模块的前缀是sub/此版本的完整标签应为sub/v2.1.6

迁移到模块

本节试图简要列举迁移到模块时要作出的主要决定,并列出其余与迁移相关的主题。一般会提供其余部分的参考,以获取更多详细信息。

该材料主要基于模块实验中社区中出现的最佳实践。所以,这是一个进行中的部分,随着社区得到更多的经验,该部分将有所改善。

摘要:

  • 该模块系统旨在容许整个Go生态系统中的不一样软件包以不一样的速率选择加入。
  • 主要因为语义导入版本控制的影响,已经在版本v2或更高版本上的软件包具备更多的迁移注意事项
  • 在采用模块时,新软件包和v0或v1上的软件包的考虑要少得多。
  • 使用Go 1.11定义的模块能够用于较旧的Go版本(尽管确切的Go版本取决于主模块使用的策略及其依赖项,以下所述)。

迁移主题:

从先前的依赖管理器自动迁移

向Go和非模块消费者的较旧版本提供依赖信息

  • go mod vendor禁用模块模式时,Go的较早版本了解如何使用由建立的vendor目录,Go 1.11和1.12+也是如此。所以,供应是模块提供依赖的一种方式,该依赖提供了对不能彻底理解模块的Go的较旧版本以及未启用模块自己的使用者的依赖。有关更多详细信息,请参见vendor常见问题解答go命令文档

更新现有安装说明

  • 在预模块中,一般包含安装说明go get -u foo若是要发布模块foo,请考虑-u为基于模块的使用者使用in指令。
    • -u要求go工具升级的全部直接和间接依赖foo
    • 模块使用者能够选择go get -u foo稍后运行,可是若是它不是初始安装说明的一部分,则“ High Fidelity Builds”还有更多好处-u有关更多详细信息,请参见“如何升级和降级依赖项”
    • go get -u foo 仍然有效,而且仍然能够做为安装说明的有效选择。
  • 另外,go get foo对于基于模块的使用者并不是严格须要。
    • 只需添加import语句import "foo"就足够了。(后续命令如go buildgo test根据须要自动下载foo和更新go.mod)。
  • vendor默认状况下,基于模块的使用者将不使用目录。
    • 若是在go工具中启用了模块模式vendor则使用模块时并不须要严格要求(鉴于中包含的信息go.mod和中的密码校验和go.sum),可是某些预先存在的安装说明假定该go工具将vendor默认使用有关更多详细信息,请参见vendor常见问题解答
  • go get foo/...在某些状况下,安装说明可能包含问题(请参阅#27215中的讨论)。

避免破坏现有的导入路径

模块go.mod经过module指令(例如)在其声明中声明其身份module github.com/my/module任何模块支持的使用者都必须使用与模块声明的模块路径匹配的导入路径(确切地说是针对根软件包,或将模块路径做为导入路径的前缀)导入模块内的全部软件包。若是导入路径与相应模块的声明模块路径不匹配,则go命令将报告unexpected module path错误。

在为一组预先存在的软件包采用模块时,应注意避免破坏现有使用者使用的现有导入路径,除非在采用模块时增长主版本。

例如,若是您先前存在的README一直在告诉消费者要使用import "gopkg.in/foo.v1",而且随后采用v1版本的模块,则您的首字母go.mod几乎确定会读为module gopkg.in/foo.v1若是您不想使用gopkg.in,这对您当前的消费者来讲将是一个巨大的变化。一种方法是将其更改成相似的内容(module github.com/repo/foo/v2若是您稍后转到v2)。

请注意,模块路径和导入路径区分大小写。从更改模块github.com/Sirupsen/logrusgithub.com/sirupsen/logrus,例如,对消费者来讲是一个重大更改,即便GitHub的自动转发从一个存储库名称到新存储库的名称。

采用模块后,更改模块路径go.mod是一项重大更改。

整体而言,这相似于经过“导入路径注释”对规范的导入路径进行模块前的强制,有时也称为“导入实用程序”或“导入路径强制”。举例来讲,该软件包go.uber.org/zap当前托管在github.com/uber-go/zap,但在软件包声明旁边使用了导入路径注释,该注释为使用错误的基于github的导入路径的全部前置模块使用者触发了错误:

package zap // import "go.uber.org/zap"

go.mod文件的module语句已淘汰了导入路径注释。

首次采用带有v2 +软件包的模块时增长主要版本

  • 若是您在采用模块以前已将其软件包标记为v2.0.0或更高版本,则建议的最佳实践是在首次采用模块时增长主要版本。例如,若是您正在使用v2.0.1而且还没有采用模块,那么您将使用v3.0.0采用模块的第一个发行版。有关更多详细信息,请参见上面的“发布模块(v2或更高版本)”部分。

v2 +模块容许在一个内部版本中使用多个主要版本

  • 若是模块在v2或更高版本上,则意味着多个主要版本能够位于单个版本中(例如,foo而且foo/v3可能最终在单个版本中)。
    • 这天然源于“具备不一样导入路径的包是不一样的包”的规则。
    • 发生这种状况时,将有多个软件包级别状态的副本(例如的软件包级别状态foo和的软件包级别状态foo/v3),而且每一个主要版本都将运行其本身的init功能。
    • 这种方法有助于解决模块系统的多个方面,包括帮助解决钻石依赖问题,在大型代码库中逐步迁移到新版本,以及容许将主要版本实现为围绕其余主要版本的填充。
  • 有关某些相关讨论,请参见https://research.swtch.com/vgo-import#27514的“避免单例问题”部分

消耗非模块代码的模块

  • 模块可以使用还没有选择加入模块的软件包,并将适当的软件包版本信息记录在导入模块的中go.mod模块可使用尚没有适当的语义化版本号标签的软件包。有关更多详细信息,请参见下面常见问题解答
  • 模块也能够导入未选择模块的v2 +软件包。+incompatible若是导入的v2 +程序包具备有效的语义化版本号标签,它将带有后缀记录有关更多详细信息,请参见下面常见问题解答

非模块代码消费模块

  • 非模块代码消耗v0和v1模块

    • 还没有选择使用模块的代码可使用和构建v0和v1模块(与使用的Go版本无关)。
  • 非模块代码消耗v2 +模块

预先存在的v2 +软件包做者的策略

对于考虑加入模块的预先存在的v2 +软件包的做者,总结替代方法的一种方法是在三种顶级策略之间进行选择。每一个选择都有后续的决定和变化(如上所述)。这些替代的顶级策略是:

  1. 要求客户端使用Go版本1.9.7 +,1.10.3 +或1.11+

    该方法使用“主要分支”方法,并依赖于“最小模块感知”,该模型被反向移植到1.9.7和1.10.3。有关更多详细信息,请参见上面的“语义导入版本控制”“发布模块(v2或更高版本)”部分。

  2. 容许客户使用甚至更旧的Go版本,如Go 1.8

    此方法使用“主要子目录”方法,并涉及建立子目录,例如/v2/v3有关更多详细信息,请参见上面的“语义导入版本控制”“发布模块(v2或更高版本)”部分。

  3. 等待加入模块

    在这种策略下,事情继续与选择了模块的客户端代码以及未选择模块的客户端代码一块儿工做。随着时间的流逝,Go版本1.9.7 +,1.10.3 +和1.11+的发布时间将愈来愈长,而且在未来的某个时候,要求Go版本变得更加天然或对客户友好1.9.7 + / 1.10.3 + / 1.11 +,此时,您能够实施以上策略1(须要Go版本1.9.7 +,1.10.3 +或1.11+),甚至能够实施以上策略2(可是若是最终要采用上述策略2来支持1.8等旧版Go,那么您如今就能够这样作。

 

常见问题

版本如何标记为不兼容?

require指令容许任何模块声明其应使用依赖项D的版本> = xyz构建(因为与模块D的版本<xyz不兼容而可能指定)。经验数据代表,这是在dep和中使用的约束的主要形式cargo此外,构建中的顶层模块能够使用不一样的代码来生成exclude特定版本的依赖项或replace其余模块。有关更多详细信息和原理,请参阅完整建议

版本模块建议的主要目标之一是为工具和开发人员在Go代码的版本周围添加通用词汇和语义。这为未来声明不兼容的其余形式奠基了基础,例如:

  • 宣布弃用版本的描述在初始vgo博客系列
  • 声明在外部系统中的模块之间的成对的不兼容性,例如所讨论这里在提案过程
  • 在发布发行版后声明模块的成对不兼容版本或不安全版本。参见例如#24031#26829中正在进行的讨论

何时出现旧行为与新的基于模块的行为?

一般,模块是Go 1.11的可选组件,所以,根据设计,默认状况下会保留旧的行为。

总结什么时候得到旧的1.10现状行为与新的基于选择加入模块的行为:

  • 内部GOPATH-默认为旧的1.10行为(忽略模块)
  • 在GOPATH以外,而在带有go.mod的文件树中-默认为模块行为
  • GO111MODULE环境变量:
    • 未设置或auto-上面的默认行为
    • on —无论目录位置如何,都强制支持模块
    • off —无论目录位置如何,都强制关闭模块支持

为何经过go get错误安装工具会失败并显示错误cannot find main module

当您进行设置GO111MODULE=ongo.mod运行时不在文件树内部会发生这种状况go get

最简单的解决方案是保持未GO111MODULE设置状态(或等效地显式设置为GO111MODULE=auto),这样能够避免出现此错误。

回想一下存在的主要缘由之一是记录精确的依赖项信息。此依赖项信息将写入您的current go.mod若是您不在带有的文件树中,go.mod可是go get经过设置告诉命令以模块模式GO111MODULE=on运行,则运行go get将致使错误,cannot find main module由于没有go.mod可用来记录依赖项信息的信息。

解决方案的替代方案包括:

  1. 保持未GO111MODULE设置状态(默认设置或显式设置GO111MODULE=auto),这将致使更友好的行为。当您不在模块中时,这将为您提供Go 1.10行为,从而避免了go get报告cannot find main module

  2. export GO111MODULE=on,但根据须要暂时禁用模块,并在过程当中启用Go 1.10行为go get,例如via GO111MODULE=off go get example.com/cmd能够将其转换为简单的脚本或shell别名,例如alias oldget='GO111MODULE=off go get'

  3. 建立一个临时go.mod文件,而后将其丢弃。这已经经过@rogpeppe简单shell脚本实现自动化该脚本容许经过可选地提供版本信息vgoget example.com/cmd[@version](这是避免错误的解决方案cannot use path@version syntax in GOPATH mode)。

  4. gobin是可识别模块的命令,用于安装和运行主软件包。默认状况下,gobin无需先手动建立模块便可安装/运行主程序包,但-m能够经过标志将该命令告知使用现有模块来解决依赖关系。请参阅gobin 自述文件常见问题解答以了解详细信息和其余用例。

  5. 建立一个go.mod用于跟踪运行的全局安装工具(例如中的)~/global-tools/go.mod,而后cd在运行以前跟踪该目录,go get跟踪go install全部全局安装的工具。

  6. go.mod为每一个工具在单独的目录(例如~/tools/gorename/go.mod和)中建立一个~/tools/goimports/go.mod,并cd在运行前为该工具go getgo install该工具建立一个相应的目录

该当前限制将获得解决。可是,主要问题是模块当前处于启用状态,完整的解决方案可能要等到GO111MODULE = on成为默认行为。有关更多讨论,请参见#24250,包括此评论:

显然,这最终必须起做用。就该版本而言,我不肯定这究竟是作什么的:它会建立一个临时模块root和go.mod,执行安装,而后将其丢弃吗?大概。可是我不太肯定,就目前而言,我不想让vgo在go.mod树以外作一些事情来令人们感到困惑。固然,最终的go命令集成必须支持这一点。

该常见问题解答一直在讨论跟踪全局安装的工具。

相反,若是要跟踪特定模块所需的工具,请参阅下一个FAQ。

如何跟踪模块的工具依赖关系?

若是你:

  • 想要stringer在处理模块时使用基于Go的工具(例如),而且
  • 想要确保每一个人都在使用该工具的相同版本,同时在模块go.mod文件中跟踪该工具的版本

那么当前推荐的一种方法是将一个tools.go文件添加到您的模块中,文件包括目标工具(例如import _ "golang.org/x/tools/cmd/stringer")的导入语句以及// +build tools构建约束。import语句使go命令能够在模块的中精确记录工具的版本信息go.mod,而// +build tools构建约束阻止正常的构建实际导入工具。

有关如何执行此操做的具体示例,请参见本“经过示例执行模块”演练

#25922中的此注释中讨论了该方法以及更早的具体示例

简要理由(一样来自#25922):

我认为tools.go文件其实是工具依赖关系的最佳实践,固然对于Go 1.11。

我喜欢它,由于它没有引入新的机制。

它只是简单地重用现有的。

IDE,编辑器和标准工具(例如goimports,gorename等)中模块支持的状态如何?

对模块的支持已开始在编辑器和IDE中得到。

例如:

  • Goland:目前拥有内外GOPATH模块,包括完成,语法分析,重构,所描述的导航全面支持这里
  • VS Code:工做正在进行中,正在寻找有助于的人。跟踪问题是#1532VS Code模块状态Wiki页面中描述了初始beta 
  • 带有加号的原子:跟踪问题是#761
  • 带vim-go的vim:语法高亮显示和格式的最初支持go.mod已经到来#1906年得到了更普遍的支持
  • 带go-mode.el的emacs#237中的跟踪问题

在雨伞问题中一直跟踪其余工具(例如goimports,guru,gorename和相似工具)的状态#24661请查看该伞的最新状态。

特定工具的一些跟踪问题包括:

一般,即便您的编辑器,IDE或其余工具还没有被模块识别,若是您在GOPATH内使用模块而且可使用,则它们的大部分功能也应与模块一块儿使用go mod vendor(由于应经过GOPATH来选择适当的依赖项) 。

完整的解决方法是把那包加载关闭的程序go/build和到golang.org/x/tools/go/packages,其知道如何定位模块感知方式封装。这极可能最终成为事实go/packages

常见问题解答-附加控制

存在哪些社区工具来使用模块?

社区开始在模块之上构建工具。例如:

  • github.com/rogpeppe/gohack
    • 一种新的社区工具,能够自动化并大大简化replace和多模块工做流程,其中包括容许您轻松修改其中的一个依赖项
    • 例如,gohack example.com/some/dependency自动克隆适当的存储库并将必要的replace指令添加到您的go.mod
    • 使用如下命令删除全部gohack替换语句 gohack undo
    • 该项目正在继续扩展,以简化与模块相关的其余工做流程
  • github.com/marwan-at-work/mod
    • 命令行工具可自动升级/降级模块的主要版本
    • go.mod在go源代码中自动调整文件和相关的导入语句
    • 帮助进行升级,或者在首次选择带有v2 +软件包的模块时提供帮助
  • github.com/akyoto/mgit
    • 使您能够查看和控制全部本地项目的语义化版本号标签
    • 显示未标记的提交,并让您一次标记全部(mgit -tag +0.0.1
  • github.com/goware/modvendor
    • 帮助将其余文件复制到该vendor/文件夹中,例如外壳程序脚本,.cpp文件,.proto文件等。
  • github.com/psampaz/go-mod-outdated
    • 以人类友好的方式显示过期的依赖关系
    • 提供一种过滤间接依赖关系和无需更新的依赖关系的方法
    • 提供了一种在依赖项过期的状况下中断CI管道的方法

何时应该使用replace指令?

上面“ go.mod”概念部分所述replace伪指令在顶层提供了额外的控制权,go.mod用于实际知足Go源代码或go.mod文件中找到的依赖关系,而replace伪指令则位于主模块以外的模块中构建主模块时,将忽略该模块。

replace指令容许您提供另外一个导入路径,该路径多是VCS(GitHub或其余地方)中的另外一个模块,或者是具备相对或绝对文件路径的本地文件系统上的另外一个模块。replace使用指令中的新导入路径,而无需更新实际源代码中的导入路径。

replace 容许顶层模块控制用于依赖项的确切版本,例如:

  • replace example.com/some/dependency => example.com/some/dependency v1.2.3

replace 还容许使用分叉的依赖项,例如:

  • replace example.com/some/dependency => example.com/some/dependency-fork v1.2.3

一个示例用例是,若是您须要修复或研究依赖项中的某些内容,则可使用本地派生,并在顶层中添加如下内容go.mod

  • replace example.com/original/import/path => /your/forked/import/path

replace 也可用于将多模块项目中模块的相对或绝对磁盘上位置通知go工具,例如:

  • replace example.com/project/foo => ../foo

注意:若是replace指令的右侧是文件系统路径,则目标必须go.mod在该位置具备文件。若是该go.mod文件不存在,则可使用建立一个go mod init

一般,您能够选择=>在replace指令的左侧指定版本,可是一般,若是您忽略此更改,则对更改的敏感性较低(例如,如replace上述全部示例所示)。

在Go 1.11中,对于直接依赖关系require,即便执行时也须要一个指令replace例如,若是foo是直接依赖项,那么您不能没有replace foo => ../foo相应的requirefor foo若是你不知道在用什么版本的require指令,你能够常用v0.0.0require foo v0.0.0这在Go 1.12中的#26241中获得了解决

您能够经过运行确认得到所需的版本go list -m all,该版本向您显示了将在构建中使用的实际最终版本,包括考虑了replace语句。

有关更多详细信息,请参见“ go mod edit”文档

github.com/rogpeppe/gohack使这些类型的工做流变得更加容易,尤为是若是您的目标是对模块依赖项进行可变签出时。有关概述,请参见存储库或以前的常见问题解答。

有关在replaceVCS以外彻底使用的详细信息,请参见下一个FAQ 

我能够在本地文件系统上彻底不在VCS上工做吗?

是。不须要VCS。

若是您要一次在VCS以外编辑单个模块,那么这很是简单(而且您总共只有一个模块,或者其余模块位于VCS中)。在这种状况下,能够将包含单个文件的文件树放置go.mod在方便的位置。go buildgo test和相似的命令将工做,即便你的单个模块是VCS以外(无需任何使用replace你的go.mod)。

若是要在本地磁盘上同时编辑多个相互关联的模块,则replace指令是一种方法。如下是一个示例go.mod该示例使用replace带有相对路径的将hello模块指向该模块在磁盘上的位置goodbye(不依赖任何VCS):

module example.com/me/hello

require (
  example.com/me/goodbye v0.0.0
)

replace example.com/me/goodbye => ../goodbye

如本示例所示,若是在VCS以外,则能够将其v0.0.0用做require指令中的版本请注意,如先前的FAQ中所述,在Go 1.11中require必须在此处手动添加require指令,但再也不须要在Go 1.12+(#26241)中手动添加指令

线程中显示了一个小的可运行示例

如何对模块使用vendor?vendor会消失吗?

最初的一系列vgo博客文章确实建议彻底放弃vendor,可是社区的反馈致使保留了对vendor的支持。

简而言之,要对模块使用vendor:

  • go mod vendor 重置主模块的vendor目录,以包括根据go.mod文件和Go源代码的状态构建和测试全部模块软件包所需的全部软件包。
  • 默认状况下,go build在模块模式下,执行诸如忽略vendor目录之命令
  • -mod=vendor标志(例如,go build -mod=vendor)指示去命令使用主模块的顶级vendor目录,以知足依赖性。所以,在此模式下,go命令将忽略go.mod中的依赖项描述,并假定vendor目录包含正确的依赖项副本。请注意,仅使用主模块的顶级vendor目录。其余位置的vendor目录仍然被忽略。
  • 有些人会但愿经过设置GOFLAGS=-mod=vendor环境变量来按期选择vendor

禁用模块模式go mod vendor,Go的较旧版本(如1.10)了解如何使用由建立的vendor目录,Go 1.11和1.12+ 也是如此。所以,供应是模块提供依赖的一种方式,该依赖提供了对不能彻底理解模块的Go的较旧版本以及未启用模块自己的使用者的依赖。

若是您正在考虑使用vendor,则值得阅读技巧文档中“模块和vendor”“提供依赖关系的vendor副本”部分。

是否存在“始终在线”的模块存储库和企业代理?

公共托管的“始终在”不可变模块存储库以及可选的私有托管的代理和存储库正变得可用。

例如:

请注意,您不须要运行代理。相反,1.11中的go工具已经过GOPROXY添加了可选的代理支持,以启用更多企业用例(例如,更好的控制),并更好地处理诸如“ GitHub停机”或人们删除GitHub存储库的状况。

我能够控制go.mod什么时候更新以及go工具什么时候使用网络知足依赖关系吗?

默认状况下,相似的命令go build会根据须要到达网络以达到导入要求。

有些团队可能但愿禁止go工具在某些时候接触网络,或者想要更好地控制go工具什么时候更新go.mod,如何得到依赖关系以及如何使用vendor。

转到工具提供了至关数量的灵活调整或关闭这些默认的行为,包括经过-mod=readonly-mod=vendorGOFLAGSGOPROXY=offGOPROXY=file:///filesystem/pathgo mod vendor,和go mod download

这些选项的详细信息遍及整个官方文档。此处是一个社区,试图对与这些行为相关的旋钮进行综合概述,其中包括指向官方文档的连接,以获取更多信息。

如何将模块与Travis或CircleCI等CI系统一块儿使用?

最简单的方法可能只是设置环境变量GO111MODULE=on,该变量应适用于大多数CI系统。

可是,因为您的某些用户还没有选择加入模块,所以在启用和禁用模块的Go 1.11上的CI中运行测试可能颇有价值。vendor也是要考虑的话题。

如下两个博客文章更具体地介绍了这些主题:

常见问题解答— go.mod和go.sum

为何“ go mod tidy”在个人“ go.mod”中记录间接和测试依赖项?

该模块系统记录您的精确的依赖要求go.mod(有关更多详细信息,请参阅上面go.mod概念部分或go.mod技巧文档)。

go mod tidy更新您的当前信息,go.mod以在模块中包括测试所需的依赖关系-若是测试失败,咱们必须知道使用了哪些依赖关系来重现失败。

go mod tidy还能够确保您的当前go.mod反映了操做系统,架构的全部可能组合的依赖性需求,并创建标签(如描述在这里)。相比之下,其余的命令同样go build,并go test只更新go.mod提供电流下被请求包导入的包GOOSGOARCH和创建标签(这是一个缘由go mod tidy想补充一点,没有被要求添加go build或相似)。

若是您模块的依赖项自己不具备go.mod(例如,由于该依赖项还没有选择加入模块自己),或者其go.mod文件缺乏其一个或多个依赖项(例如,因为模块做者未运行go mod tidy) ,那么缺乏的传递依赖将被添加到您的模块的要求,与沿// indirect注释,代表依赖是否是从你的模块中的直接进口。

请注意,这还意味着直接或间接依赖项中缺乏的任何测试依赖项也将记录在go.mod(如下状况很重要的示例:对模块go test all全部直接和间接依赖项进行测试,这是验证您当前版本组合是否能够协同工做的一种方法。若是在运行时测试在您的一种依赖项中失败go test all,重要的是要记录一整套测试依赖项信息,以便您具备可重现的go test all行为。

// indirect您的go.mod文件中可能具备依赖项的另外一个缘由是,若是您已经升级(或降级了)一个间接依赖项,超出了直接依赖项所要求的范围(例如,运行go get -u或)go get foo@1.2.3go工具须要一个位置来记录这些新版本,而且它会在您的go.mod文件中记录(而且不会深刻到您的依赖项中来修改 go.mod文件)。

一般,上述行为是模块如何经过记录精确的依赖项信息来提供100%可复制的构建和测试的一部分。

若是你是好奇,为何一个特定的模块,显示在你起来go.mod,你能够运行go mod why -m <module>回答这个问题。用于检查需求和版本的其余有用工具包括go mod graphgo list -m all

'go.sum'是锁定文件吗?为何“ go.sum”包含有关我再也不使用的模块版本的信息?

不,go.sum不是锁定文件。go.mod构建中文件为100%可复制的构建提供了足够的信息。

为了进行验证,go.sum包含特定模块版本的内容的预期密码校验和。有关详细信息(包括为何一般须要签入)以及技巧文档中“模块下载和验证”部分,请参见下面FAQgo.sumgo.sum

部分因为go.sum不是锁文件,所以即便您中止使用模块或特定模块版本,它也会保留模块版本的加密校验和。若是您之后继续使用某些内容,则能够验证校验和,从而提升了安全性。

另外,您的模块go.sum记录了构建中使用的全部直接和间接依赖项的校验和(所以,go.sum列出的模块一般比的要多go.mod)。

我是否应该提交“ go.sum”文件以及“ go.mod”文件?

一般,模块的go.sum文件应与go.mod文件一块儿提交。

  • go.sum 包含特定模块版本内容的预期密码校验和。
  • 若是有人克隆您的存储库并使用go命令下载了您的依赖项,那么若是他们下载的依赖项副本和的相应条目之间存在任何不匹配,就会收到错误消息go.sum
  • 此外,go mod verify检查磁盘下载的模块下载在磁盘上的缓存副本是否仍与中的条目匹配go.sum
  • 请注意,go.sum它不是某些替代性依赖项管理系统中使用的锁定文件。go.mod为可复制的构建提供足够的信息)。
  • 请参阅Filippo Valsorda的很是简单的理由,了解您为何要登机go.sum有关更多详细信息,请参见技巧文档“模块下载和验证”部分。请参阅#24117#25530中讨论的未来可能的扩展

若是我没有任何依赖关系,还应该添加一个“ go.mod”文件吗?

是。这支持在GOPATH以外进行工做,帮助与您选择模块的生态系统进行通讯,此外module,您指令还能够go.mod用做代码身份的明确声明(这是最终不建议使用导入注释的缘由之一) )。固然,模块在Go 1.11中纯粹是可选功能。

常见问题解答—语义导入版本控制

为何主版本号必须出如今导入路径中?

请参阅上面“语义导入版本控制”概念部分中有关语义导入版本控制和导入兼容性规则的讨论另请参阅宣布提案博客文章,其中更多地讨论了导入兼容性规则的动机和理由。

为何导入路径中省略了主要版本v0,v1?”

请参阅问题“为何导入路径中省略了主要版本v0,v1?” 在较早的FAQ中,来自官方提案的讨论

用主要版本v0,v1标记个人项目,或使用v2 +进行重大更改有什么含义?

在回应有关“ k8发行次要版本,但在每一个次要版本中更改Go API”的评论时,Russ Cox作出如下回应,着重强调了选择v0,v1与频繁使用v2,v3,v4进行重大更改的一些含义。 ,等等。

我并不彻底了解k8s开发周期等,可是我认为一般k8s团队须要决定/确认他们打算向用户保证稳定性的内容,而后相应地应用版本号来表达这一点。

  • 要保证有关API兼容性(这彷佛是最佳的用户体验!),而后开始使用1.XY
  • 为了灵活地在每一个发行版中进行向后不兼容的更改,但容许大型程序的不一样部分按不一样的时间表升级其代码,这意味着不一样的部分能够在一个程序中使用API​​的不一样主要版本,而后使用XY0,以及导入路径,例如k8s.io/client/vX/foo。
  • 为了避免保证API兼容,而且不管什么状况,每一个构建都只须要一个k8s库的副本,这意味着即便不是全部构建都准备好了,构建的全部部分也必须使用相同版本。 ,而后使用0.XY

与此相关的是,Kubernetes具备一些非典型的构建方法(当前在Godep之上包括自定义包装脚本),所以Kubernetes对于许多其余项目来讲是不完善的示例,可是随着Kubernetes向采用Go 1.11迈进,这多是一个有趣的示例。模块

模块可使用未选择模块的软件包吗?

是。

若是存储库未选择使用模块,但已使用有效的语义化版本号标签(包括所需的前导v)进行了标记,则能够在中使用这些语义化版本号标签go get,而且相应的语义化版本号版本将记录在导入模块的go.mod文件中。若是存储库没有任何有效的语义化版本号标签,则将使用“伪版本”记录存储库的版本,例如 v0.0.0-20171006230638-a6e239ea1c69(其中包括时间戳和提交哈希,而且其设计目的是容许对记录在其中的各个版本进行总排序)go.mod并使其更容易推断出哪一个记录​​版本比另外一个记录版本“晚”。

例如,若是foo标记了软件包的最新版本,v1.2.3foo自身还没有选择加入模块,则在模块M的内部运行go get foogo get foo@v1.2.3从模块M记录的内容将记录为模块M的go.mod文件,以下所示:

require  foo  v1.2.3

go工具还将在其余工做流程中为非模块程序包使用可用的语义化版本号标记(例如go list -u=patch,将模块的依赖项升级到可用的补丁程序版本,或将go list -u -m all,显示可用的升级等)。

有关还没有选择模块的v2 +软件包的更多详细信息,请参见下一个常见问题解答。

一个模块可使用未选择模块的v2 +软件包吗?“ +不兼容”是什么意思?

是的,模块能够导入还没有选择模块的v2 +软件包,而且若是导入的v2 +软件包具备有效的语义化版本号标签,则将记录+incompatible后缀。

额外细节

请熟悉上面“语义导入版本控制”部分中的材料

首先回顾一些一般有用但在考虑本FAQ中描述的行为时要记住的特别重要的核心原则会有所帮助。

工具以模块模式(例如运行时,如下核心原则始终是正确的goGO111MODULE=on

  1. 软件包的导入路径定义了软件包的标识。
    • 具备不一样导入路径的软件包被视为不一样的软件包。
    • 具备相同导入路径的软件包被视为相同的软件包(即便 VCS标签说这些软件包具备不一样的主要版本,也是如此)。
  2. 不带a的导入路径/vN被视为v1或v0模块(即便导入的程序包未选择加入模块而且具备表示主要版本大于1的VCS标记,也是如此)。
  3. 在模块文件module foo/v2开头声明的模块路径(例如go.mod均为:
    • 该模块身份的明确声明
    • 关于必须如何经过使用代码导入该模块的明确声明

咱们将在接下来的FAQ看到,当这些原则并不老是正确的go工具是不是在模块模式,可是当这些原则是老是正确的go工具模块模式。

简而言之,+incompatible后缀表示当知足如下条件时,上述原则2有效:

  • 导入的软件包还没有选择使用模块,而且
  • 它的VCS标签说主要版本大于1,而且
  • 原理2覆盖了VCS标签-不带a的导入路径/vN被视为v1或v0模块(即便VCS标签另有说明)

go工具处于模块模式时,它将假定非模块v2 +软件包不了解语义导入版本控制,并将其视为该软件包的v1版本系列的(不兼容)扩展(而且+incompatible后缀表示go工具正在这样作)。

假设:

  • oldpackage 是一个在引入模块以前的软件包
  • oldpackage从未选择使用模块(所以自己没有模块go.mod
  • oldpackage具备有效的语义化版本号标签v3.0.1,这是它的最新标签

在这种状况下,例如go get oldpackage@latest从模块M内部运行将在模块M的go.mod文件中记录如下内容

require  oldpackage  v3.0.1+incompatible

请注意,上面命令或记录的指令/v3的末尾没有使用– 在模块路径和导入路径中使用是语义导入版本控制的功能,而且未表示接受并理解了语义导入版本控制(还没有给出)经过在其内部包含文件来选择加入模块换句话说,即便具备语义化版本号标签也不会被授予语义导入版本控制的权利和责任(例如,在导入路径中使用),由于还没有声明要这样作。oldpackagego getrequire/vNoldpackageoldpackagego.modoldpackageoldpackagev3.0.1oldpackage/vNoldpackage

+incompatible后缀代表该v3.0.1版本oldpackage并无主动选择加入的模块,所以v3.0.1版本oldpackage被认为理解语义进口版本或如何使用进口路径主要版本。所以,在模块模式下运行时,该go工具会将的非模块v3.0.1版本oldpackage视为的v1版本系列的(不兼容)扩展,oldpackage并假定的v3.0.1版本oldpackage不了解语义导入版本控制,而且+incompatible后缀表示该go工具正在这样作。

该事实v3.0.1的版本oldpackage被认为是根据语义进口版本的v1发行版系列的一部分意味着,例如版本v1.0.0v2.0.0以及v3.0.1使用相同的导入路径都始终输入:

import  "oldpackage"

再次注意,/v3末尾没有用过oldpackage

一般,具备不一样导入路径的软件包是不一样的软件包。在这个例子中,给出的版本v1.0.0v2.0.0v3.0.1oldpackage会使用相同的导入路径须要进口,所以它们是经过构建视为同一包(也由于oldpackage在尚未选择到语义进口版本),以单拷贝oldpackage最终在任何给定的版本中。(使用的版本在任何require指令中在语义上都是最高的版本;请参见“版本选择”)。

若是咱们假设稍后建立一个新的v4.0.0发行版,oldpackage发行版采用模块并所以包含一个go.mod文件,则该信号oldpackage如今能够理解语义导入版本控制的权利和责任,所以基于模块的使用者如今能够/v4导入中使用导入路径:

import  "oldpackage/v4"

该版本将记录为:

require  oldpackage/v4  v4.0.0

oldpackage/v4如今的导入路径不一样于oldpackage,所以包也不一样。若是构建中的某些使用方拥有import "oldpackage/v4"同一个构建中的其余使用方,则两个副本(每一个导入路径一个)将最终生成一个支持模块的构建import "oldpackage"做为容许逐步采用模块的策略的一部分,这是理想的。另外,即便在模块退出其当前过渡阶段以后,也但愿此行为容许随着时间的推移逐步进行代码演化,其中不一样的使用者以不一样的速率升级到较新版本(例如,容许大型版本中的不一样使用者选择升级)从不一样的速率oldpackage/v4一些将来oldpackage/v5)。

若是未启用模块支持,如何在版本中处理v2 +模块?1.9.7 +,1.10.3 +和1.11中的“最小模块兼容性”如何工做?

在考虑还没有加入模块的较旧的Go版本或Go代码时,语义导入版本控制具备与v2 +模块相关的显着向后兼容性含义。

如上文“语义导入版本控制”部分所述:

  • 版本v2或更高版本的模块必须/vN在声明的其本身的模块路径中包含go.mod
  • 基于模块的使用者(即已选择加入模块的代码)必须/vN在导入路径中包含,以导入v2 +模块。

可是,预计生态系统将以不一样的采用模块和语义导入版本控制的速度进行。

“如何释放v2 +模块”部分中更详细描述的那样,在“主要子目录”方法中,v2 +模块的做者建立了诸如mymodule/v2或的子目录,mymodule/v3并在这些子目录下移动或复制了相应的软件包。这意味着传统的导入路径逻辑(即便在旧的Go版本中,如Go 1.8或1.7)也会在看到诸如的导入语句时找到合适的软件包import "mymodule/v2/mypkg"所以,即便未启用模块支持,也能够找到并使用“主要子目录” v2 +模块中的软件包(这是由于您正在运行Go 1.11且未启用模块,仍是由于您正在运行旧版本,如Go)没有完整模块支持的1.七、1.八、1.9或1.10)。请看“如何发布v2 +模块”部分提供了有关“主要子目录”方法的更多详细信息。

该常见问题解答的其他部分集中于“如何发布v2 +模块”部分中介绍的“主要分支”方法在“主要分支”方法中,不/vN建立任何子目录,而是经过go.mod文件并经过将语义化版本号标签应用于提交来传达模块版本信息(一般在上master,但能够在不一样的分支上)。

为了在当前过渡时期提供帮助,Go 1.11 引入“最小模块兼容性” ,觉得还没有加入模块的Go代码提供更大的兼容性,而且“最小模块兼容性”也被反向移植到Go 1.9。 7和1.10.3(鉴于那些旧版Go版本不具备完整模块支持,这些版本始终在禁用完整模块模式的状况下始终有效运行)。

“最小模块兼容性”的主要目标是:

  1. 容许较早的Go版本1.9.7+和1.10.3+可以更轻松地编译/vN在导入路径中使用语义导入版本控制的模块,并在Go 1.11中禁用模块模式时提供相同的行为

  2. 容许旧代码可以使用v2 +模块,而无需使用旧的消费者代码在使用/vNv2 +模块时当即更改成使用新的导入路径。

  3. 这样作无需依赖模块做者便可建立/vN子目录。

其余详细信息–“最小模块兼容性”

“最小模块兼容性”仅在禁用该工具的完整模块模式时才生效go,例如,若是您GO111MODULE=off在Go 1.11中进行了设置,或者正在使用Go 1.9.7+或1.10.3+版本。

当v2 +模块做者还没有建立/v2建立/vN子目录时,您转而依赖于Go 1.9.7 +,1.10.3 +和1.11中的“最小模块兼容性”机制:

  • 选择模块的软件包任何导入的v2 +模块的导入路径中不会包含主版本。
  • 相反,一个包已经选择加入的模块必须包括在导入路径主要版本导入任何V2 +模块。
    • 若是软件包已选择加入模块,但在导入v2 +模块时在导入路径中未包含主版本,则当该go工具在全模块模式下运行时,它将不会导入该模块的v2 +版本(假定已选择使用模块的软件包“说”语义导入版本控制。若是foo是具备v2 +版本的模块,则在“语义导入版本控制”下表示import "foo"要导入的v1语义导入版本控制系列foo)。
  • 用于实现“最小模块兼容性”的机制故意很是狭窄:
    • 整个逻辑是–当在GOPATH模式下运行时,若是导入语句位于选择加入模块的代码内(即,文件中的导入语句),则/vN在删除后,将再次尝试包含a的没法解析的导入语句带有有效文件)。/vN.gogo.mod
    • 最终结果是,导入语句(例如import "foo/v2"位于模块内部的代码内)仍将在GOPATH模式下(分别为1.9.7 +,1.10.3 +和1.11)正确编译,而且将像说的那样进行解析import "foo"(不带/v2) ,这意味着它将使用foo驻留在您的GOPATH中的版本,而不会被多余的混淆/v2
    • “最小模块兼容性”不会影响其余任何内容,包括它不会影响go命令行中使用的路径(例如go get或的参数go list)。
  • 这种过渡的“最小模块感知”机制有意打破了“将具备不一样导入路径的软件包视为不一样的软件包”的规则,以实现很是具体的向后兼容性目标–容许旧代码在使用v2 +模块时进行编译,而无需修改。稍微详细一点:
    • 若是旧代码使用v2 +模块的惟一方法是首先更改旧代码,那么对整个生态系统来讲,负担将更大。
    • 若是咱们不修改旧代码,则该旧代码必须适用于v2 +模块的模块前导入路径。
    • 另外一方面,选择加入模块的新代码或更新代码必须/vN对v2 +模块使用新的导入。
    • 新的导入路径不等于旧的导入路径,可是二者均可以在单个版本中使用,所以,咱们有两个不一样的功能导入路径能够解析为同一程序包。
    • 例如,在GOPATH模式下运行时,import "foo/v2"出如今基于模块的代码中将解析为与您的GOPATH中驻留的代码相同的代码import "foo",而且该构建最终以-的一个副本结尾foo-特别是不管GOPATH磁盘上的版本如何。这使得基于模块的代码 import "foo/v2"甚至能够在1.9.7 +,1.10.3 +和1.11的GOPATH模式下进行编译。
  • 相反,当go工具以全模块模式运行时:
    • “具备不一样导入路径的软件包是不一样的软件包”规则也没有例外(包括在彻底模块模式下对vendor进行了改进,以也遵照此规则)。
    • 例如,若是该go工具处于完整模块模式而且foo是v2 +模块,则import "foo"要求提供foovs 的v1版本,import "foo/v2"要求提供的v2版本foo

若是我建立go.mod但不将语义化版本号标记应用于存储库,会发生什么状况?

语义化版本号是模块系统的基础。为了向消费者提供最佳体验,鼓励模块做者使用语义化版本号 VCS标签(例如v0.1.0v1.2.3-rc.1),但严格要求不使用语义化版本号 VCS标签:

  1. 要求模块遵循语义化版本号规范,以使该go命令按照记录的方式运行。这包括遵循关于如何以及什么时候容许更改的语义化版本号规范。

  2. 消费者使用伪版本形式的语义化版本号版本记录没有语义化版本号 VCS标签的模块一般,这将是v0主版本,除非模块做者遵循“主子目录”方法构造了v2 +模块

  3. 所以,不该用语义化版本号 VCS标记且未建立“主要子目录”的模块将有效地声明本身属于语义化版本号 v0主版本系列,而且基于模块的使用者将其视为具备语义化版本号 v0主版本。

模块能够依赖于其自身的不一样版本吗?

一个模块能够依赖于其自身的不一样主要版本:总的来讲,这至关于依赖于不一样的模块。出于各类缘由,这可能颇有用,包括容许将模块的主要版本实现为围绕其余主要版本的填充程序。

此外,一个模块能够在一个周期中依赖于其自身的不一样主要版本,就像两个彻底不一样的模块能够在一个周期中彼此​​依赖同样。

可是,若是您不但愿模块依赖于其自身的不一样版本,则多是错误的征兆。例如,打算从v3模块导入软件包的.go代码可能缺乏/v3import语句中所需的内容。根据自己的v1版本,该错误可能表现为v3模块。

若是您惊讶地看到一个模块依赖于其自身的不一样版本,那么值得回顾一下上面“语义导入版本控制”部分以及常见问题解答“若是我没有看到预期的版本,该怎么办?依赖?” 

两个程序包可能在一个周期中彼此​​不依赖仍然是一个约束

FAQS —多模块存储库

什么是多模块存储库?

多模块存储库是一个包含多个模块的存储库,每一个模块都有本身的go.mod文件。每一个模块均从包含其go.mod文件的目录开始,并递归包含该目录及其子目录中的全部程序包,但不包括包含另外一个go.mod文件的任何子树。

每一个模块都有本身的版本信息。存储库根目录下的模块的版本标签必须包含相对目录做为前缀。例如,考虑如下存储库:

my-repo
`-- foo
    `-- rop
        `-- go.mod

模块“ my-repo / foo / rop”的1.2.3版本的标签是“ foo / rop / v1.2.3”。

一般,存储库中一个模块的路径将是其余模块的前缀。例如,考虑如下存储库:

my-repo
|-- bar
|-- foo
|   |-- rop
|   `-- yut
|-- go.mod
`-- mig
    |-- go.mod
    `-- vub

图。顶级模块的路径是另外一个模块的路径的前缀。

图。顶级模块的路径是另外一个模块的路径的前缀。

该存储库包含两个模块。可是,模块“ my-repo”是模块“ my-repo / mig”的路径的前缀。

我应该在一个存储库中有多个模块吗?

在这样的配置中添加模块,删除模块和版本控制模块须要至关的谨慎和考虑,所以,管理单个模块存储库而不是现有存储库中的多个模块几乎老是更容易,更简单。

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

对于除电源用户之外的全部用户,您可能但愿采用一种惯例,即一个repo =一个模块。对于代码存储选项的长期发展很重要,一个仓库能够包含多个模块,可是默认状况下您几乎确定不想这样作。

关于如何使多模块更有效的两个示例:

  • go test ./... 从存储库根目录开始将再也不测试存储库中的全部内容
  • 您可能须要经过replace指令按期管理模块之间的关系

可是,除了这两个示例以外,还有其余细微差异。若是您考虑在单个存储库中包含多个模块,请仔细阅读本小节中的FAQ 

有两个示例场景,其中go.mod一个存储库中可能有多个合理的场景

  1. 若是您有一些使用示例,其中这些示例自己具备一组复杂的依赖关系(例如,也许您的软件包很小,但包括一个将软件包与kubernetes结合使用的示例)。在这种状况下,对于您的存储库来讲,拥有一个examples一个_examples本身的目录是颇有意义go.mod,例如here

  2. 若是您的存储库具备一组复杂的依赖关系,可是您的客户端API的依赖关系集较少。在某些状况下,拥有一个api一个clientapi多个具备本身的目录go.mod或将其分隔clientapi到本身的存储库中多是有意义的

可是,对于这两种状况,若是您考虑为多组间接依赖项建立性能或下载大小的多模块存储库,则强烈建议您首先尝试使用GOPROXY,它将在Go中默认启用1.13。使用GOPROXY一般等同于可能会因建立多模块存储库而带来的任何性能优点或依赖项下载大小优点。

是否能够将模块添加到多模块存储库?

是。可是,此问题有两类:

第一类:要添加模块的软件包还没有处于版本控制中(新软件包)。这种状况很简单:将包和go.mod添加到同一提交中,标记该提交,而后推送。

第二类:添加模块的路径在版本控制中,而且包含一个或多个现有软件包。这种状况须要至关多的护理。为了说明,再次考虑如下存储库(如今位于github.com位置,以更好地模拟真实世界):

github.com/my-repo
|-- bar
|-- foo
|   |-- rop
|   `-- yut
|-- go.mod
`-- mig
    `-- vub

考虑添加模块“ github.com/my-repo/mig”。若是要采用与上述相同的方法,则能够经过两个不一样的模块提供软件包/ my-repo / mig:旧版本的“ github.com/my-repo”和新的独立模块“ github”。 com / my-repo / mig。若是两个模块都处于活动状态,则导入“ github.com/my-repo/mig”将在编译时致使“模棱两可的导入”错误。

解决此问题的方法是使新添加的模块取决于“雕刻”出的模块,而后再将其雕刻出来。

假设“ github.com/my-repo”当前位于v1.2.3,让咱们经过上面的存储库逐步进行操做:

  1. 添加github.com/my-repo/mig/go.mod:

    cd path-to/github.com/my-repo/mig
    go mod init github.com/my-repo/mig
    
    # Note: if "my-repo/mig" does not actually depend on "my-repo", add a blank
    # import.
    # Note: version must be at or after the carve-out.
    go mod edit -require github.com/myrepo@v1.3
  2. git commit

  3. git tag v1.3.0

  4. git tag mig/v1.0.0

  5. 接下来,让咱们测试一下。咱们不能go build仍是go test天真地作,由于go命令会尝试从模块缓存中获取每一个相关模块。所以,咱们须要使用替换规则来使go命令使用本地副本:

    cd path-to/github.com/my-repo/mig
    go mod edit -replace github.com/my-repo@v1.3.0=../
    go test ./...
    go mod edit -dropreplace github.com/my-repo@v1.3.0
  6. git push origin master v1.2.4 mig/v1.0.0 推送提交和两个标签

请注意,未来golang.org/issue/28835应该使测试步骤更直接。

还要注意,在次要版本之间,代码已从模块“ github.com/my-repo”中删除。不将其视为主要更改彷佛很奇怪,可是在这种状况下,传递性依存关系继续在其原始导入路径中提供已删除软件包的兼容实现。

是否能够从多模块存储库中删除模块?

是的,具备与上述相同的两种状况和相似的步骤。

一个模块能够依赖于内部模块吗?

是。一个模块中的程序包能够从另外一个模块中导入内部程序包,只要它们共享与内部/路径组件相同的路径前缀便可。例如,考虑如下存储库:

my-repo
|-- foo
|   `-- go.mod
|-- go.mod
`-- internal

在这里,只要模块“ my-repo / foo”依赖于模块“ my-repo”,软件包foo就能够导入/ my-repo / internal。一样,在如下存储库中:

my-repo
|-- foo
|   `-- go.mod
`-- internal
    `-- go.mod

在这里,只要模块“ my-repo / foo”依赖于模块“ my-repo / internal”,软件包foo就能够导入my-repo / internal。二者的语义相同:因为my-repo是my-repo / internal和my-repo / foo之间的共享路径前缀,所以容许foo包导入内部包。

额外的go.mod能够排除没必要要的内容吗?模块是否等效于.gitignore文件?

go.mod在单个存储库中具备多个文件的另外一个用例是,若是存储库中的文件应从模块中删除。例如,存储库可能具备Go模块不须要的很是大的文件,或者多语言存储库可能具备许多非Go文件。

go.mod目录中的空白将致使该目录及其全部子目录从顶级Go模块中排除。

若是排除的目录不包含任何.go文件,则除了放置空go.mod文件以外,不须要其余步骤若是排除的目录中确实包含.go文件,请首先仔细阅读此多模块存储库部分中的其余常见问题解答

常见问题解答–最小版本选择

最少的版本选择是否会使开发人员没法得到重要的更新?

请参阅问题“最小版本选择是否会使开发人员没法得到重要更新?” 在较早的FAQ中,来自官方提案的讨论

常见问题解答-可能的问题

若是我发现问题,能够进行哪些常规检查?

  • 经过运行go env来确认是否启用了模块,以确认其未为只读GOMOD变量显示空值
    • 注意:永远不要将其设置GOMOD为变量,由于它其实是输出的只读调试go env输出。
    • 若是GO111MODULE=on要启用模块,请仔细检查它是否不是复数形式GO111MODULES=on(人们有时天然会包括,S由于该功能一般称为“模块”)。
  • 若是指望使用vendor,请检查该-mod=vendor标志是否正在传递给go build 传递给标志或已GOFLAGS=-mod=vendor被设置。
    • 默认状况下,模块会忽略vendor目录,除非您要求go使用工具vendor
  • 检查go list -m all查看为您的构建选择的实际版本列表 一般会颇有帮助
    • go list -m all与仅查找go.mod文件相比,一般能够提供更多详细信息
  • 若是运行go get foo因某种go build缘由而失败,或者特定软件包失败foo,则一般能够检查go get -v foo的输出go get -v -x foo
    • 一般,go get一般会提供比更为详细的错误消息go build
    • -v标志go get要求打印更多详细信息,但请注意,根据配置远程存储库的方式,某些“错误”(例如404错误)可能会发生。
    • 若是问题的本质仍然不清楚,您也能够尝试更详细的说明go get -v -x foo,它也显示了git或其余已发布的VCS命令。(若是有保证,您一般能够在go工具的上下文以外执行相同的git命令以进行故障排除)。
  • 您能够检查是否使用了特别旧的git版本
    • 较早版本的git是vgo原型和Go 1.11 beta 的常见问题根源,但在GA 1.11中则不多出现。
  • Go 1.11中的模块缓存有时可能会致使各类错误,主要是若是先前存在网络问题或go并行执行多个命令(请参阅#26794,Go 1.12已解决)。做为故障排除步骤,您能够将$ GOPATH / pkg / mod复制到备份目录(以防稍后须要进一步调查),运行go clean -modcache,而后查看原始问题是否仍然存在。
  • 若是您使用的是Docker,则检查是否能够在Docker以外重现行为可能会有所帮助(而且若是该行为仅在Docker中发生,则上面的项目符号列表能够用做比较Docker内部与外面)。

当前正在检查的错误多是因为构建中没有特定模块或软件包的预期版本而引发的第二个问题。所以,若是致使特定错误的缘由不明显,则能够按照下一个FAQ中的说明对您的版本进行抽查。

若是没有看到指望的依赖版本,该如何检查?

  1. 一个好的第一步是运行go mod tidy这可能会解决问题,但也有可能使您的go.mod文件相对于.go源代码处于一致状态,这将使之后的调查更加容易。(若是go mod tidy它自己以某种您不但愿的方式更改了依赖关系的版本,请先阅读'go mod tidy'上的常见问题解答。若是仍没法解决,您能够尝试重置您的go.mod,而后运行go list -mod=readonly all,这可能会带来更多的变化。有关须要更改其版本的特定消息)。

  2. 第二步一般应检查go list -m all以查看为您的构建选择的实际版本列表。 go list -m all向您显示最终选择的版本,包括用于间接依赖性的版本以及在解决全部共享依赖性的版本以后的版本。它还显示any replaceexclude指令的结果

  3. 下一步是检查go mod graph的输出go mod graph | grep <module-of-interest>。 go mod graph打印模块需求图(包括考虑的更换)。输出中的每一行都有两个字段:第一列是使用模块,第二列是该模块的要求之一(包括该使用模块所需的版本)。这是查看哪些模块须要特定依赖项的快速方法,包括当构建的依赖项具备与构建中的不一样使用者不一样的所需版本时(若是是这种状况,熟悉该模块很重要)。行为在上面的“版本选择”部分中进行了说明)。

go mod why -m <module> 在这里也能够是有用的,尽管一般对于查看为何彻底包含依赖项(而不是为何依赖项以特定版本结束)更为有用。

go list提供了更多的查询变体,在须要时能够用于查询模块。如下是一个示例,它将显示构建中使用的确切版本,不包括仅测试依赖项:

go list -deps -f '{{with .Module}}{{.Path}} {{.Version}}{{end}}' ./... | sort -u

用于询问你的模块的更详细的命令集和实施例的可以在可运行“转到模块经过实施例”中能够看出walkthough

致使意外版本的一个缘由多是因为某人建立了一个go.mod非预期的无效或意外文件,或者是相关的错误(例如:一个v2.0.1模块版本可能错误地将自身声明为module foo在其中,go.mod而没有必要/v2; import语句)在.go打算导入v3的模块可能缺乏所需的代码/v3;一require在一份声明中go.mod对V4模块可能缺乏必要的/v4)。所以,若是您看不到引发特定问题的缘由,那么值得首先阅读“ go.mod”“语义导入版本控制”中的材料上面的部分(考虑到这些包括模块必须遵循的重要规则),而后花几分钟来检查最相关的go.mod文件和import语句。

为何会出现错误“找不到提供软件包foo的模块”?

这是一条常见的错误消息,可能会因几种不一样的根本缘由而发生。

在某些状况下,此错误仅是因为路径键入错误引发的,所以第一步可能应该是根据错误消息中列出的详细信息再次检查错误的路径。

若是您尚未这样作,那么下一步一般是尝试go get -v foo如下操做go get -v -x foo

  • 一般,go get一般会提供比更为详细的错误消息go build
  • 有关更多详细信息,请参见上面本节中的第一个故障排除常见问题解答

其余一些可能的缘由:

  • cannot find module providing package foo若是您已发出go build在当前目录中go build .没有.go任何源文件,则可能会看到错误若是这是您遇到的问题,则解决方案多是另外一种调用,例如go build ./...(其中./...展开以匹配当前模块中的全部软件包)。参见#27122

  • Go 1.11中的模块缓存可能致使此错误,包括面对网络问题或go并行执行的多个命令。这在Go 1.12中已解决。有关更多详细信息和可能的纠正步骤,请参阅上面本节中的第一个故障排除常见问题解答

为何“ go mod init”给出错误“没法肯定源目录的模块路径”?

go mod init没有任何参数的状况下,将根据不一样的提示(例如VCS元数据)尝试猜想正确的模块路径。可是,不能go mod init老是可以猜想出正确的模块路径。

若是go mod init给您这个错误,则这些试探法没法猜想,您必须本身提供模块路径(例如go mod init github.com/you/hello)。

我有一个还没有选择模块的复杂依赖性问题。我可使用其当前依赖项管理器中的信息吗?

是。这须要一些手动步骤,但在某些更复杂的状况下可能会有所帮助。

go mod init初始化本身的模块时运行时,它将经过转换配置文件(如Gopkg.lockglide.lock)或包含相应指令vendor.jsongo.mod文件自动从先前的依赖项管理器转换requireGopkg.lock例如,现有文件中的信息一般描述全部直接和间接依赖项的版本信息。

可是,若是改成添加还没有选择加入模块自己的新依赖项,则任何先前的依赖项管理器都不会使用相似的自动转换过程,而新的依赖项可能已经在使用该转换过程。若是该新依赖项自己具备发生了重大更改的非模块依赖项,则在某些状况下可能会致使不兼容问题。换句话说,新依赖项的先前依赖项管理器不会自动使用,在某些状况下,这可能会致使间接依赖项出现问题。

一种方法是go mod init在有问题的非模块直接依赖项上运行,以从其当前的依赖项管理器进行转换,而后使用require结果临时文件中指令go.mod填充或更新go.mod模块中的。

例如,若是github.com/some/nonmodule当前正在使用另外一个依赖性管理器的模块存在直接的依赖性问题,则能够执行如下操做:

$ git clone -b v1.2.3 https://github.com/some/nonmodule /tmp/scratchpad/nonmodule
$ cd /tmp/scratchpad/nonmodule
$ go mod init
$ cat go.mod

require临时的结果信息go.mod能够手动移至go.mod模块的实际信息中,或者您能够考虑使用https://github.com/rogpeppe/gomodmerge,这是针对此用例的社区工具。另外,您将须要require github.com/some/nonmodule v1.2.3在实际中添加一个go.mod以匹配您手动克隆的版本。

#28489注释中针对Docker使用此技术的具体示例说明了如何获取一致的Docker依赖项版本集,从而避免github.com/sirupsen/logrusvs. 之间区分大小写的问题github.com/Sirupsen/logrus

如何解决因为导入路径与声明的模块标识不匹配而致使的“解析go.mod:意外的模块路径”和“错误加载模块要求”错误?

为何会发生此错误?

一般,模块go.mod经过module指令(例如)在其声明中声明其身份module example.com/m这是该模块的“模块路径”,而且该go工具在该声明的模块路径与任何使用者使用的导入路径之间强制保持一致性。若是模块的go.mod文件为module example.com/m,则使用者必须使用以该模块路径开头的导入路径(例如import "example.com/m"import "example.com/m/sub/pkg"从该模块导入软件包

若是使用者使用的导入路径与相应的声明模块路径不匹配,则go命令将报告parsing go.mod: unexpected module path致命错误。另外,在某些状况下,该go命令随后将报告更通用的error loading module requirements错误。

致使此错误的最多见缘由是,是否进行了名称更改(例如github.com/Sirupsen/logrusgithub.com/sirupsen/logrus),或者因为虚荣导入路径而致使模块有时在模块以前经过两个不一样的名称使用(例如github.com/golang/syncvs.建议golang.org/x/sync)。

若是您有一个依存关系仍经过较旧的名称(例如,github.com/Sirupsen/logrus)或非规范名称(例如,github.com/golang/sync导入可是该依赖关系随后采用了模块,而且如今在中声明了其规范名称,则可能会致使问题go.mod而后,当发现模块的升级版本声明再也不与旧的导入路径匹配的规范模块路径时,在升级期间会触发此错误。

问题场景示例

  • 您间接依赖github.com/Quasilyte/go-consistent
  • 该项目采用模块,而后将其名称更改成github.com/quasilyte/go-consistent(更改Q为小写q),这是一个重大更改。GitHub从旧名称转发到新名称。
  • 您运行go get -u,它将尝试升级全部直接和间接依赖项。
  • github.com/Quasilyte/go-consistent试图进行升级,可是最新go.mod发现为module github.com/quasilyte/go-consistent
  • 整体升级操做没法完成,出现如下错误:

转到:github.com/Quasilyte/go-consistent@v0.0.0-20190521200055-c6f3937de18c:解析go.mod:意外的模块路径“ github.com/quasilyte/go-consistent”转到:错误加载模块要求

解决

错误的最多见形式是:

转到:example.com/some/OLD/name@vX.YZ:解析go.mod:意外的模块路径“ example.com/some/NEW/name”

若是您访问存储库的信息example.com/some/NEW/name(从错误的右侧开始),则能够检查go.mod文件的最新版本,或者master查看它是否在go.modas 的第一行中声明了本身module example.com/some/NEW/name若是是这样,则暗示您看到的是“旧模块名称”与“新模块名称”的问题。

本节的其他部分重点在于按顺序执行如下步骤来解决此错误的“旧名称”和“新名称”形式:

  1. 检查您本身的代码,看看是否要使用导入example.com/some/OLD/name若是是这样,请更新您的代码以导入example.com/some/NEW/name

  2. 若是您在升级过程当中收到此错误,则应尝试使用Go的尖端版本进行升级,该尖端版本具备针对性更强的升级逻辑(#26902),一般能够回避此问题,而且在这种状况下一般还具备更好的错误消息。请注意,go gettip / 1.13中参数与1.12 中的参数不一样。获取提示并使用它升级依赖项的示例:

go get golang.org/dl/gotip && gotip download
gotip get -u all
gotip mod tidy

因为有问题的旧导入一般是间接依赖的,所以使用tip升级而后运行go mod tidy能够常常将您升级为有问题的版本,而后从go.mod再也不须要的版本中删除有问题的版本,这将使您进入正常运行状态返回到使用Go 1.12或1.11进行平常使用。例如,看到这种方法工做在这里升级过去github.com/golang/lintgolang.org/x/lint问题。

  1. 若是您在执行go get -u foo或时收到此错误go get -u foo@latest,请尝试删除-u这将为您提供一组所使用的依赖项,foo@latest而无需升级发布foofoo可能会验证为工做过去版本的依赖项foo这在过渡时期可能很重要,在这种过渡时期,某些直接或间接的依赖关系foo可能还没有采用语义化版本号或模块。(一个常见的错误是认为go get -u foo仅获取的最新版本foo。实际上,-uin go get -u foogo get -u foo@latest手段获取的全部直接和间接依赖项的最新版本foo可能正是您想要的,但若是因为深层间接依赖而致使失败,则可能不会特别如此。

  2. 若是上述步骤不能解决错误,则下一种方法会稍微复杂一些,但大多数状况下应该能够解决此错误的“旧名称”和“新名称”形式。这仅使用仅来自错误消息自己的信息,并简要介绍了一些VCS历史记录。

    4.1。转到example.com/some/NEW/name存储库

    4.2。肯定什么时候将go.mod文件引入那里(例如,经过查看的责任或历史视图go.mod)。

    4.3。挑选释放或提交以前go.mod介绍有文件。

    4.4。在您的go.mod文件中,在replace语句的两边添加一个使用旧名称的replace语句: replace example.com/some/OLD/name => example.com/some/OLD/name <version-just-before-go.mod> 使用咱们先前的示例,其中哪里github.com/Quasilyte/go-consistent是旧名称,又github.com/quasilyte/go-consistent是新名称,咱们能够看到go.mod最先是在commit 00c5b0cf371a中引入的该存储库未使用语义化版本号标记,所以咱们将紧接以前的提交00dd7fb039e提交,并使用两边的旧大写Quasilyte名称将其添加到替换中replace

replace github.com/Quasilyte/go-consistent => github.com/Quasilyte/go-consistent 00dd7fb039e

replace而后,语句使咱们可以经过有效地防止在存在的状况下将旧名称升级为新名称,从而解决了有问题的“旧名称”与“新名称”不匹配的问题go.mod一般,经过go get -u或相似方式进行升级如今能够避免该错误。若是升级完成,则能够检查是否有人仍在导入旧名称(例如go mod graph | grep github.com/Quasilyte/go-consistent),若是没有导入,则replace能够将其删除。(这常常起做用的缘由是,若是使用了旧的有问题的导入路径,升级自己可能会失败,即便升级完成后在最终结果中可能不会使用升级路径也是如此(在#30831中进行了跟踪)。

  1. 若是上述步骤不能解决问题,则多是由于一个或多个依赖项的最新版本仍在使用有问题的旧导入路径。在这种状况下,重要的是肯定谁仍在使用有问题的旧导入路径,并查找或打开一个问题,要求有问题的进口商更改成使用如今的规范导入路径。gotip在上面的第2步中使用可能会发现有问题的进口商,但并不是在全部状况下均可以,尤为是对于升级(#30661)。若是不清楚是谁在使用有问题的旧导入路径进行导入,一般能够经过建立干净的模块高速缓存,执行触发错误的一个或多个操做,而后在模块高速缓存中查找旧的有问题的导入路径来找出答案。例如:
export GOPATH=$(mktemp -d)
go get -u foo               # peform operation that generates the error of interest
cd $GOPATH/pkg/mod
grep -R --include="*.go" github.com/Quasilyte/go-consistent
  1. 若是这些步骤不足以解决问题,或者您是某个项目的维护者,但因为循环引用而彷佛没法删除对较旧的有问题的导入路径的引用,请在上查看有关该问题的详细说明。单独的Wiki页面

最后,上述步骤集中于如何解决潜在的“旧名称”与“新名称”问题。可是,若是将a go.mod放置在错误的位置或仅具备错误的模块路径,也会出现相同的错误消息在这种状况下,导入该模块应始终失败。若是要导入刚刚建立且从未成功导入的新模块,则应检查go.mod文件是否正肯定位,以及文件是否具备与该位置对应的正确模块路径。(最多见的方法是go.mod每一个存储库使用单个go.mod文件,并将单个文件放置在存储库根目录中,并使用存储库名称做为module指令中声明的模块路径)。参见“ go.mod”

为何“开始构建”须要gcc,为何不使用诸如net / http之类的预构建软件包?

简而言之:

由于预构建的软件包是非模块构建的,因此不能重复使用。抱歉。如今禁用cgo或安装gcc。

仅在选择加入模块时(例如经过GO111MODULE=on,这才是一个问题有关其余讨论,请参见#26988

模块能够与相似的进口商品一块儿使用import "./subdir"吗?

否。请参阅#26645,其中包括:

在模块中,最后有一个子目录的名称。若是父目录显示“模块m”,则子目录将导入为“ m / subdir”,而再也不是“ ./subdir”。

某些vendor目录中可能没有所需的文件

没有.go文件的vendor目录不会经过复制在目录go mod vendor这是设计使然。

简而言之,撇开任何特定的vendor行为– go构建的整体模型是构建软件包所需的文件应位于包含.go文件的目录中

以cgo为例,修改其余目录中的C源代码不会触发重建,而是您的构建将使用陈旧的缓存条目。cgo文档如今包括

请注意,对其余目录中文件的更改不会致使从新编译该软件包,所以,该软件包的全部非Go源代码应存储在软件包目录中,而不是子目录中。

社区工具https://github.com/goware/modvendor容许您轻松地将一整套.c,.h,.s,.proto或其余文件从模块复制到vendorDirector中。尽管这可能会有所帮助,可是若是您有一些须要的文件来构建包含该.go文件的目录以外的文件,则必须格外当心,以确保整体上能够正确处理go编译(与vendor无关)

请参阅#26366中的其余讨论

传统vendor的另外一种方法是检入模块缓存。它最终可能会得到与传统vendor相似的好处,而且在某些方面最终会得到更高的保真度。将此方法解释为“经过示例执行模块” 演练

相关文章
相关标签/搜索