这篇文章记录 golang 编译的一些知识备忘。
一、背景
go中没有项目的说法,只有包, 其中有两个重要的路径,GOROOT
和 GOPATH
- GOROOT:Go的安装目录,(类似于java的JDK)
- GOPATH:GOPATH 是我们的工作空间,保存go项目代码和第三方依赖包
我们可以使用 go env
查看相关设置信息:
二、历史
1. GOPATH
使用 GOPATH 时,GO 会在以下目录中搜索包:
GOROOT/src
:该目录保存了Go标准库代码。
GOPATH/src
:该目录保存了应用自身的代码和第三方依赖的代码。
程序中引入包时,Go会先去 GOROOT的scr目录 中查找,然后在 GOPATH的src目录 找,如果该目录不存在,会报错找不到package。
现在已不再推荐使用 GOPATH 来构建应用了。
2. vendor
Vendor 目录是 Golang 从 1.5 版本开始引入的,为项目开发提供了一种离线保存第三方依赖包的方法。
1.5 版本需要手动设置环境变量 GO15VENDOREXPERIMENT=1
。
在执行 go build
或 go run
命令时,会按照以下顺序去查找包:
- 当前包下的 vendor 目录
- 向上级目录查找,直到找到 src 下的 vendor 目录
- 在 GOROOT 目录下查找
- 在 GOPATH 下面查找依赖包
在发布 1.6 版本时,该环境变量的值已经默认设置为 1 了,该值可以使用 go env
命令查看。
在发布 1.7 版本时,已去掉该环境变量,默认开启 vendor
特性。
3. vendor管理工具混战
主要是一下三个工具:
godep
godep
通过 godep save
把第三包的版本依赖信息记录在Godeps.json
下,并且把第三包完整拷贝一份到vendor
下面。通过对 Godeps.json
文件进行版本管理即可以管理整个项目的第三方包依赖信息。
govendor
govendor init
之后会在根目录下生成一个vendor
文件夹。只需要对vendor/vendor.json
进行版本控制,即可对第三包依赖关系进行控制。
glide
glide
通过glide create
或glide init
命令初始化第三方包管理,会在项目根目录下生成一个glide.yaml
,这个文件记录用到的第三方包的依赖关系,支持编辑修改。
glide
通过glide install
, 会把所有缺少的第三方包都下载到vendor
文件夹下,并且会在glide.yaml
中添加所有依赖的第三方包名称,在glide.lock
文件中记录具体的版本管理信息。
4. module
2018年 8 月, Go 发布了 1.11版本,引入Module功能,新增了一组go mod
命令,可以通过go help mod
查看使用帮助,目前一共有8个子命令:
从 Go 1.13 版本开始,go module
是Go语言默认的依赖管理工具。
三、go module使用
1. 配置
GO111MODULE
设置环境变量 GO111MODULE=on
,就可以使用 go module
了:
GO111MODULE=off
禁用模块支持,编译时会从GOPATH
和vendor
文件夹中查找包。
GO111MODULE=on
启用模块支持,编译时会忽略GOPATH
和vendor
文件夹,只根据 go.mod
下载依赖,将依赖下载至%GOPATH%/pkg/mod/
目录下。
GO111MODULE=auto
,当项目在$GOPATH/src
外且项目根目录有go.mod
文件时,开启支持。
go module 管理依赖后会在项目根目录下生成两个文件go.mod
和go.sum
。
GOPROXY
Go1.13 之后 GOPROXY
默认值为https://proxy.golang.org
,在国内无法访问,要设置成国内的代理地址,我是用这个域名:
export GOPROXY=https://goproxy.cn
go mod 命令
go mod download 下载依赖的module到本地cache(默认为$GOPATH/pkg/mod目录)
go mod edit 编辑go.mod文件
go mod graph 打印模块依赖图
go mod init 初始化当前文件夹, 创建go.mod文件
go mod tidy 增加缺少的module,删除无用的module
go mod vendor 将依赖复制到vendor下
go mod verify 校验依赖
go mod why 解释为什么需要依赖
go.mod 文件
依赖信息大致如下:
module
用来定义包名
require
用来定义依赖包及版本
indirect
表示间接引用
在国内访问 golang.org/x 的各个包都需要翻墙,我们可以在 go.mod 中使用 replace 替换成 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
)
go get
在项目中执行go get
命令可以下载依赖包,并且还可以指定下载的版本。
- 运行
go get -u
将会升级到最新的次要版本或者修订版本(x.y.z, z是修订版本号, y是次要版本号)
- 运行
go get -u=patch
将会升级到最新的修订版本
- 运行
go get package@version
将会升级到指定的版本号version
如果下载所有依赖可以使用go mod download
命令。
go mod vendor
go mod vendor
会复制modules下载到vendor中, 只会下载你代码中引用的库,而不是 go.mod 中定义全部的module。用在 module 和 go vendor 混用的情况。
2. 初始化
我这边用 cobra 创建了个简单的项目,参考另一篇文章《golang Cobra 包介绍》:
对其做 module 初始化:
$ go mod init appname // 初始化当前文件夹, 创建go.mod文件
$ go mod tidy // 增加缺少的module,删除无用的module
使用 go mod init
要带上package的名字,比如我这里用了appname,这样当其它应用引用包时,就可以直接引用了。
go mod init xxxx
3. 编译
如果你还是选择使用 vendor 来进行编译,那么运行这个命令:
$ go mod vendor // 把原有 vendor 目录里的内容,reset成当前程序所需的依赖包,然后复制到原有vendor目录下
$ go build -mod=vendor // 使用 vendor 进行build
4. 查看包列表
go list // 当前目录引用的包
go list -m all // 本机引用的包
go help list // 查看go list 的详细说明
参考资料