Go 依赖解析
02 May 2023在 Go 里我们使用类似 github.com/pkg/errors
路径来 import 依赖,import 之后,使用类似 errors.Wrap(err, "read failed")
的语句来使用相关 exported 对象。这里有一个不太容易注意的点是,import 语句里的最后一个部分 errors
和 代码里调用的前缀 errors
并不一定需要一致。Go 里一个 package 由两个部分组成,一个是 path 一个是 name [1]。上面例子里的 github.com/pkg/errors
是 path,后面调用语句里的前缀 errors
是 name,只是通常大家约定俗成的会将 path 的最后一个部分和 name 保持一致。从逻辑上来讲,Go 依赖解析的时候所做的事情就是根据 import 里的 path 定位到相关文件夹,然后将对应文件夹里的所有 .go 文件遍历,任意文件的 package 语句定义的名字就是 package name,也就是我们使用的时候需要带上的前缀。所以 go 里,一个文件夹下面(子文件夹不包括)的 .go 的文件的 package 必须定义成一样。
Go 是怎么根据 path 定位到文件夹的呢?Go 在 1.11 版本引入了模块化,引入模块化之前定位文件夹很简单,就是根据 path 在 $GOPATH/src/
目录下去找。要安装依赖,我们使用 go get -d github.com/pkg/errors
(-d
在 1.18 版本不再需要)来安装依赖,Go 会从 github 拉取源文件到 $GOPATH/src/github.com/pkg/errors
目录下。引入模块化之后,go get
命令会将依赖下载到 $GOPATH/pkg/mod/
目录下,每个 Go 项目的根目录下会有 go.mod
文件,里面描述了模块名及模块的相关依赖。相应的 package path 目录就是到 $GOPATH/pkg/mod/
下去找了。
1.11 版本引入模块化功能的同时还提供了 GO111MODULE
环境变量用于让开发人员显式开启(配置成 on
)或关闭(配置成 off
)模块化功能。关闭模块化功能之后 Go 的行为就和未引入模块化之前的行为一致了。除了 on
和 off
,还可以将其配置成 auto
(如果不显式指定,GO111MODULE
的值就是 auto
)。auto
的含义就是让 Go 根据当前目录是不是在 $GOPATH
下以及当前目录下有没有 go.mod
文件来决定是不是开启模块化。在 1.13 之前,只要你在 $GOPATH
目录下,那么模块化就是关闭的,即使当前目录下有 go.mod
文件。1.13 及之后的版本是只要不在 $GOPATH
下或者存在 go.mod
文件,那么模块化就是开启的 [2]。
go get
命令会根据 package path 来决定去哪里拉取源文件,支持常见的 github,bitbucket 等源码托管平台。但是有时候我们会看到一些项目使用的是自定义域名,那么这种是怎么让 go 识别并拉取到源文件的呢?这就要说到 go module proxy 协议了,如果发现用户下载的依赖不是常见的源码托管平台,那么它会去发起 http 请求到对应 package path,然后根据返回的 html 源码里的元标签
<meta name="go-import" content="import-prefix vcs repo-root">
来获取对应的源码[3, 4]
[1] Package names