一、前言
Go语言经历了许多年的发展,一开始Go对于包的管理并不完善。时至今日,Go语言引入了go mod 才算是彻底解决了Go包管理杂乱的问题。
目前Go语言包管理分为3个流派,分别是使用GOPATH管理、使用GOVENDOR管理、使用go mod管理。
目前大多数github上的go 包项目都已经完成了向go mod 管理包的转换。
注意:三种方式的教程分别独立,没有关联,使用哪种就按照哪种教程执行就好
二、安装goimports工具
如果使用 GOPATH 或 GOVENDOR 方式管理包,则先安装goimports工具。如果使用go mod 方式,则先忽略该步骤。
goimports是Go语言的代码格式化检测工具,它会保证团队内所有成员的代码风格是统一的。
比如你某一行多了一个空格,当你点击保存时,go imports 会自动将多的哪个空格去掉,变成符合代码规范的,当然,这需要通过在idea中进行设置。
先通过 go env
命令确认当前会话的环境变量,尤其是 GOPATH、GO111MODULE 两个
# 进入我的GOPATH目录
cd /Users/project/go
# 创建基本目录
mkdir bin pkg src
# 拉取goimports工具
go get -t golang.org/x/tools/cmd/goimports
执行命令后,目录结构是这样的
三、通过GOPATH方式管理项目包
先安装goimports工具(参看第二大点)
使用GOPATH管理包是最早的方式,编译时Go会尝试去GOPATH目录下的src以及GOROOT下的src目录找对应的包。
1、关掉GO111MODULE
export GO111MODULE=off
如果要使用GOPATH方式管理项目,则需要先将GO111MODULE关闭,因为如果不关闭,则1.11以后的版本会默认使用 go mod 方式来管理包管理工具。
2、创建项目目录
cd /Users/project/go/src
mkdir test_1
3、引入外部包
# 进入项目目录
cd /Users/project/go/src/test_1
# 拉取外部包
go get -u go.uber.org/zap
拉取后,目录结构是这样的:
可以看到,通过GOPATH方式管理包,包和项目在统一级别目录下,都在 GOPATH/src 文件夹下。
我们写代码就在项目文件夹下,在这里就是test_1文件夹下。
4、实验是否能正常使用外部包
cd /Users/project/go/src/test_1
vi hello.go
package main
import "go.uber.org/zap"
func main() {
log, _ := zap.NewProduction()
log.Warn("Warning Test")
}
编译后执行结果为{"level":"warn","ts":1646275075.348703,"caller":"test_1/hello.go:7","msg":"Warning Test"}
说明可以在项目中正常使用外部包
5、使用GOPATH管理包,它的目录结构最终是这样的:
6、注意:
只有包管理方式为go mod 时,GO111MODULE 才可以也必须设置为on,其他两种包管理方式 GOPATH、GOVENDOR, 都应该也必须将 GO111MODULE 设置为off。
如果在GoLand中无法正常运行,请在GoLand中执行 go env 查看环境变量是否和理想的一致。
如果某项不一致,可以通过 export 命令临时修改会话中的设置,或者 go env -w方式永久修改相关设置。
主要需要查看以下几项:GO111MODULE、GOBIN、GOPATH、GOROOT、GOPROXY 几项。
7、总结:
问题很明显,随着项目的越来越大,src下的目录也会越来越多,因为每引入一个外部包,就会在 GOPATH/src 目录下多一个文件夹,使得项目文件越来越杂乱且臃肿。
而且还存在另一个问题,就是一个项目中会有多个小组,由于时间问题,每个小组使用的某个包的版本可能不一样,这个问题如果使用 GOPATH 来管理包,就没有好的解决方案。基于此,Go又推出了第二种包管理方式 GOVENDOR。
再次提醒,在2018年以后,go就推出了go mod方式来管理项目,相当于php 的composer,在这之前go的包管理其实是一只被诟病的,但随着go mod的诞生,go语言的包管理不再是被诟病的点。
但毕竟还是有些项目还在用GOPATH,所以我们也有必要对其使用进行了解。
四、通过GOVENDOR来管理包
先安装goimports工具(参看第二大点)
我的 GOPATH 是 /Users/project/go。
使用GOVENDOR来管理包是基于使用GOPATH管理包存在的问题提出的解决方案。
每一个子项目下都有一个vendor目录,自己的项目所用到的包放在自己的vendor目录下。比如项目组a和项目组b都用到了github的zap包,但是因为开发时间不同,使用的zap包版本不一样,这时,就可以将包拉取到自己项目下的vendor目录,以此实现一个大项目下,多个子项目使用不同版本的同一个包。
Go在编译时,会先去当前项目的vendor目录下找对应的包,如果vendor目录下找不到对应的包,才会继续到GOPATH和GOROOT目录下的src目录下找。
1、关掉GO111MODULE
export GO111MODULE=off
2、创建项目目录
# 进入GOPATH/src文件夹
cd /Users/project/go/src
# 创建项目目录
mkdir test_2
3、引入外部包
# 进入项目目录
cd /Users/project/go/src/test_2
# 拉取外部包
go get -u go.uber.org/zap
4、在项目目录下创建vendor目录
mkdir -p /Users/project/go/src/test_2/vendor
5、将src下刚刚拉取的uber包移动到vendor目录下
cd /Users/project/go/src
mv go.uber.org test_2/vendor
mv golang.org test_2/vendor
6、创建测试go文件,确认是否可以正常使用拉取的包
cd /Users/project/go/src/test_2
vi hello.go
package main
import "go.uber.org/zap"
func main() {
log, _ := zap.NewProduction()
log.Warn("Warning Test")
}
执行结束后,输出结果{"level":"warn","ts":1646283603.876498,"caller":"test_2/hello.go:7","msg":"Warning Test"}
说明通过GOVENDOR方式管理go 包成功。
7、通过GOVENDOR方式管理包,最终的目录结构是这样的
毫无疑问,go vendor就非常类似于PHP的composer了,而且看起来也很清爽,但是有一个问题,就是使用GOVENDOR管理包,需要先使用 go get 将包先拉取到 GOPATH/src 目录下,然后再手工移动到项目的vendor目录下,很麻烦。
幸运的是随着发展,Go也有了类似于composer的第三方管理工具,业内比较流行使用的是 glide(已停止运营)、dep、go dep....
但还有另一个问题,虽然说管理包越来越清晰,但是对目录结构的要求也越来越强,就像不断的打补丁打补丁。
当然Go语言在不断进步,在2018年底Go官方终于推出了非常完善好用的包管理方式,go mod。
五、使用 go mod 方式管理包
不用先安装goimports(不用看第二大点)
go mod 是三个包管理方式中最晚推出的,也解决了前两个包管理方式存在的弊端。
我们可以很简单的就将通过GOPATH或GOVENDOR管理包的项目转换到go mod方式管理。
1、将 GO111MODULE 设置为on
export GO111MODULE=on
可以通过修改~/.bash_profile 或使用 go env -w 方式将该值永久设置为on
2、创建基本目录及项目目录
# 进入GOPATH
cd /Users/project/go
# 进入GOPATH
cd /Users/project/go
# 创建基本目录
mkdir bin pkg src
# 创建项目目录
mkdir /Users/project/go/src/test_3
# 进入项目目录
cd /Users/project/go/src/test_3
# 项目go mod初始化,这一步会生成一个go.mod文件
go mod init test_3
3、引入外部包
$ cd /Users/project/go/src/test_3
$ go get -u go.uber.org/zap
go get: added go.uber.org/atomic v1.9.0
go get: added go.uber.org/multierr v1.8.0
go get: added go.uber.org/zap v1.21.0
包的位置不再像 GOPATH 或 GOROOT 方式下默认把包放在GOPATH目录下的src文件夹,而是改为放在GOPATH目录下的pkg文件夹。
其好处是,可以同时共存多个版本,比如两个团队一起开发,开始时间节点不同,导致使用的zap包版本不一样,就可以选择通过GOVENDOR方式或go mod方式来管理包,但毫无疑问,go mod更加方便快捷,且目前github上的go 包基本都已经向go mod方向转变,并大多已经完成了转变,这样我们还是选择go mod更恰当。
4、创建测试go文件,确认是否可以正常使用拉取的包
# 进入项目目录
cd /Users/project/go/src/test_3
vi hello.go
package main
import "go.uber.org/zap"
func main() {
log, _ := zap.NewProduction()
log.Warn("Warning Test")
}
执行结束后,输出结果{"level":"warn","ts":1646284608.952651,"caller":"test_3/hello.go:7","msg":"Warning Test"}
说明通过 go mod 方式管理go 包成功。
5、安装goimports
# 进入项目目录
cd /Users/project/go/src/test_3
# 拉取goimports
go get -t golang.org/x/tools/cmd/goimports
六、总结
GOPATH 方式管理包,是通过在项目根目录执行 go get,包的位置在GOPATH或GOROOT目录下的src目录下;
GOROOT 方式管理包,是通过在项目根目录执行go get,然后手动将包移动到项目的vendor目录下(当然也可以使用第三方包管理工具,比如glide),但最终包的位置就是位于项目根目录下的vendor目录下。
Go mod方式管理包,则是将GO111MODULE置为on后,在项目根目录执行go get,包的位置在GOPATH目下的pkg文件夹下。