这是我参与更文挑战的第16天,活动详情查看:更文挑战
Go工程和依赖管理基本机制
依赖管理机制的变迁
GoPath—>GoVendor—->GoMod
- GoPath:会要求工程代码要保存在GoPath/src的目录下面。同时要求你的依赖库也要是这个目录下面的:
$GOPATH/src/yourProject/
$GOPATH/src/gorm :如果依赖了gorm
复制代码
编译的时候会直接使用GoPath/src目录下的代码。Go Get下载的代码也会放在GoPath中。
往往我们在安装Go以后,通过go env
查看我们的Go Path,进入该文件目录,目录结构是这样的:
├── bin //用来存放编译后的可执行文件
│ ├── dlv
│ ├── go-outline
│ ├── gomodifytags
│ ├── gopkgs
│ ├── goplay
│ ├── gopls
│ ├── gotests
│ ├── impl
│ └── staticcheck
├── pkg //用于存放编译后生成的归档文件
│ ├── mod
│ └── sumdb
└── src // 用来存放go源码文件
└── github.com
复制代码
这个结构,配上GoPath的要求,我们就必须要把我们的代码写在src文件下。或者是在其他任意目录写代码,但是最后构建的二进制文件要放在src下,才能执行。
刚刚说了,Go Get下载的代码也会放在GoPath中,这样你的工程代码和第三方代码都在同一个目录下,甚至你的多个工程代码也会在一个Go path目录下,十分混乱。
为了解决这个问题(还是使用GoPath),我们就会每个工程都指定一个新的GoPath,这样看起来项目管理很清晰,大家都分开了。然而第三方库,你每次都要下载。
- GoVendor
通过命令启用govendor:
export GO15VENDOREXPEIMENT=1
复制代码
效果是所有的依赖包都会下载到./vendor
目录下面。编译的时候也会使用目录下的依赖包,使用go vendor命令更新依赖包。这样子解决了依赖包和工程代码混杂的问题,但是没有解决多个工程代码之间混杂的问题。
- Go Mod
export GO111MODULE=on // 启用go mod
复制代码
本质是使用go.mod文件来描述我们依赖库的名称和版本。下载的依赖会放在
$GOPAHT/pkg/mod文件夹下面
复制代码
同时工程代码不一定要放在GOPATH下面。
但是在企业实践中,我们还是建议所有工程都放在GOPATH目录下,这样工程结构会比较清晰,也就是govendor其实和go mod效果差不多。
Go mod 如何工作的
- gomod版本的表达方式
- 语义化版本,也就是常见的v1.0.3这种形式
v${major}.${minor}.${patch}
major不同则认为是两个不同的仓库
复制代码
- 伪版本号
可以基于某个commit来,比如我们常见:
xx/xxx/xxx v0.0.0-20210323104329-2dfsdfsdf
xx/xxx/xxx v0.0.0 -20210323104329 -2dfsdfsdf
基本版本前缀 commit utc时间 commit hash前12位
复制代码
- 主要的命令
go get xxxx依赖库
- go get 依赖库可以跟上具体的commit hash 也可以跟上某个分支例如:
- go get 依赖库@hash
- go get 依赖库@master
go mod tidy // 自动添加或者删除项目依赖的库
复制代码
- gomod 版本选择机制
问:x项目依赖a,b,a、b都依赖c项目,但是a依赖c的v1.1.1,b依赖c的v1.2.2,最后编译x的时候使用的c项目的版本是什么?
答案是最高版本v1.2.2。下图是Go mod的选择策略Minimal version selection (MVS)

工程和依赖管理常见问题
-
在go mod文件中我们会看见一些标识:
- // indirect
此依赖不是本项目直接的依赖,但在本项目中指定该依赖。(a依赖b,b依赖c,在a中可以指定c是什么版本)
- // incompatible
该依赖未使用gomod管理依赖,不影响使用
-
一些工具
go mod graph :查看依赖之前的关系
go mod vendor: 把依赖的文件放在工程目录下的vendor文件夹中,类似go vendor。一般不用提交vendor下的文件,主要是用来查、跳转代码用的。
- 临时修改依赖库中的代码进行测试
git clone下来代码到本地,checkout对应版本,go mod文件中replace
业务实践中的经典案例
- go get -u 会拉取依赖的依赖,可能会导致一些意想不到的问题,建议不要加上-u
- 如果依赖没有使用mod,默认是拉取最新的版本;建议无论依赖有没有使用mod,我们的新项目都要用mod
- 依赖的a项目的tag删除了,该怎么办
- 如果只是你的项目,那么就重新go get,获取最新的版本
- 如果不仅是你的项目,而且你的依赖b也依赖这个a项目,那么就在你的项目上replace这个a项目。长期来看,还是要把你的依赖b重新go get一下。