go 包管理
golang 的包管理一直为人所诟病,从GOPATH到vendor再到vgo,都不太理想。于是出现了各种第三方的解决方案。最近发布的 1.11版本,官方推出了 go module,试图统一这种乱象,解决包管理方面的不足。
go modules 是什么
go modules 是golang 官方在 go 1.11 之后推出的包管理机制。
为什么使用 go modules
简单介绍下golang 包管理的发展里程,说明go modules的出现是为了解决什么问题。
go 包管理的发展分为三个阶段:
1.使用 $GOPATH 环境变量
即包需要都放在 $GOPATH/src 目录下。项目引用到的包,都会在这个目录搜索。实际开发中,会有使用同样的包名
使用 GOPATH 有几个缺陷:
- 能拉取源码的平台很有限,绝大多数依赖的是 github.com
- 不能区分版本,以至于令开发者以最后一项包名作为版本划分
- 依赖 列表/关系 无法持久化到本地,需要找出所有依赖包然后一个个 go get
- 只能依赖本地全局仓库(GOPATH/GOROOT),无法将库放置于局部仓库($PROJECT_HOME/vendor)
于是衍生出其他解决方案
2.使用 vendor 机制。
vendor 机制,在 go 1.5版本推出。工作机理很简单,就是项目中的同名包优先级高于 GOPATH/src目录下的包这种需求,可通过将包放到项目根目录下的 vendor 目录解决。
简而言之,就是vendor下的包,优先级比 GOPATH/src 下的高。项目代码编译时首先搜索的是项目目录下的vendor目录。
使用vendor已经解决项目开发中的难点,然而还谈不上好用,算不上包管理(因为对源代码的复制移动,根本没有算不上对包进行了管理)。具体来说,没有其他语言的包管理工具那样好用的功能。例如
- vendor目录中依赖包没有版本信息。这样依赖包脱离了版本管理,对于升级、问题追溯,会有点困难。
- 无法方便的得到本项目依赖了哪些包。
- 需要把依赖的包人工拷贝到 vendor 目录
于是出现了各种第三方的解决方案,比如godep、govendor glide。
3. Go module
go 1.11 之后官方开始支持 go module。即为了解决以上痛点而生。值得一提的是,推出时,引起一定的争议和反对,许多开发者认为,其设计并不比第三方解决方案出色。但是由于golang官方决定在 1.13(2019年8月) 时正式移除GOPATH的支持,默认使用 go module( GO111MODULE 在 1.11和1.22时使用的是auto,即两种共存,到1.13,将会设置为on,关于GO111MODULE的说明,请看下文)。所以我们还是今早拥抱变化,以官方的为准。
如何使用 Go module
1.相关文件和环境变量说明
go.mod
模块管理以赖于此文件。该文件记录了所依赖的包,及包的版本,类似开发iOS 和 mac 项目中的 Podfile 文件,或者说node.js 中的 package.json文件的作用
格式:
require github.com/astaxie/beego v1.11.1
go.sum
go.sum 的作用,其实有点类似开发 iOS 和mac项目的包管理cocopods的Podfile.lock文件的作用。Podfile.lock 记录的是当时所使用的确切的三方库的版本。go.sum 记录的是编译项目时,各个包的hash快照,都是为了能确定当时编译时所使用的代码,以保证不同项目在不同机器上编译部署的一致性。
GO111MODULE
作为后续版本推出的功能,必然需要一个过渡期,所以golang使用了一个环境变量GO111MODULE 作为该功能的开关。GO111MODULE 有三个值可设置
GO111MODULE=off
无模块支持,go 会从 GOPATH 和 vendor 文件夹寻找包。
GO111MODULE=on
模块支持,go 会忽略 GOPATH 和 vendor 文件夹,只根据 go.mod
下载依赖。
GO111MODULE=auto
在 $GOPATH/src
外面且根目录有 go.mod
文件时,开启模块支持。
使用步骤
1.创建 go.sum 文件
如果项目中原来没有go.sum文件,即未使用go module管理,那么需要创建 在项目根目录下执行
go mod init packagename
packagename,即为当前项目所起的包名
注意,go mod init 必须GO111MODULE=on 开启状态。
如果没有开启,输入命令:export GO111MODULE=on
2.检查并拉取所依赖的包
通过命令
go get ./..
可将当前项目所引用到的包,记录到 go.sum 文件,并下载到 $GOPATH/pkg/mod/cache/ 目录下。这样,编译项目时,可索引到这个位置。
如果希望同时使用vendor的机制呢?
通过命令
go mod vendor
就会将所用到的包,复制一份到项目根目录的 vendor 文件夹下面,这样做的好处是,项目可完整拷贝到其他机器上编译运行。并且可被 vccode 插件 gocode识别,方便 autocomplete。虽然也是拷贝,但比原来的vendor机制一个一个包拷贝进去,这样可以一键完成,算是一种改进。
需要注意的是,如果 GO111MODULE 是开启状态,go build 将不会搜索 vendor目录下的包进行编译,需要在编译时添加参数 go build -mod=vendor
3. go module 其他常用功能解析
3.1 如何升级依赖库?
要升级或降级到更具体的版本,go get
允许通过在 package 参数中添加@version
后缀或“模块查询”来覆盖版本选择,例如 go get foo@v1.6.2
,go get foo @ e3702bed2
,或者 go foo @'<v1.6.2'
go get -u
使用最新的次要版本或补丁版本((x.y.z, z是补丁版本号, y是次要版本号))go get -u=patch
将会升级到最新的补丁版本go get package@version
将会升级到指定的版本号version
go list -u -m all
查看所有直接和间接依赖项的可用 minor 和 patch 程序升级
3.2 迁移项目时怎么做?
执行 go mod download
将所需要的包下载到 $GOPATH/pkg/mod/cache/ 目录下即可。
3.3 怎么替换掉实际下载路径?
什么情况下需要替换下载路径呢?主要三种场景:
- 场景一:需要科学上网才能下载到的包
- 场景二:公司内部的项目地址未提交最新,优先使用本地的代码进行开发
- 场景三:go get 必须要 https,需要下载http的包。
3.3.1 场景一
重新映射下载地址,比如 golang.org 域名被屏蔽,我们可从github上下载包
replace (
golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac => github.com/golang/crypto v0.0.0-20180820150726-614d502a4dac
golang.org/x/net v0.0.0-20180821023952-922f4815f713 => github.com/golang/net v0.0.0-20180826012351-8a410e7b638d
golang.org/x/text v0.3.0 => github.com/golang/text v0.3.0
)
3.3.2 场景二
replace 到本地地址即可
module example.com/proj.git
replace (
example.com/server/common/pub.git => /localpath
)
require (
example.com/server/common/pub.git v1.0.0
)
3.3.3 场景三
并不是所有的包都是https的,如果公司内部的包实用的http,但go 官方并不打算支持http的包下载。那么可通过配合修改git配置来解决。
修改 .gitconfig 文件
[url "git@insecure-git.com:"] insteadOf = http://insecure-git.com/
另外还有一种使用场景,如果需要返回的输入