Golang專案管理實踐一--Golang包管理特點以及Glide工具的使用
文章內容
- Golang包管理的特點
- Golang包管理的注意點
- 結合Glide工具進行包管理實踐
- 總結
由於Golang特殊的包管理策略,同時,目前並沒有成熟的包管理工具,因此需要我們在管理Golang專案時,需要首先考慮適合專案的一種包管理策略,已達到“ReproducibleBuild”的最終目的;
注:文章基於Golang 1.7+
1.Golang包管理的特點
Golang的包管理同其他語言的兩個鮮明的特點是:
簡潔:可通過get方便獲取依賴包
依賴和目錄結構強關聯
程式碼和外部依賴未分離
1.1.簡潔
go get github.com/golang/tools
僅此一條命令,就可以將github中repository新增到當前的$GOPATH/src目錄之下,並且可以直接使用;這很符合“簡潔比程式碼複用更重要”的思想,但是會存在以下幾個問題:
- 如果有牆?
- 如果要使用第三方庫的某個特定的版本?
簡單的go get都無法在以上兩種情況下體現他的優勢;
1.2.依賴和目錄結構強關聯
首先,Golang預設會在以下目錄中查詢依賴:
- 當前package目錄下的vendor目錄(當前package必須在$GOPATH下)
- 當前package目錄上層的vendor目錄
- $GOROOT環境變數目錄下
- $GOPATH環境變數下
在編譯Golang程式時,會按照此順序依次查詢依賴,知道找到為止,注意“當前package必須在
對比JAVA,當前目錄是預設的CLASSPATH;
好了,說會依賴與目錄強關聯的問題,比如還是上面的依賴:
import "github.com/golang/tools"
...
tool.Func()
...
如果把程式碼放在了,<根據上面規則確定的依賴目錄>/example.com/golang/tools下:
這種情況會出現在比如公司想在某個開源庫上做二次開發,有可能你會需要對原始碼直接進行修改
牆的問題,你會考慮將第三方庫clone到公司的私有庫中
那麼,新的依賴將會是
import "example.com/golang/tools"
...
tool.Func()
...
那麼問題是什麼呢?
對於,新編寫的模組程式碼是沒有影響,而對於以前的程式碼以及包內部的依賴將會是災難的,所有和這個包相關的依賴都不可用,需要額外的增加修改或者移動操作。
1.3.程式碼和外部依賴未分離
預設情況下,go get 下來的第三方包和專案原始碼在$GOPATH下是屬於同等公民,新的Vendor機制能夠一定程度上緩解這個問題,但是,這種將程式碼和依賴混在一起的問題總是會給專案程式碼管理帶來額外的工作;
對比JAVA相關的專案,我們可以增加一個lib目錄,專門由於存放所有外部jar包,而在原始碼中只需要通過一個pom檔案來維護專案的依賴;其實在$GOPATH下也有一個類似的pkg目錄,用於存放所有依賴編譯後的依賴(.a),但是由於他們是平臺相關的,也就不能像jar包一樣,所有的依賴都是通過原始碼編譯的—>原始碼的話又會和專案程式碼混在一起—>………
2. Golang包管理的注意點
根據自己實踐總結下來,有以下幾個主要需要關注的問題:
- 牆,因為它使得我們使用Golang的體驗大打折扣
- 依賴的版本管理
- 原始碼和依賴的分離
3. 結合Glide工具進行包管理實踐
由於以上的問題,github上誕生了很多Golang包管理的工具,它們各具特色,下面是比較官方的一個對比:
官方包管理工具對比
當然,你也可以自己寫一個,其實通過一些簡單的批處理命令就能解決上面的主要問題;
而我們選擇了Glide,主要看中一下幾點:
- 能夠在已有的專案上重建依賴關係(glide.yaml)
- 能通過mirror解決牆的問題,但是不能徹底解決需要做些調整來解決
- 解決依賴版本問題,可以細化到某一個branch/tag/commit
- 快取,同一個repo,同一個版本的依賴會快取在使用者目錄中
- 解決目錄和依賴關聯問題,通過定義glide.lock檔案,import的目錄和實際repo可以不一致
Glide的官方文件對於此工具的使用已經進行了比較詳細的闡述,這裡就不累述了,主要針對目前遇到的兩種情況,給出實踐建議:
3.0.相關概念
glide.yaml
當前專案的依賴關係(直接依賴)
glide.lock
這是定義專案依賴的唯一的檔案,當前專案所有依賴以及版本資訊,glide install 下載依賴時直接使用這個檔案,glide update修改這個檔案;與glide.yaml檔案格式基本一致,而glide.yaml只提供update使用;
vender目錄
Glide支援Vender化的包管理,將下載的依賴放在當前目錄的vender子目錄中,對於當前目錄下的子目錄都是有效的;
3.1.對於已有專案使用Glide進行Vendor化
首先通過update幫助生成基本的依賴關係,在進行具體的修改,包括版本、repo地址
glide init
掃描當前目錄,獲取直接依賴關係,寫入到當前目錄的glide.yaml
glide update
遞迴掃描整個目錄的依賴,可以使用嚮導設定是否使用release版本的依賴;在進行的過程中,可能會提示超時問題,通過glide mirror 設定映象,使得update能夠向下查詢依賴關係。重複幾次之後,直到glide.lock檔案生成,對於需要單獨解決的依賴問題,可以再glide.yaml中設定忽略;
修改glide.lock
解決一些特殊的依賴(比如,模組間依賴,被牆),可以很靈活的定義使用庫;嘗試
使用update生成glide.lock時會遇到一個特例,如果一個依賴只是一個子包,比如
github.com/golang/tools/present
而glide會預設把它當做一個repo來處理,即他回到https://github.com/golang/tools/present.git去獲取程式碼,這顯然是不會成功的;對於這種情況,可以先忽略,在生成的glide.lock中單獨通過subpackage的方式新增;
3.2.新建專案的Vender化建議
直接定義glide.lock檔案,就像使用Maven時定義pom檔案一樣,不要輕易使用update 命令;
4. 總結
雖然,Golang本身的包管理存在一定的問題,在加上牆的緣故,需要我們做更多的考慮;但是結合Vender化的機制和一些優秀的包管理工具,問題還是能夠解決;而我們也可以看出,Golang至始至終都體現了“簡潔大於程式碼重用”的思想。