YON Blog

Go 依赖解析

在 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 的行为就和未引入模块化之前的行为一致了。除了 onoff,还可以将其配置成 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

[2] Go 模块解惑:到处都是 GO111MODULE ,这到底什么?

[3] 小厂内部私有Go module拉取方案

[4] Module proxy protocol