go.mod

未匹配的标注

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

module example.com/my/thing

go 1.12

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

指令一致的相邻行,可以用括号括起来,形成一个块,类似 Go 编码中的 import 语法:

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 程序可以使用golang.org/x/mod/modfile 软件包以编程方式进行相同的更改。

go.mod文件在两种场景下是必备的,第一种是 主模块 中,第二种是使用了本地路径的 替代模块(replace 指令)。然而,引用模块(被 require 的模块)中可以允许 go.mod 文件的缺失,这个规则同样适用于使用模块路径和版本的替代模块中。请参阅 非模块包的兼容性

词汇元素

当一个 go.mod 文件被解析时,它的内容被分解成一个标记序列。 有几种标记:空格,注释,标点符号,关键字,标识符和字符串。

空白 由空格 (U+0020),制表符 (U+0009),回车 (U+000D) 和换行符 (U+000A) 组成。 除了换行符之外的空白字符不表示任何作用,除非分隔符本来就是组合的标记。 另外换行符是重要的标记。

注释 是以 // 开头并运行到行尾。 其中 /* */ 注释是不允许的。

标点符号 包括 ()=>

Keywords (关键字) 在 go.mod 文件中区分不同种类的指令。 允许的关键字有 modulegorequirereplaceexcluderetract

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

Strings (字符串) 是带引号的字符序列。 其中有两种字符串类型:以引号开头和结尾的解释字符串(", 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 文件中的版本可以是 canonical 规范,也可以是非规范版本。规范版本以字母 v 开头,后跟语义版本控制2.0.0规范。有关详细信息,请参阅版本

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

在模块路径与版本相关联的地方(如 requirereplace,和 exclude 指令),最终路径元素必须与版本一致。请参阅版本

语法

go.mod 语法如下。有关 EBNF 语法的详细信息,请参阅Go语言规范

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

换行符,标识符和字符串用 newlineident, 和 string 表示。

模块路径和版本用 ModulePath 和 Version 表示。

ModulePath = ident | string . /* 参阅上面 */
Version = ident | string .    /* 参阅上面 */

module 指令

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

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

示例:

module golang.org/x/net

Deprecation

模块可以被标记为弃用,通过为模块添加以 Deprecated: (区分大小写) 开头的弃用注释。弃用信息写在冒号后边,直到末尾。弃用注释可以写在 module 指令的前一行或同一行的末尾。

示例:

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

从 Go 1.17 起,go list -m -u 会检查 编译列表 中所有的弃用模块信息。go get 则只会检查编译所需的弃用模块信息。

当 go 命令检索模块的弃用信息时,会加载 go.mod 文件中与 @latest 版本查询 相匹配的版本,而不考虑撤回或排除项。同时 go 命令也会从该 go.mod 文件中加载 撤回版本

需要弃用一个模块时,作者可以添加 // Deprecated: 注释并给出一个新的实现。作者可以在更高版本中修改或删除弃用信息。

弃用支持模块的所有次要版本。为实现这个功能,高于 v2 的主要版本被视为单独的模块,因为主要版本 major version suffixes 会有不同的模块路径。

弃用信息用于通知用户该模块不再提供维护并提供迁移说明。单个次要版本和补丁版本不可以弃用;它们更适合使用 回收

go 指令

go指令表示一个模块是按照给定的 Go 版本的语义来编写的。版本必须是有效的 Go 发布版本:一个正整数,后面跟着一个点和一个非负整数(例如,1.91.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 命令会根据 go 指令所指定的版本改变其行为。会有以下影响。

  • go 1.14或更高版本中,可以启用自动加载(详情见Vendoring)。如果文件 vendor/modules.txt 存在并与 go.mod 一致,就不需要明确使用 -mod=vendor 标志。
  • go 1.16或更高版本中,所有软件包模式只匹配由主模块中的软件包和测试导入的软件包(详情见 Main module)。这也是 go mod vendor 自引入模块以来所保留的软件包的集合。在较低的版本中,all 也包括由主模块中的包导入的包的测试,这些包的测试,依此类推。
  • go 1.17或更高版本:
    • go.mod文件包括一个明确的Require指令,为每个模块提供任何由主模块中的包或测试过渡导入的包。(在go 1.16和更低的版本中,只有在最低的版本会选择不同的版本时,才会包括间接依赖。) 这些额外的信息使得模块精简懒式加载成为可能。
    • 由于可能存在比以前的go版本更多的//间接依赖关系,间接依赖关系被记录在go.mod文件中的一个单独的块中。
    • go mod vendor省略了go.modgo.sum文件中的厂商依赖关系。(这允许在vendor的子目录中调用go命令,以识别正确的主模块)
    • go mod vendorvendor/modules.txt中记录每个依赖的go.mod文件的go版本。

一个 go.mod 文件最多可以包含一个 go 指令。如果没有,大多数命令会添加一个当前 Go 版本的go 指令。

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

例子:

go 1.14

require 指令

一个 require 指令声明了一个特定模块依赖的最低版本要求。对于每个所需的模块版本,go 命令加载该版本的 go.mod 文件,并纳入该文件中的需求。一旦所有需求被加载,go 命令使用最小版本选择(MVS)解决它们,以产生 构建列表

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

如果 go指令指定 go 1.16 或更低,当选择的模块版本高于主模块的其他依赖关系已经包含当前版本时,go 指令会增加一个间接需求。这可能是由于明确的升级(go get -u ./...),移除一些以前采用需求的依赖关系(go mod tidy),或者依赖关系导入的软件包在其自身的 go.mod 文件中没有相应的需求(例如依赖关系完全缺乏 go.mod 文件)。

go 1.17 及以上版本中,go 命令为每个模块增加了一个间接要求,即提供任何由主模块中的包或测试导入(即使是间接)或作为参数传递给go get 的包。这些更全面的要求实现了模块图精简懒式模块加载

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

例子:

require golang.org/x/net v1.2.3

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

exclude 指令

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

从 Go 1.16 开始,如果任何 go.mod 文件中的 require 指令所引用的版本被主模块的 go.mod 文件中的 exclude 指令所排除,则该需求被忽略。这可能会导致 go getgo mod tidy等命令在 go.mod 中添加更高版本的新需求,如果合适的话,会加上//间接注释。

在 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 文件,在其他模块中被忽略。详见最小版本选择

注意 replace 指令本身并不能将一个模块添加到模块图。还需要在主模块的 go.mod 文件或依赖模块的 go.mod 文件中添加一个指向被替换模块版本的require指令。如果左边的模块版本不是必需的,replace 指令就没有作用。

ReplaceDirective = "replace" ( ReplaceSpec | "(" newline { ReplaceSpec } ")" newline ) .
ReplaceSpec = ModulePath [ Version ] "=>" FilePath newline
            | ModulePath [ Version ] "=>" ModulePath Version newline .
FilePath = /* 特定平台的相对或绝对文件路径 */

例子:

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 get, go mod tidy 或其他命令自动升级到该版本。依赖于撤回版本的构建应该继续工作,但用户在使用 go list -m -u 检查更新或使用 go get 更新相关模块时,将被告知撤回的情况。

要撤回一个版本,模块作者应该在 go.mod 中添加一个 retract 指令,然后发布一个包含该指令的新版本。新版本必须高于其他发布或预发布的版本;也就是说,在考虑撤回之前,@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 中添加两个 retract 指令,然后用 retractions 标记v1.0.1

retract (
    v1.0.0 // 无意发布.
    v1.0.1 // 仅包含撤回.
)

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

retract 指令既可以写成单一版本(如 v1.0.0),也可以写成有上下限的封闭的版本区间,以 [] 为界(如 [v1.1.0, v1.2.0])。一个版本相当于一个上限和下限相同的区间。像其他指令一样,多个retract 指令可以组合在一个块中,由( 在一行的末尾和 ) 在它自己的行上分隔。

每个 retract 指令都应该有一个注释,可理解为撤回的理由,尽管这不是强制性的。go 命令可以在关于撤回版本的警告中和 go list 输出中显示理由注释。理由注释可以紧接着写在 retract 指令的上方(中间没有空行),也可以写在同一行的后面。如果注释出现在一个区块的上方,它适用于该区块内所有没有自己注释的 retract 指令。一个合理的注释可以横跨多行。

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

例子:

retract v1.0.0
retract [v1.0.0, v1.9.9]
retract (
    v1.0.0
    [v1.0.0, v1.9.9]
)

retract 指令是在 Go 1.16 中添加的。Go 1.15 及以前版本如果在主模块 go.mod 文件中写入 retract 指令,将报告错误,并忽略依赖的 go.mod 文件中的 retract 指令。

自动更新

如果 go.mod 缺少信息或不能准确反映实际,大多数命令都会报告错误。go getgo mod tidy命令可以用来解决大部分的问题。此外, -mod=mod 标识可用于大多数模块识别命令(go buildgo test,等等),以指示 go 命令自动修复 go.mod 和 go.sum 中的问题。

例如,思考这个 go.mod 文件:

module example.com/M

go 1.16

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

exclude example.com/D v1.2.3

-mod=mod 触发的更新将非经典的版本标识符重写为canonical semver形式,所以 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.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 。

因为模块图定义了导入语句的含义, 因此加载包的任何命令使用 go.mod 都可以更新它, 包括 go buildgo getgo installgo listgo testgo mod tidy.

在Go 1.15及更低版本中, 默认情况下启用了 -mod=mod , 因此自动执行更新. 自 Go 1.16 以来,  go 命令用作设置 -mod=readonly : 如果对 go.mod 命令进行任何更改, 则 go 命令报告错误并建议修复.

本文章首发在 LearnKu.com 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://learnku.com/docs/go-mod/1.17/go-...

译文地址:https://learnku.com/docs/go-mod/1.17/go-...

上一篇 下一篇
贡献者:9
讨论数量: 0
发起讨论 只看当前版本


暂无话题~