一文详细了解 Go Module


一文详细了解 Go Module

文章插图
 
1 速览在正式了解Golang Modules之前,我们先速览一下其使用方式 。
在$GOPATH之外的任意地方,创建一个文件夹:
$ mkdir -p /tmp/hello$ cd /tmp/hello然后初始化一个新的Module:
$ go mod init github.com/olzhy/hello输出:
go: creating new go.mod: module github.com/olzhy/hellogo.mod内容为:
module github.com/olzhy/hellogo 1.12然后写一段代码:
$ cat < hello.gopackage mainimport ( "fmt" "github.com/olzhy/quote")func main() { fmt.Println(quote.Hello())}EOFbuild一下:
$ go buildgo: finding github.com/olzhy/quote latestgo: downloading github.com/olzhy/quote v0.0.0-20190510033103-5cb7d4598cfago: extracting github.com/olzhy/quote v0.0.0-20190510033103-5cb7d4598cfago.mod内容为:
module github.com/olzhy/hellogo 1.12require github.com/olzhy/quote v0.0.0-20190510033103-5cb7d4598cfa可以看到,其会从https://github.com/olzhy/quote master分支拉取最新提交5cb7d4598cfa 。
该依赖工程非Module管理模式,其仅有两个文件:
hello.goREADME.md现在给依赖工程打一个TAG,名为v1.0.0 。
然后hello工程更新依赖:
$ go get -ugo: finding github.com/olzhy/quote v1.0.0go: downloading github.com/olzhy/quote v1.0.0go: extracting github.com/olzhy/quote v1.0.0查看go.mod内容为:
module github.com/olzhy/hellogo 1.12require github.com/olzhy/quote v1.0.0如下为使用Golang Module后的日常工作流 。
每日工作流:
  • a)源码根据需要加入包引入语句;
  • b)标准命令,如go build及go test等会自动更新go.mod并下载依赖包;
  • c)当需要特定版本时,可以使用诸如go get foo@v1.2.3,go get foo@master,go get foo@e3702bed2命令或直接编辑go.mod文件 。
其它通用命令:
  • a)go list -m all 查看一次构建使用的直接及间接依赖的最终版本;
  • b)go list -u -m all 查看直接及间接依赖的可用的小版本或补丁版本更新;
  • c)go get -u 或 go get -u=patch 将直接与间接依赖更新为最新小版本或补丁版本;
  • d)go build ./... 或 go test ./... 构建或测试模块中的所有包;
  • e)go mod tidy 从go.mod清理不再使用的包;
  • f)go mod edit -replace foo@v1.2=../foo 替换依赖为本地复制或指定版本;
  • g)go mod vendor 转换为vendor依赖方式 。
2 概念2.1 模块
Module是一组相关Go package的集合,其作为一个单独的单元来版本化 。
Module记录精确的依赖项,提供可重复的构建 。
通常,一个版本控制仓库仅包含一个Module(支持单仓库多Module,但其会比单仓库单Module复杂很多) 。
仓库、模块与包的关系:
  • a)一个仓库包含一个或多个模块;
  • b)一个模块包含一个或多个包;
  • c)一个包在一个文件夹包含一个或多个.go文件 。
模块必须以语义学版本命名,格式为v(主版本).(小版本).(补丁),诸如v0.1.0、v1.2.3,v1.5.0-rc.1等 。
模块由一组源文件树在根目录定义一个go.mod文件,模块源码可以位于GOPATH之外,有如下原语module,require,replace,exclude 。
如下为github.com/olzhy/hello模块的go.mod文件示例内容:
module github.com/olzhy/hellorequire ( github.com/some/dependency v1.2.3 github.com/another/dependency/v4 v4.0.0)可以看到,一个模块通过module原语声明模块ID,其标识模块路径 。该模块下某一个包的被引用路径由该模块路径与自go.mod所在路径起一直到包的路径止的相对路径共同决定 。
如,一个模块在go.mod声明其ID为example.com/my/module,那么引用该模块下mypkg包的代码为:
import "example.com/my/module/mypkg"2.2 版本选择
若源码中增加了go.mod中未require的新的依赖包,绝大多数诸如go build,go test命令会自动找到对应的包并在go.mod中采用require原语加入该直接依赖的最高版本 。
例如,您依赖的模块M的带标签的发布版本为v1.2.3,那么您的go.mod会新加入require M v1.2.3这一行语句,意味着依赖模块M所允许的版本>= v1.2.3并< v2(v2被认为与v1不兼容) 。 最小版本选择算法用于对一次构建的所有模块选择版本,对于每个模块,采用该算法选择的版本为语义学最高版本 。
下面举个例子:
若您依赖的模块A依赖D(require D v1.0.0),而您依赖的模块B同样依赖D(require D v1.1.1),然后,最小版本选择(选择最高的版本)将会选择v1.1.1版本的D 。而选择v1.1.1版本的D是一致的,即使将来发布了v1.2.0版本的D 。


推荐阅读