Go Modules 参考

引言

Go 使用 Modules 来管理依赖项。

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

Modules、Packages 和 Versions

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

模块通过 模块路径 (module path) 标识,该路径在 go.mod 文件 中声明,并包含有关模块依赖项的信息。模块根目录 (module root directory) 是包含 go.mod 文件的目录。主模块 (main module) 是包含调用 go 命令的目录的模块。

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

模块路径

模块路径 (module path) 是模块的规范名称,通过模块的 go.mod 文件 中的 module 指令 声明。模块路径是模块内包路径的前缀。

模块路径应描述模块的功能以及在哪里可以找到它。通常,模块路径由仓库根路径、仓库内的目录(通常为空)和主版本后缀(仅适用于主版本 2 或更高版本)组成。

  • 仓库根路径 (repository root path) 是模块路径中与开发模块的版本控制仓库的根目录相对应的部分。大多数模块都在其仓库的根目录中定义,因此这通常是整个路径。例如,golang.org/x/net 是同名模块的仓库根路径。有关 go 命令如何使用派生自模块路径的 HTTP 请求定位仓库的信息,请参阅 查找模块路径的仓库
  • 如果模块未在仓库的根目录中定义,则 模块子目录 (module subdirectory) 是模块路径中命名目录的部分,不包括主版本后缀。这也可以作为语义版本标签的前缀。例如,模块 golang.org/x/tools/gopls 位于根路径为 golang.org/x/tools 的仓库的 gopls 子目录中,因此其模块子目录为 gopls。请参阅 将版本映射到提交仓库内的模块目录
  • 如果模块发布的主版本为 2 或更高,则模块路径必须以 主版本后缀 结尾,例如 /v2。这可能包含也可能不包含在子目录名称中。例如,路径为 golang.org/x/repo/sub/v2 的模块可能位于仓库 golang.org/x/repo/sub/sub/v2 子目录中。

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

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

版本

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

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

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

  • 在对模块的公共接口或文档功能进行向后不兼容更改后,例如删除包后,必须增加 主版本 (major version),并将次版本和补丁版本设置为零。
  • 在向后兼容更改后,例如添加新函数后,必须增加 次版本 (minor version),并将补丁版本设置为零。
  • 在不影响模块公共接口的更改(例如错误修复或优化)后,必须增加 补丁版本 (patch version)
  • 预发布后缀表示版本是 预发布版本。预发布版本在相应的发布版本之前排序。例如,v1.2.3-prev1.2.3 之前。
  • 构建元数据后缀在比较版本时被忽略。go 命令接受带有构建元数据的版本,并将它们转换为伪版本以维持版本之间的总排序。
    • 特殊后缀 +incompatible 表示在迁移到模块主版本 2 或更高版本之前发布的版本(请参阅 与非模块仓库的兼容性)。
    • 特殊后缀 +dirty 附加到二进制文件的版本信息中,当它使用 Go toolchain 1.24 或更高版本在包含工作目录中未提交更改的有效本地版本控制系统 (VCS) 仓库中构建时。

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

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

伪版本

伪版本 (pseudo-version) 是一种特殊格式的 预发布 版本,它编码了版本控制仓库中特定修订版的信息。例如,v0.0.0-20191109021931-daa7c04131f5 是一个伪版本。

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

每个伪版本有三个部分

  • 一个基本版本前缀(vX.0.0vX.Y.Z-0),它要么来自在修订版之前的语义版本标签,要么在没有此类标签时为 vX.0.0
  • 一个时间戳(yyyymmddhhmmss),这是修订版创建的 UTC 时间。在 Git 中,这是提交时间,而不是作者时间。
  • 一个修订标识符(abcdefabcdef),它是提交哈希的 12 个字符前缀,或者在 Subversion 中是零填充的修订号。

根据基本版本,每个伪版本可以采用三种形式之一。这些形式确保伪版本与它的基本版本相比排序更高,但低于下一个带标签的版本。

  • 当没有已知基本版本时,使用 vX.0.0-yyyymmddhhmmss-abcdefabcdef。与所有版本一样,主版本 X 必须与模块的 主版本后缀 匹配。
  • 当基本版本是预发布版本(例如 vX.Y.Z-pre)时,使用 vX.Y.Z-pre.0.yyyymmddhhmmss-abcdefabcdef
  • 当基本版本是发布版本(例如 vX.Y.Z)时,使用 vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdefabcdef。例如,如果基本版本是 v1.2.3,则伪版本可能是 v1.2.4-0.20191109021931-daa7c04131f5

通过使用不同的基本版本,多个伪版本可以引用相同的提交。当在写入伪版本之后标记较低版本时,这自然会发生。

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

  • 已知基本版本的伪版本排序高于这些版本,但低于后续版本的其他预发布版本。
  • 相同基本版本前缀的伪版本按时间顺序排序。

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

  • 如果指定了基本版本,则必须有一个相应的语义版本标签作为伪版本描述的修订版的祖先。这可以防止开发人员使用比所有带标签版本都高的伪版本(例如 v1.999.999-99999999999999-daa7c04131f5)来绕过 最小版本选择
  • 时间戳必须与修订版的时间戳匹配。这可以防止攻击者向 模块代理 泛滥注入无限数量的 otherwise 相同的伪版本。这还可以防止模块消费者改变版本的相对排序。
  • 该修订版必须是模块仓库分支或标签的祖先之一。这可以防止攻击者引用未经批准的更改或拉取请求。

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

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

主版本后缀

从主版本 2 开始,模块路径必须具有与主版本匹配的 主版本后缀 (major version suffix),例如 /v2。例如,如果模块在 v1.0.0 时的路径是 example.com/mod,则在 v2.0.0 版本时其路径必须是 example.com/mod/v2

主版本后缀实现了 导入兼容性规则 (import compatibility rule)

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

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

主版本后缀不允许用于主版本 v0v1v0v1 之间无需更改模块路径,因为 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 命令可能会发出以下请求

  • 并行请求 https://corp.example.com/
    • 请求 golang.org/x/net/html 的最新版本
    • 请求 golang.org/x/net 的最新版本
    • 请求 golang.org/x 的最新版本
    • 请求 golang.org 的最新版本
  • 如果所有对 https://corp.example.com/ 的请求都以 404 或 410 失败,则请求 https://proxy.golang.org/
    • 请求 golang.org/x/net/html 的最新版本
    • 请求 golang.org/x/net 的最新版本
    • 请求 golang.org/x 的最新版本
    • 请求 golang.org 的最新版本

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

go.mod 文件

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

module example.com/my/thing

go 1.23.0

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 文件中的大多数标识符和字符串要么是模块路径,要么是版本。

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

  • 路径必须由一个或多个用斜杠(/,U+002F)分隔的路径元素组成。它不能以斜杠开头或结尾。
  • 每个路径元素都是一个非空字符串,由 ASCII 字母、ASCII 数字和有限的 ASCII 标点符号(-._~)组成。
  • 路径元素不能以点(.,U+002E)开头或结尾。
  • 元素前缀直到第一个点不得是 Windows 上保留的文件名,无论大小写(例如 CONcom1NuL 等)。
  • 元素前缀直到第一个点不得以波浪号后跟一个或多个数字结尾(例如 EXAMPL~1.COM)。

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

  • 按照惯例,开头的路径元素(直到第一个斜杠,如果有的话),域名,必须只包含小写 ASCII 字母、ASCII 数字、点(.,U+002E)和破折号(-,U+002D);它必须包含至少一个点,并且不能以破折号开头。
  • 对于形如 /vN 的最终路径元素,其中 N 看起来是数字(ASCII 数字和点),N 不能以零开头,不能是 /v1,并且不能包含任何点。
    • 对于以 gopkg.in/ 开头的路径,此要求被替换为路径遵循 gopkg.in 服务的约定。

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

规范版本以字母 v 开头,后跟遵循 语义化版本 2.0.0 规范的语义版本。有关更多信息,请参阅 版本

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

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

语法

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

GoMod = { Directive } .
Directive = ModuleDirective |
            GoDirective |
            ToolDirective |
            IgnoreDirective |
            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.141.21rc11.23.0

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

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

go 指令会影响新语言功能的使用

  • 对于模块内的包,编译器会拒绝使用在 go 指令指定版本之后引入的语言功能。例如,如果模块具有指令 go 1.12,则其包不能使用像 1_000_000 这样的数字文字,这些数字文字是在 Go 1.13 中引入的。
  • 如果一个较旧的 Go 版本构建模块的某个包并遇到编译错误,则错误会指出该模块是为较新的 Go 版本编写的。例如,假设一个模块有 go 1.13 并且一个包使用了数字文字 1_000_000。如果该包使用 Go 1.12 构建,编译器会指出该代码是为 Go 1.13 编写的。

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

  • go 1.14 或更高版本中,可能会启用自动 vendoring。如果文件 vendor/modules.txt 存在且与 go.mod 一致,则无需显式使用 -mod=vendor 标志。
  • go 1.16 或更高版本中,all 包模式仅匹配由 主模块 中的包和测试传递导入的包。这与自模块引入以来 go mod vendor 保留的包集相同。在较低版本中,all 还包括由主模块中的包导入的包的测试,以及这些包的测试,等等。
  • go 1.17 或更高版本中
    • go.mod 文件包含一个显式的 require 指令,用于提供主模块中包或测试(甚至 间接)传递导入的任何包的每个模块。(在 go 1.16 及更低版本中,仅当 最小版本选择 会选择不同版本时,才会包含间接依赖项。)这些额外信息支持 模块图修剪惰性模块加载
    • 因为 // indirect 依赖项可能比以前的 go 版本多得多,所以间接依赖项记录在 go.mod 文件中的单独块中。
    • go mod vendor 会省略 vendored 依赖项的 go.modgo.sum 文件。(这允许在 vendor 的子目录中调用 go 命令来识别正确的主模块。)
    • go mod vendor 将每个依赖项的 go.mod 文件中的 go 版本记录在 vendor/modules.txt 中。
  • go 1.21 或更高版本中
    • go 行声明了使用此模块所需的最低 Go 版本。
    • go 行必须大于或等于所有依赖项的 go 行。
    • go 命令不再尝试保持与以前旧版 Go 的兼容性。
    • go 命令更谨慎地维护 go.sum 文件中 go.mod 文件的校验和。

一个 go.mod 文件最多包含一个 go 指令。如果不存在 go 指令,大多数命令都会添加一个带有当前 Go 版本的 go 指令。

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

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

示例

go 1.23.0

toolchain 指令

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

为了可重现性,go 命令在更新 go.mod 文件中的 go 版本时(通常在 go get 期间)会将其自身的工具链名称写入 toolchain 行。

有关详细信息,请参阅“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
)

tool 指令

tool 指令将一个包添加为当前模块的依赖项。当当前工作目录位于此模块内,或者位于包含此模块的工作区内时,它还可以通过 go tool 运行。

如果工具包不在当前模块中,则必须存在一个 require 指令来指定要使用的工具版本。

tool 元模式解析为当前模块的 go.mod 中定义的工具列表,或者在工作区模式下解析为工作区中所有模块中定义的所有工具的并集。

ToolDirective = "tool" ( ToolSpec | "(" newline { ToolSpec } ")" newline ) .
ToolSpec = ModulePath newline .

示例

tool golang.org/x/tools/cmd/stringer

tool (
    example.com/module/cmd/a
    example.com/module/cmd/b
)

ignore 指令

ignore 指令将导致 go 命令在匹配包模式时忽略斜杠分隔的目录路径以及其中递归包含的任何文件或目录。

如果路径以 ./ 开头,则路径被解释为相对于模块根目录,并且在匹配包模式时将忽略该目录以及其中递归包含的任何目录或文件。

否则,模块中任何深度下具有该路径的任何目录,以及其中递归包含的任何目录或文件都将被忽略。

IgnoreDirective = "ignore" ( IgnoreSpec | "(" newline { IgnoreSpec } ")" newline ) .
IgnoreSpec = RelativeFilePath newline .
RelativeFilePath = /* slash-separated relative file path */ .

示例

ignore ./node_modules

ignore (
    static
    content/html
    ./third_party/javascript
)

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 中删除或覆盖。

请注意,单独的 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 指令很有用。撤回的版本应保留在版本控制仓库和 模块代理 上,以确保依赖它们的构建不会中断。撤回 (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.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 .

示例

  • 撤回 v1.0.0v1.9.9 之间的所有版本
retract v1.0.0
retract [v1.0.0, v1.9.9]
retract (
    v1.0.0
    [v1.0.0, v1.9.9]
)
  • 在过早发布 v1.0.0 版本后恢复到未版本化
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.

Go 1.16 中添加了 retract 指令。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.23.0

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.modexample.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 在每个支持模块的命令开始时用于计算它。

考虑下图中的示例。主模块要求模块 A 的版本为 1.2 或更高,模块 B 的版本为 1.2 或更高。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.mod 文件或工作区 go.work 文件中的 replace 指令 进行替换。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 在升级后出现在构建列表中,因为 B 1.3 需要 E 1.1。

为了保留升级,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.17go.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 文件,并检查它们的要求是否与主模块的要求局部一致。(由于版本控制合并、手动编辑以及使用本地文件系统路径 替换 的模块更改,可能会出现不一致。)

工作区

工作区 (workspace) 是磁盘上模块的集合,当运行 最小版本选择 (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.23.0

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 命令将维护一个 go.work.sum 文件,用于跟踪工作区使用的、未包含在集体工作区模块的 go.sum 文件中的哈希值。

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

  • 已签入的 go.work 文件可能会覆盖开发人员从父目录中自己的 go.work 文件,导致他们的 use 指令不适用时产生混淆。
  • 已签入的 go.work 文件可能会导致持续集成 (CI) 系统选择并因此测试模块依赖项的错误版本。CI 系统通常不应被允许使用 go.work 文件,以便它们可以测试模块在被其他模块需要时的行为,其中模块内的 go.work 文件无效。

话虽如此,在某些情况下提交 go.work 文件是有意义的。例如,当仓库中的模块仅相互开发,而不与外部模块一起开发时,开发人员可能没有理由希望在工作区中使用不同组合的模块。在这种情况下,模块作者应确保单独的模块得到正确测试和发布。

词法元素

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

语法

go.work 语法使用扩展巴克斯-诺尔范式 (EBNF) 在下面指定。有关 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.23.0

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 命令会将 +incompatible 后缀添加到没有 go.mod 文件的主版本为 2 或更高版本的版本。+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 模式下解析为目录时

  • 当解析形式为 $modpath/$vn/$dir 的导入时,其中
    • $modpath 是有效的模块路径,
    • $vn 是主版本后缀,
    • $dir 是可能为空的子目录,
  • 如果以下所有条件都为真
    • $modpath/$vn/$dir 不存在于任何相关的vendor 目录中。
    • 一个 go.mod 文件存在于导入文件所在的目录或其父目录中,直到 $GOPATH/src 根目录,
    • 不存在 $GOPATH[i]/src/$modpath/$vn/$suffix 目录(对于任何根目录 $GOPATH[i]),
    • 文件 $GOPATH[d]/src/$modpath/go.mod 存在(对于某些根目录 $GOPATH[d])并声明模块路径为 $modpath/$vn
  • 那么 $modpath/$vn/$dir 的导入将被解析到目录 $GOPATH[d]/src/$modpath/$dir

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

模块感知命令

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

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

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

  • 如果 GO111MODULE=offgo 命令会忽略 go.mod 文件,并以 GOPATH 模式运行。
  • 如果 GO111MODULE=on 或未设置,go 命令将以模块感知模式运行,即使不存在 go.mod 文件。并非所有命令在没有 go.mod 文件的情况下都能工作:请参阅模块外部的模块命令
  • 如果 GO111MODULE=auto,当当前目录或任何父目录中存在 go.mod 文件时,go 命令将以模块感知模式运行。在 Go 1.15 及更低版本中,这是默认行为。go mod 子命令和带有版本查询go install 命令即使没有 go.mod 文件也会以模块感知模式运行。

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

构建命令

所有加载包信息的命令都支持模块。这包括

  • go build
  • go fix
  • go generate
  • go install
  • go list
  • go run
  • go test
  • go vet

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

  • -mod 标志控制 go.mod 是否可以自动更新以及是否使用 vendor 目录。
    • -mod=mod 指示 go 命令忽略 vendor 目录并自动更新 go.mod,例如,当导入的包没有由任何已知模块提供时。
    • -mod=readonly 告诉 go 命令忽略 vendor 目录,并在 go.mod 需要更新时报告错误。
    • -mod=vendor 告诉 go 命令使用 vendor 目录。在此模式下,go 命令将不使用网络或模块缓存。
    • 默认情况下,如果 go.mod 中的 go 版本1.14 或更高版本并且存在 vendor 目录,则 go 命令的行为与使用 -mod=vendor 相同。否则,go 命令的行为与使用 -mod=readonly 相同。
    • go get 拒绝此标志,因为此命令的目的是修改依赖项,这只能通过 -mod=mod 允许。
  • -modcacherw 标志指示 go 命令在模块缓存中创建具有读写权限的新目录,而不是将它们设置为只读。当一致地使用此标志时(通常通过在环境变量中设置 GOFLAGS=-modcacherw 或运行 go env -w GOFLAGS=-modcacherw),可以使用 rm -r 等命令删除模块缓存,而无需先更改权限。go clean -modcache 命令可用于删除模块缓存,无论是否使用了 -modcacherw
  • -modfile=file.mod 标志指示 go 命令读取(并可能写入)一个备用文件,而不是模块根目录中的 go.mod。文件名必须以 .mod 结尾。仍必须存在名为 go.mod 的文件才能确定模块根目录,但不会访问它。指定 -modfile 时,也会使用备用 go.sum 文件:其路径通过从 -modfile 标志中删除 .mod 扩展名并附加 .sum 派生。

Vendoring(供应商化)

使用模块时,go 命令通常通过从源下载模块到模块缓存中来满足依赖项,然后从这些下载的副本中加载包。Vendoring 可用于与 Go 的旧版本进行互操作,或确保构建使用的所有文件都存储在单个文件树中。

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

go mod vendor 还会创建文件 vendor/modules.txt,其中包含 vendored 包的列表以及它们从中复制的模块版本。启用 vendoring 时,此清单用作模块版本信息的来源,如 go list -mgo version -m 所报告。当 go 命令读取 vendor/modules.txt 时,它会检查模块版本是否与 go.mod 一致。如果自生成 vendor/modules.txt 以来 go.mod 已更改,go 命令将报告错误。应再次运行 go mod vendor 以更新 vendor 目录。

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

启用 vendoring 后,构建命令(如 go buildgo test)会从 vendor 目录加载包,而不是访问网络或本地模块缓存。go list -m 命令只打印 go.mod 中列出的模块信息。go mod 命令(如go mod downloadgo mod tidy)在启用 vendoring 时不会有不同的工作方式,仍会下载模块并访问模块缓存。go get 在启用 vendoring 时也不会有不同的工作方式。

GOPATH 模式下的 vendoring 不同,go 命令会忽略主模块根目录以外位置的 vendor 目录。此外,由于不使用其他模块中的 vendor 目录,go 命令在构建模块 zip 文件时不会包含 vendor 目录(但请参阅已知 bug #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/text@v0.3.2

# 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/text@v0.3.0。版本查询后缀由 @ 符号后跟版本查询组成,版本查询可以指示特定版本(v0.3.0)、版本前缀(v0.3)、分支或标签名称(master)、修订(1234abcd),或特殊查询 latestupgradepatchnone 之一。如果未给出版本,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/a@v1.5.0 也会将 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 环境变量,则默认为 $GOPATH/bin$HOME/go/bin

go get 支持以下标志

  • -d 标志告诉 go get 不要构建或安装包。当使用 -d 时,go get 只管理 go.mod 中的依赖项。不带 -d 使用 go get 来构建和安装包已被弃用(从 Go 1.17 开始)。在 Go 1.18 中,-d 将始终启用。
  • -u 标志告诉 go get 升级提供命令行上命名包直接或间接导入的模块。通过 -u 选择的每个模块都将升级到其最新版本,除非它已被更高版本(预发布版本)要求。
  • -u=patch 标志(不是 -u patch)也指示 go get 升级依赖项,但 go get 会将每个依赖项升级到最新的补丁版本(类似于 @patch 版本查询)。
  • -t 标志告诉 go get 考虑构建命令行上命名包的测试所需的模块。当 -t-u 一起使用时,go get 也会更新测试依赖项。
  • -insecure 标志不应再使用。它允许 go get 解析自定义导入路径并使用不安全的方案(如 HTTP)从仓库和模块代理获取。GOINSECURE 环境变量提供了更精细的控制,应改用它。

从 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/gopls@v0.6.4

# 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 环境变量,则默认为 $GOPATH/bin$HOME/go/bin$GOROOT 中的可执行文件安装在 $GOROOT/bin$GOTOOLDIR 中,而不是 $GOBIN。非可执行包被构建和缓存,但不安装。

从 Go 1.16 开始,如果参数带有版本后缀(例如 @latest@v1.0.0),go install 会以模块感知模式构建包,忽略当前目录或任何父目录中的 go.mod 文件(如果存在)。这对于安装可执行文件而不会影响主模块的依赖项非常有用。

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

  • 参数必须是包路径或包模式(带有“...”通配符)。它们不能是标准包(如 fmt)、元模式(stdcmdallworktool),或相对或绝对文件路径。
  • 所有参数必须具有相同的版本后缀。不允许使用不同的查询,即使它们指向相同的版本。
  • 所有参数必须指同一模块的同一版本中的包。
  • 包路径参数必须指向 main 包。模式参数将只匹配 main 包。
  • 不将任何模块视为主模块。
    • 如果包含命令行上命名包的模块有 go.mod 文件,则它不能包含(replaceexclude)指令,否则如果它作为主模块,则会被不同地解释。
    • 该模块不能要求其自身的更高版本。
    • 在任何模块中都不使用 Vendor 目录。(Vendor 目录不包含在模块 zip 文件中,因此 go install 不会下载它们。)

有关支持的版本查询语法,请参阅版本查询。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 的最高发布或预发布版本,即使该版本已被撤回。retract 指令弃用是从此版本的 go.mod 文件中加载的。-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/mod@v0.2.0

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/a@v1.0.0=./a

# Remove a replace directive.
$ go mod edit -dropreplace example.com/a@v1.0.0

# 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/m@v1.0.0 -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 文件,但可以在编辑标志之后指定不同的目标文件。

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

  • -module 标志更改模块的路径(go.mod 文件的模块行)。
  • -go=version 标志设置预期的 Go 语言版本。
  • -require=path@version-droprequire=path 标志添加和删除对给定模块路径和版本的依赖。请注意,-require 会覆盖对 path 的任何现有依赖。这些标志主要用于理解模块图的工具。用户应首选 go get path@versiongo get path@none,它们会根据需要进行其他 go.mod 调整以满足其他模块施加的约束。请参阅go get
  • -exclude=path@version-dropexclude=path@version 标志添加和删除对给定模块路径和版本的排除项。请注意,如果该排除项已存在,-exclude=path@version 将不执行任何操作。
  • -replace=old[@v]=new[@v] 标志添加给定模块路径和版本对的替换。如果 old@v 中的 @v 被省略,则添加一个左侧没有版本的替换,它适用于旧模块路径的所有版本。如果 new@v 中的 @v 被省略,则新路径应为本地模块根目录,而不是模块路径。请注意,-replace 会覆盖 old[@v] 的任何冗余替换,因此省略 @v 将删除特定版本的替换。
  • -dropreplace=old[@v] 标志删除给定模块路径和版本对的替换。如果提供了 @v,则删除具有给定版本的替换。左侧没有版本的现有替换仍可能替换模块。如果省略了 @v,则删除没有版本的替换。
  • -retract=version-dropretract=version 标志添加和删除给定版本的撤回,该版本可以是单个版本(如 v1.2.3)或一个区间(如 [v1.1.0,v1.2.0])。请注意,-retract 标志无法为 retract 指令添加理由注释。推荐使用理由注释,并且它们可能会由 go list -m -u 和其他命令显示。
  • -tool=path-droptool=path 标志添加和删除给定路径的 tool 指令。请注意,这不会向构建图添加必要的依赖项。用户应首选 go get -tool path 来添加工具,或 go get -tool path@none 来删除工具。

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

go mod edit 还有控制其输出的附加标志。

  • -fmt 标志重新格式化 go.mod 文件而不进行其他更改。此重新格式化也由任何其他使用或重写 go.mod 文件的修改所隐含。此标志仅在未指定其他标志时才需要,例如 go mod edit -fmt
  • -print 标志以文本格式打印最终的 go.mod,而不是将其写回磁盘。
  • -json 标志以 JSON 格式打印最终的 go.mod,而不是以文本格式写回磁盘。JSON 输出对应于这些 Go 类型
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
}

type Tool struct {
    Path      string
}

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

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

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

go mod graph

用法

go mod graph [-go=version]

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

example.com/main example.com/a@v1.1.0
example.com/main example.com/b@v1.2.0
example.com/a@v1.1.0 example.com/b@v1.1.1
example.com/a@v1.1.0 example.com/c@v1.3.0
example.com/b@v1.1.0 example.com/c@v1.1.0
example.com/b@v1.2.0 example.com/c@v1.2.0

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

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 中)推断模块路径。

go mod tidy

用法

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

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

-e 标志(Go 1.16 中添加)使 go mod tidy 尝试在加载包时遇到错误的情况下继续进行。

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

-x 标志使 go mod tidy 打印 tidy 执行的命令。

-diff 标志使 go mod tidy 不修改 go.mod 或 go.sum,而是将必要的更改打印为统一的 diff。如果 diff 不为空,则以非零代码退出。

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 之外的构建约束

启用 vendoring 后,go 命令将从 vendor 目录加载包,而不是从其源下载模块到模块缓存并使用那些下载的包。有关更多信息,请参阅Vendoring

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

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

-e 标志(Go 1.16 中添加)使 go mod vendor 尝试在加载包时遇到错误的情况下继续进行。

-v 标志使 go mod vendor 将 vendored 模块和包的名称打印到标准错误。

-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 会打印“all modules verified”。否则,它会报告哪些模块已更改并以非零状态退出。

请注意,所有模块感知命令都会验证主模块 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.mod 中的go 指令)的模块中,此标志在 Go 1.16 之后无效,因为 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 获得。

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

  • path: 用于构建可执行文件的 main 包的路径。
  • mod: 包含 main 包的模块。列分别是模块路径、版本和校验和。主模块的版本为 (devel),没有校验和。
  • dep: 提供一个或多个链接到可执行文件中的包的模块。格式与 mod 相同。
  • =>: 上一行的模块的替换。如果替换是本地目录,则只列出目录路径(无版本或校验和)。如果替换是模块版本,则列出路径、版本和校验和,与 moddep 相同。被替换的模块没有校验和。

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

版本查询可以是以下之一

  • 一个完全指定的语义版本,例如 v1.2.3,它选择一个特定版本。有关语法,请参阅版本
  • 语义版本前缀,例如 v1v1.2,它选择具有该前缀的最高可用版本。
  • 语义版本比较,例如 <v1.2.3>=v1.5.6,它选择最接近比较目标的可用版本(>>= 的最低版本,<<= 的最高版本)。
  • 底层源代码仓库的修订标识符,例如提交哈希前缀、修订标签或分支名称。如果修订用语义版本标记,则此查询选择该版本。否则,此查询选择底层提交的伪版本。请注意,名称与其他版本查询匹配的分支和标签不能以这种方式选择。例如,查询 v2 选择以 v2 开头的最新版本,而不是名为 v2 的分支。
  • 字符串 latest,它选择可用的最高发布版本。如果没有发布版本,latest 会选择最高的预发布版本。如果没有标记版本,latest 会选择仓库默认分支尖端提交的伪版本。
  • 字符串 upgrade,与 latest 类似,但如果模块当前要求的版本高于 latest 将选择的版本(例如,预发布版本),upgrade 将选择当前版本。
  • 字符串 patch,它选择与当前所需版本具有相同主版本和次版本号的最新可用版本。如果当前未要求任何版本,则 patch 等同于 latest。自 Go 1.16 以来,go get 在使用 patch 时需要当前版本(但 -u=patch 标志没有此要求)。

除了针对特定命名版本或修订的查询外,所有查询都考虑 go list -m -versions 报告的可用版本(请参阅go list -m)。此列表仅包含标记版本,不包含伪版本。主模块的go.mod 文件exclude 指令不允许的模块版本不予考虑。同一模块的 latest 版本的 go.mod 文件中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.mod 文件的情况下运行 go get 时没有主模块,因此不应用 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 文件

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

  • -fmt 标志重新格式化 go.work 文件而不进行其他更改。这种重新格式化也由任何其他使用或重写 go.work 文件的修改所隐含。此标志仅在未指定其他标志时才需要,例如“go work edit -fmt”。
  • -use=path-dropuse=path 标志从 go.work 文件的模块目录集中添加和删除 use 指令。
  • -replace=old[@v]=new[@v] 标志添加给定模块路径和版本对的替换。如果 old@v 中的 @v 被省略,则添加一个左侧没有版本的替换,它适用于旧模块路径的所有版本。如果 new@v 中的 @v 被省略,则新路径应为本地模块根目录,而不是模块路径。请注意,-replace 会覆盖 old[@v] 的任何冗余替换,因此省略 @v 将删除特定版本的现有替换。
  • -dropreplace=old[@v] 标志删除给定模块路径和版本对的替换。如果省略 @v,则删除左侧没有版本的替换。
  • -go=version 标志设置预期的 Go 语言版本。

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

go work edit 还有控制其输出的附加标志

  • -print 标志以其文本格式打印最终的 go.work,而不是将其写回 go.mod。
  • -json 标志以 JSON 格式打印最终的 go.work 文件,而不是以文本格式写回 go.mod。JSON 输出对应于这些 Go 类型
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 文件添加一个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 (Not Found) 和 410 (Gone) 表示请求的模块或版本在代理上不可用,但可能在其他地方找到。错误响应的内容类型应为 text/plain,字符集为 utf-8us-ascii

go 命令可以配置为使用 GOPROXY 环境变量联系代理或源代码管理服务器,该变量接受代理 URL 列表。该列表可以包含关键字 directoff(有关详细信息,请参阅环境变量)。列表元素可以通过逗号 (,) 或管道 (|) 分隔,这决定了错误回退行为。当 URL 后跟逗号时,go 命令仅在收到 404 (Not Found) 或 410 (Gone) 响应后才会回退到后续来源。当 URL 后跟管道时,go 命令会在任何错误(包括超时等非 HTTP 错误)后回退到后续来源。这种错误处理行为允许代理充当未知模块的看门人。例如,代理可以对不在批准列表上的模块响应错误 403 (Forbidden)(请参阅为私有模块提供服务的私有代理)。

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

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

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

返回关于模块特定版本的 JSON 格式元数据。响应必须是一个 JSON 对象,对应于下面的 Go 数据结构

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.mod 文件并执行最小版本选择 (MVS) 计算构建列表
  • 读取命令行上命名的包及其导入的包。
  • 如果包未由构建列表中的任何模块提供,则查找提供该包的模块。在 go.mod 中添加对其最新版本的模块要求,然后重新开始。
  • 所有内容加载后构建包。

go 命令计算构建列表时,它会加载模块图中每个模块的 go.mod 文件。如果 go.mod 文件不在缓存中,go 命令将使用 $module/@v/$version.mod 请求从代理下载它(其中 $module 是模块路径,$version 是版本)。这些请求可以使用 curl 等工具进行测试。例如,以下命令下载 golang.org/x/mod 版本 v0.2.0go.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/mod@v0.2.0/LICENSE
     1303  00-00-1980 00:00   golang.org/x/mod@v0.2.0/PATENTS
      559  00-00-1980 00:00   golang.org/x/mod@v0.2.0/README
       21  00-00-1980 00:00   golang.org/x/mod@v0.2.0/codereview.cfg
      214  00-00-1980 00:00   golang.org/x/mod@v0.2.0/go.mod
     1476  00-00-1980 00:00   golang.org/x/mod@v0.2.0/go.sum
     5224  00-00-1980 00:00   golang.org/x/mod@v0.2.0/gosumcheck/main.go

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

如果 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 响应中查找带有名称 go-import<meta> 标签。该标签的内容必须包含 仓库根路径、版本控制系统和 URL,并用空格分隔。有关详细信息,请参阅 为模块路径查找仓库

如果版本控制系统是 modgo 命令将使用 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 处的仓库,预期会在 bar 子目录中找到该模块。go 命令将根据版本控制工具支持的协议猜测要使用的协议。

如果模块路径没有限定符,go 命令会向从模块路径派生的 URL 发送 HTTP GET 请求,并带有一个 ?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 [subdirectory]">

root-path 是仓库根路径,即模块路径中对应于仓库根目录的部分,如果存在 subdirectory 且使用 Go 1.25 或更高版本(请参阅下面的 subdirectory 部分),则对应于 subdirectory。它必须是请求模块路径的前缀或精确匹配。如果不是精确匹配,则会为该前缀发出另一个请求以验证 <meta> 标签是否匹配。

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

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

如果存在 subdirectory,它是仓库的斜杠分隔的子目录,root-path 对应于该子目录,覆盖了仓库根目录的默认值。提供 subdirectorygo-import meta 标签仅由 Go 1.25 及更高版本识别。在早期 Go 版本上尝试获取解析模块将忽略 meta 标签,如果模块无法在其他地方解析,则会导致解析失败。

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

再次以 golang.org/x/mod 为例。go 命令会向 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/v2,后者将具有像 v2.0.0 这样的标签。

如果不存在 go.mod 文件并且模块位于仓库根目录中,则主要版本为 v2 或更高版本的标签可能属于没有主要版本后缀的模块。这种版本用后缀 +incompatible 表示。版本标签本身不能带有后缀。请参阅 与非模块仓库的兼容性

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

将伪版本映射到提交

go 命令可以签出仓库中特定修订版的模块,该修订版编码为 伪版本,例如 v1.3.2-0.20191109021931-daa7c04131f5

伪版本的最后 12 个字符(上例中的 daa7c04131f5)指示要在仓库中签出的修订版。这意味着取决于版本控制系统。对于 Git 和 Mercurial,这是提交哈希的前缀。对于 Subversion,这是零填充的修订号。

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

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

将分支和提交映射到版本

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

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),并且仅在私有模块或镜像拒绝提供公共包(通常出于法律原因)时才回退到版本控制。因此,客户端仍然可以通过 Go 模块镜像默认访问从 Bazaar、Fossil 或 Subversion 仓库提供的公共代码,因为这些下载使用 Go 模块镜像,它承担了使用自定义沙箱运行版本控制命令的安全风险。

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

GOVCS 变量的一般形式是以逗号分隔的 pattern:vcslist 规则列表。模式是 glob 模式,必须匹配模块或导入路径的一个或多个前导元素。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 文件及其在模块缓存中提取内容的 location。

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

文件路径和大小限制

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

  • 模块 zip 文件大小不得超过 500 MiB。其文件的总未压缩大小也限制为 500 MiB。go.mod 文件限制为 16 MiB。LICENSE 文件也限制为 16 MiB。这些限制旨在减轻对用户、代理和模块生态系统其他部分的拒绝服务攻击。在模块目录树中包含超过 500 MiB 文件的仓库应在仅包含构建模块包所需文件的提交处标记模块版本;视频、模型和其他大型资产通常不需要用于构建。
  • 模块 zip 文件中的每个文件都必须以 $module@$version/ 前缀开头,其中 $module 是模块路径,$version 是版本,例如 golang.org/x/mod@v0.3.0/。模块路径必须有效,版本必须有效且规范,并且版本必须与模块路径的主要版本后缀匹配。有关具体定义和限制,请参阅 模块路径和版本
  • 文件模式、时间戳和其他元数据将被忽略。
  • 空目录(路径以斜杠结尾的条目)可以包含在模块 zip 文件中,但不会被提取。go 命令在创建 zip 文件时不包含空目录。
  • 在创建 zip 文件时,符号链接和其他不规则文件将被忽略,因为它们不能跨操作系统和文件系统移植,并且在 zip 文件格式中没有可移植的方式来表示它们。
  • 在创建 zip 文件时,vendor 目录中的文件将被忽略,因为主模块之外的 vendor 目录从不使用。
  • 在创建 zip 文件时,除了模块根目录之外,包含 go.mod 文件的目录中的文件将被忽略,因为它们不属于该模块。go 命令在提取 zip 文件时会忽略包含 go.mod 文件的子目录。
  • zip 文件中没有两个文件的路径在 Unicode 大小写折叠下相等(请参阅 strings.EqualFold)。这确保 zip 文件可以在不区分大小写的文件系统上提取而不会发生冲突。
  • go.mod 文件可能出现在顶级目录($module@$version/go.mod)中,也可能不出现。如果存在,它必须命名为 go.mod(全部小写)。其他任何目录中都不允许名为 go.mod 的文件。
  • 模块中的文件和目录名可以由 Unicode 字母、ASCII 数字、ASCII 空格字符 (U+0020) 以及 ASCII 标点符号 !#$%&()+,-.=@[]^_{}~ 组成。请注意,包路径可能不包含所有这些字符。有关差异,请参阅 module.CheckFilePathmodule.CheckImportPath
  • 文件或目录名直到第一个点必须不是 Windows 上的保留文件名,无论大小写(例如 CONcom1NuL 等)。

私有模块

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

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

  • GOPROXY — 模块代理 URL 列表。go 命令将尝试按顺序从列表中的每个服务器下载模块。关键字 direct 指示 go 命令从它们开发的版本控制仓库下载模块,而不是使用代理。
  • GOPRIVATE — glob 模式的模块路径前缀列表,应被视为私有。作为 GONOPROXYGONOSUMDB 的默认值。
  • GONOPROXY — glob 模式的模块路径前缀列表,不应从代理下载。go 命令将从它们开发的版本控制仓库下载匹配的模块,无论 GOPROXY 如何。
  • GONOSUMDB — glob 模式的模块路径前缀列表,不应使用公共校验和数据库 sum.golang.org 检查。
  • GOINSECURE — glob 模式的模块路径前缀列表,可以通过 HTTP 和其他不安全协议检索。

这些变量可以在开发环境中设置(例如,在 .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:hunter2@proxy.corp.example.com

采用这种方法时请务必小心:环境变量可能会出现在 shell 历史记录和日志中。

向私有仓库传递凭据

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

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

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

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

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

[url "git@github.com:"]
    insteadOf = https://github.com/

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

隐私

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

GOPROXY 的默认值为

https://proxy.golang.org,direct

在此设置下,当 go 命令下载模块或模块元数据时,它将首先向 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.sum 中,否则 go 命令不会验证它们。请参阅 验证模块

模块缓存

模块缓存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 来验证哈希。zip 文件哈希计算成本高昂,因此 go 命令会检查与 zip 文件一起存储的预计算哈希,而不是重新哈希文件。go mod verify 命令可用于检查 zip 文件和提取的目录自添加到模块缓存以来是否未被修改。

go.sum 文件

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

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

  • 模块路径是哈希所属模块的名称。
  • 版本是哈希所属模块的版本。如果版本以 /go.mod 结尾,则哈希仅适用于模块的 go.mod 文件;否则,哈希适用于模块 .zip 文件中的文件。
  • 哈希列由算法名称(如 h1)和 base64 编码的加密哈希组成,用冒号 (:) 分隔。目前,SHA-256 (h1) 是唯一支持的哈希算法。如果将来发现 SHA-256 的漏洞,将添加对另一种算法(命名为 h2 等)的支持。

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

校验和数据库

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

校验和数据库为所有公共可用模块版本提供全球一致性和可靠性。它使得不受信任的代理成为可能,因为它们无法在不被注意的情况下提供错误的代码。它还确保与特定版本关联的位不会日复一日地更改,即使模块的作者随后更改了其仓库中的标签。

校验和数据库由 sum.golang.org 提供服务,该服务由 Google 运营。它是 go.sum 行哈希的 透明日志(或“Merkle 树”),并由 Trillian 支持。Merkle 树的主要优点是独立审计员可以验证它是否未被篡改,因此它比简单的数据库更值得信赖。

go 命令使用最初在 提案:保护公共 Go 模块生态系统中概述的协议与校验和数据库交互。

下表指定了校验和数据库必须响应的查询。对于每个路径,$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/text@v0.3.2

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

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

路径 描述
$base/latest 返回最新日志的签名、编码树描述。此签名描述采用 note 的形式,即由一个或多个服务器密钥签名并可使用服务器的公钥验证的文本。树描述提供树的大小和该大小下树头部的哈希。此编码在 golang.org/x/mod/sumdb/tlog#FormatTree 中描述。
$base/lookup/$module@$version 返回有关 $module$version 处的条目的日志记录号,后跟记录数据(即 $module$version 处的 go.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,或者如果使用 -insecure 标志调用 go get,则不查询校验和数据库,并且接受所有无法识别的模块,代价是放弃了所有模块验证可重复下载的安全保证。对于特定模块绕过校验和数据库的更好方法是使用 GOPRIVATEGONOSUMDB 环境变量。有关详细信息,请参阅 私有模块

go env -w 命令可用于为将来的 go 命令调用 设置这些变量

环境变量

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

变量 描述
GO111MODULE

控制 go 命令是在模块感知模式还是 GOPATH 模式下运行。识别三个值

  • offgo 命令忽略 go.mod 文件并在 GOPATH 模式下运行。
  • on(或未设置):go 命令在模块感知模式下运行,即使不存在 go.mod 文件。
  • 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 中的模式匹配定义)或其他匹配全局模式的模块的版本控制工具集。

如果未设置 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-prev1.2.3 之前。另请参阅发布版本

伪版本: 编码了修订标识符(例如 Git 提交哈希)和版本控制系统时间戳的版本。例如,v0.0.0-20191109021931-daa7c04131f5。用于与非模块仓库兼容以及在没有可用标记版本时的其他情况。

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

仓库根路径: 模块路径中对应版本控制仓库根目录的部分。请参阅模块路径

撤回版本: 不应依赖的版本,原因可能是发布过早,或发布后发现了严重问题。请参阅retract 指令

语义版本标签: 版本控制仓库中的标签,将版本映射到特定修订。请参阅将版本映射到提交

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

供应商目录: 名为 vendor 的目录,其中包含构建主模块中包所需的其他模块中的包。使用go mod vendor进行维护。请参阅供应商化

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

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