关于go mod的on,off,auto下引入外部包读取顺序的探索

1.前言

网上的文章鱼龙混杂,且都只是给结论,你根本不知道谁说的是对的,谁是错的,为了得到可信的结论,于是有了这篇文章,所谓——实践出真知。

2.实验过程

前期准备

  • 在GOPAHT外部建立目录 my-app:
    midir my-app

  • 进入my-app,编写程序, vim main.go:

package main

import "github.com/astaxie/beego"

func main() {
    beego.Run()
}

1. GO111MODULE="on":

1.1 不在$GOPATH/src目录下

  • 开启GO MOD:
go env -w GO111MODULE="on"

  • 生成go.mod :
go mod init my-app
image.png
  • 执行 go mod tidy,自动下载包到$GOPATH/pkg/mod:

    image.png

  • vim $GOPATH/pkg/mod/github.com/astaxie/beego@v1.12.3/beego.go
    增加一行 (58行) :

fmt.Println("mod")

当然记得要引入包:import "fmt"

image.png

  • 执行 go run main.go

    image.png

    可以证明确实读取了 $GOPATH/pkg/mod的beego。

  • 生成 vendor,执行 go mod vendor

  • my-app下执行 vim vendor/github.com/astaxie/beego/beego.go
    还是增加一行 (58行) :

fmt.Println("vendor")

如图所示:


image.png
  • 执行 go run main.go
    image.png

    可以看到此时优先读取了 vendor里面的外部包

1.2 在$GOPATH/src目录下

  • 复制my-app的目录到$GOPATH/src目录下
  • 执行go run main.go
    image.png

    可以看到优先读取的还是vendor
  • 执行 rm -rf vendor
  • 再次 执行go run main.go
    image.png

    依然读的是mod的。

2. GO111MODULE="off"

先执行命令:

go env -w GO111MODULE="off"

此时我的配置如下:


image.png

2.1 GO111MODULE="off"且项目不在 $GOPATH/src下

  • 还是之前在GOPATH/src外的my-app目录,复制vendor里面的目录到GOPATH/src下面:

    image.png

  • 执行vim $GOPATH/src/github.com/astaxie/beego/beego.go
    还是之前的位置,改为

fmt.Println("src")
image.png
  • 执行 go run main.go
    image.png

    直接读取的是 $GOPATH/src下面的包。

2.2 GO111MODULE="off"且项目在 $GOPATH/src下

  • 还是之前在GOPATH/src外的my-app目录,整个目录复制到GOPATH/src;
  • 在 $GOPATH/src/my-app 执行 go run main.go
    image.png

    发现竟然读的是vendor的。
  • 执行:mv vendor vendor_bak 即去除vendor的影响,再执行 go run main.go
    image.png

    这回读取的就是src的了。

3. GO111MODULE="auto"

执行:

 go env -w GO111MODULE="auto"

修改后环境变量如下:


image.png

3.1 没有go.mod 且项目在$GOPATH/src下

注意:记得要在:GOPATH/src,GOPATH/pkg/mod, vendor里面的beego包加入了之前的调试语句:

fmt.Println("src")
fmt.Println("mod")
fmt.Println("vendor")
  • 目录文件如下:


    image.png
  • 执行 go run main.go
    居然报错了:

    image.png

  • 看看$GOPATH/src是否有包, ls /mnt/d/GoProjects/src/github.com/astaxie/beego

    image.png

    包是有的。

  • 执行一下:go get -v github.com/astaxie/beego

    image.png

  • 再运行:go run main.go,发现可以了:

    image.png

  • 猜想走的是$GOPATH/pkg/mod里面的包,改了一下, vim /mnt/d/GoProjects/pkg/mod/github.com/astaxie/beego@v1.12.3/beego.go:

    image.png

  • 再运行:go run main.go,证实了:

    image.png

  • 开启vendor,目录结构如下:


    image.png

    image.png
  • 运行:go run main.go

    image.png

还是走mod。

查阅资料发现,只要main.go依赖的包用了go.mod,则默认会启用GOMODULE来下载依赖。

3.2 没有go.mod 且项目不在$GOPATH/src下

按3.1的流程跑了一遍,测试结果同上。

  • 目录结构:


    image.png
  • 运行:go run main.go
    image.png

    走的是mod
  • 开启vendor,目录结构:


    image.png
  • 运行:go run main.go
    image.png

    还是走的是mod。

意味着,GO111MODULE="auto" 的情况下,只要依赖包包含了go.mod,那么无论有没有vendor,无论在不在$GOPATH/src下,都会开启mod,并走mod的外部引入包。

3.3 有go.mod 且项目不在$GOPATH/src下

  • 目录结构:


    image.png
  • 运行:go run main.go

    image.png

    即有vendor情况下走的是vendor

  • 我们执行以下命令mv vendor vendor_bak去除vendor影响,目录结构:

    image.png

  • 运行:go run main.go

    image.png

走的是mod。

3.4 有go.mod 且项目在$GOPATH/src下

  • 目录结构:


    image.png
  • 运行:go run main.go

    image.png

    即有vendor是直接走vendor

  • 我们执行以下命令mv vendor vendor_bak去除vendor影响,目录结构:

    image.png

  • 运行:go run main.go

    image.png

    走的是mod。

3.结论

1.GO111MODULE="on"

1.1 GO111MODULE="on" 且项目不在 $GOPATH/src下

  • 当vendor目录存在,优先读取vendor的外部包
  • 当vendor目录不存在,读取$GOPATH/pkg/mod下的外部包

1.2 GO111MODULE="on" 且项目在 $GOPATH/src下

跟上面情况(GO111MODULE="on" 且项目不在 $GOPATH/src下)一致。

2. GO111MODULE="off"

2.1.GO111MODULE="off" 且项目不在 $GOPATH/src下

  • 无论有没有vendor,直接读取$GOPATH/src里面的外部引入包,即不会读取vendor里面的包

2.2.GO111MODULE="off" 且项目在 $GOPATH/src下

  • 有vendor则读取vendor的外部引入包
  • 没有vendor则读取$GOPATH/src里面的外部引入包

3.GO111MODULE="auto"

3.1 没有go.mod 且项目不在$GOPATH/src下

  • 没有vendor,且依赖的包里面有go.mod,直接走$GOPATH/pkg/mod
  • 没有vendor,且依赖的包里面都没有go.mod,直接走$GOPATH/src (待验证)
  • 有vendor,也没有用,只要依赖的包里面有go.mod,还是走$GOPATH/pkg/mod
  • 有vendor,且依赖的包里面都没有go.mod,走$GOPATH/src (待验证)

3.2 没有go.mod 且项目在$GOPATH/src下

  • 没有vendor,且依赖的包里面有go.mod,直接走$GOPATH/pkg/mod
  • 没有vendor,且依赖的包里面都没有go.mod,直接走$GOPATH/src (待验证)
  • 有vendor,也没有用,只要依赖的包里面有go.mod,还是走$GOPATH/pkg/mod
  • 有vendor,且依赖的包里面都没有go.mod,走vendor (待验证)

3.3 有go.mod 且项目不在$GOPATH/src下

  • 有vendor, 则读取vendor的外部引入包
  • 没有vendor则读取$GOPATH/pkg/mod里面的外部引入包

3.4 有go.mod 且项目在$GOPATH/src下

与上面的情况(有go.mod 且项目不在$GOPATH/src下)一致。

推荐阅读更多精彩内容