go
语言的发展极其迅速,短短几年便已经历了高速变迁和进化,可能仅仅是几年前的项目,在现在的开发者眼中看起来都像是“上古时代”的产物。其中go
的包管理方式就是其中一种,因为各种历史遗留原因导致目前go module
、go path
和go vendor
三种包管理方式在各种项目中都有混杂使用,这篇文章主要来介绍下三种方式的区别和优缺点,现在的go
项目该选择哪一种。
先说结论,以目前的情况来说,更推荐选择go module
。
go path
和go vendor
都是在发展的过程中因为不适应需求逐渐被更好的包管理方式替代,所以也不能说目前的go module
就一定是最佳选择,只能说是目前的最佳实践。
两个命令
go install xxx
(安装xxx第三方二进制可执行文件 )
go get xxx
(下载xxx第三方依赖包并安装)
简单说go install
类似go build
, 将生成的可执行文件放到【$GOPATH/bin】
目录中。go get
是下载并安装,即git clone
+go install
,将clone的源码放到【$GOPATH/src】
中。
需注意,
go get
从go 1.17
开始官方不建议(deprecated)用于安装二进制可执行文件,并且从go 1.18
开始不再支持。选项-d
未来将成为默认参数,仅执行下载,不进行安装,这么做是为了更符合get
的语义。所以安装二进制可执行文件推荐使用go install
参见Deprecation of 'go get' for installing executables
两个路径
GOROOT:go的安装目录,类似java的jdk,存放一些内置的开发包和工具。
GOPATH:go指定的工作空间,用于保存go项目的代码和第三方依赖包。
可以通过
go env
查看go相关的环境变量
三个包管理工具
-
go path【不推荐使用】
go path是最早的依赖包管理方式(Go1.0),启用GOPATH模式(注意区分GOPATH模式和GOPATH路径,这是两个语境),要求将所有工程代码要求放在GOPATH/src
目录下。在工程经过go build
、go install
或 go get
等指令后,会将拉取的第三方xxx依赖包放在GOPATH/src
目录下,产生的二进制可执行文件放在GOPATH/bin
目录下,生成的中间缓存文件会被保存在 GOPATH/pkg
下。
go path有什么问题呢?GOPATH模式下没有版本控制的概念,在执行 go get 的时候,获取的永远是最新的依赖包,下载到GOPATH/src,如果你有两个工程依赖一个包的v1和v2版本,则会发生冲突,因为 GOPATH 模式下两个工程内依赖的导入路径都是一样的,因此两个工程获取的都是v2版本。
所以这个模式已经不推荐使用了。
-
govendor【不推荐使用】
在Go 1.5
版本之后,Go 提供了 GO15VENDOREXPERIMENT
环境变量(Go 1.6
版本默认开启该环境变量)和 Go vendor
包管理工具,用于将 go build 时的应用路径搜索调整成为当前工程路径/vendor
目录的方式,有效的解决了不同工程使用自己独立的依赖包问题。编译 Go 代码会优先从工程目录下的vendor
目录先寻找依赖包,如果没有找到,然后GOPATH
中查找,都没找到最后在 GOROOT
中查找。
优势:因为将第三方依赖完全和工程整合,完全本地化,使得项目构建速度快,且可以工作在无法连接外网的CI/CD流程中。
问题:放弃了依赖重用,使得冗余度上升。同一个依赖包如果不同工程想重用,都必须各自复制一份在自己的vendor
目录下。
而且有趣的是,在go vendor库的官方
README
文件已经推荐改用go module
了。
-
go module
从Go1.11
开始,官方推出Go module作为包管理工具,且从Go1.13
开始为默认选择启用。GOMODULE
模式下所有依赖的包存放在GOPATH/pkg/mod
目录下,所有第三方二进制可执行文件放在GOPATH/bin
目录下,且工程项目可以放在GOPATH
路径之外,但要求项目中需要有go.mod
文件(该文件通过go mod init
命令初始化可以生成)。
GOMODULE
模式和GOPATH
模式一样都指定了依赖包存放的位置,而不是如vendor模式放入工程内,区别在于GOMODULE的go.mod
文件中记录了所依赖包的具体版本,既实现了工程之间重用依赖包,且解决了GOPATH
模式下不同工程无法依赖同一个包的不同版本的问题。
使用GO MODULE模式,需要先开启配置。在Go1.13之后,可以通过如下命令设置GO MODULE启用状态。
export GO111MODULE=on
GO111MODULE
有三个可选值:on
、off
和auto
,go 1.16
之前默认auto
,go 1.16
之后默认on
-
GO111MODULE=off
无模块支持,go 会从 GOPATH 和 vendor 文件夹寻找包。 -
GO111MODULE=on
模块支持,go 会忽略 GOPATH 和 vendor 文件夹,只根据 go.mod 下载依赖。 -
GO111MODULE=auto
在 $GOPATH/src 外面且根目录有 go.mod 文件时,开启模块支持。
关于
GO111MODULE
有兴趣的可以参考这篇文章 【Why is GO111MODULE everywhere, and everything about Go Modules (updated with Go 1.17)】
go mod命令:go help mod查看相关帮助命令(govendor是第三方可执行文件,但是go mod命令是go自带的,不用额外go install)
命令 | 功能 |
---|---|
go mod init | 初始化当前文件夹,创建go.mod 文件,事实上,如果你的环境中GO111MODULE=on,使用类似goland的工具创建工程会自动生成go.mod |
go mod tidy | 包整理(多的删去、少的拉取),使用之前自然是import了需要的库了 |
go mod download | 下载依赖到本地(默认为 GOPATH/pkg/mod 目录) |
go mod vendor | 将依赖包复制到工程文件的vendor 目录下 |
在GO111MODULE=on
且项目中包含go.mod文件时,执行go get xxx 或 go install xxx下载的包或者二进制可执行文件将放入GOPATH/pkg/mod
目录和GOPATH/bin
目录下。
关于go.mod
文件的具体内容解析,可以参考下面这篇文章:
【深入Go Module之go.mod文件解析】