依赖管理
为什么需要依赖管理
早期,Go所依赖的三方库都在GOPATH这个目录下面,这就导致同一个三方库职能保存一个版本,如果不同项目依赖不同版本的三方库,怎么解决?
godep
Go语言从v1.5开始引入vendor模式,如果项目目录下面有vendor目录,那么go工具链回优先使用vendor内的包进行编译、测试等,作用用途类似前端的npm包管理工具。
安装
执行下面的命令安装godep工具:
go get github.com/tools/godep
基本命令
安装完毕以后,在终端输入godep查看支持的所有命令:
godep save 将依赖项输出并复制到Godeps.json文件中
godep go 使用保存的依赖项运行go工具
godep get 下载并安装具有指定依赖项的包
godep path 打印依赖的GOPATH路径
godep restore 在GOPATH中拉取依赖的版本
godep update 更新选定的包或go版本
godep diff 显示当前和以前保存的依赖项集之间的差异
godep version 查看版本信息
使用godep
在项目目录下执行godep save命令,我这里遇到了一个错误:
godep: [WARNING]: godep should only be used inside a valid go package directory and
godep: [WARNING]: may not function correctly. You are probably outside of your $GOPATH.
godep: [WARNING]: Current Directory: /Users/hometown/Desktop/golang-project/hello
godep: [WARNING]: $GOPATH: /Users/hometown/stduy/go
godep: Unable to find SrcRoot for package .
错误原因大致是说:
godep只能在有效的go软件包目录中使用
可能无法正常工作。你可能在$GOPATH之外
所以我得出了一个结论:当用godep去管理项目依赖的时候,必须在GOPATH的src目录下的项目中执行。
回到指定目录重新执行,生成了一个Godeps和vender两个文件夹。
Godeps文件夹下有一个Godeps.json的文件,记录项目依赖的包信息(类似package.josn)。
vender文件夹下是项目依赖的包的源代码文件(类似node_modules)。
vender机制
控制Go语言程序编译时依赖包搜索路径的优先级。
例如查找项目的某个依赖包,首先会在项目根目录下的vender文件夹下查找,如果没有找到就回去$GOPATH/src目录下查找,类似(去项目中的node_modules中找,如果没有找到,就去golbal node_modules中找)。
godep开发流程
- 保证程序能够正常编译
- 执行
godep save保存当前项目的所有第三方依赖版本信息和代码 - 提交Godeps目录和vender目录到代码库
- 如果要更新依赖的版本,可直接修改
Godeps.json文件中的对应项
go module
go module是Go1.11版本之后官方推出的版本管理工具,并且从GO1.13版本开始,go module将是Go语言默认的依赖管理工具。
GO111MODULE
要启用go module 需要设置环境变量GO111MODULE,有三个可选值off、on、auto,默认是auto。
- off --> 禁用模块支持,编译时从
GOPATH&vender文件夹中找包。 - on ---> 启用模块支持,编译时会忽略
GOPATH&vender文件夹,只根据go.mod下载依赖。 - auto ---> 当项目在
$GOPATH/src外且项目根目录有go.mod文件时,开启模块支持
简单来说,设置GO111MODULE=on之后就可以使用go module了,以后就没有必要在GOPATH中创建项目了,并且还能够很好的管理项目依赖的第三方包信息。
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 hello
go 1.17
require (
github.com/kr/fs v0.1.0 // indirect
github.com/kr/pretty v0.3.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/tools/godep v0.0.0-20180126220526-ce0bfadeb516 // indirect
golang.org/x/tools v0.1.9 // indirect
)
- module 是包名
- require 用来定义依赖包及版本
- indirect 表示间接引用
go get
go get下载依赖
go get -u升级到最新的次要版本或者修订版本go get -u=patch升级到最新的修订版本go get 包名@版本升级到指定版本号
go mod tidy
删除依赖代码后,相关依赖库不会自动从go.mod文件中删除,执行go mod tidy更新go.mod中的依赖关系(类似更新package.json执行npm uninstall xxx)
项目中使用go module
现有项目
- 在项目目录下执行
go mod init生成 go.mod 文件 - 执行
go get,查找并记录当前项目的依赖,同时生成go.sum记录每个依赖库的版本和哈希值
新项目
- 执行
go mod init 项目名 - 手动编辑
go.mod或中的require依赖项或者go get自动发现。
使用go module导入本地包
导入本地包的时候碰到了一个问题,报错如下:
package xxx is not in GOROOT
这是因为GOROOT下面没有这个包,Go引入包的方式两种GOPATH&go mod,go mod引入包,开头以项目根目录执行go mod init 项目名的这个项目名开头(也是go.mod的module 名)。
解决了上面的问题以后就可以执行go run main.go执行成功了。
我这写了一个demo:
/main.go
package main
import (
"fmt"
"moduledemo/mypackage"
)
func main() {
mypackage.DoSomething()
fmt.Println("你好帅哥")
}
/mypackage/mypackage.go
package mypackage
import "fmt"
func DoSomething() {
fmt.Println("做点啥啊?Do something??")
}
/hispackage/hispackage.go
package hispackage
import "fmt"
func DoSomething() {
fmt.Println("他不做")
}
执行成功!
遇到了一个新的问题,就是虽然可以执行成功,但是Goland标红,是因为编辑器没有配置(很无语),打开这个设置即可:
过程中还手动开启了GO111MODULE
go env -w GO111MODULE=on