Go 包管理工具分析

185 阅读4分钟

Go 包管理工具介绍

golang用过的包管理工具有:

  • src
  • dep
  • vendor
  • mod

Go 包管理工具现状

src 是 Go 自带包

src 是 Golang 默认源码目录, 一般 golang 运行时安装后, src 路径就确定了。 src 路径下主要是 Golang 的自带原生包。 不过 src 下的 package 也是可以修改编译的, 但是只能在本地使用。

dep(当前已经弃用)

dep 包管理工具氛围官方的 dep 包管理工具和 godep 这个三方包管理工具。 不过 dep 和 godep 两个包管理工具大体是相同作者开发设计的。 由于当前 dep 包管理工具已经弃用, 不再多做介绍。

vendor 是 Go 内置的依赖管理逻辑

如果当前项目中存在 vendor 目录, 则 Golang 会在vendor 目录中查找依赖, vendor 文件夹可以分多层, 如下:

.
|-- vendor
|   |-- ...
|-- pkg
|   |-- a.go
|   |-- vendor
|   |   |-- ...

mod(Go Module) 是当前 Go 最主流的包管理工具, 也是官方的包管理工具

使用 Go Module 来管理依赖, 会在当前 project 项目下生成 go.mod 文件来记录当前项目的依赖模块和版本;

当编译的时候, go mod 会根据指定的依赖生成 go.sum 文件, go.sum 文件是记录每个依赖模块的sha256信息, 防止依赖被非官方篡改。 如果启用了 GOSUMDB 服务器校验的话, Go mod 还会访问 GOSUMDB 的服务器来校验依赖的可信度。

当启用 Go mod 后, 每次项目的依赖都会被放到 $GOPATH/pkg/mod 目录下; $GOPATH/pkg/mod 目录下的包都是带上了版本信息的, 不同版本存放在不同的目录下。

go.mod 由4个关键词组成

  • module: 用于指定当前项目的模块名称, 方便其他项目引用
  • go: 用于指定当前项目的 Go语言版本
  • require: 用于指定当前项目的一个依赖模块和版本
  • replace: 用于指定当前项目的一个依赖模块使用指定的替代版本, 也可以指向本地目录

示例

module k8s.io/kubernetes

go 1.18

require (
        bitbucket.org/bertimus9/systemstat v0.0.0-20180207000608-0eeff89b0690
        github.com/Azure/azure-sdk-for-go v55.0.0+incompatible
        ...
        )

replace (
        bazil.org/fuse => bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898
        bitbucket.org/bertimus9/systemstat => ../systemstat
        ...
        )

示例分析

  • module

module k8s.io/kubernetes

表示当前项目的模块名为 k8s.io/kubernetes; 其他项目引用本项目的时候, 直接在 require 中加上一行 k8s.io/kubernetes 和版本即可。

  • go

go 1.18

表示本项目使用的 Golang 版本是 1.18

  • require
require (
        bitbucket.org/bertimus9/systemstat v0.0.0-20180207000608-0eeff89b0690
        github.com/Azure/azure-sdk-for-go v55.0.0+incompatible
        )

表示本项目依赖了 bitbucket.org/bertimus9/systemstat 模块, 且依赖的版本是 v0.0.0-20180207000608-0eeff89b0690;

这里的版本 v0.0.0-20180207000608-0eeff89b0690 表示该版本没有tag, 直接使用了指定的commit版本;

其中分为了三段, v0.0.0 表示无tag, 20180207000608 表示commit的时间, 0eeff89b0690 表示该commit的id;

第二个 require 模块的版本 v55.0.0+incompatible 中的 v55.0.0 是该模块的tag, 后缀 +incompatible 表示该模块没有遵循官方推荐的版本命名规则。

官方推荐的版本命名规则如下

一个 Module 的版本号需要遵循 v.. 的格式;

此外,如果 major 版本号大于 1 时,其版本号还需要体现在 Module 名字中。

比如 Module github.com/[orgName]/demo,如果其版本号增长到 v2.x.x 时,其 Module 名字也需要相应的改变为: github.com/[orgName]/demo/v2。

即,如果 major 版本号大于 1 时,需要在 Module 名字中体现版本。

  • replace
replace (
        bazil.org/fuse => bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898
        bitbucket.org/bertimus9/systemstat => ../systemstat
        ...
        )

这段表明项目中的 bazil.org/fuse 依赖替换为 bazil.org/fusev0.0.0-20160811212531-371fbbdaa898 版本;

bitbucket.org/bertimus9/systemstat 依赖替换为本地 go.mod 同级的 systemstat package。

更具体的细节, 建议自行谷歌, 本文不再繁冗介绍; 后续有时间也可能会单独写一篇 Go mod 的规则介绍。

各依赖管理工具的关系

这个是本文的重点

src vendor mod 三个包管理工具是可以并存的, 在编译的时候, 规则如下:

  • 在启用了 go.mod 的前提下会直接在 $GOPATH/pkg/mod 查找依赖, $GOPATH 不再作为build时的依赖查找对象;
  • 未启用 go.mod 时:
    1. 如果当前项目的当前目录存在 vendor, 则优先查找点前vendor下的包;
    2. 如果当前目录下vendor没有找到, 则继续查找上级目录下的vendor目录;
    3. 如果到项目根目录的vendor都没有找到依赖包, 则继续查找 $GOPATH/src 的包;
    4. 如果 $GOPATH/src 没有找到, 则继续查找 $GOROOT/src 的包。

vendor 的价值:

在有些离线编译的环境, 通过vendor的方式, 可以把依赖全部打入当前项目的vendor目录下; 可以通过如下命令一键将go.mod的依赖导入当前项目的vendor目录:

go mod tidy
go mod vendor

本文正在参加技术专题18期-聊聊Go语言框架