Go modules

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.modgo.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具有三个可选值onoffauto(默认)
描述
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

你可能感兴趣的:(Go modules)