Go的包管理工具(一)

aoho發表於2019-02-18

在前面轉載了系列文章: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,專案結構如下:

Go的包管理工具(一)

編譯時會去$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這個命令將所有的依賴庫下來到$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的依賴包主要有以下多種型別:

Go的包管理工具(一)

使用步驟

進入專案的根目錄。

# 建立 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依賴包管理工具對比

相關文章