【环境篇】go mod--让人又爱又恨的包管理工具

语言: CN / TW / HK

为什么我们需要包管理

Python有pip,Nodejs有npm。是不是别的语言有包管理工具,所以go就要有?当然不是,来看看下面这段代码:

import (
    "github.com/TomatoMr/something"
)

这是go引用包的方式,很明显这是个第三方包,那么它在我们本机上具体是怎么被找到的呢?

先说说还没有go mod的时候,它是这么找的:

  1. 项目根目录下有vendor,那就在vendor下找;
  2. vendor找不到包就往GOROOT、$GOPATH找;
  3. 以上都找不到,就去网上找,比如"github.com/TomatoMr/something",那就去GitHub上找。

找到包之后就会将它下载到GOPATH下,简单易懂。但这不足够,比如当我对这些包有了版本控制的需求,那这个包管理方式还有所欠缺,具体场景如下:

我引用的第三包有不同的版本,如果我不用其他的包管理工具,那么我默认获得的包是最新版,也就是v0.0.2的版本,而这时的版本的Hi()方法已经不存在了,而我的代码里面仍然调用了Hi()方法,这就会使我的代码报错,我现在需要有版本控制的包管理工具。

go mod来了

在go的1.11版本之后,go mod就被指定为亲生的包管理工具,它提供了更加灵活的管理办法,可以对包进行版本控制,而且你的项目也不需要一定放在GOPATH下了(放GOPATH下也没啥不好)。看看它是具体怎么做的:

$ go mod init <module_name> # 对一个项目的module进行初始化,module_name是选填的,可以在初始化的时候就制定module名
$ go mod tidy # 添加包,清除没有引用的包

执行完两句命令之后,你会看到,项目下多了go.mod和go.sum文件。

go.mod: 可以理解为包管理文件

go.sum: 可以理解为包的版本控制文件

好了,我们现在来看看,我们前面的需求,我想要引入包的时候能够选择版本,用gomod怎么做。我们先将go.mod文件中的版本号改为:v0.0.1。

再在项目根目录执行

$ go mod tidy

我们可以看到go.sum文件也相应地改变了。

再看看我们的代码,调用Hi()方法没有报错了。

掌握go mod的一些基本知识

1.需要学习什么是go mod

Go mod provides access to operations on modules.

Note that support for modules is built into all the go commands,
not just 'go mod'. For example, day-to-day adding, removing, upgrading,
and downgrading of dependencies should be done using 'go get'.
See 'go help modules' for an overview of module functionality.

Usage:

        go mod <command> [arguments]

The commands are:

        download    download modules to local cache # 下载包
        edit        edit go.mod from tools or scripts # 修改或替换包
        graph       print module requirement graph # 打印依赖关系
        init        initialize new module in current directory # 初始化项目
        tidy        add missing and remove unused modules # 更新依赖包
        vendor      make vendored copy of dependencies # 复制依赖包到vendor
        verify      verify dependencies have expected content # 检查依赖是否正确
        why         explain why packages or modules are needed # 解释为什么需要包或模块,应该说是那些模块会调用到那些依赖包和模块

Use "go help mod <command>" for more information about a command.

使用go mod之后,包下载之后是放在了$GOPATH/pkg/mod下:

2.有些环境变量应该知道

GO111MODULE=
# 默认当项目中有go.mod文件就用go mod管理,没有就还是旧的方式,GO111MODULE=on强制使用go mod,GO111MODULE=off关闭go mod
GOPROXY="https://goproxy.cn,https://proxy.golang.org,https://goproxy.io,direct"
# GOPROXY设置代理,我建议设置成与我上面一致,第一个地址是七牛云的代理,防止被墙,direct值得是从源站下载
GOSUMDB="sum.golang.org"
# 指示校验和服务器的地址和公钥,这里指"sum.golang.org"的公钥可省略,若要关闭校验,GOSUMDB=off。
GOPRIVATE=""
# GOPRIVATE表示私有仓库。私有仓库下的所有依赖一律从源站下载,而且不做校验。
GONOPROXY=""
# 与GOPROXY对应
GONOSUMDB=""
# 与GOSUMDB对应

熟悉这几个环境变量有实际的好处,比如因为某种不可描述的原因而不能下载国外网站的包,又或者因为自己公司搭建了私有仓库,需要做相应的配置。

3.使用replace替换包

有这样一写应用场景,比如说:

  1. 你要下载的包改名字了;
  2. 你引用了一个本地的包,这个包并没有提交到仓库里;

这个时候就需要replace来替换包,如下:

package main

import (
    "github.com/TomatoMr/bar" # 在这个地址下面没有这个包,可能是原包换名字或者这是我一个本地包
    "github.com/TomatoMr/something"
)

func main()  {
    something.Hello()
    something.Hi()
    bar.HelloBar()
}

看看我们在go.mod文件里面应该怎么修改:

module Foo

go 1.13

require (
    github.com/TomatoMr/something v0.0.1
)

replace github.com/TomatoMr/bar => D:\\share\\go\\src\\foo\\bar # 将bar这个包替换成我的本地路径(绝对路径或者相对路径均可)

然后执行:

go mod tidy

接着go.mod发生了变化:

module Foo

go 1.13

require (
    github.com/TomatoMr/bar v0.0.0-00010101000000-000000000000
    github.com/TomatoMr/something v0.0.1
)

replace github.com/TomatoMr/bar => D:\\share\\go\\src\\foo\\bar

可以看到github.com/TomatoMr/bar已经require进去了。

4.清除mod下载的包

go clean -modcache

这个比较少用,当你的环境被破坏,或者想删除所有的mod缓存的话,可以这么干。

欢迎关注我的公众号: onepunchgo ,会整理相关的文档和资料。

分享到: