Golang具有相当长的包管理工具的变迁史,由于官方没有实现足够好用的包管理工具,各种包管理工具层出不穷。
GOPATH
- 使用GOPATH管理依赖
Golang的包管理方式最初采用的是monorepo
模式,即所有包都放在GOPATH
下,使用类似命名空间的包路径来区分包。
几乎所有的包管理工具在Golang1.11版本之前都绕不开GOPATH环境变量,GOPATH主要用来放置项目依赖包的源代码。由于GOPATH不区分项目,代码中任何import
的路径都需要从GOPATH为根目录的位置开始查找。
$ go env | grep GOPATH
set GOPATH=F:\Go\workspce
GOPATH目录下包含三个子目录分别是bin、pkg、src
子目录 | 描述 |
---|---|
bin | 存储编译后生成的二进制文件 |
pkg | 存储预编译的目标文件,以加快程序后续编译速度。 |
src | 存储源代码 |
使用GOPATH方式管理项目依赖时,编写Go应用程序时,程序包和库文件会以$GOPATH/src/github.com/foo/bar
的路径形式进行存放。因此就必须将项目源代码固定存放在$GOPATH/src
目录下,若执行go get
拉取外部依赖时会自动下载并安装到GOPATH目录下。
使用GOPATH环境变量管理项目依赖包的缺点
- 由于依赖包可能会引入破坏性的更新,会产生生产环境和测试环境不一致的问题。
- 不区分依赖项的版本
多个项目时不同项目对于依赖库的版本需求经常是不一致的,然后却无法在一个GOPATH路径下放置不同版本的依赖项。因此只能通过人工判断更新,使用非常不便。 - 依赖项列表无法数据化
Go Modules之前是没有任何语义化的数据可以知道当前项目的所有依赖项,因此需要手工找出所有依赖。对项目而言,需要将所有依赖项全部放入到源代码控制中。若剔除某个依赖则需在源码中手工确认某个依赖是否被剔除。
为解决GOPATH的缺陷,Go官方和社区推出了许多解决方案,比如godep、govendor、glide等。但这些工具要么无法彻底解决GOPATH存在的问题,要么使用起来繁冗。
Go Modules
- Go Modules是语义化版本管理依赖项的包管理工具,解决GOPATH存在的缺陷。
- Go Modules的前身是vgo,于Golang v1.11新增特性。
使用Go Modules后无需再将代码放置到GOPATH
下的src
目录中,Go Modules替换旧的基于GOPATH
来指定在给定构件中使用的源文件。Go Modules可将项目文件夹下所有依赖整理后写入名为go.mod
的项目依赖管理文件中。
使用Go Modules管理项目依赖包时会在项目根目录下生成两个文件,分别是go.mod
和go.sum
-
go.mod
文件中会记录当前项目的所有依赖项 -
go.sum
文件中会记录每个依赖包的版本和对应哈希值
go env
查看当前Go版本
$ go version
go version go1.15.6 windows/amd64
查看环境变量中Go模块的配置项,查看是否已开启Go Modules功能。
配置项 | 默认值 | 描述 |
---|---|---|
GO111Modules | off | 是否开启Go Modules功能 |
GOPROXY | https://proxy.golang.org,direct | 设置Golang代理商为用户提供包下载的地址 |
$ go env
set GO111MODULE=on
set GOPROXY=https://goproxy.io
GO111MODULE
- GO111MODULE这个命名代表着Golang于1.11版本时添加对Go Modules的支持。
- GO111MODULE具有三个可选值
on
、off
、auto
(默认)
值 | 描述 |
---|---|
auto | 默认值,根据当前目录来决定是否启用Go Modules功能。 |
off | 禁用Go Modules功能 |
on | 启用Go Modules功能 |
-
auto
默认值,go
命令根据当前目录来决定是否启用Go Modules功能。
只有在当前目录位于GOPATH/src
之外且该目录包含go.mod
文件,或者当前文件在包含go.mod
文件的目录下时才会自动开启。 -
off
表示go
命令禁用Go Modules功能,寻找依赖包时将会沿用旧版本通过vendor
目录或GOPATH
模式来查找。 -
on
表示go
命令启用Go Modules功能,因此不会去GOPATH
目录下查找依赖。
命令行开启Go Modules功能
$ export GO111MODULE = on
当Go Modules模块功能开启后,依赖包的存放位置将会变更为$GOPATH/pkg
,同时会允许同一个package
包多个版本共存,且多个项目可以共享缓存的模块。
GOPROXY
- GOPROXY的值会以一个英文逗号
,
作为分隔符来分割多个Go依赖包代理商列表。 - GOPROXY中的
direct
是一个特殊的标识符,用于指示Golang回溯到依赖项的源地址去抓取。
$ go env | grep GOPROXY
set GOPROXY=https://goproxy.cn,direct
中国Golang中间代理商包下载地址
代理商 | 地址 |
---|---|
Golang中国 | https://goproxy.io |
阿里云 | https://mirrors.aliyun.com/proxy/ |
七牛云 | https://goproxy.cn |
设置Golang代理商为七牛云
$ go env -w GOPROXY=https://goproxy.cn,direct
$ go env|grep GOPROXY
set GOPROXY=https://goproxy.cn,direct
go mod
Go Modules包管理器提供了go mod
命令用于管理项目依赖项。
-
go mod
命令支持记录和解析对模块的依赖性 -
go mod
管理项目依赖无需再将项目放到GOPATH/src
指定目录下,可在磁盘的任何位置新建项目。
$ go help mod
Usage:
go mod [arguments]
The commands are:
download download modules to local cache
edit edit go.mod from tools or scripts
graph print module requirement graph
init initialize new module in current directory
tidy add missing and remove unused modules
vendor make vendored copy of dependencies
verify verify dependencies have expected content
why explain why packages or modules are needed
Use "go help mod " for more information about a command.
命令 | 描述 |
---|---|
go mod download | 下载依赖包 |
go mod edit | 编辑go.mod 文件 |
go mod graph | 打印模块依赖图 |
go mod init | 当前目录下初始化模块 |
go mod tidy | 拉取缺少的依赖项或移除无用的依赖项 |
go mod vendor | 将依赖复制到项目根目录下的vendor目录中 |
go mod verify | 验证依赖项的哈希值是否正确 |
go mod why | 解释为什么需要依赖 |
go mod init
初始化Go Modules,首先需要进入项目根目录,执行Go Modules初始化命令,生成go.mod
项目依赖管理文件。
$ mkdir projname && cd projname
$ go mod init projname
使用go mod init
初始化项目依赖后会生成两个文件
文件 | 描述 |
---|---|
go.mod | 项目依赖包管理文件 |
go.sum | 项目依赖包的版本控制文件 |
go.mod
$ vim go.mod
module ginv
go 1.15
require (
github.com/allegro/bigcache/v2 v2.2.5 // indirect
github.com/allegro/bigcache/v3 v3.0.0
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
)
go.mod文件只能存在于模块(包)的根目录下,子目录中的导入路径会使用“模块的导入路径+子目录路径”的形式。
Go命令行工具会自动处理go.mod中指定的模块版本,当在源代码中使用import
导入指定的依赖项不存在于go.mod文件中时,Go命令行工具会自动搜索这个依赖项,并会将最新的版本(最后一个tag且非预发布的稳定版本)添加到go.mod文件中。
github.com/allegro/bigcache/v3 v3.0.0
若依赖项没有tag,则会使用伪版本(专门的版本语法,用于标记没有tag的提交)。
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
go.mod文件是面向行的,当前模块或主模块通常会位于第一行,接下来根据路径依次排序需要加载的依赖项。go.mod文件中每一行会包含一个指令,该指令由一个前导动词后跟参数组成。
前导动词 | 描述 |
---|---|
module | 指定当前模块的名字或路径 |
go | 设置预期Golang语言的版本号 |
require | 指定给定版本或更高版本的特定依赖项 |
exclude | 指定忽略或排除的特定版本的依赖项 |
replace | 指定可替换的依赖项 |
- 前导动词可以按块的方式来使用,使用圆括号
()
包裹。 - 注释仅能使用
//单行注释
,禁止使用/*多行注释*/
-
indirect
注释表示当前依赖项不是被当前模块直接导入的,是间接引用的。
go mod vendor
- Golang v1.5开始Golang引入了
vendor
包模式
当内部构建系统处于无网环境,依赖项需要纳入内部版本控制时,Go Modules提供go mod vendor
工具。
$ go mod vendor
go mod vendor
命令会在当前项目根目录下创建vendor
目录,然后将项目所有依赖缓存到此,另外vendor
目录可直接进入内部版本控制。使用go mod vendor
的好处在于不依赖网络上游的版本,在本地内部自由地使用稳定可控的版本进行构建,因此也成为了非开源项目的主要构建方式。
默认情况下go build
构建时会主动忽略vendor
目录,若希望从本地vendor
目录下开始构建则需使用go build -mod vendor
命令。
$ go build -mod vendor
若项目目录下存在vendor
目录,Go工具链会优先使用vendor
内的包进行编译、测试等。之后第三方的管理方式都通过此种方式来实现。
go mod download
go mod download
命令用于下载依赖到文本,而非使用go get
。若GOPROXY
设置镜像地址,此时会将依赖全部下载依赖到本地缓存GOPATH
文件夹,依赖项版本数据均会缓存在$GOPATH/pkg/mod
和$GOPATH/pkg/sum
文件夹下。同时会在项目根目录下生成go.sum
文件。
go mod edit
编辑go.mod
文件,格式化该文件。
$ go mod edit -fmt
添加依赖
$ go mod edit -require=golang.org/x/text
go list
- 使用命令行查看项目依赖文件
$ go list -m all
go list -m
显示所有import
库信息,-json
表示使用JSON格式显示,all
表示全部库。
$ go list -m -json all