本文内容纲要:

- 背景

- 环境准备

- 安装Go

- GOPATH和GOROOT

- 包管理

- 内部依赖管理

- 外部依赖管理

- 总结

在前面转载了系列文章:Golang 需要避免踩的 50 个坑,总得来说阅读量都挺大。今天这篇文章,咱们一起聊聊Go的依赖包管理工具。

背景

GOPATHGOPATHGOPATHGOROOTGOPATH

环境准备

安装Go

笔者是Mac系统,安装Go有多种方式,通过brew、下载源码安装go等方式可以安装go。

在bash_profile中自定义GOPATH和GOBIN位置:

GOROOT=/usr/local/go
export GOPATH=/Users/user/aoho/go-workspace
export GOBIN=$GOPATH/bin
export PATH=$PATH:$GOBIN:$GOROOT/bin
go env
GOARCH="amd64"
GOBIN="/usr/local/go/bin/go"
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/user/aoho/go-workspace"
GORACE=""
GOROOT="/usr/local/go"
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GCCGO="gccgo"
CC="clang"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/st/gkm45qzd2tv8mc32my38_n_00000gp/T/go-build646095787=/tmp/go-build -gno-record-gcc-switches -fno-common"
CXX="clang++"
CGO_ENABLED="1"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
go version go1.9.3 darwin/amd64

GOPATH和GOROOT

GOROOT不是必须要设置的。默认go会安装在/usr/local/go下,但也允许自定义安装位置,GOROOT的目的就是告知go当前的安装位置,编译的时候从GOROOT去找SDK的system libariry。

export GOROOT=$HOME/go1.9.3

GOPATH必须要设置,但并不是固定不变的。GOPATH的目的是为了告知go,需要代码的时候,去哪里查找。注意这里的代码,包括本项目和引用外部项目的代码。GOPATH可以随着项目的不同而重新设置。

GOPATH下会有3个目录:src、bin、pkg。

  • src目录:go编译时查找代码的地方;
  • bin目录:go get这种bin工具的时候,二进制文件下载的目的地;
  • pkg目录:编译生成的lib文件存储的地方。

包管理

$GOPATH

内部依赖管理

如笔者示例route_auth.go的引入:

import (
    "gwp/Chapter_2_Go_ChitChat/chitchat/data"
    "net/http"
)

route_auth.go需要引用data/user.go,项目结构如下:

image

$GOPATH/src/$GOPATH/src/gwp/Chapter_2_Go_ChitChat/chitchat/data

外部依赖管理

对于外部依赖的管理,在最开始go没有像java使用maven来管理依赖包、包版本,而是直接使用GOPATH来管理外部依赖。

GOPATH来管理外部依赖

go get github.com/globalsign/mgo$GOPATH/src/github.com/globalsign/mgoimport github.com/globalsign/mgo

看到这里也就明白了,对于go来说,其实并不在意你的代码是内部还是外部的,总之都在GOPATH里,任何import包的路径都是从GOPATH开始的;唯一的区别,就是内部依赖的包是开发者自己写的,外部依赖的包是go get下来的。Go 语言原生包管理的缺陷:

  • 能拉取源码的平台很有限,绝大多数依赖的是 github.com
  • 不能区分版本,以至于令开发者以最后一项包名作为版本划分
  • 依赖 列表/关系 无法持久化到本地,需要找出所有依赖包然后一个个 go get
  • 只能依赖本地全局仓库(GOPATH/GOROOT),无法将库放置于局部仓库($PROJECT_HOME/vendor)

vendor

依赖GOPATH来解决go import存在的问题在上面小节已经列举。为了解决这个问题,go在1.5版本引入了vendor属性(默认关闭,需要设置go环境变量GO15VENDOREXPERIMENT=1),并在1.6版本中默认开启了vendor属性。

简单来说,vendor属性就是让go编译时,优先从项目源码树根目录下的vendor目录查找代码(可以理解为切了一次GOPATH),如果vendor中有,则不再去GOPATH中去查找。

以kube-keepalived-vip为例。该项目会调用k8s.io/kubernetes的库(Client),但如果你用1.5版本的kubernetes代码来编译keepalived,会编译不过。1.5版本中代码有变化,已经没有这个Client了。这就是前面说的依赖GOPATH来解决go import所带来的问题,代码不对上了。

$GOPATH/src$GOPATH/src$GOPATH/src

通过如上vendor解决了部分问题,然而又引起了新的问题:

  • vendor目录中依赖包没有版本信息。这样依赖包脱离了版本管理,对于升级、问题追溯,会有点困难。
  • 如何方便的得到本项目依赖了哪些包,并方便的将其拷贝到vendor目录下?依靠人工实在不现实。

为了解决这些问题,开源社区在vendor基础上开发了多个管理工具,比较常用的有godep、govendor glide等,go官方发布了dep。

godep

godep是解决包依赖的管理工具,原理是扫描记录版本控制的信息,并在go命令前加壳来做到依赖管理。godep早期版本并不依赖vendor,所以对go的版本要求很松,go 1.5之前的版本也可以用,只是行为上有所不同。在vendor推出以后,godep也改为使用vendor了。godep 建议在 golang 1.6 以后使用,且godep 依赖 vendor 。

godep的使用者众多,如docker,kubernetes, coreos等go项目很多都是使用godep来管理其依赖,当然原因可能是早期也没的工具可选。

go get -u -v github.com/tools/godep
$GOPATH
编译运行

因为go命令是直接到GOPATH目录下去找第三方库,且在1.6以后支持vendor方式编译,而使用godep下载的依赖库放到Godeps/workspace目录下的,但是不影响继续使用依赖GOPATH目录,所以与三方工具本身不冲突。因此使用:

godep go build main.go
godep go
检出依赖

如果要增加新的依赖包:

go get github.com/globalsign/mgoimport github.com/globalsign/mgo
GOPATH
godep save
Godeps/Godeps.json
godep save

依赖包会有更新,如何更新依赖包?可以通过如下的命令实现。

go get -u github.com/globalsign/mgogodep update github.com/globalsign/mgo
拉取依赖 restore
godep restore$GOPATH\src

godep restore执行时,godep会按照Godeps/Godeps.json内列表,依次执行go get -d -v来下载对应依赖包到GOPATH路径下。

govendor

govendor是在vendor之后出来的,功能相对godep多一点,不过就核心问题的解决来说基本是一样的。该工具将项目依赖的外部包拷贝到项目下的 vendor 目录下,并通过 vendor.json 文件来记录依赖包的版本,方便用户使用相对稳定的依赖。

go get -u github.com/kardianos/govendor

如上的命令即可安装govendor,govendor生成vendor目录的时候需要2条命令:

  • govendor init生成vendor/vendor.json,此时文件中只有本项目的信息
  • govendor add +external更新vendor/vendor.json,并拷贝GOPATH下的代码到vendor目录中。
    govendor还可以直接指定依赖包版本来获取包。

govendor的依赖包主要有以下多种类型:

image

使用步骤

进入项目的根目录。

# 创建 vendor 文件夹和 vendor.json 文件
govendor init
 # 从 $GOPATH 中添加依赖包,会加到 vendor.json

govendor add +external 
 # 列出已经存在的依赖包

govendor list 
 # 找出使用的对应包

govendor list -v fmt 
 # 拉取指定版本的包

govendor fetch golang.org/x/net/context@a4bbce9fcae005b22ae5443f6af064d80a6f5a55

govendor fetch golang.org/x/net/context@v1   # Get latest v1.. tag or branch.

govendor fetch golang.org/x/net/context@=v1  # Get the tag or branch named "v1".

相对上面的工具来说,govendor功能更加丰富。

总结

本文主要介绍了几种go依赖包管理工具,首先介绍了go的环境安装,配置对应的环境变量;其次讲到包管理的两种类型:内部依赖和外部依赖的管理。内部依赖包的管理很简单,go原生的外部依赖包管理存在很多缺陷,随后介绍了开源社区推出的godep和govendor,在vendor基础上进行了功能的完善。还有目前常用的包依赖管理工具glide和官方的dep,将会在后面的文章介绍,尽请期待。

微信公众号

参考

本文内容总结:背景,环境准备,安装Go,GOPATH和GOROOT,包管理,内部依赖管理,外部依赖管理,总结,

原文链接:https://www.cnblogs.com/jpfss/p/11806832.html