备注:本猿翻译水平有限,请各位看客见谅
翻译内容: go1.11 版本中 go help modules文档内容
总述
模块是Go Packages的关联集合,是源代码交换和版本控制的单元。
Go命令直接支持使用模块,包括记录和解决对其他模块的依赖。
模块取代了传统的基于GOPATH的方法来指定在给定的构建中使用哪个源文件。
模块的初级支持
Go 1.11包含了对Go Module的初级支持,包括一个新的感知模块命令“go get”。我们打算继续修正这一支持,并同时保持兼容性,直到它可以被宣布为正式版本(不再是初级版本)。在之后的版本,我们会移除对GOPATH和旧的“go get”命令的支持。获得最新Go 1.11 Module技术支持的最快途径是将你的代码仓库check out到GOPATH/src之外的一个目录中,在那里创建一个go.mod文件(具体在下一节中描述),并在该文件根目录中运行Go命令。
对于更细粒度的控制,Go支持包含一个临时的环境变量GO111MODULE,它可以设置为三个字符串值之一:OFF、ON或AUTO(默认值)。
如果GO111MODULE=OFF,则Go命令永远不会使用新的模块功能支持。相反,它会在vendor目录和GOPATH中查找依赖项,即我们现在将其称为“GOPATH模式”。
如果GO111MODULE=ON,那么Go命令需要使用模块,而不能使用GOPATH模式。我们将其称为“模块感知模式”中的“模块感知”命令。
如果GO111MODULE=AUTO或未设置,则Go命令启用或禁用取决于当前目录的对于模块功能的支持。
只有项目代码根目录位于GOPATH/src同时本身包含go.mod文件,或者位于包含go.mod文件的目录下时,才能启用模块支持。
在模块感知模式下,GOPATH不再定义构建期间导入的含义,但它仍然存储下载的依赖项(在GOPATH/pkg/mod中)和已安装的命令(在GOPATH/bin中,除非设置了Gobin)。
模块的定义
一个模块由一个GO源代码文件树来定义,在该树的根目录中有一个go.mod文件。目录包含一个go.mod文件被称为模块根(module root)。通常模块根也将对应于源代码存储库根(但这通常不必要的)。模块是模块根及其子目录中所有Go packages的集合,但不包括带有自己的go.mod文件的子树。
“模块路径(module path)”是与模块根对应的导入路径前缀。*.mod文件定义了模块路径,并通过给出模块路径和版本,再列出了在构建期间解析导入时应该使用的其他模块的特定版本。
例如,go.mod声明包含它的目录是带有路径example.com/m的模块的根目录,它还声明该模块依赖于golang.org/x/text和gopkg.in/yaml.v 2的特定版本:
module example.com/m
require (
golang.org/x/text v0.3.0
gopkg.in/yaml.v2 v2.1.0
)
mod文件还可以指定仅在直接构建模块时应用的替换和排除版本;当模块被合并到更大的构建中时,它们将被忽略。有关go.mod文件的更多信息,请参见“Go Help go.mod”。
要启动一个新模块,只需在模块目录树的根目录中创建一个go.mod文件,只包含一个模块语句。“go mod init”命令可用于执行以下操作:
go mod init example.com/m
在已经使用现有依赖项管理工具(如godep、glide或dep)的项目中,“Go mod init”还将添加与现有配置相匹配的Required语句。
一旦go.mod文件存在,就不需要其他步骤了:Go命令,比如‘go build’、‘go test’,甚至‘go list’都会根据需要自动添加新的依赖项来满足导入。
主模块和构建列表
“主模块”是包含运行Go命令的目录的模块。Go命令通过查找当前目录中的go.mod,或者当前目录的父目录,或者父目录的父目录,以此类推来查找模块根。
主模块的go.mod文件通过请求、替换和排除语句定义了可供Go命令使用的软件包的精确集合。依赖模块(通过以下Required语句找到)也有助于定义这组包,
但只能通过它们的go.mod文件的Required语句来实现:依赖模块中的任何替换和排除语句都会被忽略。因此,替换和排除语句允许主模块完成对自己构建的控制,
而不受依赖项的完全控制。
为生成提供包的一组模块称为“构建列表”。构建列表最初只包含主模块。然后,Go命令递归地将已经在列表中的模块所需的确切模块版本添加到列表中,
直到列表中没有任何可添加的内容为止。如果某个特定模块的多个版本被添加到列表中,那么在最后只保留最新版本(根据语义版本排序)以供在构建中使用。
“GoList”命令提供关于主模块和构建列表的信息。例如:
go list -m # print path of main module
go list -m -f={{.Dir}} # print root directory of main module
go list -m all # print build list
维护模块Requirements
mod文件意味着程序员和工具都可以阅读和编辑。Go命令本身自动更新go.mod文件,以维护标准格式和Required语句的准确性。
任何找到不熟悉导入的go命令都会查找包含该导入的模块,并自动将该模块的最新版本添加到go.mod中。因此,在大多数情况下,将导入添加到源代码并运行“go build”、“go test”或甚至“go List”就足够了:作为分析包的一部分,Go命令将发现并解析导入并更新go.mod文件。
任何go命令都可以确定缺少模块需求,并且必须添加,即使只考虑模块中的单个包。另一方面,要确定模块需求不再是必要的,并且可以删除,则需要全面查看模块中的所有包,包括所有可能的构建配置(体系结构、操作系统、构建标记等)。“Go mod tidy”命令构建该视图,然后添加任何缺少的模块需求并删除不必要的需求。
作为在go.mod中维护Required语句的一部分,Go命令跟踪哪些提供由当前模块直接导入的包,哪些提供仅由其他模块依赖项间接使用的包。仅用于间接使用的需求在go.mod文件中标记为“// indirect”注释。间接需求一旦被其他直接需求暗示,就会自动从go.mod文件中删除。只有在使用模块时才会出现间接需求,这些模块不能声明自己的一些依赖关系,或者在模块的依赖项显式升级之前,才会出现它自己声明的需求。
由于这种自动维护,go.mod中的信息是最新的、可读的构建描述。
“go get”命令更新go.mod以更改构建中使用的模块版本。一个模块的升级可能意味着升级其他模块,同样地,一个模块的降级可能意味着降低其他模块的等级。“go get”命令也会进行这些隐含的更改。如果go.mod是直接编辑的,像‘Go Build’或‘Go List’这样的命令将假定升级是有意的,并自动进行任何隐含的升级,并更新go.mod以反映它们。
所需的"go mod"命令提供了在维护中使用的其他功能了解模块和go.mod文件。请参见"go help mod".
-mod 构建标识为更新和使用go.mod提供了额外的控制
如果使用-mod=readonly调用,则上述go.mod的隐式自动更新不允许go命令。相反,当需要对go.mod进行任何更改时,它就会失败。此设置对于检查go.mod是否不需要更新非常有用。例如,在持续集成和测试系统中。即使使用-mod=readonly,“go get”命令仍然允许更新go.mod,并且“go mod”命令不接受-mod标志(或任何其他构建标志)。
如果使用-mod=vendor调用,Go命令假定vendor目录保存了依赖项的正确副本,并忽略了go.mod中的依赖项描述。
主模块go.mod中排除语句不允许的模块版本被认为不可用,查询不能返回。例如,这些命令都是有效的:
go get github.com/gorilla/mux@latest # same (@latest is default for 'go get')
go get github.com/gorilla/mux@v1.6.2 # records v1.6.2
go get github.com/gorilla/mux@e3702bed2 # records v1.6.2
go get github.com/gorilla/mux@c856192 # records v0.0.0-20180517173623-c85619274f5d
go get github.com/gorilla/mux@master # records current meaning of master
模块兼容性与语义版本化
Go命令要求模块使用语义版本,并期望版本准确地描述兼容性:它假定v1.5.4是v1.5.3、v1.4.0甚至v1.0.0的向后兼容替代品。更普遍的情况是,Go命令期望包遵循“导入兼容性规则”,即:“如果旧包和新包具有相同的导入路径,则新包必须向后兼容旧包。”因为Go命令假定了导入兼容性规则,所以模块定义只能设置其依赖项之一的最低要求版本:它不能设置最大版本或排除选定版本。尽管如此,导入兼容性规则并不是一个保证:可能是v1.5.4是错误的,而不是版本1.5.3的向后兼容的替代。正因为如此,Go命令从不自动更新从旧版本到新版本的模块。
在语义版本控制中,更改主版本号表明与早期版本缺乏向后兼容性。为了保持导入兼容性,GO命令要求主版本v2或更高版本的模块使用以该主版本作为最终元素的模块路径。例如,example.com/m的版本v2.0.0必须使用模块路径example.com/m/v2,该模块中的包将使用该路径作为导入路径前缀,如example.com/m/v2/sub/pkg中所示。以这种方式包含模块路径中的主版本号和导入路径称为“语义导入版本控制”。主要版本v2的模块的伪版本,而不是V0开始于主要版本,如v2.0.0-20180326061214-4fc5987536ef。作为特例,模块路径以gopkg.in/继续使用在该系统上建立的约定:主版本总是存在的,它前面是一个点,而不是斜杠:gopkg.in/yaml.v1和gopkg.in/yaml.v2,而不是gopkg.in/yAML和gopkg.in/yAML/v2。
GO命令将具有不同模块路径的模块视为不相关的:它在example.com/m和example.com/m/v2之间没有连接。具有不同主要版本的模块可以在构建中一起使用,并且由于它们的包使用不同的导入路径这一事实而保持独立。
在语义版本控制中,主要版本V0用于初始开发,没有对稳定性或向后兼容性的期望。主版本V0不会出现在模块路径中,因为这些版本正在为v1.0.0做准备,
而v1也没有出现在模块路径中。
在引入语义导入版本控制约定之前编写的代码可以使用主要版本v2和更高版本来描述V0和v1中使用的同一组不版本化导入路径。为了适应这些代码,如果源代码存储库有一个没有go.mod的文件树的v2.0.0或更高的标记,则该版本被认为是v1模块可用版本的一部分,并且在转换为模块版本时给出了一个不兼容的后缀,如v2.0.0不兼容。不兼容标签也适用于从这些版本派生出来的伪版本,如v2.0.1-0.yyyymmddhmmss-abc defabc def不兼容。
通常,在V0版本、预发布版本、伪版本或不兼容版本上的构建列表(如“go list -m all”所报告的)中存在依赖关系,这表明升级依赖项时更有可能出现问题,因为对这些版本没有预期的兼容性。
有关语义导入版本控制的更多信息,请参见https://research.swtch.com/vgo-import;有关语义版本控制的更多信息,请参见https://semver.org/。
模块编码设计
有关信息,请参见https://research.swtch.com/vgo-module的信息关于版本控制系统中的源代码如何映射到模块文件树。
模块下载和验证
Go命令在主模块的根目录中与go.mod一起维护一个名为go.sum的文件,该文件包含特定模块版本内容的预期密码校验和。每次使用依赖项时,如果缺少该依赖项,则将其校验和添加到go.sum中,或者如果需要匹配go.sum中的现有条目,则将其添加到go.sum。
Go命令维护下载包的缓存,并在下载时计算和记录每个包的加密校验和。在正常操作中,go命令针对主模块的go.sum文件检查这些预先计算的校验和,而不是在每次命令调用时重新计算它们。“要验证”命令检查模块下载的缓存副本是否仍然与其记录的校验和以及go.sum中的条目匹配。
根据GOPROXY环境变量的设置,GO命令可以从代理中获取模块,而不是直接连接到源代码控制系统。有关代理的详细信息以及缓存下载的包的格式,请参见“go help goproxy”。
模块和vendoring
在使用模块时,Go命令完全忽略vendor目录。
默认情况下,Go命令通过从源代码下载模块并使用下载的副本来满足依赖关系(在验证之后,如上一节所述)。为了允许与较早版本的GO进行互操作,或者为了确保用于构建的所有文件都存储在一个文件树中,“go mod vendor”在主模块的根目录中创建一个名为vendor的目录,并将所有依赖模块中的包存储在主模块中支持包的生成和测试所需的依赖模块中。
若要使用主模块的顶级vendor目录进行构建以满足依赖关系(禁用常用网络源和本地缓存的使用),请使用“go build -mod=vendor”。注意,只使用主模块的顶级vendor目录;其他位置的vendor目录仍然被忽略。
网友评论