Go 模块参考

介绍

模块是 Go 管理依赖项的方式。

本文档是 Go 模块系统的详细参考手册。有关创建 Go 项目的介绍,请参阅 如何编写 Go 代码。有关使用模块、将项目迁移到模块以及其他主题的信息,请参阅从 使用 Go 模块 开始的博客系列。

模块、包和版本

一个 模块 是一个包的集合,它们一起发布、版本化和分发。模块可以从版本控制存储库或模块代理服务器直接下载。

模块由 模块路径 标识,该路径在 go.mod 文件 中声明,以及有关模块的依赖项的信息。 模块根目录 是包含 go.mod 文件的目录。 主模块 是包含调用 go 命令的目录的模块。

模块中的每个 都是同一个目录中一起编译的源文件的集合。 包路径 是模块路径与包含包的子目录(相对于模块根目录)相连接。例如,模块 "golang.org/x/net""html" 目录中包含一个包。该包的路径是 "golang.org/x/net/html"

模块路径

一个 模块路径 是模块的规范名称,由模块的 go.mod 文件 中的 module 指令 声明。模块的路径是模块中包路径的前缀。

模块路径应该描述模块的功能和位置。通常,模块路径包含一个存储库根路径、存储库中的一个目录(通常为空)和一个主版本后缀(仅限于主版本 2 或更高)。

如果其他模块可能依赖于模块,则必须遵循这些规则,以便 go 命令可以找到和下载模块。还有一些关于模块路径中允许的字符的 词汇限制

永远不会作为其他模块的依赖项获取的模块可以使用任何有效的包路径作为其模块路径,但必须注意不要与模块的依赖项或 Go 标准库可能使用的路径发生冲突。Go 标准库使用不包含第一个路径元素中的点的包路径,go 命令不会尝试从网络服务器解析此类路径。路径 exampletest 为用户保留:它们不会在标准库中使用,适合用于自包含模块,例如教程或示例代码中定义的模块,或作为测试的一部分创建和操作的模块。

版本

一个 版本 标识模块的不可变快照,可以是 发布版预发布版。每个版本都以字母 v 开头,后跟一个语义版本。有关版本如何格式化、解释和比较的详细信息,请参阅 语义版本控制 2.0.0

总而言之,语义版本由三个非负整数(主版本、次版本和修订版本,从左到右)组成,用点隔开。修订版本后面可以接一个可选的预发布字符串,该字符串以连字符开头。预发布字符串或修订版本后面可以接一个以加号开头的构建元数据字符串。例如,v0.0.0v1.12.134v8.0.5-prev2.0.9+meta 是有效的版本。

版本的每个部分都指示版本是否稳定以及它是否与以前的版本兼容。

如果版本的版本号为 0 或带有预发布后缀,则该版本被视为不稳定。不稳定版本不受兼容性要求的约束。例如,v0.2.0 可能与 v0.1.0 不兼容,v1.5.0-beta 可能与 v1.5.0 不兼容。

Go 可以使用不遵循这些约定的标签、分支或修订版访问版本控制系统中的模块。但是,在主模块内,go 命令将自动将不遵循此标准的修订版名称转换为规范版本。go 命令还会在此过程中删除构建元数据后缀(除了 +incompatible)。这可能会导致一个 伪版本,这是一个预发布版本,它对版本控制系统中的修订版标识符(例如 Git 提交哈希)和时间戳进行编码。例如,命令 go get golang.org/x/net@daa7c041 将提交哈希 daa7c041 转换为伪版本 v0.0.0-20191109021931-daa7c04131f5。规范版本在主模块外部是必需的,如果 go.mod 文件中出现像 master 这样的非规范版本,go 命令将报告错误。

伪版本

一个 伪版本 是一个特殊格式的 预发布 版本,它对版本控制存储库中特定修订版的信息进行编码。例如,v0.0.0-20191109021931-daa7c04131f5 是一个伪版本。

伪版本可能引用没有可用的 语义版本标签 的修订版。例如,它们可以用于在创建版本标签之前测试提交,例如,在开发分支上。

每个伪版本都有三个部分

每个伪版本可以有三种形式,具体取决于基准版本。这些形式确保伪版本比其基准版本高,但比下一个标记版本低。

多个伪版本可以使用不同的基准版本引用同一个提交。当较低版本在写入伪版本后被标记时,这种情况自然发生。

这些形式赋予伪版本两个有用的属性

go 命令执行一些检查,以确保模块作者能够控制伪版本与其他版本的比较方式,并且伪版本引用的是实际属于模块提交历史的修订。

伪版本永远不需要手动输入。许多命令接受提交哈希或分支名称,并将其自动转换为伪版本(或如果可用则为标记版本)。例如

go get example.com/mod@master
go list -m -json example.com/mod@abcd1234

主版本后缀

从主版本 2 开始,模块路径必须具有一个 主版本后缀(如 /v2),它与主版本匹配。例如,如果模块在 v1.0.0 处具有路径 example.com/mod,则它必须在版本 v2.0.0 处具有路径 example.com/mod/v2

主版本后缀实现了 导入兼容性规则

如果旧包和新包具有相同的导入路径,则新包必须向后兼容旧包。

根据定义,模块新主版本中的包与前一个主版本中的对应包不向后兼容。因此,从 v2 开始,包需要新的导入路径。这是通过在模块路径中添加主版本后缀来实现的。由于模块路径是模块中每个包导入路径的前缀,因此在模块路径中添加主版本后缀为每个不兼容版本提供了不同的导入路径。

主版本后缀不允许在主版本 v0v1 处使用。在 v0v1 之间不需要更改模块路径,因为 v0 版本是不稳定的,没有兼容性保证。此外,对于大多数模块来说,v1 向后兼容最后一个 v0 版本;v1 版本是作为对兼容性的承诺,而不是与 v0 相比的不兼容更改的指示。

作为特殊情况,以 gopkg.in/ 开头的模块路径必须始终具有主版本后缀,即使在 v0v1 处也是如此。后缀必须以点而不是斜杠开头(例如,gopkg.in/yaml.v2)。

主版本后缀允许模块的多个主版本在同一个构建中共存。这可能是由于 菱形依赖问题 造成的。通常,如果模块在两个不同的版本中被传递依赖项要求,则将使用较高版本。但是,如果两个版本不兼容,则没有版本可以满足所有客户端。由于不兼容的版本必须具有不同的主版本号,因此它们也必须由于主版本后缀而具有不同的模块路径。这解决了冲突:具有不同后缀的模块被视为不同的模块,并且它们的包(即使是相对于其模块根目录位于相同子目录中的包)也是不同的。

在迁移到模块之前(可能是在模块出现之前),许多 Go 项目在 v2 或更高版本处发布版本,而没有使用主版本后缀。这些版本用 +incompatible 构建标签进行注释(例如,v2.0.0+incompatible)。有关更多信息,请参阅 与非模块存储库的兼容性

将包解析为模块

go 命令使用 包路径 加载包时,它需要确定哪个模块提供了该包。

go 命令首先在 构建列表 中搜索路径为包路径前缀的模块。例如,如果导入包 example.com/a/b,并且模块 example.com/a 位于构建列表中,go 命令将检查 example.com/a 是否包含该包,在目录 b 中。为了被视为包,目录中必须至少存在一个具有 .go 扩展名的文件。 构建约束 不会用于此目的。如果构建列表中只有一个模块提供了该包,则使用该模块。如果没有任何模块提供该包,或者两个或多个模块提供了该包,go 命令将报告错误。-mod=mod 标志指示 go 命令尝试查找提供缺少包的新模块,并更新 go.modgo.sumgo getgo mod tidy 命令会自动执行此操作。

go 命令为包路径查找新模块时,它会检查 GOPROXY 环境变量,该变量是一个逗号分隔的代理 URL 列表,或者关键字 directoff。代理 URL 表示 go 命令应该使用 GOPROXY 协议 联系 模块代理direct 表示 go 命令应该 与版本控制系统通信off 表示不应尝试进行任何通信。GOPRIVATEGONOPROXY 环境变量 也可以用于控制这种行为。

对于 GOPROXY 列表中的每个条目,go 命令请求可能提供该包的每个模块路径(即包路径的每个前缀)的最新版本。对于每个成功请求的模块路径,go 命令将下载该模块的最新版本,并检查该模块是否包含请求的包。如果一个或多个模块包含请求的包,则使用路径最长的模块。如果找到一个或多个模块,但没有一个包含请求的包,则会报告错误。如果没有找到模块,go 命令将尝试 GOPROXY 列表中的下一个条目。如果没有剩余的条目,则会报告错误。

例如,假设 go 命令正在寻找提供包 golang.org/x/net/html 的模块,并且 GOPROXY 设置为 https://corp.example.com,https://proxy.golang.orggo 命令可能会发出以下请求

找到合适的模块后,go 命令将使用新模块的路径和版本,向主模块的 go.mod 文件添加一个新的 需求。这确保了在将来加载相同包时,将使用相同模块的相同版本。如果解析的包没有被主模块中的包导入,则新需求将具有 // indirect 注释。

go.mod 文件

模块由其根目录中的名为 go.mod 的 UTF-8 编码文本文件定义。go.mod 文件是面向行的。每行包含一个指令,由一个关键字和参数组成。例如

module example.com/my/thing

go 1.12

require example.com/other/thing v1.0.2
require example.com/new/thing/v2 v2.3.4
exclude example.com/old/thing v1.2.3
replace example.com/bad/thing v1.4.5 => example.com/good/thing v1.4.5
retract [v1.9.0, v1.9.5]

可以将前面的关键字从相邻行中分解出来,形成一个块,就像 Go 导入一样。

require (
    example.com/new/thing/v2 v2.3.4
    example.com/old/thing v1.2.3
)

go.mod 文件旨在可读且可写。go 命令提供了一些子命令,可以更改 go.mod 文件。例如,go get 可以升级或降级特定依赖项。加载模块图的命令会在需要时 自动更新 go.modgo mod edit 可以执行低级编辑。golang.org/x/mod/modfile 包可由 Go 程序用于以编程方式进行相同的更改。

go.mod 文件是 主模块 所必需的,以及使用本地文件路径指定的任何 替换模块。但是,没有显式 go.mod 文件的模块仍然可以作为依赖项 被要求,或者用模块路径和版本指定作为替换使用;请参阅 与非模块存储库的兼容性

词法元素

解析 go.mod 文件时,其内容将被分解成一系列标记。有几种类型的标记:空格、注释、标点符号、关键字、标识符和字符串。

空格 包括空格 (U+0020)、制表符 (U+0009)、回车符 (U+000D) 和换行符 (U+000A)。除换行符之外的空格字符没有任何效果,只是将原本会组合在一起的标记分隔开。换行符是重要的标记。

注释// 开头,一直持续到行尾。/* */ 注释不允许使用。

标点符号 标记包括 ()=>

关键字 区分 go.mod 文件中不同类型的指令。允许的关键字是 modulegorequirereplaceexcluderetract

标识符 是非空格字符的序列,例如模块路径或语义版本。

字符串是由引号括起来的字符序列。字符串有两种:用引号(",U+0022)开头和结尾的解释型字符串,以及用重音符(`,U+0060)开头和结尾的原始字符串。解释型字符串可以包含转义序列,转义序列由反斜杠(\,U+005C)后跟另一个字符组成。转义的引号(\")不会终止解释型字符串。解释型字符串的未引号值是在引号之间的字符序列,其中每个转义序列都被反斜杠后的字符替换(例如,\" 被替换为 "\n 被替换为 n)。相反,原始字符串的未引号值只是重音符之间的字符序列;反斜杠在原始字符串中没有特殊含义。

标识符和字符串在 go.mod 语法中可以互换。

模块路径和版本

go.mod 文件中的大多数标识符和字符串都是模块路径或版本。

模块路径必须满足以下要求

如果模块路径出现在 require 指令中并且没有被替换,或者如果模块路径出现在 replace 指令的右侧,go 命令可能需要下载具有该路径的模块,并且必须满足一些额外的要求。

go.mod 文件中的版本可以是 规范的 或非规范的。

规范版本以字母 v 开头,后跟一个符合 语义版本控制 2.0.0 规范的语义版本。有关更多信息,请参见 版本

大多数其他标识符和字符串可以用作非规范版本,尽管有一些限制,以避免出现文件系统、存储库和 模块代理 的问题。非规范版本只允许在主模块的 go.mod 文件中使用。当 go 命令自动 更新 go.mod 文件时,它将尝试用等效的规范版本替换每个非规范版本。

在模块路径与版本关联的地方(如 requirereplaceexclude 指令),最终路径元素必须与版本一致。请参见 主版本后缀

语法

go.mod 语法使用扩展巴科斯-诺尔范式 (EBNF) 在下面指定。有关 EBNF 语法的详细信息,请参见 Go 语言规范中的符号部分

GoMod = { Directive } .
Directive = ModuleDirective |
            GoDirective |
            RequireDirective |
            ExcludeDirective |
            ReplaceDirective |
            RetractDirective .

换行符、标识符和字符串分别用 newlineidentstring 表示。

模块路径和版本分别用 ModulePathVersion 表示。

ModulePath = ident | string . /* see restrictions above */
Version = ident | string .    /* see restrictions above */

module 指令

module 指令定义了主模块的 路径go.mod 文件必须恰好包含一个 module 指令。

ModuleDirective = "module" ( ModulePath | "(" newline ModulePath newline ")" ) newline .

示例

module golang.org/x/net

弃用

模块可以在一段注释中被标记为已弃用,该注释包含字符串 Deprecated:(区分大小写),位于段落的开头。弃用消息从冒号开始,一直延续到段落的末尾。注释可以出现在 module 指令之前,也可以出现在同一行上的之后。

示例

// Deprecated: use example.com/mod/v2 instead.
module example.com/mod

从 Go 1.17 开始,go list -m -u 会检查 构建列表 中所有已弃用模块的信息。go get 会检查构建命令行上命名的包所需的已弃用模块。

go 命令检索模块的弃用信息时,它会从与 @latest 版本查询 匹配的版本中加载 go.mod 文件,而不考虑 撤回排除go 命令从同一个 go.mod 文件中加载 已撤回版本 的列表。

为了弃用一个模块,作者可以添加一个 // Deprecated: 注释,并标记一个新的版本。作者可以在更高版本中更改或删除弃用消息。

弃用适用于模块的所有次要版本。高于 v2 的主要版本为此目的被视为独立的模块,因为它们的 主版本后缀 为它们提供了不同的模块路径。

弃用消息旨在通知用户该模块不再受支持,并提供迁移说明,例如迁移到最新的主要版本。单个次要版本和补丁版本不能被弃用;retract 可能更适合于这种情况。

go 指令

go 指令指示一个模块是在假设给定版本的 Go 的语义的情况下编写的。版本必须是有效的 Go 版本,例如 1.91.141.21rc1

go 指令设置了使用该模块所需的 Go 的最低版本。在 Go 1.21 之前,该指令只是建议性的;现在它是强制性的要求:Go 工具链拒绝使用声明了更新版本的 Go 的模块。

go 指令是选择要运行的 Go 工具链的输入。有关详细信息,请参见“Go 工具链”。

go 指令会影响新语言特性的使用

go 指令也会影响 go 命令的行为

go.mod 文件最多只能包含一个 go 指令。大多数命令会在没有指令的情况下添加一个带有当前 Go 版本的 go 指令。

如果缺少 go 指令,则假设为 go 1.16

GoDirective = "go" GoVersion newline .
GoVersion = string | ident .  /* valid release version; see above */

示例

go 1.14

toolchain 指令

toolchain 指令声明了一个建议的 Go 工具链,与模块一起使用。建议的 Go 工具链的版本不能低于 go 指令中声明的所需 Go 版本。toolchain 指令只有在模块为主模块,并且默认工具链的版本低于建议的工具链的版本时才有效。

为了可重现性,go 命令在任何时候都会在 toolchain 行中写入它自己的工具链名称,只要它在 go.mod 文件中更新 go 版本(通常是在 go get 期间)即可。

有关详细信息,请参见“Go 工具链”。

ToolchainDirective = "toolchain" ToolchainName newline .
ToolchainName = string | ident .  /* valid toolchain name; see “Go toolchains” */

示例

toolchain go1.21.0

godebug 指令

godebug 指令声明了一个单一的 GODEBUG 设置,在该模块为主模块时应用。可以有多个这样的行,并且可以将其分解。对于主模块来说,命名一个不存在的 GODEBUG 键是一个错误。godebug key=value 的效果就好像每个被编译的主包都包含一个源文件,该文件列出了 //go:debug key=value

GodebugDirective = "godebug" ( GodebugSpec | "(" newline { GodebugSpec } ")" newline ) .
GodebugSpec = GodebugKey "=" GodebugValue newline.
GodebugKey = GodebugChar { GodebugChar }.
GodebugValue = GodebugChar { GodebugChar }.
GodebugChar = any non-space character except , " ` ' (comma and quotes).

示例

godebug default=go1.21
godebug (
    panicnil=1
    asynctimerchan=0
)

require 指令

require 指令声明给定模块依赖项的最低所需版本。对于每个所需的模块版本,go 命令会加载该版本的 go.mod 文件,并合并该文件中的要求。在加载所有要求之后,go 命令使用 最小版本选择 (MVS) 来解析它们,以生成 构建列表

go 命令会自动为某些要求添加 // indirect 注释。// indirect 注释表示主模块中没有任何包直接导入所需模块中的任何包。

如果 go 指令 指定了 go 1.16 或更低版本,则当模块的选定版本高于主模块的其他依赖项隐式(传递)指示的版本时,go 命令会添加一个间接要求。这可能是由于显式升级(go get -u ./...)、删除了一些以前强加了要求的其他依赖项(go mod tidy)、或一个依赖项导入了一个包,而其自己的 go.mod 文件中没有对应的要求(例如,一个完全没有 go.mod 文件的依赖项)。

go 1.17及以上版本中,go 命令为每个模块添加了一个间接需求,该需求对应任何由主模块中的包或测试导入(即使是间接导入)的包,或者作为go get 的参数传递的包。这些更全面的需求使模块图修剪延迟模块加载成为可能。

RequireDirective = "require" ( RequireSpec | "(" newline { RequireSpec } ")" newline ) .
RequireSpec = ModulePath Version newline .

示例

require golang.org/x/net v1.2.3

require (
    golang.org/x/crypto v1.4.5 // indirect
    golang.org/x/text v1.6.7
)

exclude 指令

exclude 指令可以阻止go 命令加载某个模块版本。

从 Go 1.16 开始,如果任何go.mod 文件中的require 指令引用的版本被主模块go.mod 文件中的exclude 指令排除,则该需求将被忽略。这可能会导致像go getgo mod tidy 这样的命令向go.mod 添加新的需求,以使用更高的版本,并在必要时添加// indirect 注释。

在 Go 1.16 之前,如果require 指令引用了被排除的版本,go 命令会列出该模块的可用版本(如go list -m -versions 所示),并加载下一个更高的未被排除的版本。这会导致版本选择不确定,因为下一个更高的版本可能会随着时间的推移而改变。发布版本和预发布版本都将被考虑,但伪版本则不会。如果没有更高的版本,go 命令会报错。

exclude 指令仅适用于主模块的go.mod 文件,在其他模块中会被忽略。有关详细信息,请参阅最小版本选择

ExcludeDirective = "exclude" ( ExcludeSpec | "(" newline { ExcludeSpec } ")" newline ) .
ExcludeSpec = ModulePath Version newline .

示例

exclude golang.org/x/net v1.2.3

exclude (
    golang.org/x/crypto v1.4.5
    golang.org/x/text v1.6.7
)

replace 指令

replace 指令将特定模块版本的內容,或所有模块版本的內容,替换为其他位置的內容。替换可以指定为另一个模块路径和版本,也可以指定为特定平台的文件路径。

如果箭头 (=>) 左侧存在版本,则仅替换该特定版本的模块;其他版本将正常访问。如果省略了左侧版本,则替换所有模块版本。

如果箭头右侧的路径是绝对路径或相对路径(以./../ 开头),则它被解释为替换模块根目录的本地文件路径,该目录必须包含go.mod 文件。在这种情况下,必须省略替换版本。

如果箭头右侧的路径不是本地路径,则它必须是有效的模块路径。在这种情况下,需要一个版本。相同的模块版本不能也出现在构建列表中。

无论替换是使用本地路径还是模块路径指定,如果替换模块具有go.mod 文件,则其module 指令必须与它所替换的模块路径匹配。

replace 指令仅适用于主模块的go.mod 文件,在其他模块中会被忽略。有关详细信息,请参阅最小版本选择

如果存在多个主模块,则所有主模块的go.mod 文件都适用。跨主模块的冲突replace 指令是禁止的,必须在go.work 文件中的替换中删除或覆盖。

请注意,仅使用replace 指令不会将模块添加到模块图中。还需要一个引用被替换模块版本的require 指令,该指令可以位于主模块的go.mod 文件中,也可以位于依赖项的go.mod 文件中。如果左侧的模块版本没有被要求,则replace 指令无效。

ReplaceDirective = "replace" ( ReplaceSpec | "(" newline { ReplaceSpec } ")" newline ) .
ReplaceSpec = ModulePath [ Version ] "=>" FilePath newline
            | ModulePath [ Version ] "=>" ModulePath Version newline .
FilePath = /* platform-specific relative or absolute file path */

示例

replace golang.org/x/net v1.2.3 => example.com/fork/net v1.4.5

replace (
    golang.org/x/net v1.2.3 => example.com/fork/net v1.4.5
    golang.org/x/net => example.com/fork/net v1.4.5
    golang.org/x/net v1.2.3 => ./fork/net
    golang.org/x/net => ./fork/net
)

retract 指令

retract 指令表示不应依赖go.mod 定义的模块的某个版本或版本范围。当某个版本发布过早或发布后发现严重问题时,retract 指令非常有用。被撤回的版本应保留在版本控制库和模块代理中,以确保依赖它们的构建不会出现问题。术语“撤回”借鉴于学术文献:被撤回的研究论文仍然可用,但存在问题,不应作为未来工作的基础。

当某个模块版本被撤回时,用户将不会使用go getgo mod tidy 或其他命令自动升级到该版本。依赖被撤回版本的构建应继续工作,但用户会在使用go list -m -u 检查更新或使用go get 更新相关模块时收到撤回通知。

要撤回某个版本,模块作者应在go.mod 中添加retract 指令,然后发布包含该指令的新版本。新版本必须高于其他发布版本或预发布版本;也就是说,在考虑撤回之前,@latest 版本查询 应该解析为新版本。go 命令会加载并应用go list -m -retracted $modpath@latest(其中$modpath 是模块路径)显示的版本的撤回。

被撤回的版本不会显示在go list -m -versions 打印的版本列表中,除非使用-retracted 标志。在解析像@>=v1.2.3@latest 这样的版本查询时,被撤回的版本会被排除。

包含撤回的版本可以撤回自身。如果模块的最高发布版本或预发布版本撤回了自身,那么在排除被撤回的版本后,@latest 查询将解析为较低的版本。

例如,假设模块example.com/m 的作者意外发布了版本v1.0.0。为了防止用户升级到v1.0.0,作者可以在go.mod 中添加两个retract 指令,然后用撤回标记v1.0.1

retract (
    v1.0.0 // Published accidentally.
    v1.0.1 // Contains retractions only.
)

当用户运行go get example.com/m@latest 时,go 命令会从v1.0.1(现在是最高版本)读取撤回。由于v1.0.0v1.0.1 都被撤回,因此go 命令会升级(或降级!)到下一个最高版本,可能是v0.9.5

retract 指令可以写成单个版本(如v1.0.0),也可以写成带有上界和下界的闭区间版本,以[] 分隔(如[v1.1.0, v1.2.0])。单个版本等效于上界和下界相同的区间。与其他指令一样,多个retract 指令可以分组在一个由行末的( 和单独行的) 分隔的块中。

每个retract 指令都应该有一个注释解释撤回的理由,但这并非强制性的。go 命令可能会在关于被撤回版本的警告中以及在go list 输出中显示理由注释。理由注释可以写在一个retract 指令的正上方(两者之间没有空行),也可以写在同一行的后面。如果注释出现在块的上面,则它适用于块中所有没有自己的注释的retract 指令。理由注释可以跨越多行。

RetractDirective = "retract" ( RetractSpec | "(" newline { RetractSpec } ")" newline ) .
RetractSpec = ( Version | "[" Version "," Version "]" ) newline .

示例

retract v1.0.0
retract [v1.0.0, v1.9.9]
retract (
    v1.0.0
    [v1.0.0, v1.9.9]
)
retract [v0.0.0, v1.0.1] // assuming v1.0.1 contains this retraction.
retract [v0.0.0-0, v0.15.2]  // assuming v0.15.2 contains this retraction.

retract 指令是在 Go 1.16 中添加的。Go 1.15 及更低版本会在主模块go.mod 文件中写入retract 指令时报错,并会忽略依赖项go.mod 文件中的retract 指令。

自动更新

如果go.mod 缺少信息或无法准确反映实际情况,大多数命令都会报错。go getgo mod tidy 命令可以用来修复大多数这类问题。此外,可以使用-mod=mod 标志与大多数支持模块的命令(go buildgo test 等)一起使用,指示go 命令自动修复go.modgo.sum 中的问题。

例如,考虑以下go.mod 文件

module example.com/M

go 1.16

require (
    example.com/A v1
    example.com/B v1.0.0
    example.com/C v1.0.0
    example.com/D v1.2.3
    example.com/E dev
)

exclude example.com/D v1.2.3

使用-mod=mod 触发的更新会将非规范版本标识符重写为规范 的语义版本格式,因此example.com/Av1 变为v1.0.0,而example.com/Edev 变为dev 分支上最新提交的伪版本,可能是v0.0.0-20180523231146-b3f5c0f6e5f1

更新会修改需求以遵守排除规则,因此对被排除的example.com/D v1.2.3 的需求会更新为使用下一个可用的example.com/D 版本,可能是v1.2.4v1.3.0

更新会删除冗余或误导性的需求。例如,如果example.com/A v1.0.0 本身需要example.com/B v1.2.0example.com/C v1.0.0,则go.mod 中对example.com/B v1.0.0 的需求是误导性的(被example.com/Av1.2.0 的需求所取代),而对example.com/C v1.0.0 的需求是冗余的(被example.com/A 对相同版本的需求所隐含),因此这两个需求都会被删除。如果主模块包含直接从example.com/Bexample.com/C 导入包的包,则这些需求会被保留,但会被更新为实际使用的版本。

最后,更新会以规范格式重新格式化go.mod,以便未来机械化的更改会产生最小的差异。如果只需要进行格式更改,go 命令不会更新go.mod

由于模块图定义了导入语句的含义,因此所有加载包的命令也会使用go.mod,因此可以更新它,包括go buildgo getgo installgo listgo testgo mod tidy

在 Go 1.15 及更低版本中,-mod=mod 标志默认启用,因此更新会自动执行。从 Go 1.16 开始,go 命令会像设置了-mod=readonly 一样工作:如果需要对go.mod 进行任何更改,go 命令会报错,并建议一个修复方法。

最小版本选择 (MVS)

Go 使用一种名为最小版本选择 (MVS) 的算法来选择在构建包时要使用的模块版本集。MVS 在 Russ Cox 的最小版本选择 中有详细描述。

从概念上讲,MVS 在模块的有向图上运行,这些模块由 go.mod 文件 指定。图中的每个顶点代表一个模块版本。每条边代表依赖项的最低所需版本,使用 require 指令指定。该图可以通过主模块的 go.mod 文件中的 excludereplace 指令以及 go.work 文件中的 replace 指令来修改。

MVS 生成的输出是 构建列表,即用于构建的模块版本列表。

MVS 从主模块(图中没有版本的特殊顶点)开始,遍历该图,跟踪每个模块的最高所需版本。在遍历结束时,最高所需版本构成构建列表:它们是最小版本,满足所有要求。

可以使用命令 go list -m all 检查构建列表。与其他依赖管理系统不同,构建列表不会保存在“锁定”文件中。MVS 是确定性的,构建列表在依赖项的新版本发布时不会改变,因此 MVS 用于在每个模块感知命令开始时计算它。

考虑下面图表中的示例。主模块需要版本 1.2 或更高版本的模块 A 和版本 1.2 或更高版本的模块 B。A 1.2 和 B 1.2 分别需要 C 1.3 和 C 1.4。C 1.3 和 C 1.4 都需要 D 1.2。

Module version graph with visited versions highlighted

MVS 访问并加载以蓝色突出显示的每个模块版本的 go.mod 文件。在图遍历结束时,MVS 返回一个包含加粗版本的构建列表:A 1.2、B 1.2、C 1.4 和 D 1.2。请注意,B 和 D 的更高版本可用,但 MVS 不会选择它们,因为没有东西需要它们。

替换

可以使用主模块的 go.mod 文件或工作区的 go.work 文件中的 replace 指令 替换模块的内容(包括其 go.mod 文件)。replace 指令可以应用于模块的特定版本,也可以应用于模块的所有版本。

替换会更改模块图,因为替换模块可能与替换版本具有不同的依赖项。

考虑下面的示例,其中 C 1.4 已被 R 替换。R 依赖于 D 1.3 而不是 D 1.2,因此 MVS 返回一个包含 A 1.2、B 1.2、C 1.4(被 R 替换)和 D 1.3 的构建列表。

Module version graph with a replacement

排除

可以使用主模块的 go.mod 文件中的 exclude 指令 在特定版本排除模块。

排除也会更改模块图。当排除一个版本时,它将从模块图中删除,对它的要求将重定向到下一个更高版本。

考虑下面的示例。C 1.3 已被排除。MVS 将表现得好像 A 1.2 需要 C 1.4(下一个更高版本)而不是 C 1.3。

Module version graph with an exclusion

升级

可以使用 go get 命令升级一组模块。为了执行升级,go 命令在运行 MVS 之前通过从访问的版本添加到升级的版本添加边来更改模块图。

考虑下面的示例。模块 B 可以从 1.2 升级到 1.3,C 可以从 1.3 升级到 1.4,D 可以从 1.2 升级到 1.3。

Module version graph with upgrades

升级(和降级)可能会添加或删除间接依赖项。在这种情况下,E 1.1 和 F 1.1 出现在升级后的构建列表中,因为 E 1.1 是 B 1.3 所需的。

为了保留升级,go 命令会更新 go.mod 中的要求。它会将 B 的要求更改为版本 1.3。它还会添加对 C 1.4 和 D 1.3 的要求,并带有 // indirect 注释,因为否则这些版本将不会被选择。

降级

也可以使用 go get 命令降级一组模块。为了执行降级,go 命令通过删除高于降级版本的版本来更改模块图。它还会删除其他模块的版本,这些模块依赖于已删除的版本,因为它们可能与依赖项的降级版本不兼容。如果主模块需要通过降级删除的模块版本,则要求将更改为尚未删除的先前版本。如果先前版本不可用,则要求将被删除。

考虑下面的示例。假设在 C 1.4 中发现了问题,因此我们降级到 C 1.3。C 1.4 从模块图中删除。B 1.2 也被删除,因为它需要 C 1.4 或更高版本。主模块对 B 的要求更改为 1.1。

Module version graph with downgrade

go get 也可以使用参数后的 @none 后缀完全删除依赖项。这与降级类似。模块图中将删除命名模块的所有版本。

模块图修剪

如果主模块位于 go 1.17 或更高版本,则用于 最小版本选择模块图 仅包含每个模块依赖项的直接要求,这些依赖项在其自己的 go.mod 文件中指定了 go 1.17 或更高版本,除非该版本的模块也(传递地)被 go 1.16 或更低版本的其他依赖项要求。(go 1.17 依赖项的传递依赖项将从模块图中修剪掉。)

由于 go 1.17 go.mod 文件包含对构建该模块中的任何包或测试所需的每个依赖项的 require 指令,因此修剪后的模块图包含所有构建 主模块 显式要求的任何依赖项中的包或测试所需的依赖项。一个模块不需要构建给定模块中的任何包或测试,因此无法影响其包的运行时行为,因此从模块图中修剪掉的依赖项只会导致原本无关的模块之间发生干扰。

要求已被修剪掉的模块仍然出现在模块图中,并且仍然由 go list -m all 报告:它们的 选中版本 已知且定义明确,并且可以从这些模块加载包(例如,作为从其他模块加载的测试的传递依赖项)。但是,由于 go 命令无法轻松识别这些模块的哪些依赖项已满足,因此 go buildgo test 的参数不能包含来自要求已被修剪掉的模块的包。go get 将包含每个命名包的模块提升为显式依赖项,从而允许对该包调用 go buildgo test

由于 Go 1.16 及更早版本不支持模块图修剪,因此对于每个指定了 go 1.16 或更低版本的模块,仍然包含依赖项的完整传递闭包——包括传递的 go 1.17 依赖项。(在 go 1.16 及更低版本,go.mod 文件仅包含 直接依赖项,因此必须加载更大的图以确保包含所有间接依赖项。)

go mod tidy 为模块记录的 go.sum 文件 默认情况下包含 Go 版本低于go 指令 中指定的版本所需的校验和。因此,go 1.17 模块包含 Go 1.16 加载的完整模块图所需的校验和,但 go 1.18 模块仅包含 Go 1.17 加载的修剪后的模块图所需的校验和。-compat 标志可用于覆盖默认版本(例如,在 go 1.17 模块中更积极地修剪 go.sum 文件)。

有关更多详细信息,请参阅 设计文档

延迟模块加载

为模块图修剪添加的更全面的要求还可以在模块内工作时启用另一种优化。如果主模块位于 go 1.17 或更高版本,则 go 命令会避免加载完整的模块图,直到(除非)需要它。相反,它只加载主模块的 go.mod 文件,然后尝试仅使用这些要求加载要构建的包。如果要导入的包(例如,主模块外部包的测试的依赖项)在这些要求中没有找到,则会按需加载模块图的其余部分。

如果所有导入的包都可以在不加载模块图的情况下找到,则 go 命令将只加载包含这些包的模块的 go.mod 文件,并将其要求与主模块的要求进行比较,以确保它们在本地一致。(由于版本控制合并、手动编辑以及已被 替换 的模块的更改(使用本地文件系统路径),可能会出现不一致。)

工作区

一个 工作区 是磁盘上的一组模块,在运行 最小版本选择(MVS) 时用作主模块。

工作区可以在 go.work 文件 中声明,该文件指定工作区中每个模块的模块目录的相对路径。当不存在 go.work 文件时,工作区由包含当前目录的单个模块组成。

大多数使用模块的 go 子命令都对由当前工作区确定的模块集进行操作。go mod initgo mod whygo mod editgo mod tidygo mod vendorgo get 始终对单个主模块进行操作。

一个命令通过首先检查 GOWORK 环境变量来确定它是否处于工作区上下文中。如果 GOWORK 设置为 off,则命令将处于单模块上下文中。如果它为空或未提供,则命令将搜索当前工作目录,然后依次搜索父目录,以查找文件 go.work。如果找到文件,则命令将在其定义的工作区中运行;否则,工作区将仅包含包含工作目录的模块。如果 GOWORK 指定了对以 .work 结尾的现有文件的路径,则工作区模式将被启用。任何其他值都是错误。可以使用 go env GOWORK 命令确定 go 命令正在使用哪个 go.work 文件。如果 go 命令没有处于工作区模式,则 go env GOWORK 将为空。

go.work 文件

工作区由一个名为 go.work 的 UTF-8 编码文本文件定义。go.work 文件是面向行的。每行包含一个指令,由关键字和参数组成。例如

go 1.18

use ./my/first/thing
use ./my/second/thing

replace example.com/bad/thing v1.4.5 => example.com/good/thing v1.4.5

go.mod 文件一样,可以将相邻行的前导关键字分解出来以创建一个块。

use (
    ./my/first/thing
    ./my/second/thing
)

go 命令提供了几个用于操作 go.work 文件的子命令。go work init 创建新的 go.work 文件。go work use 将模块目录添加到 go.work 文件中。go work edit 执行低级编辑。可以使用 golang.org/x/mod/modfile 包以编程方式进行相同的更改。

go 命令将维护一个 go.work.sum 文件,以跟踪工作区中未在集体工作区模块的 go.sum 文件中的哈希值。

通常不建议将 go.work 文件提交到版本控制系统,原因有两个

也就是说,在某些情况下,提交 go.work 文件是有意义的。例如,当存储库中的模块仅彼此开发但未与外部模块一起开发时,开发人员可能没有理由在工作区中使用不同的模块组合。在这种情况下,模块作者应确保对各个模块进行正确测试和发布。

词法元素

go.work 文件中的词法元素的定义方式与 go.mod 文件 完全相同。

语法

以下是使用扩展巴科斯-诺尔范式 (EBNF) 指定的 go.work 语法。有关 EBNF 语法的详细信息,请参见 Go 语言规范中的符号部分

GoWork = { Directive } .
Directive = GoDirective |
            ToolchainDirective |
            UseDirective |
            ReplaceDirective .

换行符、标识符和字符串分别用 newlineidentstring 表示。

模块路径和版本用 ModulePathVersion 表示。模块路径和版本的指定方式与 go.mod 文件 完全相同。

ModulePath = ident | string . /* see restrictions above */
Version = ident | string .    /* see restrictions above */

go 指令

有效的 go.work 文件中需要 go 指令。该版本必须是有效的 Go 版本:一个正整数后跟一个点和一个非负整数(例如,1.181.19)。

go 指令指示与 go.work 文件一起使用 go 工具链版本。如果对 go.work 文件格式进行了更改,将来的工具链版本将根据其指示的版本来解释该文件。

go.work 文件最多可以包含一个 go 指令。

GoDirective = "go" GoVersion newline .
GoVersion = string | ident .  /* valid release version; see above */

示例

go 1.18

toolchain 指令

toolchain 指令声明了建议在工作区中使用的 Go 工具链。它仅在默认工具链早于建议的工具链时才生效。

有关详细信息,请参见“Go 工具链”。

ToolchainDirective = "toolchain" ToolchainName newline .
ToolchainName = string | ident .  /* valid toolchain name; see “Go toolchains” */

示例

toolchain go1.21.0

godebug 指令

godebug 指令声明了在工作区中工作时要应用的单个 GODEBUG 设置。语法和效果与 go.mod 文件的 godebug 指令 相同。当使用工作区时,go.mod 文件中的 godebug 指令将被忽略。

use 指令

use 将磁盘上的模块添加到工作区中主模块集。其参数是到包含模块 go.mod 文件的目录的相对路径。use 指令不会添加其参数目录的子目录中包含的模块。这些模块可以通过包含其 go.mod 文件的目录在单独的 use 指令中添加。

UseDirective = "use" ( UseSpec | "(" newline { UseSpec } ")" newline ) .
UseSpec = FilePath newline .
FilePath = /* platform-specific relative or absolute file path */

示例

use ./mymod  // example.com/mymod

use (
    ../othermod
    ./subdir/thirdmod
)

replace 指令

go.mod 文件中的 replace 指令类似,go.work 文件中的 replace 指令将特定版本的模块或所有版本的模块的内容替换为在其他地方找到的内容。go.work 中的通配符替换会覆盖 go.mod 文件中特定版本的 replace

go.work 文件中的 replace 指令会覆盖工作区模块中对相同模块或模块版本的任何替换。

ReplaceDirective = "replace" ( ReplaceSpec | "(" newline { ReplaceSpec } ")" newline ) .
ReplaceSpec = ModulePath [ Version ] "=>" FilePath newline
            | ModulePath [ Version ] "=>" ModulePath Version newline .
FilePath = /* platform-specific relative or absolute file path */

示例

replace golang.org/x/net v1.2.3 => example.com/fork/net v1.4.5

replace (
    golang.org/x/net v1.2.3 => example.com/fork/net v1.4.5
    golang.org/x/net => example.com/fork/net v1.4.5
    golang.org/x/net v1.2.3 => ./fork/net
    golang.org/x/net => ./fork/net
)

与非模块存储库的兼容性

为了确保从 GOPATH 平滑过渡到模块,go 命令可以通过添加 go.mod 文件,以模块感知模式从尚未迁移到模块的存储库中下载和构建包。

go 命令在给定版本 直接 从存储库中下载模块时,它会查找模块路径的存储库 URL,将版本映射到存储库中的修订版,然后在该修订版中提取存储库的存档。如果 模块的路径 等于 存储库根路径,并且存储库根目录不包含 go.mod 文件,则 go 命令会在模块缓存中合成一个 go.mod 文件,其中包含 module 指令,其他内容没有。由于合成 go.mod 文件不包含 require 指令 以供其依赖项使用,因此依赖它们的模块可能需要其他 require 指令(带有 // indirect 注释)以确保在每次构建时以相同版本获取每个依赖项。

go 命令从 代理 下载模块时,它会从模块内容的其余部分中单独下载 go.mod 文件。如果原始模块没有 go.mod 文件,则预计代理会提供合成 go.mod 文件。

+incompatible 版本

在主版本 2 或更高版本发布的模块必须在模块路径上具有匹配的 主版本后缀。例如,如果模块在 v2.0.0 发布,则其路径必须具有 /v2 后缀。这使 go 命令能够将项目的多个主版本视为不同的模块,即使它们在同一个存储库中开发也是如此。

主版本后缀要求是在模块支持添加到 go 命令时引入的,许多存储库在之前已经使用主版本 2 或更高版本标记了发布版本。为了与这些存储库保持兼容性,go 命令会向主版本为 2 或更高且没有 go.mod 文件的版本添加 +incompatible 后缀。+incompatible 指示版本与具有较低主版本号的版本属于同一个模块;因此,go 命令可能会自动升级到更高的 +incompatible 版本,即使这可能会破坏构建。

请考虑以下示例需求

require example.com/m v4.1.2+incompatible

版本 v4.1.2+incompatible 指的是提供 example.com/m 模块的存储库中的 语义版本标签 v4.1.2。该模块必须位于存储库根目录(也就是说,存储库根路径 也必须为 example.com/m),并且 go.mod 文件必须不存在。该模块可能具有主版本号更低的版本,如 v1.5.2,并且 go 命令可能会从这些版本自动升级到 v4.1.2+incompatible(有关升级方式的信息,请参见 最小版本选择 (MVS))。

在标记版本 v2.0.0 之后迁移到模块的存储库通常应该发布一个新的主版本。在上面的示例中,作者应该创建一个模块,其路径为 example.com/m/v5,并且应该发布版本 v5.0.0。作者还应该更新模块中包的导入,以使用前缀 example.com/m/v5 而不是 example.com/m。有关更详细的示例,请参见 Go 模块:v2 及更高版本

请注意,+incompatible 后缀不应出现在存储库中的标签上;类似 v4.1.2+incompatible 的标签将被忽略。该后缀仅出现在 go 命令使用的版本中。有关版本和标签之间区别的详细信息,请参见 将版本映射到提交

另请注意,+incompatible 后缀可能会出现在 伪版本 上。例如,v2.0.1-20200722182040-012345abcdef+incompatible 可能是一个有效的伪版本。

最小模块兼容性

在主版本 2 或更高版本发布的模块需要在 模块路径 上具有 主版本后缀。该模块可以在其存储库的 主版本子目录 中开发,也可以不在其中开发。这会影响在构建 GOPATH 模式时导入模块中包的包。

通常在 GOPATH 模式下,包会存储在与 存储库的根路径 连接到存储库中的目录匹配的目录中。例如,存储库中根路径为 example.com/repo 的子目录 sub 中的包将存储在 $GOPATH/src/example.com/repo/sub 中,并将作为 example.com/repo/sub 导入。

对于具有主版本后缀的模块,人们可能期望在目录 $GOPATH/src/example.com/repo/v2/sub 中找到包 example.com/repo/v2/sub。这将要求模块在其存储库的 v2 子目录中开发。go 命令支持这一点,但不要求这样做(请参见 将版本映射到提交)。

如果模块在主版本子目录中开发,则其在 GOPATH 中的目录将不包含主版本后缀,并且其包可以在没有主版本后缀的情况下导入。在上面的示例中,该包将位于目录 $GOPATH/src/example.com/repo/sub 中,并将作为 example.com/repo/sub 导入。

这会为旨在在模块模式和 GOPATH 模式下构建的包创建一个问题:模块模式需要后缀,而 GOPATH 模式不需要。

为了解决这个问题,在 Go 1.11 中添加了最小模块兼容性,并且已将其移植到 Go 1.9.7 和 1.10.3。当导入路径在 GOPATH 模式下解析为目录时

此规则允许在 GOPATH 模式下构建时,已迁移到模块的包导入其他已迁移到模块的包,即使未使用主版本子目录也是如此。

模块感知命令

大多数 go 命令可以在模块感知模式GOPATH 模式下运行。在模块感知模式下,go 命令使用 go.mod 文件查找版本化的依赖项,并且通常从 模块缓存 中加载包,如果缺少模块则会下载模块。在 GOPATH 模式下,go 命令会忽略模块;它会在 vendor 目录GOPATH 中查找依赖项。

从 Go 1.16 开始,模块感知模式默认启用,无论是否存在 go.mod 文件。在较低版本中,当当前目录或任何父目录中存在 go.mod 文件时,模块感知模式启用。

模块感知模式可以使用 GO111MODULE 环境变量控制,该变量可以设置为 onoffauto

在模块感知模式下,GOPATH 在构建期间不再定义导入的含义,但它仍然存储下载的依赖项(在 GOPATH/pkg/mod 中;请参见 模块缓存)和安装的命令(在 GOPATH/bin 中,除非设置了 GOBIN)。

构建命令

所有加载有关包信息的命令都具有模块感知功能。这包括

在模块感知模式下运行时,这些命令使用 `go.mod` 文件来解释命令行上列出的或 Go 源代码文件中写入的导入路径。这些命令接受以下标志,这些标志对所有模块命令通用。

供应商

在使用模块时,`go` 命令通常通过从其源代码下载模块到模块缓存来满足依赖关系,然后从这些下载的副本中加载包。`供应商` 可用于允许与旧版本的 Go 交互,或确保用于构建的所有文件都存储在单个文件树中。

`go mod vendor` 命令在 `主模块` 根目录中构建一个名为 `vendor` 的目录,其中包含构建和测试 `主模块` 中的包所需的所有包的副本。仅由 `主模块` 外部的包的测试导入的包将不会被包含在内。与 `go mod tidy` 和其他模块命令一样,在构建 `vendor` 目录时,除了 `ignore` 之外的构建约束都不会被考虑在内。

`go mod vendor` 还创建了文件 `vendor/modules.txt`,其中包含一个供应商包列表以及它们复制的模块版本。当启用供应商功能时,此清单将用作模块版本信息的来源,如 `go list -m` 和 `go version -m` 所报告的那样。当 `go` 命令读取 `vendor/modules.txt` 时,它会检查模块版本是否与 `go.mod` 一致。如果 `go.mod` 自 `vendor/modules.txt` 生成后发生了更改,则 `go` 命令将报错。应该再次运行 `go mod vendor` 来更新 `vendor` 目录。

如果 `vendor` 目录存在于 `主模块` 根目录中,并且 `主模块` 的 `go.mod` 文件中的 `go` 版本为 `1.14` 或更高版本,则该目录将自动使用。要显式启用供应商功能,请使用标志 `-mod=vendor` 调用 `go` 命令。要禁用供应商功能,请使用标志 `-mod=readonly` 或 `-mod=mod`。

当启用供应商功能时,`构建命令`(例如 `go build` 和 `go test`)会从 `vendor` 目录中加载包,而不是访问网络或本地模块缓存。`go list -m` 命令只打印有关 `go.mod` 中列出的模块的信息。当启用供应商功能时,`go mod` 命令(如 `go mod download` 和 `go mod tidy`)的行为不会有所不同,它们仍然会下载模块并访问模块缓存。`go get` 在启用供应商功能时也不会有什么不同。

与 `GOPATH` 模式下的供应商功能不同,`go` 命令会忽略 `主模块` 根目录以外位置的供应商目录。此外,由于不会使用其他模块中的供应商目录,因此 `go` 命令在构建 `模块 zip 文件` 时不会包含供应商目录(但请参阅已知错误 #31562 和 #37397)。

go get

用法

go get [-d] [-t] [-u] [build flags] [packages]

示例

# Upgrade a specific module.
$ go get golang.org/x/net

# Upgrade modules that provide packages imported by packages in the main module.
$ go get -u ./...

# Upgrade or downgrade to a specific version of a module.
$ go get golang.org/x/[email protected]

# Update to the commit on the module's master branch.
$ go get golang.org/x/text@master

# Remove a dependency on a module and downgrade modules that require it
# to versions that don't require it.
$ go get golang.org/x/text@none

# Upgrade the minimum required Go version for the main module.
$ go get go

# Upgrade the suggested Go toolchain, leaving the minimum Go version alone.
$ go get toolchain

# Upgrade to the latest patch release of the suggested Go toolchain.
$ go get toolchain@patch

`go get` 命令更新 `主模块` 的 `go.mod` 文件中的模块依赖项,然后构建和安装命令行上列出的包。

第一步是确定要更新哪些模块。`go get` 接受包、包模式和模块路径列表作为参数。如果指定了包参数,则 `go get` 将更新提供该包的模块。如果指定了包模式(例如,`all` 或包含 `...` 通配符的路径),则 `go get` 将将模式扩展为一组包,然后更新提供这些包的模块。如果参数指定了一个模块而不是一个包(例如,模块 `golang.org/x/net` 在其根目录中没有包),则 `go get` 将更新该模块,但不会构建包。如果没有指定任何参数,则 `go get` 的行为就像指定了 `.` 一样(当前目录中的包);这可以与 `-u` 标志一起使用来更新提供导入包的模块。

每个参数都可以包含一个 `版本查询后缀`,表示所需的版本,例如 `go get golang.org/x/[email protected]`。版本查询后缀由 `@` 符号后跟一个 `版本查询` 组成,该查询可以表示一个特定版本 ( `v0.3.0` ),一个版本前缀 ( `v0.3` ),一个分支或标签名称 ( `master` ),一个修订版 ( `1234abcd` ),或以下特殊查询之一: `latest`、`upgrade`、`patch` 或 `none`。如果没有给出版本,`go get` 将使用 `@upgrade` 查询。

一旦 `go get` 将其参数解析为特定的模块和版本,`go get` 将在 `主模块` 的 `go.mod` 文件中添加、更改或删除 `require` 指令,以确保这些模块在将来保持在所需的版本。请注意,`go.mod` 文件中的所需版本是 `最小版本`,并且随着新依赖项的添加,可能会自动增加。有关如何选择版本和模块感知命令如何解决冲突的详细信息,请参阅 `最小版本选择 (MVS)`。

当命令行上命名的模块被添加、升级或降级时,其他模块也可能被升级,如果该命名模块的新版本需要其他模块处于更高版本。例如,假设模块 `example.com/a` 被升级到版本 `v1.5.0`,并且该版本需要 `example.com/b` 模块处于版本 `v1.2.0`。如果 `example.com/b` 模块当前需要处于版本 `v1.1.0`,则 `go get example.com/[email protected]` 也会将 `example.com/b` 升级到 `v1.2.0`。

go get upgrading a transitive requirement

当命令行上命名的模块被降级或删除时,其他模块也可能被降级。继续上面的例子,假设 `example.com/b` 模块被降级到 `v1.1.0`。`example.com/a` 模块也会被降级到需要 `example.com/b` 模块处于 `v1.1.0` 或更低版本的版本。

go get downgrading a transitive requirement

可以使用版本后缀 `@none` 来删除模块需求。这是一种特殊的降级。依赖于被删除模块的模块将根据需要被降级或删除。即使一个或多个模块的包被 `主模块` 中的包导入,也可以删除模块需求。在这种情况下,下一个构建命令可能会添加一个新的模块需求。

如果需要在两个不同的版本中使用一个模块(在命令行参数中明确指定或为了满足升级和降级),`go get` 将报错。

在 `go get` 选择了一组新的版本后,它会检查是否在命令行上命名的任何新选择的模块版本或任何提供包的模块是 `撤回` 的或 `弃用` 的。`go get` 会为它发现的每个撤回的版本或弃用的模块打印一个警告。`go list -m -u all` 可用于检查所有依赖项中的撤回和弃用。

在 `go get` 更新 `go.mod` 文件后,它会构建命令行上命名的包。可执行文件将被安装在 `GOBIN` 环境变量命名的目录中,该目录默认为 `$GOPATH/bin` 或 `$HOME/go/bin`(如果未设置 `GOPATH` 环境变量)。

`go get` 支持以下标志

自 Go 1.16 起,go install 是构建和安装程序的推荐命令。当与版本后缀(如 @latest@v1.4.6)一起使用时,go install 以模块感知模式构建包,忽略当前目录或任何父目录中的 go.mod 文件(如果有)。

go get 更专注于管理 go.mod 中的要求。-d 标志已弃用,在 Go 1.18 中,它将始终启用。

go install

用法

go install [build flags] [packages]

示例

# Install the latest version of a program,
# ignoring go.mod in the current directory (if any).
$ go install golang.org/x/tools/gopls@latest

# Install a specific version of a program.
$ go install golang.org/x/tools/[email protected]

# Install a program at the version selected by the module in the current directory.
$ go install golang.org/x/tools/gopls

# Install all programs in a directory.
$ go install ./cmd/...

go install 命令构建并安装命令行中路径命名的包。可执行文件(main 包)安装到 GOBIN 环境变量命名的目录中,默认情况下为 $GOPATH/bin$HOME/go/bin(如果未设置 GOPATH 环境变量)。$GOROOT 中的可执行文件安装在 $GOROOT/bin$GOTOOLDIR 中,而不是 $GOBIN。非可执行包将被构建并缓存,但不会安装。

自 Go 1.16 起,如果参数具有版本后缀(如 @latest@v1.0.0),go install 以模块感知模式构建包,忽略当前目录或任何父目录中的 go.mod 文件(如果有)。这对于在不影响主模块的依赖项的情况下安装可执行文件很有用。

为了消除对构建中使用哪些模块版本存在歧义,参数必须满足以下约束

有关支持的版本查询语法,请参阅 版本查询。Go 1.15 及更低版本不支持对 go install 使用版本查询。

如果参数没有版本后缀,go install 可能会以模块感知模式或 GOPATH 模式运行,具体取决于 GO111MODULE 环境变量和 go.mod 文件的存在。有关详细信息,请参阅 模块感知命令。如果启用模块感知模式,go install 在主模块的上下文中运行,该模块可能与包含正在安装的包的模块不同。

go list -m

用法

go list -m [-u] [-retracted] [-versions] [list flags] [modules]

示例

$ go list -m all
$ go list -m -versions example.com/m
$ go list -m -json example.com/m@latest

-m 标志导致 go list 列出模块而不是包。在此模式下,go list 的参数可以是模块、模块模式(包含 ... 通配符)、版本查询 或特殊模式 all,它匹配 构建列表 中的所有模块。如果未指定任何参数,则列出 主模块

列出模块时,-f 标志仍然指定应用于 Go 结构的格式模板,但现在是 Module 结构

type Module struct {
    Path       string        // module path
    Version    string        // module version
    Versions   []string      // available module versions
    Replace    *Module       // replaced by this module
    Time       *time.Time    // time version was created
    Update     *Module       // available update (with -u)
    Main       bool          // is this the main module?
    Indirect   bool          // module is only indirectly needed by main module
    Dir        string        // directory holding local copy of files, if any
    GoMod      string        // path to go.mod file describing module, if any
    GoVersion  string        // go version used in module
    Retracted  []string      // retraction information, if any (with -retracted or -u)
    Deprecated string        // deprecation message, if any (with -u)
    Error      *ModuleError  // error loading module
}

type ModuleError struct {
    Err string // the error itself
}

默认输出是打印模块路径,然后打印有关版本和替换的信息(如果有)。例如,go list -m all 可能会打印

example.com/main/module
golang.org/x/net v0.1.0
golang.org/x/text v0.3.0 => /tmp/text
rsc.io/pdf v0.1.1

Module 结构具有一个 String 方法,该方法格式化此输出行,以便默认格式等效于 -f '{{.String}}'

请注意,当模块已被替换时,其 Replace 字段描述替换模块,其 Dir 字段设置为替换模块的源代码(如果有)。(也就是说,如果 Replace 非空,则 Dir 设置为 Replace.Dir,没有访问被替换的源代码。)

-u 标志添加有关可用升级的信息。当给定模块的最新版本比当前版本更新时,list -u 将模块的 Update 字段设置为有关更新模块的信息。list -u 还打印当前选择的版本是否 已撤回 以及模块是否 已弃用。模块的 String 方法通过在当前版本之后用方括号格式化更新版本来指示可用升级。例如,go list -m -u all 可能会打印

example.com/main/module
golang.org/x/old v1.9.9 (deprecated)
golang.org/x/net v0.1.0 (retracted) [v0.2.0]
golang.org/x/text v0.3.0 [v0.4.0] => /tmp/text
rsc.io/pdf v0.1.1 [v0.1.2]

(对于工具,go list -m -u -json all 可能更方便解析。)

-versions 标志导致 list 将模块的 Versions 字段设置为该模块所有已知版本的列表,这些版本根据语义版本排序,从最低到最高。该标志还将默认输出格式更改为显示模块路径,后跟空格分隔的版本列表。已撤回的版本将从此列表中省略,除非还指定了 -retracted 标志。

-retracted 标志指示 list 在使用 -versions 标志打印的列表中显示已撤回的版本,并在解析 版本查询 时考虑已撤回的版本。例如,go list -m -retracted example.com/m@latest 显示模块 example.com/m 的最高发布版本或预发布版本,即使该版本已撤回。从该版本的 go.mod 文件中加载 retract 指令弃用-retracted 标志是在 Go 1.16 中添加的。

模板函数 module 采用单个字符串参数,该参数必须是模块路径或查询,并返回指定的模块作为 Module 结构。如果发生错误,结果将是 Module 结构,其 Error 字段非空。

go mod download

用法

go mod download [-x] [-json] [-reuse=old.json] [modules]

示例

$ go mod download
$ go mod download golang.org/x/[email protected]

go mod download 命令将命名的模块下载到 模块缓存 中。参数可以是模块路径或模块模式,用于选择主模块的依赖项或形式为 path@version版本查询。没有参数,download 适用于 主模块 的所有依赖项。

go 命令将在普通执行期间根据需要自动下载模块。go mod download 命令主要用于预填充模块缓存或加载要由 模块代理 提供服务的数据。

默认情况下,download 不会向标准输出写入任何内容。它将进度消息和错误打印到标准错误。

-json 标志导致 download 向标准输出打印一系列 JSON 对象,描述每个下载的模块(或失败),对应于此 Go 结构

type Module struct {
    Path     string // module path
    Query    string // version query corresponding to this version
    Version  string // module version
    Error    string // error loading module
    Info     string // absolute path to cached .info file
    GoMod    string // absolute path to cached .mod file
    Zip      string // absolute path to cached .zip file
    Dir      string // absolute path to cached source root directory
    Sum      string // checksum for path, version (as in go.sum)
    GoModSum string // checksum for go.mod (as in go.sum)
    Origin   any    // provenance of module
    Reuse    bool   // reuse of old module info is safe
}

-x 标志导致 downloaddownload 执行的命令打印到标准错误。

-reuse 标志接受包含先前‘go mod download -json’调用 JSON 输出的文件的名称。go 命令可以使用此文件来确定模块自上次调用以来是否未更改,并避免重新下载它。未重新下载的模块将在新输出中通过将 Reuse 字段设置为 true 来标记。通常,模块缓存会自动提供这种类型的重用;-reuse 标志可用于不保留模块缓存的系统上。

go mod edit

用法

go mod edit [editing flags] [-fmt|-print|-json] [go.mod]

示例

# Add a replace directive.
$ go mod edit -replace example.com/[email protected]=./a

# Remove a replace directive.
$ go mod edit -dropreplace example.com/[email protected]

# Set the go version, add a requirement, and print the file
# instead of writing it to disk.
$ go mod edit -go=1.14 -require=example.com/[email protected] -print

# Format the go.mod file.
$ go mod edit -fmt

# Format and print a different .mod file.
$ go mod edit -print tools.mod

# Print a JSON representation of the go.mod file.
$ go mod edit -json

go mod edit 命令提供了一个命令行界面,用于编辑和格式化 go.mod 文件,主要供工具和脚本使用。go mod edit 只读取一个 go.mod 文件;它不会查找有关其他模块的信息。默认情况下,go mod edit 读取和写入主模块的 go.mod 文件,但可以在编辑标志之后指定不同的目标文件。

编辑标志指定一系列编辑操作。

编辑标志可以重复使用。更改按给定的顺序应用。

go mod edit 还有其他标志来控制其输出。

type Module struct {
    Path    string
    Version string
}

type GoMod struct {
    Module  ModPath
    Go      string
    Require []Require
    Exclude []Module
    Replace []Replace
    Retract []Retract
}

type ModPath struct {
    Path       string
    Deprecated string
}

type Require struct {
    Path     string
    Version  string
    Indirect bool
}

type Replace struct {
    Old Module
    New Module
}

type Retract struct {
    Low       string
    High      string
    Rationale string
}

请注意,这仅描述 go.mod 文件本身,而不是间接引用的其他模块。要获取构建可用的全部模块集,请使用 go list -m -json all。请参见 go list -m

例如,工具可以通过解析 go mod edit -json 的输出获取 go.mod 文件作为数据结构,然后通过调用 go mod edit 并使用 -require-exclude 等标志来进行更改。

工具还可以使用 golang.org/x/mod/modfile 包来解析、编辑和格式化 go.mod 文件。

go mod graph

用法

go mod graph [-go=version]

go mod graph 命令以文本形式打印 模块依赖关系图(应用了替换)。例如

example.com/main example.com/[email protected]
example.com/main example.com/[email protected]
example.com/[email protected] example.com/[email protected]
example.com/[email protected] example.com/[email protected]
example.com/[email protected] example.com/[email protected]
example.com/[email protected] example.com/[email protected]

模块图中的每个顶点代表模块的特定版本。图中的每条边代表对依赖项的最小版本的依赖关系。

go mod graph 打印图的边,每行一条。每行有两个空格分隔的字段:模块版本和它的一个依赖项。每个模块版本都用 path@version 格式的字符串标识。主模块没有 @version 后缀,因为它没有版本。

-go 标志使 go mod graph 报告由给定 Go 版本加载的模块图,而不是由 go.mod 文件中的 go 指令 指示的版本。

有关如何选择版本的更多信息,请参见 最小版本选择 (MVS)。还请参见 go list -m 以打印选定的版本,以及 go mod why 以了解为什么需要某个模块。

go mod init

用法

go mod init [module-path]

示例

go mod init
go mod init example.com/m

go mod init 命令在当前目录中初始化并写入一个新的 go.mod 文件,实际上是在当前目录中创建了一个新的模块。go.mod 文件必须不存在。

init 接受一个可选参数,即新模块的 模块路径。有关选择模块路径的说明,请参见 模块路径。如果省略了模块路径参数,init 将尝试使用 .go 文件中的导入注释、供应商工具配置文件和当前目录(如果在 GOPATH 中)来推断模块路径。

如果存在供应商工具的配置文件,init 将尝试从中导入模块依赖关系。init 支持以下配置文件。

供应商工具配置文件不能始终如一地进行完全忠实的转换。例如,如果同一个存储库中的多个包在不同的版本中导入,而存储库只包含一个模块,那么导入的 go.mod 只能要求模块处于一个版本。您可能希望运行 go list -m all 来检查 构建列表 中的所有版本,并运行 go mod tidy 来添加缺少的依赖关系并删除未使用的依赖关系。

go mod tidy

用法

go mod tidy [-e] [-v] [-go=version] [-compat=version]

go mod tidy 确保 go.mod 文件与模块中的源代码匹配。它添加了构建当前模块的包及其依赖项所需的任何缺少的模块依赖关系,并删除了对不提供任何相关包的模块的依赖关系。它还会将任何缺少的条目添加到 go.sum 中,并删除不必要的条目。

-e 标志(在 Go 1.16 中添加)会导致 go mod tidy 在加载包时遇到错误时尝试继续执行。

-v 标志会导致 go mod tidy 将有关已删除模块的信息打印到标准错误。

go mod tidy 的工作原理是加载 主模块 中的所有包以及它们导入的所有包(递归地)。这包括由测试导入的包(包括其他模块中的测试)。go mod tidy 的行为就像所有构建标签都被启用一样,因此它将考虑特定于平台的源文件和需要自定义构建标签的文件,即使这些源文件通常不会被构建。有一个例外:ignore 构建标签没有启用,因此具有 // +build ignore 构建约束的文件将不会被考虑。请注意,go mod tidy 不会考虑主模块中名为 testdata 的目录或以 ._ 开头的目录中的包,除非这些包被其他包显式导入。

go mod tidy 加载完这组包后,它将确保每个提供一个或多个包的模块在主模块的 go.mod 文件中都具有 require 指令,或者(如果主模块的版本为 go 1.16 或更低)被另一个必需的模块所要求。go mod tidy 将为每个缺少的模块添加一个对最新版本的依赖关系(有关 latest 版本的定义,请参见 版本查询)。go mod tidy 将删除对不提供上述集合中任何包的模块的 require 指令。

go mod tidy 还可以添加或删除 require 指令上的 // indirect 注释。// indirect 注释表示一个不提供主模块的包导入的包的模块。(有关何时添加 // indirect 依赖关系和注释的更多详细信息,请参见 require 指令。)

如果设置了 -go 标志,go mod tidy 将根据指示的版本更新 go 指令,根据该版本启用或禁用 模块图修剪延迟模块加载(并根据需要添加或删除间接依赖关系)。

默认情况下,go mod tidy 将检查在模块图由 go 指令中指示的版本之前的 Go 版本加载时,模块的 选定版本 是否不会更改。还可以通过 -compat 标志显式指定要检查兼容性的版本。

go mod vendor

用法

go mod vendor [-e] [-v] [-o]

go mod vendor 命令在 主模块 的根目录中构建一个名为 vendor 的目录,其中包含构建和测试主模块中的包所需的全部包的副本。仅由主模块之外的包的测试导入的包将不会被包含。与 go mod tidy 和其他模块命令一样,在构建 vendor 目录时,不会考虑除 ignore 之外的 构建约束

当启用供应商时,go 命令将从 vendor 目录中加载包,而不是从其源中下载模块到模块缓存并使用这些下载副本中的包。有关更多信息,请参见 供应商

go mod vendor 还创建了 vendor/modules.txt 文件,其中包含一个供应商包列表以及它们从哪个模块版本中复制而来。当启用供应商时,此清单将用作模块版本信息的来源,如 go list -mgo version -m 所报告的那样。当 go 命令读取 vendor/modules.txt 时,它会检查模块版本是否与 go.mod 一致。如果 go.modvendor/modules.txt 生成以来发生了更改,则应再次运行 go mod vendor

请注意,go mod vendor 在重新构建它之前会删除现有的 vendor 目录。不应对供应商包进行本地更改。go 命令不检查 vendor 目录中的包是否未被修改,但可以通过运行 go mod vendor 并检查是否进行了任何更改来验证 vendor 目录的完整性。

-e 标志(在 Go 1.16 中添加)会导致 go mod vendor 在加载包时遇到错误时尝试继续执行。

-v 标志会导致 go mod vendor 将供应商模块和包的名称打印到标准错误。

-o 标志(在 Go 1.18 中添加)会导致 go mod vendor 在指定的目录中输出供应商树,而不是 vendor。参数可以是绝对路径,也可以是相对于模块根目录的路径。

go mod verify

用法

go mod verify

go mod verify 检查存储在 模块缓存 中的 主模块 的依赖关系自下载以来是否被修改。为了执行此检查,go mod verify 会对每个下载的模块 .zip 文件 和解压缩的目录进行哈希处理,然后将这些哈希值与模块首次下载时记录的哈希值进行比较。go mod verify 检查 构建列表 中的每个模块(可以使用 go list -m all 打印)。

如果所有模块都未被修改,go mod verify 将打印“所有模块已验证”。否则,它会报告哪些模块已更改并以非零状态退出。

请注意,所有支持模块的命令都会验证主模块的 go.sum 文件中的哈希值是否与为下载到模块缓存中的模块记录的哈希值匹配。如果 go.sum 中缺少哈希值(例如,因为该模块是首次使用),则 go 命令将使用 校验和数据库 来验证其哈希值(除非模块路径与 GOPRIVATEGONOSUMDB 匹配)。有关详细信息,请参见 验证模块

相比之下,go mod verify 检查模块 .zip 文件及其解压缩目录的哈希值是否与模块缓存中首次下载时的记录哈希值匹配。这有助于检测模块下载和验证后模块缓存中文件发生的变化。go mod verify 不会下载缓存中不存在的模块的内容,也不会使用 go.sum 文件来验证模块内容。但是,go mod verify 可能会下载 go.mod 文件以执行 最小版本选择。它将使用 go.sum 来验证这些文件,并且可能会添加 go.sum 条目以用于缺少的哈希值。

go mod why

用法

go mod why [-m] [-vendor] packages...

go mod why 显示从主模块到命令行中列出的每个包的导入图中的最短路径。

输出是一系列节,每个节代表命令行中命名的一个包或模块,节之间用空行分隔。每个节都以以 # 开头的注释行开头,给出目标包或模块。后续行给出导入图中的路径,每行一个包。如果该包或模块未从主模块引用,该节将显示一个括号内的注释,指示该事实。

例如

$ go mod why golang.org/x/text/language golang.org/x/text/encoding
# golang.org/x/text/language
rsc.io/quote
rsc.io/sampler
golang.org/x/text/language

# golang.org/x/text/encoding
(main module does not need package golang.org/x/text/encoding)

-m 标志使 go mod why 将其参数视为模块列表。go mod why 将打印每个模块中任何包的路径。请注意,即使使用 -mgo mod why 也会查询包图,而不是 go mod graph 打印的模块图。

-vendor 标志使 go mod why 忽略主模块外部包测试中的导入(与 go mod vendor 一样)。默认情况下,go mod why 考虑与 all 模式匹配的包图。此标志在 Go 1.16 之后对声明 go 1.16 或更高版本(使用 go 指令go.mod 中)的模块没有影响,因为 all 的含义已更改为匹配 go mod vendor 匹配的包集。

go version -m

用法

go version [-m] [-v] [file ...]

示例

# Print Go version used to build go.
$ go version

# Print Go version used to build a specific executable.
$ go version ~/go/bin/gopls

# Print Go version and module versions used to build a specific executable.
$ go version -m ~/go/bin/gopls

# Print Go version and module versions used to build executables in a directory.
$ go version -m ~/go/bin/

go version 报告用于构建命令行中命名的每个可执行文件的 Go 版本。

如果命令行中没有命名文件,go version 将打印其自身的版本信息。

如果命名了目录,go version 将递归遍历该目录,查找已识别的 Go 二进制文件并报告它们的版本。默认情况下,go version 不会报告目录扫描期间发现的未识别文件。-v 标志使其报告未识别文件。

-m 标志使 go version 打印每个可执行文件的嵌入式模块版本信息(如果可用)。对于每个可执行文件,go version -m 打印一个带有制表符分隔列的表格,如下所示。

$ go version -m ~/go/bin/goimports
/home/jrgopher/go/bin/goimports: go1.14.3
        path    golang.org/x/tools/cmd/goimports
        mod     golang.org/x/tools      v0.0.0-20200518203908-8018eb2c26ba      h1:0Lcy64USfQQL6GAJma8BdHCgeofcchQj+Z7j0SXYAzU=
        dep     golang.org/x/mod        v0.2.0          h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
        dep     golang.org/x/xerrors    v0.0.0-20191204190536-9bdfabe68543      h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=

表格的格式将来可能会更改。相同的信息可以从 runtime/debug.ReadBuildInfo 获得。

表格中每一行的含义由第一列中的单词决定。

go clean -modcache

用法

go clean [-modcache]

-modcache 标志使 go clean 删除整个 模块缓存,包括版本化依赖项的解压缩源代码。

这通常是删除模块缓存的最佳方法。默认情况下,模块缓存中的大多数文件和目录都是只读的,以防止测试和编辑器在 身份验证 后意外更改文件。不幸的是,这会导致像 rm -r 这样的命令失败,因为在将它们的父目录设为可写之前无法删除文件。

-modcacherw 标志(由 go build 和其他模块感知命令接受)导致模块缓存中的新目录可写。要将 -modcacherw 传递给所有模块感知命令,请将其添加到 GOFLAGS 变量中。GOFLAGS 可以设置在环境中或使用 go env -w 设置。例如,以下命令将其永久设置

go env -w GOFLAGS=-modcacherw

-modcacherw 应该谨慎使用;开发人员应该注意不要对模块缓存中的文件进行更改。可以使用 go mod verify 检查缓存中的文件是否与主模块的 go.sum 文件中的哈希值匹配。

版本查询

几个命令允许您使用版本查询指定模块的版本,版本查询出现在命令行上模块或包路径后面的 @ 字符之后。

示例

go get example.com/m@latest
go mod download example.com/m@master
go list -m -json example.com/m@e3702bed2

版本查询可以是以下之一

除了针对特定命名版本或修订的查询外,所有查询都考虑 go list -m -versions 报告的可用版本(请参见 go list -m)。此列表仅包含标记的版本,不包含伪版本。主模块的 go.mod 文件 中的 exclude 指令 禁止的模块版本不予考虑。go.mod 文件中来自相同模块的 latest 版本的 retract 指令 涵盖的版本也将被忽略,除非使用 go list -m 时使用 -retracted 标志,并且除非加载 retract 指令。

发布版本 比预发布版本优先。例如,如果版本 v1.2.2v1.2.3-pre 可用,latest 查询将选择 v1.2.2,即使 v1.2.3-pre 更高。<v1.2.4 查询也会选择 v1.2.2,即使 v1.2.3-pre 更接近 v1.2.4。如果没有发布或预发布版本可用,latestupgradepatch 查询将为存储库默认分支顶端的提交选择一个伪版本。其他查询将报告错误。

模块外部的模块命令

模块感知的 Go 命令通常在工作目录或父目录中的 go.mod 文件定义的 主模块 的上下文中运行。某些命令可以在没有 go.mod 文件的情况下以模块感知模式运行,但大多数命令在没有 go.mod 文件时会以不同方式工作或报告错误。

有关启用和禁用模块感知模式的信息,请参见 模块感知命令

命令 行为
go build
go doc
go fix
go fmt
go generate
go install
go list
go run
go test
go vet
只能加载、导入和构建标准库中的包以及在命令行中指定为 .go 文件的包。无法构建来自其他模块的包,因为没有地方记录模块需求并确保确定性构建。
go get 包和可执行文件可以像往常一样构建和安装。请注意,当 go get 在没有 go.mod 文件的情况下运行时,没有主模块,因此不会应用 replaceexclude 指令。
go list -m 大多数参数需要显式 版本查询,除非使用 -versions 标志。
go mod download 大多数参数需要显式 版本查询
go mod edit 需要一个显式文件参数。
go mod graph
go mod tidy
go mod vendor
go mod verify
go mod why
这些命令需要一个 go.mod 文件,如果不存在,将报告错误。

go work init

用法

go work init [moddirs]

Init 初始化并在当前目录中写入一个新的 go.work 文件,实际上是在当前目录中创建一个新的工作区。

go work init 可选地接受工作区模块的路径作为参数。如果省略参数,将创建没有模块的空工作区。

每个参数路径都会添加到 go.work 文件中的 use 指令中。当前的 go 版本也将列在 go.work 文件中。

go work edit

用法

go work edit [editing flags] [go.work]

go work edit 命令提供了一个用于编辑 go.work 的命令行界面,主要供工具或脚本使用。它只读取 go.work;它不会查找有关所涉及模块的信息。如果未指定文件,Edit 将在当前目录及其父目录中查找 go.work 文件

编辑标志指定一系列编辑操作。

编辑标志可以重复使用。更改按给定的顺序应用。

go work edit 具有控制其输出的额外标志。

type Module struct {
    Path    string
    Version string
}

type GoWork struct {
    Go        string
    Directory []Directory
    Replace   []Replace
}

type Use struct {
    Path       string
    ModulePath string
}

type Replace struct {
    Old Module
    New Module
}

go work use

用法

go work use [-r] [moddirs]

go work use 命令提供了一个命令行界面,用于将目录(可选地递归地)添加到 go.work 文件。

如果存在于磁盘上,则会为命令行 go.work 文件上列出的每个参数目录在 go.work 文件中添加一个 use 指令;如果不存在于磁盘上,则会从 go.work 文件中删除。

-r 标志递归地搜索参数目录中的模块,use 命令的工作方式就好像每个目录都作为参数指定一样。

go work sync

用法

go work sync

go work sync 命令将工作区的构建列表同步回工作区的模块。

工作区的构建列表是在工作区中进行构建所使用的所有(传递)依赖模块的版本集。go work sync 使用 最小版本选择 (MVS) 算法生成该构建列表,然后将这些版本同步回工作区中指定的每个模块(使用 use 指令)。

计算出工作区构建列表后,将使用与工作区构建列表匹配的依赖项来重写工作区中每个模块的 go.mod 文件,以升级这些依赖项。请注意,最小版本选择 保证构建列表中每个模块的版本始终与每个工作区模块中的版本相同或更高。

模块代理

GOPROXY 协议

一个 模块代理 是一个 HTTP 服务器,它可以响应针对以下指定的路径的 GET 请求。这些请求没有查询参数,也不需要特定的标头,因此即使从固定文件系统(包括 file:// URL)提供服务的站点也可以用作模块代理。

成功的 HTTP 响应必须具有状态代码 200(OK)。重定向 (3xx) 将被跟随。状态代码为 4xx 和 5xx 的响应将被视为错误。错误代码 404(未找到)和 410(已消失)表示请求的模块或版本在代理上不可用,但可能在其他地方找到。错误响应应具有内容类型 text/plaincharsetutf-8us-ascii

go 命令可以使用 GOPROXY 环境变量配置为联系代理或源代码控制服务器,该变量接受代理 URL 列表。该列表可能包括关键字 directoff(有关详细信息,请参阅 环境变量)。列表元素可以用逗号 (,) 或管道 (|) 分隔,它们决定错误回退行为。当一个 URL 后面跟着一个逗号时,go 命令只有在收到 404(未找到)或 410(已消失)响应后才会回退到后面的来源。当一个 URL 后面跟着一个管道时,go 命令会在任何错误后(包括非 HTTP 错误,如超时)回退到后面的来源。这种错误处理行为使代理能够充当未知模块的门卫。例如,代理可以对未在批准列表中的模块响应错误 403(禁止)(请参阅 提供私有模块的私有代理)。

下表指定了模块代理必须响应的查询。对于每个路径,$base 是代理 URL 的路径部分,$module 是模块路径,$version 是版本。例如,如果代理 URL 是 https://example.com/mod,并且客户端请求模块 golang.org/x/text 在版本 v0.3.2 处的 go.mod 文件,则客户端会发送对 https://example.com/mod/golang.org/x/text/@v/v0.3.2.modGET 请求。

为了避免在不区分大小写的文件系统中提供服务时出现歧义,$module$version 元素通过将每个大写字母替换为感叹号加上对应的小写字母来进行大小写编码。这样可以使模块 example.com/Mexample.com/m 都存储在磁盘上,因为前者被编码为 example.com/!m

路径 描述
$base/$module/@v/list 以纯文本形式返回给定模块的已知版本的列表,每行一个。此列表不应包含伪版本。
$base/$module/@v/$version.info

返回有关模块的特定版本的 JSON 格式的元数据。响应必须是与以下 Go 数据结构相对应的 JSON 对象。

type Info struct {
    Version string    // version string
    Time    time.Time // commit time
}

Version 字段是必需的,并且必须包含一个有效的 规范版本(请参阅 版本)。请求路径中的 $version 不需要与该版本相同,甚至不需要是一个有效的版本;此端点可用于查找分支名称或修订标识符的版本。但是,如果 $version 是与 $module 兼容的主版本具有规范版本的版本,则成功响应中的 Version 字段必须相同。

Time 字段是可选的。如果存在,它必须是 RFC 3339 格式的字符串。它表示该版本创建的时间。

将来可能会添加更多字段,因此其他名称被保留。

$base/$module/@v/$version.mod 返回模块的特定版本的 go.mod 文件。如果该模块在请求的版本处没有 go.mod 文件,则必须返回一个仅包含具有请求的模块路径的 module 语句的文件。否则,必须返回原始的、未修改的 go.mod 文件。
$base/$module/@v/$version.zip 返回一个包含模块的特定版本的全部内容的 zip 文件。有关此 zip 文件的格式,请参阅 模块 zip 文件
$base/$module/@latest 返回有关模块的最新已知版本的 JSON 格式的元数据,其格式与 $base/$module/@v/$version.info 相同。最新版本应该是 go 命令在 $base/$module/@v/list 为空或没有列出的版本适合的情况下应使用的模块版本。此端点是可选的,模块代理不需要实现它。

在解析模块的最新版本时,go 命令将请求 $base/$module/@v/list,然后,如果没有找到合适的版本,则请求 $base/$module/@latestgo 命令优先考虑:语义上最高的发布版本、语义上最高的预发布版本和时间上最新的伪版本。在 Go 1.12 及更早版本中,go 命令将 $base/$module/@v/list 中的伪版本视为预发布版本,但自 Go 1.13 以来,情况不再如此。

模块代理必须始终为 $base/$module/$version.mod$base/$module/$version.zip 查询的成功响应提供相同的内容。此内容使用 go.sum 文件 和默认的 校验和数据库 进行 加密身份验证

go 命令将其从模块代理下载的大多数内容缓存在其模块缓存中的 $GOPATH/pkg/mod/cache/download 中。即使从版本控制系统直接下载,go 命令也会合成显式的 infomodzip 文件,并将它们存储在此目录中,就像它直接从代理下载一样。缓存布局与代理 URL 空间相同,因此在 (或复制到) https://example.com/proxy 上提供服务 $GOPATH/pkg/mod/cache/download 将允许用户通过将 GOPROXY 设置为 https://example.com/proxy 来访问缓存的模块版本。

与代理通信

go 命令可以从 模块代理 下载模块源代码和元数据。GOPROXY 环境变量 可用于配置 go 命令可以连接到的代理以及它是否可以与 版本控制系统 直接通信。下载的模块数据保存在 模块缓存 中。go 命令只有在需要缓存中不存在的信息时才会联系代理。

GOPROXY 协议 部分描述了可以发送到 GOPROXY 服务器的请求。但是,了解 go 命令何时发出这些请求也很有用。例如,go build 遵循以下过程。

go 命令计算构建列表时,它会加载 模块图 中每个模块的 go.mod 文件。如果缓存中没有 go.mod 文件,则 go 命令将使用 $module/@v/$version.mod 请求(其中 $module 是模块路径,$version 是版本)从代理下载它。可以使用像 curl 这样的工具测试这些请求。例如,以下命令下载 golang.org/x/mod 在版本 v0.2.0 处的 go.mod 文件。

$ curl https://proxy.golang.org/golang.org/x/mod/@v/v0.2.0.mod
module golang.org/x/mod

go 1.12

require (
    golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550
    golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e
    golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898
)

为了加载包,go 命令需要提供它的模块的源代码。模块源代码以 .zip 文件形式分发,这些文件会被解压缩到模块缓存中。如果模块 .zip 不在缓存中,则 go 命令将使用 $module/@v/$version.zip 请求下载它。

$ curl -O https://proxy.golang.org/golang.org/x/mod/@v/v0.2.0.zip
$ unzip -l v0.2.0.zip | head
Archive:  v0.2.0.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
     1479  00-00-1980 00:00   golang.org/x/[email protected]/LICENSE
     1303  00-00-1980 00:00   golang.org/x/[email protected]/PATENTS
      559  00-00-1980 00:00   golang.org/x/[email protected]/README
       21  00-00-1980 00:00   golang.org/x/[email protected]/codereview.cfg
      214  00-00-1980 00:00   golang.org/x/[email protected]/go.mod
     1476  00-00-1980 00:00   golang.org/x/[email protected]/go.sum
     5224  00-00-1980 00:00   golang.org/x/[email protected]/gosumcheck/main.go

请注意,.mod.zip 请求是分开的,即使 go.mod 文件通常包含在 .zip 文件中。go 命令可能需要为许多不同的模块下载 go.mod 文件,而 .mod 文件远小于 .zip 文件。此外,如果 Go 项目没有 go.mod 文件,则代理将提供一个合成 go.mod 文件,该文件只包含一个 module 指令。从 版本控制系统 下载时,go 命令会生成合成 go.mod 文件。

如果 go 命令需要加载构建列表中任何模块都不提供的包,它将尝试找到一个新的模块来提供它。部分将包解析为模块描述了这个过程。简而言之,go 命令请求有关可能包含该包的每个模块路径的最新版本的的信息。例如,对于包 golang.org/x/net/htmlgo 命令将尝试找到模块 golang.org/x/net/htmlgolang.org/x/netgolang.org/x/golang.org 的最新版本。只有 golang.org/x/net 实际上存在并提供该包,因此 go 命令使用该模块的最新版本。如果多个模块提供该包,go 命令将使用路径最长的模块。

go 命令请求模块的最新版本时,它首先发送对 $module/@v/list 的请求。如果列表为空或返回的版本均不可用,它将发送对 $module/@latest 的请求。选择版本后,go 命令将发送 $module/@v/$version.info 请求以获取元数据。然后它可能会发送 $module/@v/$version.mod$module/@v/$version.zip 请求以加载 go.mod 文件和源代码。

$ curl https://proxy.golang.org/golang.org/x/mod/@v/list
v0.1.0
v0.2.0

$ curl https://proxy.golang.org/golang.org/x/mod/@v/v0.2.0.info
{"Version":"v0.2.0","Time":"2020-01-02T17:33:45Z"}

下载 .mod.zip 文件后,go 命令将计算密码散列并检查它是否与主模块的 go.sum 文件中的散列匹配。如果 go.sum 中没有散列,默认情况下,go 命令将从校验和数据库中检索它。如果计算出的散列不匹配,go 命令将报告安全错误,并且不会将文件安装到模块缓存中。GOPRIVATEGONOSUMDB 环境变量可用于禁用对特定模块的校验和数据库的请求。GOSUMDB 环境变量也可以设置为 off 以完全禁用对校验和数据库的请求。有关更多信息,请参见模块身份验证。请注意,为 .info 请求返回的版本列表和版本元数据未经身份验证,可能会随着时间的推移而改变。

直接从代理服务器提供模块

大多数模块都是从版本控制存储库中开发和提供的。在直接模式中,go 命令使用版本控制工具(参见版本控制系统)下载此类模块。也可以直接从模块代理服务器提供模块。这对希望提供模块而不暴露其版本控制服务器的组织以及使用 go 命令不支持的版本控制工具的组织很有用。

go 命令在直接模式下下载模块时,它首先根据模块路径使用 HTTP GET 请求查找模块服务器的 URL。它会在 HTML 响应中查找名为 <meta> 且名称为 go-import 的标记。该标记的内容必须包含存储库根路径、版本控制系统和 URL,用空格分隔。有关详细信息,请参见查找模块路径的存储库

如果版本控制系统为 mod,则 go 命令将使用GOPROXY 协议从给定 URL 下载模块。

例如,假设 go 命令尝试下载版本为 v1.0.0 的模块 example.com/gopher。它向 https://example.com/gopher?go-get=1 发送请求。服务器将响应一个包含以下标记的 HTML 文档

<meta name="go-import" content="example.com/gopher mod https://modproxy.example.com">

根据此响应,go 命令通过发送对 https://modproxy.example.com/example.com/gopher/@v/v1.0.0.infov1.0.0.modv1.0.0.zip 的请求来下载模块。

请注意,直接从代理服务器提供的模块无法使用 GOPATH 模式下的 go get 下载。

版本控制系统

go 命令可以直接从版本控制存储库下载模块源代码和元数据。从代理服务器下载模块通常更快,但如果代理服务器不可用或模块的存储库无法访问代理服务器(私有存储库经常如此),则需要直接连接到存储库。支持 Git、Subversion、Mercurial、Bazaar 和 Fossil。版本控制工具必须安装在 PATH 中的目录中,以便 go 命令使用它。

要从源存储库而不是代理服务器下载特定模块,请设置 GOPRIVATEGONOPROXY 环境变量。要配置 go 命令直接从源存储库下载所有模块,请将 GOPROXY 设置为 direct。有关更多信息,请参见环境变量

查找模块路径的存储库

go 命令在 direct 模式下下载模块时,它首先会找到包含该模块的存储库。

如果模块路径在路径组件的末尾具有 VCS 限定符(.bzr.fossil.git.hg.svn 中的任何一个),则 go 命令将使用该路径限定符之前的任何内容作为存储库 URL。例如,对于模块 example.com/foo.git/bargo 命令使用 git 下载位于 example.com/foo.git 的存储库,并期望在 bar 子目录中找到该模块。go 命令将根据版本控制工具支持的协议猜测要使用的协议。

如果模块路径没有限定符,则 go 命令会向从模块路径派生的 URL 发送 HTTP GET 请求,该 URL 带有 ?go-get=1 查询字符串。例如,对于模块 golang.org/x/modgo 命令可能会发送以下请求

https://golang.ac.cn/x/mod?go-get=1 (preferred)
https://golang.ac.cn/x/mod?go-get=1  (fallback, only with GOINSECURE)

go 命令会跟踪重定向,但会忽略其他响应状态代码,因此服务器可能会返回 404 或任何其他错误状态。GOINSECURE 环境变量可以设置为允许回退并重定向到特定模块的未加密 HTTP。

服务器必须响应一个包含文档 <head> 中的 <meta> 标记的 HTML 文档。<meta> 标记应该出现在文档的开头,以避免混淆 go 命令的受限解析器。特别是,它应该出现在任何原始 JavaScript 或 CSS 之前。<meta> 标记必须具有以下形式

<meta name="go-import" content="root-path vcs repo-url">

root-path 是存储库根路径,是对应于存储库根目录的模块路径的一部分。它必须是请求的模块路径的前缀或完全匹配。如果它不是完全匹配,则会针对前缀发出另一个请求以验证 <meta> 标记是否匹配。

vcs 是版本控制系统。它必须是下面表格中列出的工具之一,或者关键字 mod,它指示 go 命令使用GOPROXY 协议从给定 URL 下载模块。有关详细信息,请参见直接从代理服务器提供模块

repo-url 是存储库的 URL。如果 URL 不包含方案(因为模块路径具有 VCS 限定符,或者因为 <meta> 标记缺少方案),则 go 命令将尝试版本控制系统支持的每种协议。例如,对于 Git,go 命令将尝试 https:// 然后 git+ssh://。不安全的协议(如 http://git://)只能在模块路径与 GOINSECURE 环境变量匹配时使用。

名称 命令 GOVCS 默认值 安全方案
Bazaar bzr 仅限私有 httpsbzr+ssh
Fossil fossil 仅限私有 https
Git git 公共和私有 httpsgit+sshssh
Mercurial hg 公共和私有 httpsssh
Subversion svn 仅限私有 httpssvn+ssh

例如,再次考虑 golang.org/x/modgo 命令向 https://golang.ac.cn/x/mod?go-get=1 发送请求。服务器将响应一个包含以下标记的 HTML 文档

<meta name="go-import" content="golang.org/x/mod git https://go.googlesource.com/mod">

从此响应中,go 命令将使用远程 URL 为 https://go.googlesource.com/mod 的 Git 存储库。

GitHub 和其他流行的托管服务会响应对所有存储库的 ?go-get=1 查询,因此通常这些站点托管的模块不需要任何服务器配置。

找到存储库 URL 后,go 命令会将存储库克隆到模块缓存中。通常,go 命令会尝试避免从存储库获取不需要的数据。但是,使用的实际命令因版本控制系统而异,并且可能会随着时间的推移而改变。对于 Git,go 命令可以在不下载提交的情况下列出大多数可用版本。它通常会获取提交而不下载祖先提交,但有时有必要这样做。

将版本映射到提交

go 命令可能会在存储库中以特定规范版本(如 v1.2.3v2.4.0-betav3.0.0+incompatible)检出模块。每个模块版本都应该在存储库中有一个语义版本标签,该标签指示应为给定版本检出哪个修订版本。

如果模块是在存储库根目录中定义的,或者是在根目录的某个主版本子目录中定义的,那么每个版本标签名称都等于相应的版本。例如,模块 golang.org/x/text 在其存储库的根目录中定义,因此版本 v0.3.2 在该存储库中具有标签 v0.3.2。对于大多数模块来说,情况都是如此。

如果模块是在存储库中的某个子目录中定义的,也就是说,模块路径的模块子目录部分不为空,那么每个标签名称必须以模块子目录为前缀,后跟一个斜杠。例如,模块 golang.org/x/tools/gopls 在根路径为 golang.org/x/tools 的存储库的 gopls 子目录中定义。该模块的版本 v0.4.0 必须在该存储库中具有名为 gopls/v0.4.0 的标签。

语义版本标签的主版本号必须与模块路径的主版本后缀(如果有)一致。例如,标签 v1.0.0 可以属于模块 example.com/mod,但不属于 example.com/mod/v2example.com/mod/v2 将具有 v2.0.0 等标签。

如果模块没有 go.mod 文件,并且模块位于存储库根目录,则主版本为 v2 或更高的标签可以属于没有主版本后缀的模块。这种类型的版本用后缀 +incompatible 表示。版本标签本身不应具有后缀。请参见与非模块存储库的兼容性

创建标签后,不应删除或更改为不同的修订版本。版本经过身份验证,以确保安全、可重复的构建。如果修改了标签,客户机在下载时可能会看到安全错误。即使标签被删除后,其内容也可能保留在模块代理服务器上。

将伪版本映射到提交

go 命令可能会在存储库中以特定修订版本检出模块,该修订版本编码为伪版本,例如 v1.3.2-0.20191109021931-daa7c04131f5

伪版本的最后 12 个字符(上面示例中的 daa7c04131f5)表示要检出的存储库中的修订版本。此修订版本的含义取决于版本控制系统。对于 Git 和 Mercurial,这是提交哈希的前缀。对于 Subversion,这是一个零填充的修订版本号。

在检出提交之前,go 命令会验证时间戳(上面的 20191109021931)是否与提交日期匹配。它还会验证基本版本(v1.3.1,上面示例中 v1.3.2 之前的版本)是否对应于该提交的祖先的语义版本标签。这些检查确保模块作者对伪版本如何与其他发布版本进行比较具有完全控制权。

有关更多信息,请参见伪版本

将分支和提交映射到版本

可以使用 版本查询 在特定分支、标签或修订版中检出模块。

go get example.com/mod@master

go 命令将这些名称转换为 规范版本,可用于 最小版本选择 (MVS)。MVS 依赖于能够明确地对版本进行排序。分支名称和修订版无法随着时间的推移可靠地进行比较,因为它们依赖于可能发生变化的存储库结构。

如果修订版被标记为一个或多个语义版本标签(例如 v1.2.3),则将使用最高有效版本的标签。go 命令仅考虑可能属于目标模块的语义版本标签;例如,标签 v1.5.2 不会被考虑用于 example.com/mod/v2,因为主版本与模块路径的后缀不匹配。

如果修订版未被标记为有效的语义版本标签,go 命令将生成一个 伪版本。如果修订版具有带有有效语义版本标签的祖先,则将使用最高祖先版本作为伪版本基础。请参阅 伪版本

存储库中的模块目录

在特定修订版中检出模块的存储库后,go 命令必须找到包含模块 go.mod 文件的目录(模块的根目录)。

回想一下,模块路径 由三个部分组成:存储库根路径(对应于存储库根目录)、模块子目录和主版本后缀(仅适用于在 v2 或更高版本发布的模块)。

对于大多数模块,模块路径等于存储库根路径,因此模块的根目录是存储库的根目录。

模块有时在存储库子目录中定义。这通常用于具有多个需要独立发布和版本化的组件的大型存储库。此类模块应该位于与存储库根路径之后的模块路径部分匹配的子目录中。例如,假设模块 example.com/monorepo/foo/bar 位于根路径为 example.com/monorepo 的存储库中。其 go.mod 文件必须位于 foo/bar 子目录中。

如果模块在主版本 v2 或更高版本中发布,其路径必须具有 主版本后缀。具有主版本后缀的模块可以在两个子目录之一中定义:一个带有后缀,另一个没有。例如,假设上面模块的新版本使用路径 example.com/monorepo/foo/bar/v2 发布。其 go.mod 文件可能位于 foo/barfoo/bar/v2 中。

具有主版本后缀的子目录称为 主版本子目录。它们可用于在单个分支上开发模块的多个主版本。当多个主版本的开发在单独的分支上进行时,这可能是不必要的。但是,主版本子目录具有一个重要属性:在 GOPATH 模式下,包导入路径与 GOPATH/src 下的目录完全匹配。go 命令在 GOPATH 模式下提供最小的模块兼容性(请参阅 与非模块存储库的兼容性),因此主版本子目录并不总是与在 GOPATH 模式下构建的项目兼容所必需的。但是,不支持最小模块兼容性的旧工具可能会遇到问题。

go 命令找到模块根目录后,它会创建目录内容的 .zip 文件,然后将 .zip 文件提取到模块缓存中。有关 .zip 文件中可以包含哪些文件的详细信息,请参阅 文件路径和大小限制.zip 文件的内容在提取到模块缓存中之前会被 验证,方式与从代理下载 .zip 文件时相同。

模块 zip 文件不包含 vendor 目录或任何嵌套模块(包含 go.mod 文件的子目录)的内容。这意味着模块必须注意不要引用其目录外部或其他模块中的文件。例如,//go:embed 模式不应匹配嵌套模块中的文件。此行为可以作为一种有用的变通方法,用于在不需要将文件包含在模块中的情况下使用。例如,如果存储库中有一个大的文件被检入到 testdata 目录中,模块作者可以在 testdata 中添加一个空的 go.mod 文件,这样他们的用户就不需要下载这些文件。当然,这可能会降低用户测试其依赖项的覆盖率。

LICENSE 文件的特殊情况

go 命令为不在存储库根目录中的模块创建 .zip 文件时,如果模块在其根目录(与 go.mod 位于同一目录)中没有名为 LICENSE 的文件,go 命令会复制名为 LICENSE 的文件,前提是它在同一修订版中的存储库根目录中存在。

此特殊情况允许同一 LICENSE 文件应用于存储库中的所有模块。这仅适用于名为 LICENSE 的文件,不包括像 .txt 这样的扩展名。不幸的是,这无法在不破坏现有模块的加密哈希的情况下扩展;请参阅 验证模块。其他工具和网站(例如 pkg.go.dev)可能会识别具有其他名称的文件。

另请注意,go 命令在创建模块 .zip 文件时不包含符号链接;请参阅 文件路径和大小限制。因此,如果存储库在其根目录中没有 LICENSE 文件,作者可以在子目录中定义的模块中创建其许可证文件的副本,以确保这些文件包含在模块 .zip 文件中。

使用 GOVCS 控制版本控制工具

go 命令使用版本控制命令(如 git)下载模块的能力对于去中心化的包生态系统至关重要,在去中心化的包生态系统中,代码可以从任何服务器导入。如果恶意服务器找到一种方法来导致调用的版本控制命令运行意外代码,这也是一个潜在的安全问题。

为了平衡功能和安全问题,go 命令默认情况下只使用 githg 从公共服务器下载代码。它将使用任何 已知的版本控制系统 从私有服务器下载代码,私有服务器定义为托管与 GOPRIVATE 环境变量 匹配的包的服务器。允许仅使用 Git 和 Mercurial 的原因是,这两个系统对作为不受信任服务器的客户端运行时的问题给予了最大关注。相比之下,Bazaar、Fossil 和 Subversion 主要用于受信任的、经过身份验证的环境中,并且没有像攻击面那样受到严格审查。

版本控制命令限制仅适用于使用直接版本控制访问下载代码时。从代理下载模块时,go 命令使用的是 GOPROXY 协议,该协议始终允许使用。默认情况下,go 命令对于公共模块使用 Go 模块镜像 (proxy.golang.org),并且仅在私有模块或镜像拒绝提供公共包时才回退到版本控制(通常是出于法律原因)。因此,客户端仍然可以通过默认方式访问从 Bazaar、Fossil 或 Subversion 存储库提供的公共代码,因为这些下载使用 Go 模块镜像,该镜像承担着使用自定义沙箱运行版本控制命令的安全风险。

GOVCS 变量可用于更改特定模块的允许版本控制系统。GOVCS 变量在模块感知模式和 GOPATH 模式下构建包时都适用。使用模块时,模式与模块路径匹配。使用 GOPATH 时,模式与对应于版本控制存储库根部的导入路径匹配。

GOVCS 变量的一般形式是逗号分隔的 pattern:vcslist 规则列表。模式是 通配符模式,必须与模块或导入路径的一个或多个前导元素匹配。vcslist 是允许的版本控制命令的管道分隔列表,或者 all 表示允许使用任何已知的命令,或者 off 表示不允许使用任何命令。请注意,如果模块与具有 vcslist off 的模式匹配,如果源服务器使用 mod 方案,它仍然可以被下载,该方案指示 go 命令使用 GOPROXY 协议 下载模块。列表中最早匹配的模式适用,即使后面的模式也可能匹配。

例如,请考虑

GOVCS=github.com:git,evil.com:off,*:git|hg

使用此设置,模块或导入路径以 github.com/ 开头的代码只能使用 gitevil.com 上的路径不能使用任何版本控制命令,而所有其他路径(* 匹配所有内容)只能使用 githg

特殊模式 publicprivate 分别与公共和私有模块或导入路径匹配。如果路径与 GOPRIVATE 变量匹配,则该路径为私有;否则,该路径为公共。

如果 GOVCS 变量中的规则与特定模块或导入路径不匹配,go 命令将应用其默认规则,现在可以用 GOVCS 表示法概括为 public:git|hg,private:all

要允许对任何包使用任何版本控制系统,请使用

GOVCS=*:all

要禁用所有版本控制的使用,请使用

GOVCS=*:off

可以使用 go env -w 命令 为将来的 go 命令调用设置 GOVCS 变量。

GOVCS 在 Go 1.16 中引入。较早版本的 Go 可能会对任何模块使用任何已知的版本控制工具。

模块 zip 文件

模块版本以 .zip 文件的形式分发。很少需要直接与这些文件交互,因为 go 命令会自动从 模块代理 和版本控制存储库创建、下载和提取它们。但是,了解这些文件对于理解跨平台兼容性约束或实现模块代理仍然有用。

go mod download 命令下载一个或多个模块的 zip 文件,然后将这些文件提取到 模块缓存 中。根据 GOPROXY 和其他 环境变量go 命令可能会从代理下载 zip 文件,也可能会克隆源代码控制存储库并从它们创建 zip 文件。-json 标志可用于查找模块缓存中下载 zip 文件的位置及其提取内容。

可以使用 golang.org/x/mod/zip 包以编程方式创建、提取或检查 zip 文件的内容。

文件路径和大小限制

模块 zip 文件的内容存在一些限制。这些限制确保 zip 文件可以在各种平台上安全一致地提取。

私有模块

Go 模块通常在公共互联网上不可用的版本控制服务器和模块代理上开发和分发。go 命令可以从私有源下载和构建模块,尽管它通常需要一些配置。

以下环境变量可用于配置对私有模块的访问。有关详细信息,请参见 环境变量。有关控制发送到公共服务器的信息,请参见 隐私

这些变量可以在开发环境中设置(例如,在 .profile 文件中),或者可以使用 go env -w 永久设置。

本节的其余部分描述了为私有模块代理和版本控制存储库提供访问权限的常见模式。

服务所有模块的私有代理

一个服务所有模块(公共和私有)的中央私有代理服务器为管理员提供了最大的控制权,并且需要个人开发人员进行最少的配置。

要配置 go 命令以使用此类服务器,请设置以下环境变量,将 https://proxy.corp.example.com 替换为您的代理 URL,将 corp.example.com 替换为您的模块前缀

GOPROXY=https://proxy.corp.example.com
GONOSUMDB=corp.example.com

GOPROXY 设置指示 go 命令只从 https://proxy.corp.example.com 下载模块;go 命令不会连接到其他代理或版本控制存储库。

GONOSUMDB 设置指示 go 命令不要使用公共校验和数据库来验证路径以 corp.example.com 开头的模块。

以这种配置运行的代理可能需要读取私有版本控制服务器的权限。它还需要访问公共互联网才能下载公共模块的新版本。

有几种现有的 GOPROXY 服务器实现可以使用这种方式。一个最小的实现将从 模块缓存 目录提供文件,并将使用 go mod download(具有适当的配置)来检索缺少的模块。

服务私有模块的私有代理

一个私有代理服务器可以服务私有模块,而无需同时服务公开可用的模块。可以配置 go 命令以回退到对私有服务器上不可用的模块的公共源。

要配置 go 命令以这种方式工作,请设置以下环境变量,将 https://proxy.corp.example.com 替换为代理 URL,将 corp.example.com 替换为模块前缀

GOPROXY=https://proxy.corp.example.com,https://proxy.golang.org,direct
GONOSUMDB=corp.example.com

GOPROXY 设置指示 go 命令首先尝试从 https://proxy.corp.example.com 下载模块。如果该服务器以 404(未找到)或 410(已消失)响应,go 命令将回退到 https://proxy.golang.org,然后直接连接到存储库。

GONOSUMDB 设置指示 go 命令不要使用公共校验和数据库来验证路径以 corp.example.com 开头的模块。

请注意,即使没有服务公共模块,在此配置中使用的代理仍然可以控制对公共模块的访问。如果代理以除 404 或 410 之外的错误状态响应请求,go 命令将不会回退到 GOPROXY 列表中的后续条目。例如,代理可以对具有不合适许可证或已知安全漏洞的模块响应 403(禁止)。

直接访问私有模块

可以配置 go 命令绕过公共代理并直接从版本控制服务器下载私有模块。当运行私有代理服务器不可行时,这很有用。

要配置 go 命令以这种方式工作,请设置 GOPRIVATE,将 corp.example.com 替换为私有模块前缀

GOPRIVATE=corp.example.com

在这种情况下,不需要更改 GOPROXY 变量。它默认为 https://proxy.golang.org,direct,这指示 go 命令首先尝试从 https://proxy.golang.org 下载模块,然后在该代理以 404(未找到)或 410(已消失)响应时回退到直接连接。

GOPRIVATE 设置指示 go 命令不要连接到代理或校验和数据库,以 corp.example.com 开头的模块。

可能仍然需要一个内部 HTTP 服务器来 将模块路径解析为存储库 URL。例如,当 go 命令下载模块 corp.example.com/mod 时,它将向 https://corp.example.com/mod?go-get=1 发送一个 GET 请求,并将查找响应中的存储库 URL。为了避免此要求,请确保每个私有模块路径都有一个 VCS 后缀(如 .git),标记存储库根前缀。例如,当 go 命令下载模块 corp.example.com/repo.git/mod 时,它将克隆 https://corp.example.com/repo.gitssh://corp.example.com/repo.git 处的 Git 存储库,而无需进行其他请求。

开发人员将需要访问包含私有模块的存储库的权限。这可以在全局 VCS 配置文件中(如 .gitconfig)中进行配置。最好是 VCS 工具配置为不需要交互式身份验证提示。默认情况下,在调用 Git 时,go 命令通过设置 GIT_TERMINAL_PROMPT=0 来禁用交互式提示,但它会尊重显式设置。

将凭据传递到私有代理

go 命令在与代理服务器通信时支持 HTTP 基本身份验证

凭据可以在 .netrc 文件 中指定。例如,包含以下行的 .netrc 文件将配置 go 命令以使用给定的用户名和密码连接到机器 proxy.corp.example.com

machine proxy.corp.example.com
login jrgopher
password hunter2

文件的位置可以使用 NETRC 环境变量设置。如果未设置 NETRCgo 命令将在类 Unix 平台上读取 $HOME/.netrc,在 Windows 上读取 %USERPROFILE%\_netrc

.netrc 中的字段用空格、制表符和换行符分隔。不幸的是,这些字符不能用在用户名或密码中。还要注意,机器名称不能是完整的 URL,因此无法为同一机器上的不同路径指定不同的用户名和密码。

或者,可以在 GOPROXY URL 中直接指定凭据。例如

GOPROXY=https://jrgopher:[email protected]

使用这种方法时要谨慎:环境变量可能会出现在 shell 历史记录和日志中。

将凭据传递到私有存储库

go 命令可以从版本控制存储库直接下载模块。如果未使用私有代理,则这对于私有模块是必需的。有关配置,请参见 直接访问私有模块

go 命令在直接下载模块时会运行版本控制工具,如 git。这些工具执行自己的身份验证,因此您可能需要在特定于工具的配置文件(如 .gitconfig)中配置凭据。

为了确保顺利运行,请确保go命令使用正确的仓库 URL,并且版本控制工具不需要交互式输入密码。go命令更倾向于使用https:// URL,而不是其他方案,例如ssh://,除非在查找仓库 URL时指定了方案。对于 GitHub 仓库,go命令默认使用https://

对于大多数服务器,您可以配置客户端通过 HTTP 进行身份验证。例如,GitHub 支持使用OAuth 个人访问令牌作为 HTTP 密码。您可以在.netrc文件中存储 HTTP 密码,就像向私有代理传递凭据时一样。

或者,您可以将https:// URL 重写为其他方案。例如,在.gitconfig

[url "[email protected]:"]
    insteadOf = https://github.com/

有关更多信息,请参见为什么“go get”在克隆仓库时使用 HTTPS?

隐私

go命令可能从模块代理服务器和版本控制系统下载模块和元数据。环境变量GOPROXY控制使用哪些服务器。环境变量GOPRIVATEGONOPROXY控制从代理获取哪些模块。

GOPROXY的默认值为

https://proxy.golang.org,direct

使用此设置,当go命令下载模块或模块元数据时,它将首先向proxy.golang.org发送请求,proxy.golang.org是 Google 运营的公共模块代理(隐私策略)。有关每个请求中发送的信息的详细信息,请参见GOPROXY协议go命令不会传输个人身份信息,但它会传输正在请求的完整模块路径。如果代理响应 404 (未找到) 或 410 (已消失) 状态,go命令将尝试直接连接到提供模块的版本控制系统。有关详细信息,请参见版本控制系统

GOPRIVATEGONOPROXY环境变量可以设置为匹配模块前缀的 glob 模式列表,这些模式是私有的,不应该从任何代理请求。例如

GOPRIVATE=*.corp.example.com,*.research.example.com

GOPRIVATE只是作为GONOPROXYGONOSUMDB的默认值,因此,除非GONOSUMDB应该具有不同的值,否则没有必要设置GONOPROXY。当模块路径与GONOPROXY匹配时,go命令会忽略该模块的GOPROXY,并直接从其版本控制仓库中获取它。当没有代理服务私有模块时,这很有用。有关详细信息,请参见直接访问私有模块

如果存在一个可信代理为所有模块提供服务,则不应该设置GONOPROXY。例如,如果GOPROXY设置为一个源,则go命令不会从其他源下载模块。在这种情况下,GONOSUMDB仍然应该设置。

GOPROXY=https://proxy.corp.example.com
GONOSUMDB=*.corp.example.com,*.research.example.com

如果存在一个可信代理仅为私有模块提供服务,则不应该设置GONOPROXY,但必须注意确保代理以正确的状态代码进行响应。例如,请考虑以下配置

GOPROXY=https://proxy.corp.example.com,https://proxy.golang.org
GONOSUMDB=*.corp.example.com,*.research.example.com

假设由于输入错误,开发人员试图下载一个不存在的模块。

go mod download corp.example.com/secret-product/typo@latest

go命令首先从proxy.corp.example.com请求此模块。如果该代理响应 404 (未找到) 或 410 (已消失),go命令将回退到proxy.golang.org,在请求 URL 中传输secret-product路径。如果私有代理响应任何其他错误代码,go命令将打印错误,并且不会回退到其他源。

除了代理之外,go命令还可以连接到校验和数据库,以验证未列在go.sum中的模块的加密哈希。GOSUMDB环境变量设置校验和数据库的名称、URL 和公钥。GOSUMDB的默认值为sum.golang.org,它是 Google 运营的公共校验和数据库(隐私策略)。有关每个请求传输内容的详细信息,请参见校验和数据库。与代理一样,go命令不会传输个人身份信息,但它会传输正在请求的完整模块路径,校验和数据库无法计算非公共模块的校验和。

GONOSUMDB环境变量可以设置为模式,指示哪些模块是私有的,不应该从校验和数据库请求。GOPRIVATE作为GONOSUMDBGONOPROXY的默认值,因此,除非GONOPROXY应该具有不同的值,否则没有必要设置GONOSUMDB

代理可以镜像校验和数据库。如果GOPROXY中的代理执行此操作,go命令将不会直接连接到校验和数据库。

GOSUMDB可以设置为off,完全禁用校验和数据库的使用。使用此设置,go命令不会对下载的模块进行身份验证,除非它们已经存在于go.sum中。有关详细信息,请参见验证模块

模块缓存

模块缓存是go命令存储下载的模块文件的位置。模块缓存与构建缓存不同,构建缓存包含已编译的包和其他构建工件。

模块缓存的默认位置是$GOPATH/pkg/mod。要使用其他位置,请设置GOMODCACHE环境变量

模块缓存没有最大大小,go命令不会自动删除其内容。

同一台机器上的多个 Go 项目可以共享缓存。go命令将使用相同的缓存,无论主模块的位置如何。多个go命令实例可以安全地同时访问同一个模块缓存。

go命令以只读权限在缓存中创建模块源文件和目录,以防止在下载模块后意外更改它们。这具有不幸的副作用,即难以使用rm -rf等命令删除缓存。可以使用go clean -modcache删除缓存。或者,当使用-modcacherw标志时,go命令将创建新的目录,并赋予它们读写权限。这增加了编辑器、测试和其他程序修改模块缓存中文件的风险。go mod verify命令可用于检测对主模块依赖项的修改。它扫描每个模块依赖项的提取内容,并确认它们与go.sum中预期的哈希值匹配。

下表解释了模块缓存中大多数文件的用途。一些临时文件(锁文件、临时目录)被省略。对于每个路径,$module是模块路径,$version是版本。以斜杠 (/) 结尾的路径是目录。模块路径和版本中的大写字母使用感叹号 (!) 进行了转义 (Azure 转义为 !azure),以避免在不区分大小写的文件系统上发生冲突。

路径 描述
$module@$version/ 包含已提取的模块.zip文件内容的目录。这用作已下载模块的模块根目录。如果原始模块没有go.mod文件,它将不会包含go.mod文件。
cache/download/ 包含从模块代理下载的文件和从版本控制系统派生的文件的目录。此目录的布局遵循GOPROXY协议,因此,此目录可以在由 HTTP 文件服务器提供服务时或通过file:// URL 引用时用作代理。
cache/download/$module/@v/list 已知版本的列表(参见GOPROXY协议)。这可能会随着时间的推移而改变,因此go命令通常会获取新的副本,而不是重复使用此文件。
cache/download/$module/@v/$version.info 有关该版本的 JSON 元数据。(参见GOPROXY协议)。这可能会随着时间的推移而改变,因此go命令通常会获取新的副本,而不是重复使用此文件。
cache/download/$module/@v/$version.mod 此版本的go.mod文件(参见GOPROXY协议)。如果原始模块没有go.mod文件,则这是一个合成的文件,没有要求。
cache/download/$module/@v/$version.zip 模块的压缩内容(参见GOPROXY协议模块 zip 文件)。
cache/download/$module/@v/$version.ziphash .zip文件中文件内容的加密哈希。请注意,.zip文件本身没有进行哈希处理,因此文件顺序、压缩、对齐和元数据不会影响哈希值。当使用模块时,go命令会验证此哈希值是否与go.sum中的相应行匹配。go mod verify命令检查模块.zip文件和已提取目录的哈希值是否与这些文件匹配。
cache/download/sumdb/ 包含从校验和数据库(通常是sum.golang.org)下载的文件的目录。
cache/vcs/ 包含从其源直接获取的模块的克隆版本控制仓库。目录名称是根据仓库类型和 URL 生成的十六进制编码的哈希值。仓库在磁盘上进行了大小优化。例如,克隆的 Git 仓库在可能的情况下是裸的和浅层的。

验证模块

go命令将模块zip 文件go.mod 文件下载到模块缓存中时,它会计算一个加密哈希值,并将其与已知值进行比较,以验证文件自第一次下载后是否发生了更改。如果下载的文件没有正确的哈希值,go命令会报告安全错误。

对于go.mod文件,go命令从文件内容计算哈希值。对于模块 zip 文件,go命令从归档文件中文件的名称和内容以确定性的顺序计算哈希值。哈希值不受文件顺序、压缩、对齐和其他元数据的影响。有关哈希实现的详细信息,请参见golang.org/x/mod/sumdb/dirhash

go命令将每个哈希值与主模块的go.sum 文件中的相应行进行比较。如果哈希值与go.sum中的哈希值不同,go命令会报告安全错误,并删除已下载的文件,而不会将其添加到模块缓存中。

如果go.sum文件不存在,或者它不包含下载文件的哈希值,go命令可能会使用校验和数据库来验证哈希值,这是一个用于公开可用模块的全局哈希值来源。一旦哈希值被验证,go命令会将其添加到go.sum中,并将下载的文件添加到模块缓存中。如果一个模块是私有的(由GOPRIVATEGONOSUMDB环境变量匹配)或校验和数据库被禁用(通过设置GOSUMDB=off),go命令会接受哈希值并将文件添加到模块缓存中,而不会验证它。

模块缓存通常由系统上的所有 Go 项目共享,并且每个模块可能都有自己的go.sum文件,其中可能包含不同的哈希值。为了避免信任其他模块,go命令会在访问模块缓存中的文件时,使用主模块的go.sum来验证哈希值。压缩文件哈希值计算起来很昂贵,因此go命令会检查与压缩文件一起存储的预先计算的哈希值,而不是重新哈希文件。可以使用go mod verify命令来检查压缩文件和解压缩目录自添加到模块缓存后是否被修改。

go.sum 文件

一个模块在其根目录中可能有一个名为go.sum的文本文件,与它的go.mod文件并存。go.sum文件包含模块的直接和间接依赖项的加密哈希值。当go命令将模块的.mod.zip文件下载到模块缓存中时,它会计算哈希值并检查哈希值是否与主模块的go.sum文件中对应的哈希值匹配。如果模块没有依赖项或所有依赖项都使用replace指令替换为本地目录,则go.sum可能为空或不存在。

go.sum中的每一行都有三个字段,用空格隔开:模块路径、版本(可能以/go.mod结尾)和哈希值。

go.sum文件可能包含模块的多个版本的哈希值。go命令可能需要从依赖项的多个版本加载go.mod文件,才能执行最小版本选择go.sum也可能包含不再需要的模块版本的哈希值(例如,在升级之后)。go mod tidy将添加缺少的哈希值,并从go.sum中删除不必要的哈希值。

校验和数据库

校验和数据库是go.sum行的全局来源。go命令可以在许多情况下使用它来检测代理或源服务器的错误行为。

校验和数据库允许对所有公开可用的模块版本进行全局一致性和可靠性。它使得不受信任的代理成为可能,因为它们无法提供错误的代码而不会被发现。它还确保与特定版本相关的位不会从一天到第二天发生变化,即使模块的作者随后更改了其存储库中的标签。

校验和数据库由sum.golang.org提供,由 Google 运行。它是一个透明日志(或“Merkle 树”)的go.sum行哈希值,由Trillian支持。Merkle 树的主要优势在于独立审计员可以验证它没有被篡改,因此它比简单的数据库更可信。

go命令使用最初在Proposal: Secure the Public Go Module Ecosystem中概述的协议与校验和数据库交互。

下表指定了校验和数据库必须响应的查询。对于每个路径,$base是校验和数据库 URL 的路径部分,$module是模块路径,$version是版本。例如,如果校验和数据库 URL 是https://sum.golang.org,并且客户端正在请求模块golang.org/x/text在版本v0.3.2的记录,客户端将发送GET请求到https://sum.golang.org/lookup/golang.org/x/[email protected]

为了避免在从不区分大小写的文件系统中提供服务时出现歧义,$module$version元素通过将每个大写字母替换为感叹号后跟相应的小写字母来进行大小写编码。这使得模块example.com/Mexample.com/m都可以在磁盘上存储,因为前者被编码为example.com/!m

路径中用方括号包围的部分,例如[.p/$W],表示可选值。

路径 描述
$base/latest 返回最新日志的已签名编码树描述。此签名描述采用注释的形式,它是由一个或多个服务器密钥签名的文本,可以使用服务器的公钥进行验证。树描述提供了树的大小和该大小下树头的哈希值。此编码在 golang.org/x/mod/sumdb/tlog#FormatTree中描述。
$base/lookup/$module@$version 返回有关$module$version的条目日志记录号,后跟记录的数据(即$module$versiongo.sum行)以及包含记录的已签名编码树描述。
$base/tile/$H/$L/$K[.p/$W] 返回一个[日志块](https://research.swtch.com/tlog#serving_tiles),它是一组构成日志一部分的哈希值。每个块在块级别$L、从左侧起第$K个的位置定义,块高度为$H。可选的.p/$W后缀表示部分日志块,只包含$W个哈希值。如果未找到部分块,客户端必须回退到获取完整块。
$base/tile/$H/data/$K[.p/$W] 返回/tile/$H/0/$K[.p/$W](使用文字data路径元素)中叶哈希值的记录数据。

如果go命令查询校验和数据库,则第一步是通过/lookup端点检索记录数据。如果模块版本尚未记录在日志中,则校验和数据库将在回复之前尝试从源服务器获取它。此/lookup数据提供了此模块版本的校验和及其在日志中的位置,告知客户端应获取哪些块以执行证明。go命令在将新的go.sum行添加到主模块的go.sum文件之前,会执行“包含”证明(特定记录是否存在于日志中)和“一致性”证明(树没有被篡改)。重要的是,/lookup中的数据绝不能在先将其对已签名树哈希进行身份验证,并将已签名树哈希对客户端已签名树哈希时间线进行身份验证之前使用。

校验和数据库提供的已签名树哈希和新块存储在模块缓存中,因此go命令只需要获取缺少的块。

go命令不需要直接连接到校验和数据库。它可以通过镜像校验和数据库并支持上述协议的模块代理请求模块校验和。这对阻止组织外部请求的私有企业代理特别有用。

GOSUMDB环境变量标识要使用的校验和数据库的名称,并可选地标识其公钥和 URL,例如

GOSUMDB="sum.golang.org"
GOSUMDB="sum.golang.org+<publickey>"
GOSUMDB="sum.golang.org+<publickey> https://sum.golang.org"

go命令知道sum.golang.org的公钥,也知道名称sum.golang.google.cn(在中国大陆可用)连接到sum.golang.org校验和数据库;使用任何其他数据库都需要显式提供公钥。URL 默认设置为https://后跟数据库名称。

GOSUMDB默认设置为sum.golang.org,即由 Google 运行的 Go 校验和数据库。有关服务的隐私策略,请参阅https://sum.golang.org/privacy

如果GOSUMDB设置为off,或如果go get使用-insecure标志调用,则不会咨询校验和数据库,并且所有无法识别的模块都被接受,但代价是放弃对所有模块的已验证可重复下载的安全保证。绕过特定模块的校验和数据库的更好方法是使用GOPRIVATEGONOSUMDB环境变量。有关详细信息,请参阅私有模块

go env -w命令可用于设置这些变量,以便在将来的go命令调用中使用。

环境变量

go命令中的模块行为可以通过使用下面列出的环境变量进行配置。此列表仅包含与模块相关的环境变量。有关go命令识别的所有环境变量的列表,请参阅go help environment

变量 描述
GO111MODULE

控制go命令是否以模块感知模式或GOPATH模式运行。识别三个值

  • offgo命令忽略go.mod文件并以GOPATH模式运行。
  • on(或未设置):即使当前目录或任何父目录中不存在 go.mod 文件,go 命令也会在模块感知模式下运行。
  • auto:如果当前目录或任何父目录中存在 go.mod 文件,则 go 命令将在模块感知模式下运行。在 Go 1.15 及更低版本中,这是默认设置。

有关更多信息,请参见 模块感知命令

GOMODCACHE

go 命令将存储下载的模块和相关文件的目录。有关此目录结构的详细信息,请参见 模块缓存

如果未设置 GOMODCACHE,则默认为 $GOPATH/pkg/mod

GOINSECURE

模块路径前缀的逗号分隔的 glob 模式列表(使用 Go 的 path.Match 语法),这些前缀始终可以以不安全的方式获取。仅适用于直接获取的依赖项。

go get 上的 -insecure 标志不同,GOINSECURE 不会禁用模块校验和数据库验证。可以使用 GOPRIVATEGONOSUMDB 来实现这一点。

GONOPROXY

模块路径前缀的逗号分隔的 glob 模式列表(使用 Go 的 path.Match 语法),这些前缀应始终直接从版本控制库获取,而不是从模块代理获取。

如果未设置 GONOPROXY,则默认为 GOPRIVATE。请参见 隐私

GONOSUMDB

模块路径前缀的逗号分隔的 glob 模式列表(使用 Go 的 path.Match 语法),go 不应使用校验和数据库验证这些前缀的校验和。

如果未设置 GONOSUMDB,则默认为 GOPRIVATE。请参见 隐私

GOPATH

GOPATH 模式下,GOPATH 变量是可能包含 Go 代码的目录列表。

在模块感知模式下,模块缓存 存储在第一个 GOPATH 目录的 pkg/mod 子目录中。缓存之外的模块源代码可以存储在任何目录中。

如果未设置 GOPATH,则默认为用户主目录的 go 子目录。

GOPRIVATE 模块路径前缀的逗号分隔的 glob 模式列表(使用 Go 的 path.Match 语法),这些前缀应被视为私有。GOPRIVATEGONOPROXYGONOSUMDB 的默认值。请参见 隐私GOPRIVATE 还会确定模块是否被视为 GOVCS 的私有模块。
GOPROXY

模块代理 URL 列表,用逗号 (,) 或管道 (|) 分隔。当 go 命令查找有关模块的信息时,它会按顺序联系列表中的每个代理,直到收到成功响应或终端错误。代理可以使用 404 (未找到) 或 410 (已消失) 状态响应,以指示该模块在该服务器上不可用。

go 命令的错误回退行为由 URL 之间的分隔符确定。如果代理 URL 后面跟着逗号,则 go 命令在 404 或 410 错误后回退到下一个 URL;所有其他错误都被视为终端错误。如果代理 URL 后面跟着管道,则 go 命令在任何错误(包括非 HTTP 错误,例如超时)后回退到下一个源。

GOPROXY URL 可能具有 httpshttpfile 方案。如果 URL 没有方案,则假定为 https。模块缓存可以直接用作文件代理

GOPROXY=file://$(go env GOMODCACHE)/cache/download

可以使用两个关键字代替代理 URL

  • off:禁止从任何来源下载模块。
  • direct:直接从版本控制库下载,而不是使用模块代理。

GOPROXY 默认为 https://proxy.golang.org,direct。在此配置下,go 命令首先联系由 Google 运行的 Go 模块镜像,如果镜像没有模块,则回退到直接连接。请参见 https://proxy.golang.org/privacy,了解镜像的隐私策略。GOPRIVATEGONOPROXY 环境变量可以设置为防止使用代理下载特定模块。请参见 隐私,了解有关私有代理配置的信息。

有关代理使用方式的更多信息,请参见 模块代理将包解析为模块

GOSUMDB

标识要使用的校验和数据库的名称,并可选地标识其公钥和 URL。例如

GOSUMDB="sum.golang.org"
GOSUMDB="sum.golang.org+<publickey>"
GOSUMDB="sum.golang.org+<publickey> https://sum.golang.org"

go 命令知道 sum.golang.org 的公钥,并且也知道名称 sum.golang.google.cn(在中国大陆境内可用)连接到 sum.golang.org 数据库;使用任何其他数据库都需要明确给出公钥。URL 默认为 https:// 后跟数据库名称。

GOSUMDB 默认为 sum.golang.org,即由 Google 运行的 Go 校验和数据库。请参见 https://sum.golang.org/privacy,了解该服务的隐私策略。

如果 GOSUMDB 设置为 off,或者如果 go get 使用 -insecure 标志调用,则不会查询校验和数据库,并且会接受所有无法识别的模块,但这会导致放弃对所有模块的已验证可重复下载的安全保证。绕过特定模块的校验和数据库的更好方法是使用 GOPRIVATEGONOSUMDB 环境变量。

有关更多信息,请参见 验证模块隐私

GOVCS

控制 go 命令可以使用的一组版本控制工具,用于下载公共和私有模块(由其路径是否与 GOPRIVATE 中的模式匹配来定义)或与 glob 模式匹配的其他模块。

如果未设置 GOVCS,或者如果模块与 GOVCS 中的任何模式都不匹配,则 go 命令可以使用 githg 来处理公共模块,或者可以使用任何已知的版本控制工具来处理私有模块。具体来说,go 命令的行为就好像 GOVCS 设置为

public:git|hg,private:all

有关完整说明,请参见 使用 GOVCS 控制版本控制工具

GOWORK

GOWORK 环境变量指示 go 命令使用提供的 [`go.work` 文件](#go-work-file) 进入工作区模式,以定义工作区。如果 GOWORK 设置为 off,则工作区模式将被禁用。这可以用于在单模块模式下运行 go 命令:例如,GOWORK=off go build . 在单模块模式下构建 . 包。如果 GOWORK 为空,则 go 命令将在 [工作区](#workspaces) 部分中描述的范围内搜索 go.work 文件。

术语表

构建约束:确定编译包时是否使用 Go 源文件的条件。构建约束可以通过文件名后缀(例如,foo_linux_amd64.go)或构建约束注释(例如,// +build linux,amd64)来表达。请参见 构建约束

构建列表:将用于构建命令(如 go buildgo listgo test)的模块版本列表。构建列表根据 主模块go.mod 文件 和使用 最小版本选择 的传递依赖模块中的 go.mod 文件来确定。构建列表包含 模块图 中所有模块的版本,而不仅仅是与特定命令相关的模块。

规范版本:格式正确的 版本,没有除 +incompatible 之外的构建元数据后缀。例如,v1.2.3 是规范版本,但 v1.2.3+meta 不是。

当前模块:主模块 的同义词。

已弃用模块:不再受其作者支持的模块(尽管主要版本被认为为此目的的独立模块)。已弃用模块在其最新版本的 go.mod 文件 中用 弃用注释 标记。

直接依赖项:其路径出现在 import 声明 中的包,该声明位于 主模块 的包或测试的 .go 源文件中,或者包含此类包的模块。(比较 间接依赖项。)

直接模式:环境变量 的设置,它会导致 go 命令直接从 版本控制系统 下载模块,而不是从 模块代理 下载。GOPROXY=direct 对所有模块执行此操作。GOPRIVATEGONOPROXY 对与模式列表匹配的模块执行此操作。

go.mod 文件:定义模块的路径、要求和其他元数据的文件。出现在 模块的根目录 中。请参见 go.mod 文件 部分。

go.work 文件 定义要用于 工作区 的模块集的文件。请参见 go.work 文件 部分

导入路径:用于在 Go 源文件中导入包的字符串。与 包路径 同义。

间接依赖项:主模块 中的包或测试传递导入的包,但其路径未出现在主模块中的任何 import 声明 中;或者出现在 模块图 中但未提供主模块直接导入的任何包的模块。(比较 直接依赖项。)

延迟模块加载:Go 1.17 中的一项更改,它避免在那些指定 go 1.17 或更高版本的模块中加载 模块图,以执行不需要该图的命令。请参见 延迟模块加载

主模块:执行 go 命令的模块。主模块由当前目录或父目录中的 go.mod 文件 定义。参见 模块、包和版本

主版本:语义版本中的第一个数字(v1.2.3 中的 1)。在具有不兼容更改的版本中,必须增加主版本,并且次版本和补丁版本必须设置为 0。主版本为 0 的语义版本被认为是不稳定的。

主版本子目录:版本控制存储库中的一个子目录,与模块的 主版本后缀 匹配,模块可能在此处定义。例如,在具有 根路径 example.com/mod 的存储库中,模块 example.com/mod/v2 可以定义在存储库根目录或主版本子目录 v2 中。参见 存储库中的模块目录

主版本后缀:与主版本号匹配的模块路径后缀。例如,example.com/mod/v2 中的 /v2。主版本后缀在 v2.0.0 及以后版本中是必需的,在更早的版本中是不允许的。参见关于 主版本后缀 的部分。

最小版本选择 (MVS):用于确定构建中将使用所有模块的版本的算法。有关详细信息,请参见 最小版本选择 部分。

次版本:语义版本中的第二个数字(v1.2.3 中的 2)。在具有新的向后兼容功能的版本中,必须增加次版本,并且补丁版本必须设置为 0。

模块:一起发布、版本化和分发的包集合。

模块缓存:存储下载模块的本地目录,位于 GOPATH/pkg/mod 中。参见 模块缓存

模块图:模块需求的有向图,以 主模块 为根。图中的每个顶点都是一个模块;每条边都是 go.mod 文件中 require 语句中的一个版本(受主模块 go.mod 文件中 replaceexclude 语句的约束)。

模块图修剪:Go 1.17 中的一项更改,通过省略指定 go 1.17 或更高版本的模块的传递依赖项来减小模块图的大小。参见 模块图修剪

模块路径:标识模块并充当模块内包导入路径前缀的路径。例如,"golang.org/x/net"

模块代理:实现 GOPROXY 协议 的 Web 服务器。go 命令从模块代理下载版本信息、go.mod 文件和模块 zip 文件。

模块根目录:包含定义模块的 go.mod 文件的目录。

模块子目录:模块路径存储库根路径 之后的子部分,指示定义模块的子目录。当不为空时,模块子目录也是 语义版本标签 的前缀。模块子目录不包括 主版本后缀(如果有),即使模块位于 主版本子目录 中也是如此。参见 模块路径

包:同一个目录中的一组源文件,它们一起编译。参见 Go 语言规范中的 包部分

包路径:唯一标识包的路径。包路径是 模块路径 与模块内子目录的组合。例如 "golang.org/x/net/html" 是模块 "golang.org/x/net""html" 子目录中包的包路径。是 导入路径 的同义词。

补丁版本:语义版本中的第三个数字(v1.2.3 中的 3)。在对模块的公共接口没有更改的版本中,必须增加补丁版本。

预发布版本:在补丁版本后紧跟一个连字符和一系列用点分隔的标识符的版本,例如 v1.2.3-beta4。预发布版本被认为是不稳定的,不假定它们与其他版本兼容。预发布版本在排序时位于相应的发布版本之前:v1.2.3-pre 位于 v1.2.3 之前。另请参见 发布版本

伪版本:一个版本,它对版本控制系统中的修订标识符(例如 Git 提交哈希值)和时间戳进行编码。例如,v0.0.0-20191109021931-daa7c04131f5。用于 与非模块存储库的兼容性 以及在没有标记版本的其他情况下使用。

发布版本:没有预发布后缀的版本。例如,v1.2.3,而不是 v1.2.3-pre。另请参见 预发布版本

存储库根路径:模块路径 中对应于版本控制存储库根目录的部分。参见 模块路径

撤回版本:不应该依赖的版本,无论是由于它过早发布还是由于发布后发现了一个严重的问题。参见 retract 指令

语义版本标签:版本控制存储库中的一个标签,它将 版本 映射到特定的修订版。参见 将版本映射到提交

选定版本:最小版本选择 选择的给定模块的版本。选定版本是 模块图 中找到的模块路径的最高版本。

vendor 目录:一个名为 vendor 的目录,它包含构建主模块中的包所需的其他模块的包。使用 go mod vendor 进行维护。参见 供应商管理

版本:模块的不可变快照的标识符,写为字母 v 后跟语义版本。参见关于 版本 的部分。

工作区:磁盘上的一组模块,它们在运行 最小版本选择 (MVS) 时用作主模块。参见关于 工作区 的部分。