文章目录
go mod之坑
背景
这是二次修改,原因是,我隔了一段时间没有编程,现在有需要再重新执笔,下载了一个第三方的开源项目,结果原作者是在gopath里面开发的,那么到了21世纪的今日,go都版本都去到15了。我怎么不能用mod去管理版本呢。而且重要的一点,开发一定要在gopath里面进行开发,这个超级不方便的嘛。
结果蒙蔽了,我不懂了,重新看回这边当初写过的文章。什么鬼。屎一般的存在。
go的版本为:go version go1.14.4 windows/amd64
编码工具为vscode
goproxy在国内有三个可用的
https://goproxy.io
https://goproxy.cn
https://mirrors.aliyun.com/goproxy/
项目
随便找个go开发的开源项目,如果你不想找,看如下链接:
https://github.com/Adminisme/ServerScan
整个打包下载回来即可。
自己开发的程序目录架构,也是可以的,主要A目录里面包含B目录,B目录里面包含C目录。
A是主程序,B是模块,C是B的模块。
简单理解:A是爷爷,B是爸爸,C是儿子。
特别注意的是:
一个项目只需要有一个mod即可
mod实战
一个项目只需要使用一个mod即可
上面这句话真的很重要。
主程序调用同目录里面的包
1、当你按照我刚刚提供的一个开源项目,下载回来解压后,项目是这样的:
PS:这里需要解释一下,ServerScan这个开源的项目里面,包含了三个独立功能的项目。要知道,go的话一个项目只认一个main文件。所以每个独立功能项目里面作者可能基于什么原因又弄了两个版本。我们进入了其中一个项目里面,轻便版扫描。
2、我们进入到子模块文件夹。
PS:在vscan子模块文件夹里面还有子模块。一环套一环。
3、了解完整个项目结构之后,真正需要使用go mod版本来进行管理了。这里我们要在主程序的目录下,使用命令初始化go mod管理。
命令是:go mod init
4、这里我们需要记住的是,当前程序我们已经通过init成立了一个包,包的名字是:scanAir,也就是说,这个主程序里面包含的所有模块都会由这个包scanAir来控制并管理。
5、实际上我们init后,是不需要去打开和管理这个mod文件的。我们在主程序中,添加一下B模块(爸爸)。
上图是原作者,导入自己开发的子模块方法。
下面是我们用了go mod后导入自己开发的模块(包)方法。
PS:这里只是主程序调用第二层的子模块,像上面我们了解到的,B模块,它自身也需要再调用C模块,那这里的话,应该按照主程序调用B模块的讨论继续往下写即可,如:A包名/B模块名称/C模块名称。
主程序调用其他目录的包
1、主程序在C盘,想调用D盘的子包;情况就是,主程序需要mod文件,D盘的子包也需要mod文件。
2、打开主程序的mod文件,进行修改。
主要修改使用到的两个关键字:require
、replace
require icmpcheck v0.0.0
replace icmpcheck => D:\code\icmpcheck
3、直接导入包即可。
重要提醒
使用开源的项目,要注意原作者使用的第三方库,我们需要人工或者配置好go mod 之后下载这些库才行。
库是包含了很多个包的。
一个项目里面,称为一个库,库里面可以设计成很多个模块(包)。
问答FAQs
1. 有些包由于特定网络原因无法访问怎么办?
Go module 加入了代理的机制,只要设置一个代理地址,就可以提供代理访问。阿里云就提供了这样一个go 的代理,是完全免费的服务。公共代理仓库会代理并缓存go模块,你可以利用该代理来避免 DNS 污染或其他问题导致的模块拉取缓慢或失败的问题,加速你的项目构建。
设置的方式非常简单,只需要设置如下环境变量即可,执行命令:
linux下:export GOPROXY=https://mirrors.aliyun.com/goproxy/
windows下:set GOPROXY=https://mirrors.aliyun.com/goproxy/
go1.13 加入了 mirror 机制,可以通过 go env -w 设置 mirror,其实就是之前的 GOPROXY 做的更到位一些,执行命令:
go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/,direct
经过测试,要goproxy的地址存在才行,如果你的goproxy是xxx.com,这里又写了aaa.cn,会提示覆盖错误的。
逗号后面可以增加多个 proxy,最后的 direct 则是在所有 proxy 都找不到的时候,直接访问,代理访问不到的私有仓库就可以正常使用了。
这个功能基本上是在家远程办公的必备工具了。
2. 公司通过 gitlab 搭建了私有库,二方依赖库下载不下来怎么办?
这个几乎是最常见的问题,比较简单的解决方案是 hack 一下 git 配置:
git config --global url."git@gitlab.your-company.com:<your>/<package>.git".insteadOf "https://gitlab.your-company.com/<your>/<package>.git"
这个方案依赖你本地的~/.ssh/id_rsa
, 这样你就可以正常 go get
了。
3. 依赖包怎么更新指定版本?
先查看版本:
$ go list -m -versions rsc.io/sampler
rsc.io/sampler v1.0.0 v1.2.0 v1.2.1 v1.3.0 v1.3.1 v1.99.99
再更新:
$ go get rsc.io/sampler@v1.3.1
go: finding rsc.io/sampler v1.3.1
go: downloading rsc.io/sampler v1.3.1
go: extracting rsc.io/sampler v1.3.1
$ go test
PASS
ok example.com/hello 0.022s
4. 某些依赖包的地址变更导致无法找到了怎么办?
go 的依赖与项目名直接相关,这就导致如果我们使用了 github 上的项目,然后项目的维护人员突发奇想改个项目名称,就会导致所有依赖它的项目都无法找到依赖。
还好有 replace 的机制:
replace golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a => github.com/golang/crypto v0.0.0-20190313024323-a1f597ede03a
5. 版本冲突怎么办?
这就要梳理版本了,是最没有捷径的。一个比较简单的办法是把所有 go.mod 里不需要指定版本的包全部删掉,仅指定必要的包版本,然后通过 go build 让项目自动构建依赖包的版本。
通过 go mod graph 可以查看具体依赖路径:
$ go mod graph
github.com/oam-dev/oam-go-sdk github.com/go-logr/logr@v0.1.0
github.com/oam-dev/oam-go-sdk github.com/onsi/ginkgo@v1.10.1
github.com/oam-dev/oam-go-sdk github.com/onsi/gomega@v1.7.0
github.com/oam-dev/oam-go-sdk github.com/stretchr/testify@v1.4.0
github.com/oam-dev/oam-go-sdk golang.org/x/net@v0.0.0-20191004110552-13f9640d40b9
github.com/oam-dev/oam-go-sdk k8s.io/api@v0.17.0
github.com/oam-dev/oam-go-sdk k8s.io/apimachinery@v0.17.0
github.com/oam-dev/oam-go-sdk k8s.io/client-go@v0.17.0
github.com/oam-dev/oam-go-sdk sigs.k8s.io/controller-runtime@v0.4.0
...
左边是项目包,右边是被依赖的包和版本。
如果确实存在两个需要指定版本的包互相冲突,那就要做取舍,修改代码,升级或降级某个包了。
6. 本地包如何引用?
如果在代码调试过程中,涉及到修改其他依赖项目代码,这时候就要引用本地包,也可以采用 replace 机制:
require (
golang.org/x/crypto v0.0.0
)
replace golang.org/x/crypto v0.0.0 => ../crypto
...
后面这个就是个相对项目路径的本地依赖所在路径。
解决了上面的这些问题,基本上你就可以愉快的使用 module 功能啦。
go mod 里还有一些其他功能,也在此列举,方便大家查阅:
go mod 子命令 | 功能 |
---|---|
download | download modules to local cache(下载依赖包) |
edit | edit go.mod from tools or scripts(编辑 go.mod) |
graph | print module requirement graph(打印模块依赖图) |
init | initialize new module in current directory(在当前目录初始化mod) |
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(解释为什么需要依赖) |