1. 模块由其根目录中名为go.modUTF-8编码文本文件定义。
  2. go.mod文件是面向行的。每行包含一个指令,由关键字后跟参数组成。例如:
1
2
3
4
5
6
7
8
9
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]
  1. 前导关键字可以从相邻行中分解出来以创建一个块,就像在Go import中一样。
1
2
3
4
require (
    example.com/new/thing/v2 v2.3.4
    example.com/old/thing v1.2.3
)
  1. go.mod文件被设计为人类可读和机器可写。go命令提供了几个更改go.mod文件的子命令。例如:
    • go get可以升级或降级特定的依赖项。
    • 加载模块图的命令将在需要时自动更新go.mod
    • go mod edit可以执行低级编辑。
  2. 主模块以及使用本地文件路径指定的任何替换指令替换都需要一个go.mod文件。但是,缺少明确的go.mod文件的模块可能仍然需要作为依赖项,或者用作使用模块。

词汇元素

  1. 对一个go.mod文件进行分析,其内容被分成标记形式。
  2. 有几种标记:空格、注释、标点符号、关键字、标识符和字符串。
    1. 空白:由空格(U+0020)、制表符(U+0009)、回车(U+000D)和换行符(U+000A)组成。
      • 除了换行符之外的空白字符没有任何作用,除非分隔本来与其他组合的标记。
      • 换行符是重要的标记。
    2. 注释:以//开始到行尾。/**/注释不允许添加注释文案。
    3. 标点符号:包括(,)和=>
    4. 关键字:是区分go.mod文件中不同种类的指令。
      • 允许的关键字modulegorequirereplaceexcluderetract
    5. 标识符:非空白字符的序列,例如模块路径或语义版本。
    6. 字符串:带引号的字符序列,有两种字符串:
      • 以引号(",U+0022)开头和结尾的解释字符串。
        • 解释的字符串可能包含由反斜杠(\,U+005C)后跟另一个字符组成的转义序列。
        • 转义引号(\")不会终止已解释的字符串。
        • 解释字符串的不带引号的值是引号之间的字符序列,每个转义序列都被反斜杠后面的字符\“替换(例如,被替换为”,\n被替换为n)。
      • 以重音符 (`,U+0060)开头和结尾的原始字符串。
        • 相比之下,原始字符串的不带引号的值只是重音符之间的字符序列,反斜杠在原始字符串中没有特殊含义。
  3. 标识符和字符串在go.mod语法中是可以互换的。

模块路径和版本

  1. go.mod文件中的大多数标识符和字符串要么是模块路径,要么是版本。
  2. 模块路径必须满足以下要求:
    • 路径必须由一个或多个以斜线(/,U+002F)分隔的路径元素组成,它不能以斜线开头或结尾。
    • 每个路径是由多个ASCII字母,ASCII数字或非空字符串,和有限的ASCII标点符号(-._~)。
    • 路径元素不能以点(., U+002E)开始或结束。
    • 直到第一个点的元素前缀不能是Windows上的保留文件名,无论大小写(CONcom1NuL等),不能以con.com1.Nul.等开头。
    • 第一个点之前的元素前缀不得以波浪号(~)后跟一个或多个数字(如EXAMPL~1.COM)结尾。
  3. 如果模块路径出现在require指令中并且没有被替换,或者模块路径出现在replace指令的右侧,则该go命令可能需要下载具有该路径的模块,并且必须满足一些附加要求。
    • 前导路径元素(直到第一个斜杠,如果有的话),按照约定,域名必须只包含小写ASCII字母、ASCII数字、点(.,U+002E)和破折号(-,U+002D)它必须至少包含一个点,并且不能以破折号开头。
    • 对于形式的最终路径元素/vN在那里N是数字(ASCII数字和点),N不得带前导零的开始,一定不能/v1,一定不能包含任何圆点。
      • 对于以gopkg.in/开头的路径,此要求将替换为路径遵循gopkg.in服务约定的要求。
  4. go.mod文件中的版本可能是规范的或非规范的,规范版本以字母v开头,后跟符合语义版本控制2.0.0规范的语义版本。
  5. 非规范版本只允许在主模块的go.mod文件中,该go命令将尝试时,它会自动用等效的规范版本替换每个非规范版本更新的go.mod文件。
  6. 在一个模块路径与版本相关的地方(如requirereplaceexclude指令),最后的路径元素必须与版本是一致的,请参阅主要版本后缀

语法

  1. go.mod下面使用扩展巴科斯-诺尔形式(EBNF)指定语法。有关EBNF语法的详细信息,请参阅Go语言规范中的符号部分。
1
2
3
4
5
6
7
GoMod = { Directive } .
Directive = ModuleDirective |
            GoDirective |
            RequireDirective |
            ExcludeDirective |
            ReplaceDirective |
            RetractDirective .
  1. 换行,标识符和字符串分别标注newlineidentstring
  2. 模块路径和版本用ModulePath和表示Version
1
2
ModulePath = ident | string . /* see restrictions above */
Version = ident | string .    /* see restrictions above */

module模块指令

  1. 一个module指令定义了主模块的路径。一个go.mod文件必须只包含一个module指令。
  2. module指令定义了模块名称:
    • ModuleDirective = "module" ( ModulePath | "(" newline ModulePath newline ")" ) newline .
  3. 示例:
module golang.org/x/net

Deprecated:弃用标志
  1. 可以在Deprecated:段落开头包含字符串(区分大小写)的注释块中将模块标记为已弃用。
  2. 弃用消息在冒号之后开始并运行到段落的末尾。注释可能出现在module指令之前或之后出现在同一行。
  3. 示例:
// Deprecated: 改用 example.com/mod/v2
module example.com/mod
  1. 从Go 1.17开始,go list -m -u检查构建列表中所有弃用模块的信息。go get检查构建在命令行上命名的包所需的不推荐使用的模块。
  2. 当该go命令检索模块的弃用信息时,它会从与@latest版本查询匹配的版本加载go.mod文件,而不考虑撤回的或排除的。go命令从同一个go.mod文件加载撤回。
  3. 要弃用模块,作者可以添加// Deprecated:评论并标记新版本。作者可能会在更高版本中更改或删除弃用消息。
  4. 弃用适用于模块的所有次要版本。以v2为例,主版本高于被认为是单独的模块,因为它们的主版本后缀为它们提供了不同的模块路径。
  5. 弃用消息旨在通知用户该模块不再受支持并提供迁移说明,例如迁移到最新的主要版本。不能弃用单个次要版本和补丁版本,retract可能更适合单个版本。

go 版本指令

  1. 一个go指令表明一个模块是在假设Go的给定版本的语义的情况下编写的。
  2. 版本必须是有效的Go发布版本:一个正整数,后跟一个点和一个非负整数(例如,1.9,1.14)。示例:go 1.14
  3. go指令最初旨在支持Go语言的向后不兼容更改(请参阅Go 2 transition)。
  4. 自引入模块以来,没有发生不兼容的语言更改,但该go指令仍然影响新语言功能的使用:
    1. 对于模块内的包,编译器拒绝使用在go指令指定的版本之后引入的语言功能。例如,如果一个模块有指令go 1.12,它的包可能不会使用像1_000_000 Go 1.13中引入的数字文字。
    2. 如果较旧的Go版本构建模块的包之一并遇到编译错误,则该错误会指出该模块是为较新的Go版本编写的。假设一个模块有go 1.13一个包使用数字文字1_000_000。如果该包是用Go 1.12构建的,编译器会注意到代码是为Go 1.13编写的。
    3. go 1.17或更高版本:
      • go.mod文件为每个模块包含一个显式的require指令,该指令提供由主模块中的包或测试传递导入的任何包。(在go 1.16及更低版本中,仅当最小版本选择会选择不同版本时才包含间接依赖项。)此额外信息启用模块图修剪和延迟模块加载。
      • 由于可能存在// indirect比以前go版本更多的依赖项 ,因此间接依赖项记录在go.mod文件中的单独块中。
      • go mod vendor省略了vendor依赖的go.mod和go.sum文件。(这允许在vendor的子目录中调用go命令来识别正确的主模块。)
      • go mod vendorgo每个依赖项go.mod文件的版本记录在vendor/modules.txt
  5. 一个go.mod文件最多可以包含一个go指令。如果当前Go版本不存在,大多数命令会添加一个go指令。
1
2
GoDirective = "go" GoVersion newline .
GoVersion = string | ident .  /* valid release version; see above */

require 需求指令

  1. 一个require指令声明一个给定的模块依赖的最低版本。对于每个所需的模块版本,go命令加载该版本的go.mod文件并合并来自该文件的require。加载所有require后,go命令将使用最小版本选择 (MVS)解决它们以生成构建列表。
  2. go命令会自动为某些require添加// indirect间接依赖注释。// indirect表示主模块中的任何包都不会直接导入所需模块中的包。
  3. 如果go指令指定go 1.16或更低版本,则当所选模块的版本高于主模块的其他依赖项已经暗示(传递)的版本时,go命令会添加一个indirect。这可能是由于显式升级(go get -u ./...)、删除了先前强加要求的其他依赖项(go mod tidy),或者导入了一个包而没有相应要求的依赖项go.mod文件(例如完全缺少go.mod文件的依赖项)
  4. go 1.17及更高版本中,go命令为每个模块添加了一个indirect,该模块提供由主模块中的包或测试导入(甚至indirect)的任何包,或作为参数传递给go get。 这些更全面的要求支持模块图修剪和延迟模块加载。
1
2
RequireDirective = "require" ( RequireSpec | "(" newline { RequireSpec } ")" newline ) .
RequireSpec = ModulePath Version newline .
  1. 示例:
1
2
3
4
5
6
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 排除指令

  1. exclude指令防止模块版本被go命令加载。(排除指定的模块版本)
  2. Go 1.16开始,如果任何go.mod文件中的require指令引用的版本被主模块的go.mod文件中的exclude指令排除,则该require将被忽略。
  3. 这可能会导致像go getgo mod tidy这样的命令将更高版本的新要求添加到go.mod,如果合适,带有// indirect
  4. Go 1.16之前,如果一个排除的版本被require指令引用,go命令会列出模块的可用版本(如go list -m -versions所示)并加载下一个更高的非排除版本。这可能导致不确定的版本选择,因为下一个更高的版本可能会随着时间的推移而改变。为此目的,发行版和预发行版都被考虑在内,但伪版本则不然。如果没有更高版本,go命令报错。
  5. exclude指令仅适用于主模块的go.mod文件,在其他模块中被忽略。有关详细信息,请参阅最小版本选择。
1
2
ExcludeDirective = "exclude" ( ExcludeSpec | "(" newline { ExcludeSpec } ")" newline ) .
ExcludeSpec = ModulePath Version newline .
  1. 示例:
1
2
3
4
5
6
exclude golang.org/x/net v1.2.3

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

replace 替换指令

  1. replace指令用在别处找到的内容替换模块的特定版本或模块的所有版本的内容。以使用另一个模块路径和版本或特定于平台的文件路径来指定替换。
  2. 如果箭头左侧(=>)存在某个版本,则仅替换该模块的特定版本。其他版本将正常访问。如果省略左版本,则替换模块的所有版本。
  3. 如果箭头右侧的路径是绝对或相对路径(以./../开头),则解释为替换模块根目录的本地文件路径,必须包含一个go.mod文件。在这种情况下必须省略替换版本。
  4. 如果右侧的路径不是本地路径,则必须是有效的模块路径。在这种情况下,需要一个版本。相同的模块版本不得出现在构建列表中。
  5. 不管替换是用本地路径还是模块路径指定的,如果替换模块有go.mod文件,它的module指令必须匹配它替换的模块路径。
  6. replace指令仅适用于主模块的go.mod文件,在其他模块中被忽略。有关详细信息,请参阅最小版本选择。
  7. 如果有多个主模块,则应用所有主模块的 go.mod 文件。 不允许跨主模块冲突的替换指令,并且必须在 go.work 文件的替换中删除或覆盖。
  8. 请注意,单独的replace指令不会将模块添加到模块图中。在主模块的go.mod文件或依赖项的go.mod文件中,还需要引用替换模块版本的require指令。如果不需要左侧的模块版本,则替换指令无效。
1
2
3
4
ReplaceDirective = "replace" ( ReplaceSpec | "(" newline { ReplaceSpec } ")" newline ) .
ReplaceSpec = ModulePath [ Version ] "=>" FilePath newline
            | ModulePath [ Version ] "=>" ModulePath Version newline .
FilePath = /* platform-specific relative or absolute file path */
  1. 示例:
1
2
3
4
5
6
7
8
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 撤回指令

  1. retract指令指示不应依赖由go.mod定义的模块的版本或版本范围。当版本过早发布或版本发布后发现严重问题时,撤回指令很有用。撤回的版本应该在版本控制存储库和模块代理中保持可用,以确保依赖于它们的构建不会被破坏。撤回这个词是从学术文献中借来的:被撤回的研究论文仍然可用,但它有问题,不应作为未来工作的基础。
  2. 当模块版本被撤回时,用户不会使用go get、go mod tidy或其他命令自动升级到它。依赖于撤回版本的构建应该继续工作,但是当用户使用go list -m -u检查更新或使用go get更新相关模块时,会收到撤回通知。
  3. 要撤回一个版本,模块作者应该向go.mod添加一个撤回指令,然后发布一个包含该指令的新版本。新版本必须高于其他发布或预发布版本。也就是说,在考虑撤回之前,@latest版本查询应该解析为新版本。go命令从go list -m -retracted $modpath@latest(其中$modpath是模块路径)显示的版本加载并应用撤回。
  4. 除非使用-retracted标志,否则从go list -m -versions打印的版本列表中隐藏已撤回的版本。解析@>=v1.2.3@latest之类的版本查询时,将排除撤回的版本。
  5. 包含撤回的版本可能会撤回自己。如果模块的最高发行版或预发行版自行收回,则在排除收回的版本后,@latest查询将解析为较低版本。
  6. 例如,考虑模块example.com/m的作者意外发布版本v1.0.0的情况。为了防止用户升级到v1.0.0,作者可以在go.mod中添加两个撤消指令,然后将撤回标记为v1.0.1
1
2
3
4
retract (
    v1.0.0 // 意外发布
    v1.0.1 // 仅包含撤回
)
  1. 当用户运行go get example.com/m@latest时,go命令从v1.0.1读取撤回,现在是最高版本。v1.0.0v1.0.1都已撤回,因此go命令将升级(或降级!)到下一个最高版本,可能是v0.9.5
  2. 撤回指令可以使用单个版本(如v1.0.0)或具有上下限的封闭区间版本编写,由[]分隔(如[v1.1.0, v1.2.0])。单个版本相当于上下限相同的区间。像其他指令一样,多个撤回指令可以组合在一个块中,由(在一行的末尾和)在它自己的行上分隔。
  3. 每个撤回指令都应该有一个注释来解释撤回的理由,尽管这不是强制性的。go命令可能会在有关撤回版本的警告和go列表输出中显示撤回注释。撤回注释可以写在撤回指令的正上方(中间没有空行),也可以写在同一行之后。如果注释出现在块上方,则它适用于块内没有自己注释的所有撤回指令。撤回注释可能跨越多行。
1
2
RetractDirective = "retract" ( RetractSpec | "(" newline { RetractSpec } ")" newline ) .
RetractSpec = ( Version | "[" Version "," Version "]" ) newline .
  1. 示例:
1
2
3
4
5
6
retract v1.0.0
retract [v1.0.0, v1.9.9]
retract (
    v1.0.0
    [v1.0.0, v1.9.9]
)
  1. Retract指令是在Go 1.16中添加的。如果在主模块的go.mod文件中写入了撤回指令,则Go 1.15及更低版本将报告错误,并且将忽略依赖项的go.mod文件中的撤回指令。

自动更新

  1. 如果go.mod缺少信息或不能准确反映现实,大多数命令都会报告错误。go get和go mod tidy命令可用于解决大多数这些问题。
  2. 此外,-mod=mod标志可以与大多数模块感知命令(go buildgo test等)一起使用,以指示go命令自动修复go.modgo.sum中的问题。
  3. 例如,考虑这个go.mod文件:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
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
  1. 使用-mod=mod触发的更新将非规范版本标识符重写为规范semver形式,因此example.com/Av1变为v1.0.0example.com/Edev成为dev上最新提交的伪版本分支,可能是v0.0.0-20180523231146-b3f5c0f6e5f1
  2. 该更新修改了要求以遵守排除规则,因此对排除的example.com/D v1.2.3的require更新为使用example.com/D的下一个可用版本,可能是v1.2.4v1.3.0
  3. 此更新删除了多余或误导性的要求。例如,如果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/A需要v1.2.0),并且它对example.com/C v1.0.0的要求是多余的(暗示example.com/A需要相同版本),因此两者都将被删除。如果主模块包含直接从example.com/Bexample.com/C导入包的包,那么需求将被保留但更新到正在使用的实际版本。
  4. 最后,更新以规范格式重新格式化go.mod,以便未来的机械更改将导致最小差异。 如果只需要更改格式,go命令将不会更新go.mod
  5. 因为模块图定义了import语句的含义,所以任何加载包的命令也使用go.mod,因此可以更新它,包括go buildgo getgo installgo listgo testgo mod tidy
  6. Go 1.15及更低版本中,默认启用-mod=mod标志,因此会自动执行更新。从Go 1.16开始,go命令的行为就像设置了-mod=readonly一样,如果需要对go.mod进行任何更改,则go命令会报告错误并建议修复。