zoukankan      html  css  js  c++  java
  • Go的包管理工具(一)

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

    背景

    每一门语言都有其依赖的生态,当我们使用Java语言的时候,使用Maven或者Gradle管理包依赖。早期的Go被很多开发者所诟病的一个问题就是依赖包的管理。Golang 1.5 release版本的发布之前,只能通过设置多个GOPATH的方式来解决这个问题,例如:我两个工程都依赖了Beego,但A工程依赖的是Beego 1.1,B工程依赖的是Beego 1.7,我必须设置两个GOPATH来区分,并且在切换工程的时候GOPATH也得切换,无比痛苦。在Golang 1.5 release 开始支持除了GOROOTGOPATH之外的依赖管理:vender,官方 wiki 推荐了多种支持这种特性的包管理工具,如:Godep、gv、gvt、glide、govendor和官方的dep等。

    环境准备

    安装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的环境变量: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的版本为: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/目录去查找需要的代码,因此只要上面data/user.go在$GOPATH/src/gwp/Chapter_2_Go_ChitChat/chitchat/data里面,go编译的时候就能找到。

    外部依赖管理

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

    GOPATH来管理外部依赖

    go允许import不同代码库的代码,例如github.com, k8s.io, golang.org等等;对于需要import的代码,可以使用 go get 命令取下来放到GOPATH对应的目录中去。例如go get github.com/globalsign/mgo,会下载到$GOPATH/src/github.com/globalsign/mgo中去,当其他项目在import 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所带来的问题,代码不对上了。

    kube-keepalived-vip项目用vendor目录解决了这个问题:该项目把所有依赖的包都拷贝到了vendor目录下,对于需要编译该项目的人来说,只要把代码从github上clone到$GOPATH/src以后,就可以进去go build了(注意,必须将kube-keepalived-vip项目拷贝到$GOPATH/src目录中,否则go会无视vendor目录,仍然去$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的bin目录下会有一个godep可执行的二进制文件,后面执行的命令都是用这个,建议这个目录加入到全局环境变量中。

    编译运行

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

    godep go build main.go
    

    godep中的go命令,就是将原先的go命令加了一层壳,执行godep go的时候,会将当前项目的workspace目录加入GOPATH变量中。

    检出依赖

    如果要增加新的依赖包:

    • 运行 go get github.com/globalsign/mgo
    • 代码中 import github.com/globalsign/mgo

    项目编写好了,使用GOPATH的依赖包测试ok了的时候,执行:

    godep save
    

    如上的命令将会自动扫描当前目录所属包中import的所有外部依赖库(非系统库),并将所有的依赖库下来下来到当前工程中,产生文件 Godeps/Godeps.json 文件。

    godep save时godep把所有依赖包代码从GOPATH路径拷贝到Godeps目录下,并去除代码管理目录。这个用处主要是为了支撑godep go tool的一系列操作,尤其是git clone了代码库下来后,通常直接用godep go install xxx即可完成编译,一定程度上能够缓解golang比较严格的代码路径和包管理带来的烦恼。在没有 Godeps 文件的情况下,生成模组依赖目录vendor文件夹。如果是开发依赖使用三方库,需要固定使用某个版本,请完全提交Godeps和vendor文件夹。

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

    • 运行 go get -u github.com/globalsign/mgo
    • 运行 godep update github.com/globalsign/mgo
    拉取依赖 restore

    通过命令 godep restore同步依赖库,如果下载的项目中只有Godeps.json文件,而没有包含第三库则可以使用godep restore这个命令将所有的依赖库下来到$GOPATHsrc中用于开发。

    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依赖包管理工具对比

    原文地址:https://www.jianshu.com/p/ff2ac55b3bbb
  • 相关阅读:
    Using CocoaPods
    IMPROVING IOS UNIT TESTS WITH OCMOCK
    梦想不为斗室所缚
    【转】class 'org.springframework.orm.hibernate3.LocalSessionFactoryBean' not found解决办法
    我们去工作是为了什么?——迈锡尼
    查看已保存的wifi密码
    点球成金
    研究生开口月薪一万 企业暗示“靠边站”
    程序员技术练级攻略
    Emacs是一种信仰!世界最强编辑器介绍 (转自王珢)
  • 原文地址:https://www.cnblogs.com/jpfss/p/11806832.html
Copyright © 2011-2022 走看看