- 模块由其根目录中名为
go.mod
,UTF-8
编码文本文件定义。
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]
|
- 前导关键字可以从相邻行中分解出来以创建一个块,就像在Go import中一样。
1
2
3
4
|
require (
example.com/new/thing/v2 v2.3.4
example.com/old/thing v1.2.3
)
|
go.mod
文件被设计为人类可读和机器可写。go命令提供了几个更改go.mod
文件的子命令。例如:
go get
可以升级或降级特定的依赖项。
- 加载模块图的命令将在需要时自动更新
go.mod
。
go mod edit
可以执行低级编辑。
- 主模块以及使用本地文件路径指定的任何替换指令替换都需要一个
go.mod
文件。但是,缺少明确的go.mod
文件的模块可能仍然需要作为依赖项,或者用作使用模块。
词汇元素#
- 对一个
go.mod
文件进行分析,其内容被分成标记形式。
- 有几种标记:空格、注释、标点符号、关键字、标识符和字符串。
- 空白:由空格(U+0020)、制表符(U+0009)、回车(U+000D)和换行符(U+000A)组成。
- 除了换行符之外的空白字符没有任何作用,除非分隔本来与其他组合的标记。
- 换行符是重要的标记。
- 注释:以
//
开始到行尾。/**/
注释不允许添加注释文案。
- 标点符号:包括(
,
)和=>
。
- 关键字:是区分
go.mod
文件中不同种类的指令。
- 允许的关键字
module
,go
,require
,replace
,exclude
,retract
。
- 标识符:非空白字符的序列,例如模块路径或语义版本。
- 字符串:带引号的字符序列,有两种字符串:
- 以引号(
"
,U+0022)开头和结尾的解释字符串。
- 解释的字符串可能包含由反斜杠(
\
,U+005C)后跟另一个字符组成的转义序列。
- 转义引号(
\"
)不会终止已解释的字符串。
- 解释字符串的不带引号的值是引号之间的字符序列,每个转义序列都被反斜杠后面的字符\“替换(例如,被替换为”,
\n
被替换为n)。
- 以重音符 (`,U+0060)开头和结尾的原始字符串。
- 相比之下,原始字符串的不带引号的值只是重音符之间的字符序列,反斜杠在原始字符串中没有特殊含义。
- 标识符和字符串在
go.mod
语法中是可以互换的。
模块路径和版本#
go.mod
文件中的大多数标识符和字符串要么是模块路径,要么是版本。
- 模块路径必须满足以下要求:
- 路径必须由一个或多个以斜线(
/
,U+002F)分隔的路径元素组成,它不能以斜线开头或结尾。
- 每个路径是由多个ASCII字母,ASCII数字或非空字符串,和有限的ASCII标点符号(
-
、.
、_
、~
)。
- 路径元素不能以点(
.
, U+002E)开始或结束。
- 直到第一个点的元素前缀不能是Windows上的保留文件名,无论大小写(
CON
、com1
、NuL
等),不能以con.
或com1.
或Nul.
等开头。
- 第一个点之前的元素前缀不得以波浪号(
~
)后跟一个或多个数字(如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
文件。
- 在一个模块路径与版本相关的地方(如
require
,replace
和exclude
指令),最后的路径元素必须与版本是一致的,请参阅主要版本后缀。
go.mod
下面使用扩展巴科斯-诺尔形式(EBNF)指定语法。有关EBNF语法的详细信息,请参阅Go语言规范中的符号部分。
1
2
3
4
5
6
7
|
GoMod = { Directive } .
Directive = ModuleDirective |
GoDirective |
RequireDirective |
ExcludeDirective |
ReplaceDirective |
RetractDirective .
|
- 换行,标识符和字符串分别标注
newline
,ident
和 string
。
- 模块路径和版本用
ModulePath
和表示Version
。
1
2
|
ModulePath = ident | string . /* see restrictions above */
Version = ident | string . /* see restrictions above */
|
module
模块指令#
- 一个
module
指令定义了主模块的路径。一个go.mod
文件必须只包含一个module
指令。
module
指令定义了模块名称:
ModuleDirective = "module" ( ModulePath | "(" newline ModulePath newline ")" ) newline .
- 示例:
Deprecated
:弃用标志
- 可以在
Deprecated:
段落开头包含字符串(区分大小写)的注释块中将模块标记为已弃用。
- 弃用消息在冒号之后开始并运行到段落的末尾。注释可能出现在
module
指令之前或之后出现在同一行。
- 示例:
// Deprecated: 改用 example.com/mod/v2
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.9
,1.14
)。示例:go 1.14
。
go
指令最初旨在支持Go语言的向后不兼容更改(请参阅Go 2 transition)。
- 自引入模块以来,没有发生不兼容的语言更改,但该
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 1.17
或更高版本:
go.mod
文件为每个模块包含一个显式的require
指令,该指令提供由主模块中的包或测试传递导入的任何包。(在go 1.16
及更低版本中,仅当最小版本选择会选择不同版本时才包含间接依赖项。)此额外信息启用模块图修剪和延迟模块加载。
- 由于可能存在
// indirect
比以前go
版本更多的依赖项 ,因此间接依赖项记录在go.mod
文件中的单独块中。
go mod vendor
省略了vendor依赖的go.mod和go.sum文件。(这允许在vendor的子目录中调用go命令来识别正确的主模块。)
go mod vendor
将go
每个依赖项go.mod
文件的版本记录在vendor/modules.txt
。
- 一个
go.mod
文件最多可以包含一个go
指令。如果当前Go版本不存在,大多数命令会添加一个go
指令。
1
2
|
GoDirective = "go" GoVersion newline .
GoVersion = string | ident . /* valid release version; see above */
|
require
需求指令#
- 一个
require
指令声明一个给定的模块依赖的最低版本。对于每个所需的模块版本,go
命令加载该版本的go.mod
文件并合并来自该文件的require
。加载所有require
后,go
命令将使用最小版本选择 (MVS)解决它们以生成构建列表。
go
命令会自动为某些require添加// indirect
间接依赖注释。// indirect
表示主模块中的任何包都不会直接导入所需模块中的包。
- 如果
go
指令指定go 1.16
或更低版本,则当所选模块的版本高于主模块的其他依赖项已经暗示(传递)的版本时,go命令会添加一个indirect
。这可能是由于显式升级(go get -u ./...
)、删除了先前强加要求的其他依赖项(go mod tidy
),或者导入了一个包而没有相应要求的依赖项go.mod
文件(例如完全缺少go.mod
文件的依赖项)
- 在
go 1.17
及更高版本中,go命令为每个模块添加了一个indirect
,该模块提供由主模块中的包或测试导入(甚至indirect
)的任何包,或作为参数传递给go get
。 这些更全面的要求支持模块图修剪和延迟模块加载。
1
2
|
RequireDirective = "require" ( RequireSpec | "(" newline { RequireSpec } ")" newline ) .
RequireSpec = ModulePath Version newline .
|
- 示例:
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
排除指令#
exclude
指令防止模块版本被go
命令加载。(排除指定的模块版本)
- 从
Go 1.16
开始,如果任何go.mod
文件中的require
指令引用的版本被主模块的go.mod
文件中的exclude
指令排除,则该require
将被忽略。
- 这可能会导致像
go get
和go mod tidy
这样的命令将更高版本的新要求添加到go.mod
,如果合适,带有// indirect
。
- 在
Go 1.16
之前,如果一个排除的版本被require
指令引用,go
命令会列出模块的可用版本(如go list -m -versions所示)并加载下一个更高的非排除版本。这可能导致不确定的版本选择,因为下一个更高的版本可能会随着时间的推移而改变。为此目的,发行版和预发行版都被考虑在内,但伪版本则不然。如果没有更高版本,go
命令报错。
exclude
指令仅适用于主模块的go.mod
文件,在其他模块中被忽略。有关详细信息,请参阅最小版本选择。
1
2
|
ExcludeDirective = "exclude" ( ExcludeSpec | "(" newline { ExcludeSpec } ")" newline ) .
ExcludeSpec = ModulePath Version newline .
|
- 示例:
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
替换指令#
replace
指令用在别处找到的内容替换模块的特定版本或模块的所有版本的内容。以使用另一个模块路径和版本或特定于平台的文件路径来指定替换。
- 如果箭头左侧(
=>
)存在某个版本,则仅替换该模块的特定版本。其他版本将正常访问。如果省略左版本,则替换模块的所有版本。
- 如果箭头右侧的路径是绝对或相对路径(以
./
或../
开头),则解释为替换模块根目录的本地文件路径,必须包含一个go.mod
文件。在这种情况下必须省略替换版本。
- 如果右侧的路径不是本地路径,则必须是有效的模块路径。在这种情况下,需要一个版本。相同的模块版本不得出现在构建列表中。
- 不管替换是用本地路径还是模块路径指定的,如果替换模块有
go.mod
文件,它的module
指令必须匹配它替换的模块路径。
replace
指令仅适用于主模块的go.mod
文件,在其他模块中被忽略。有关详细信息,请参阅最小版本选择。
- 如果有多个主模块,则应用所有主模块的 go.mod 文件。 不允许跨主模块冲突的替换指令,并且必须在 go.work 文件的替换中删除或覆盖。
- 请注意,单独的
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
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
撤回指令#
retract
指令指示不应依赖由go.mod
定义的模块的版本或版本范围。当版本过早发布或版本发布后发现严重问题时,撤回指令很有用。撤回的版本应该在版本控制存储库和模块代理中保持可用,以确保依赖于它们的构建不会被破坏。撤回这个词是从学术文献中借来的:被撤回的研究论文仍然可用,但它有问题,不应作为未来工作的基础。
- 当模块版本被撤回时,用户不会使用go get、go mod tidy或其他命令自动升级到它。依赖于撤回版本的构建应该继续工作,但是当用户使用go list -m -u检查更新或使用
go get
更新相关模块时,会收到撤回通知。
- 要撤回一个版本,模块作者应该向
go.mod
添加一个撤回指令,然后发布一个包含该指令的新版本。新版本必须高于其他发布或预发布版本。也就是说,在考虑撤回之前,@latest
版本查询应该解析为新版本。go
命令从go list -m -retracted $modpath@latest
(其中$modpath
是模块路径)显示的版本加载并应用撤回。
- 除非使用
-retracted
标志,否则从go list -m -versions打印的版本列表中隐藏已撤回的版本。解析@>=v1.2.3
或@latest
之类的版本查询时,将排除撤回的版本。
- 包含撤回的版本可能会撤回自己。如果模块的最高发行版或预发行版自行收回,则在排除收回的版本后,
@latest
查询将解析为较低版本。
- 例如,考虑模块
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 // 仅包含撤回
)
|
- 当用户运行
go get example.com/m@latest
时,go
命令从v1.0.1
读取撤回,现在是最高版本。v1.0.0
和v1.0.1
都已撤回,因此go
命令将升级(或降级!)到下一个最高版本,可能是v0.9.5
。
- 撤回指令可以使用单个版本(如
v1.0.0
)或具有上下限的封闭区间版本编写,由[
和]
分隔(如[v1.1.0, v1.2.0]
)。单个版本相当于上下限相同的区间。像其他指令一样,多个撤回指令可以组合在一个块中,由(在一行的末尾和)在它自己的行上分隔。
- 每个撤回指令都应该有一个注释来解释撤回的理由,尽管这不是强制性的。
go
命令可能会在有关撤回版本的警告和go
列表输出中显示撤回注释。撤回注释可以写在撤回指令的正上方(中间没有空行),也可以写在同一行之后。如果注释出现在块上方,则它适用于块内没有自己注释的所有撤回指令。撤回注释可能跨越多行。
1
2
|
RetractDirective = "retract" ( RetractSpec | "(" newline { RetractSpec } ")" newline ) .
RetractSpec = ( Version | "[" Version "," Version "]" ) newline .
|
- 示例:
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]
)
|
Retract
指令是在Go 1.16
中添加的。如果在主模块的go.mod
文件中写入了撤回指令,则Go 1.15
及更低版本将报告错误,并且将忽略依赖项的go.mod
文件中的撤回指令。
自动更新#
- 如果
go.mod
缺少信息或不能准确反映现实,大多数命令都会报告错误。go get和go mod tidy命令可用于解决大多数这些问题。
- 此外,
-mod=mod
标志可以与大多数模块感知命令(go build
、go test
等)一起使用,以指示go
命令自动修复go.mod
和go.sum
中的问题。
- 例如,考虑这个
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
|
- 使用
-mod=mod
触发的更新将非规范版本标识符重写为规范semver
形式,因此example.com/A
的v1
变为v1.0.0
,example.com/E
的dev
成为dev
上最新提交的伪版本分支,可能是v0.0.0-20180523231146-b3f5c0f6e5f1
。
- 该更新修改了要求以遵守排除规则,因此对排除的
example.com/D v1.2.3
的require更新为使用example.com/D
的下一个可用版本,可能是v1.2.4
或v1.3.0
。
- 此更新删除了多余或误导性的要求。例如,如果
example.com/A v1.0.0
本身需要example.com/B v1.2.0
和example.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/B
或example.com/C
导入包的包,那么需求将被保留但更新到正在使用的实际版本。
- 最后,更新以规范格式重新格式化
go.mod
,以便未来的机械更改将导致最小差异。 如果只需要更改格式,go
命令将不会更新go.mod
。
- 因为模块图定义了
import
语句的含义,所以任何加载包的命令也使用go.mod
,因此可以更新它,包括go build
、go get
、go install
、go list
、go test
、go mod tidy
。
- 在
Go 1.15
及更低版本中,默认启用-mod=mod
标志,因此会自动执行更新。从Go 1.16
开始,go
命令的行为就像设置了-mod=readonly
一样,如果需要对go.mod
进行任何更改,则go
命令会报告错误并建议修复。