与非模块存储库的兼容性

Compatibility with non-module repositories 与非模块存储库的兼容性

原文:https://go.dev/ref/mod#non-module-compat

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

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

​ 当go命令从proxy(代理)下载模块时,它将go.mod文件与其余模块内容分开下载。如果原始模块没有go.mod文件,那么代理将提供一个合成的go.mod文件。

+incompatible versions

​ 在主版本2或更高版本发布的模块必须在其模块路径上有一个匹配的major version suffix(主版本后缀)。例如,如果一个模块是以v2.0.0发布的,其路径必须有/v2的后缀。这允许 go 命令可以将一个项目的多个主版本视为不同的模块,即使它们是在同一个存储库中开发的。

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

​ 考虑下面的示例需求:

require example.com/m v4.1.2+incompatible

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

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

​ 注意 +incompatible 后缀不应该出现在存储库的标签上;像 v4.1.2+incompatible 这样的标签会被忽略。这个后缀只出现在 go 命令所使用的版本中。关于版本和标签之间的区别,请参见Mapping versions to commits(将版本映射到提交)

​ 还要注意的是,+incompatible 后缀可能会出现在pseudo-versions(伪版本)中。例如,v2.0.1-20200722182040-012345abcdef+incompatible可能是一个有效的伪版本。

Minimal module compatibility 最小的模块兼容性

​ 以主版本2或更高版本发布的模块需要在其module path(模块路径)上有一个major version suffix(主版本后缀)。该模块可以在其存储库中的major version subdirectory(主版本子目录)下开发,也可以不在其中开发。这对于在构建 GOPATH 模式时在模块中导入包的包有一定的影响。

​ 通常在 GOPATH 模式下,包被存储在与其repository’s root path(存储库的根路径)相匹配的目录中,该根路径与其在存储库中的目录相关联。例如,存储库中根路径 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命令支持这一点,但不需要它(请参见Mapping versions to commits(将版本映射到提交))。

​ 如果模块不是在主版本的子目录中开发的,那么它在GOPATH中的目录将不包含主版本后缀,并且它的包可以在没有主版本后缀的情况下被导入。在上面的示例中,可以在目录 $GOPATH/src/example.com/repo/sub 中找到该包,并被导入为 example.com/repo/sub

​ 这给打算在模块模式和 GOPATH 模式下构建的包带来问题:模块模式需要后缀,而 GOPATH 模式不需要。

​ 为了解决这个问题,在Go 1.11中加入了最小模块兼容性,并向后移植到Go 1.9.7和1.10.3。当导入路径被解析为 GOPATH 模式下的目录时:

(a)当解析形式为$modpath/$vn/$dir的导入时,其中:

  • $modpath 是有效的模块路径。
  • $vn是主版本后缀。
  • $dir可能是一个是空的子目录。

(b)如果以下所有情况均为 true:

  • $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

(c)那么将$modpath/$vn/$dir的导入解析到目录$GOPATH[d]/src/$modpath/$dir

​ 这个规则允许已经迁移到模块的包在GOPATH模式下构建时导入其他已经迁移到模块的包,即使没有使用主版本子目录。