在前面轉載了系列文章:Golang 須要避免踩的 50 個坑,總得來講閱讀量都挺大。今天這篇文章,我們一塊兒聊聊Go的依賴包管理工具。java
每一門語言都有其依賴的生態,當咱們使用Java語言的時候,使用Maven或者Gradle管理包依賴。早期的Go被不少開發者所詬病的一個問題就是依賴包的管理。Golang 1.5 release版本的發佈以前,只能經過設置多個GOPATH
的方式來解決這個問題,例如:我兩個工程都依賴了Beego,但A工程依賴的是Beego 1.1,B工程依賴的是Beego 1.7,我必須設置兩個GOPATH
來區分,而且在切換工程的時候GOPATH
也得切換,無比痛苦。在Golang 1.5 release 開始支持除了GOROOT
和GOPATH
以外的依賴管理:vender,官方 wiki 推薦了多種支持這種特性的包管理工具,如:Godep、gv、gvt、glide、govendor和官方的dep等。git
筆者是Mac系統,安裝Go有多種方式,經過brew、下載源碼安裝go等方式能夠安裝go。github
在bash_profile中自定義GOPATH和GOBIN位置:golang
GOROOT=/usr/local/go
export GOPATH=/Users/user/aoho/go-workspace
export GOBIN=$GOPATH/bin
export PATH=$PATH:$GOBIN:$GOROOT/bin
複製代碼
安裝完成以後,查看go的環境變量:go env
。docker
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
。json
GOROOT不是必需要設置的。默認go會安裝在/usr/local/go下,但也容許自定義安裝位置,GOROOT的目的就是告知go當前的安裝位置,編譯的時候從GOROOT去找SDK的system libariry。bash
如上面展現的結果,筆者使用的就是默認的安裝地址,也能夠經過 export GOROOT=$HOME/go1.9.3
指定。微信
GOPATH必需要設置,但並非固定不變的。GOPATH的目的是爲了告知go,須要代碼的時候,去哪裏查找。注意這裏的代碼,包括本項目和引用外部項目的代碼。GOPATH能夠隨着項目的不一樣而從新設置。maven
GOPATH下會有3個目錄:src、bin、pkg。ide
上面小節提到,依賴的代碼去$GOPATH
指定的位置尋找,這部分代碼多是本項目或者外部引用的項目。下面依次介紹這兩種狀況。
如筆者示例route_auth.go的引入:
import (
"gwp/Chapter_2_Go_ChitChat/chitchat/data"
"net/http"
)
複製代碼
route_auth.go須要引用data/user.go,項目結構以下:
編譯時會去$GOPATH/src/
目錄去查找須要的代碼,所以只要上面data/user.go在$GOPATH/src/gwp/Chapter_2_Go_ChitChat/chitchat/data
裏面,go編譯的時候就能找到。
對於外部依賴的管理,在最開始go沒有像java使用maven來管理依賴包、包版本,而是直接使用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 語言原生包管理的缺陷:
依賴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基礎上開發了多個管理工具,比較經常使用的有godep、govendor glide等,go官方發佈了dep。
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
經過命令 godep restore
同步依賴庫,若是下載的項目中只有Godeps.json文件,而沒有包含第三庫則可使用godep restore這個命令將全部的依賴庫下來到$GOPATH\src
中用於開發。
godep restore執行時,godep會按照Godeps/Godeps.json內列表,依次執行go get -d -v來下載對應依賴包到GOPATH路徑下。
govendor是在vendor以後出來的,功能相對godep多一點,不過就核心問題的解決來講基本是同樣的。該工具將項目依賴的外部包拷貝到項目下的 vendor 目錄下,並經過 vendor.json 文件來記錄依賴包的版本,方便用戶使用相對穩定的依賴。
go get -u github.com/kardianos/govendor
複製代碼
如上的命令便可安裝govendor,govendor生成vendor目錄的時候須要2條命令:
govendor的依賴包主要有如下多種類型:
進入項目的根目錄。
# 建立 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,將會在後面的文章介紹,盡請期待。