對於從 ruby、python 或者 node 等程式語言轉向 go 語言的開發者,可能會有乙個疑問: go 語言中的包依賴關係是怎麼管理的?有沒有什麼方便使用的工具呢? 我最近研究了一下這個問題,以下是我的研究報告。
在 go 語言中,我們可以使用go get
命令安裝遠端倉庫中託管的**,不同於 ruby gem、pypi 等集中式的包管理機制, go 語言的包管理系統是去中心化的。簡單來講,go get
命令支援任何乙個位置託管的 git 或 mercurial 的倉庫,無論是 github 還是 google code 上的包,都可以通過這個命令安裝。
我們知道,在 go 語言中的import
語句對於已經使用go get
安裝到本地的包,依然要使用其去絕對路徑引入。 比如對於從 github 上安裝的 goji,其在 github 上的路徑 url 是,因此在
import
它的時候需要使用下面的**:
正因為如此,go 語言可以通過直接分析**中的import
語句來查詢依賴關係。go get
命令在執行時,就會自動解析import
來安裝所有的依賴。
除了go get
,go 語言還提供了乙個 workspace 的機制,這個機制也是很容易讓人困惑的設計。簡單來說就是通過設定gopath
環境變數,指定除了goroot
所指定的目錄之外,go **所在的位置(也就是 workspace 的位置)。 一般來說,gopath
目錄下會包含pkg
、src
和bin
三個子目錄,這三個目錄各有用處。
我的gopath
目錄樹如下所示:
1234567
891011
1213
├── bin├── pkg
│ └── darwin_amd64
│ └── github.com
│ └── zenazn
│ └── goji
└── src
├── code.google.com
│ └── p
│ └── go.crypto
└── github.com
└── zenazn
└── goji
一般來說,你自己的**不應該直接放置在src
目錄下,而應該為其建立對應的專案資料夾。go get
也會把第三方包的源**放到這個目錄下,因此一般推薦設定兩個gopath
,比如:
1
export gopath="/usr/local/share/go:$home/codes/go"
這樣第三方包就會預設放置在第乙個路徑中,而你可以在第二個路徑下編寫自己的**。 雖然 go 語言本身已經提供了相當強大的包管理方式了,但是仍然有一些不足:
不能很方便地隔離不同專案的環境
不能很方便地控制某個依賴包的版本
不能管理 go 本身的版本
因此我們還需要一些第三方的工具來彌補這些缺陷。
由於存在gopath
的機制,我們可以使用多個gopath
來實現專案隔離的方法。 譬如,對於每個專案,都分配乙個不同的路徑作為gopath
。 可以實現這樣的目的的工具有**p等。
對於 **p 來說,想要針對當前目錄建立乙個gopath
,只需要執行**p init
即可。 **p 會在當前專案的目錄下新建乙個隱藏的資料夾作為gopath
指向的位置。 切換環境時使用下面兩個命令來修改環境變數。這種做法跟 python 中的virtualenv比較類似。
12
source **p in# 進入當前目錄對應的 gopath 環境
source **p out # 登出當前目錄對應的 gopath 環境
至於對依賴包更版本更細緻的管理,可以配合的工具還有 gpm。gpm
有點類似於 python 中的pip工具。他可以生成乙個名為godeps
的檔案, 其中記錄了每個依賴包的 url 以及使用的版本(hash tag)。 之前的一篇文章提到gpm
只能管理來自 github 的依賴,不過當前的版本已經支援了非 git 方式託管的依賴包了。
基於同樣原理管理依賴包版本的工具還有godep。 這個工具在 github 上具有相當高的關注度。它所生成的godeps
檔案採用 json 格式儲存, 是乙個跟 node.js 中 npm 相仿的工具。
總體來說以上幾個工具已經可以解決隔離專案環境和控制依賴包版本的問題了。但是使用上還不算方便, 為了能在我們 cd 到某個目錄時自動的切換環境變數,我們可能還需要在 shell 做一些配置使其在cd
到專案目錄下時自動切換環境變數。
這方面做的比較好的乙個選擇是 go manager(gom), 它生成的gomfile
格式上幾乎跟 ruby gem 一樣。gom 可能是這些工具當中使用最方便的乙個, 只要使用gom build
命令代替原來的go build
命令進行編譯,你基本不需要配置 shell 或者和環境變數打交道。
對於 go 語言,一般來說並沒有使多個語言版本並存的需求。go 語言現在還沒有經歷過類似 python 2.x 到 3.x 或者 ruby 1.x 到 2.x 這樣破壞性的版本公升級。舊的**在新的語言版本當中一般是能夠正確執行的。 不過若遇到非要並存多個版本的時候,**m就是乙個不錯的選擇。
**m 的使用跟 rvm 比較類似。
12
**m install go1 # 安裝 go1 版本**m use go1 # 修改環境變數使用 go1 版本的 go
是否有必要使用多個 workspace 仍然具有爭議,譬如這個 stackoverflow 上的相關問答中, 就有人提出只使用乙個 workspace 就可以應付大多數情況了。
在研究相關問題的時候,我發現很多 go 語言的使用者都還帶著原來程式語言的思維, 這點從上面介紹的多個工具的特點當中就可以很容易看出來:**p
和gpm
就是典型的 python 的包管理模式,**p
對應著virtualenv
,gpm
對應著pip
;如果你之前是 node.js 和 npm 的使用者, 那麼godeps
肯定會讓你有種熟悉的感覺;更不用說最後介紹的gom
了,它從名稱到檔案格式都在模仿 ruby gem。
不同程式設計背景的開發者來到 go 語言之後各自帶來了自己的依賴包管理方式,而且形成了各自的社群。 這種現象雖然使得各自圈子的開發者免去了選擇恐懼症,但是造成的解決方案**和互不相容的情況也需要正視。 這時我們不禁要問,go 自己的解決方式應該是什麼樣的?go 語言為何沒有乙個官方標準的解決方案呢?
從go faq的一段文字當中我們可以得到部分答案:
因此現階段來看,對於 go 語言的包管理解決方案,我們也就只能「仁者見仁,智者見智」了。
(三)Go語言的依賴管理
一.理論概述 1.依賴的概念 我們不可能所有的編碼都要自己寫,我們需要大量引用第三方的庫,依賴它來進行編譯。2.依賴管理的三個階段gopath,govendor,go mod a.gopath gopath顧名思義,就是go的環境目錄,有以下幾個特點 預設在 go unix,linux userus...
go 語言 Makefile 指定依賴包位置
編譯 go 程式可以使用自帶的一些 makefile 指令碼來簡化編寫 makefile 官方的文件過於簡略,沒提到需要指定依賴包位置的方法。翻過那幾個指令碼 後,發現原來有 ldimports 和 gcimports 可以指定。比如 include goroot src make.inc ldim...
依賴的包 vs code 安裝go依賴包失敗
提示類似下面的資訊,設定一下 即可。錯誤提示installing github.com uudashr gopkgs v2 cmd gopkgs failedinstalling github.com acroca go symbols failedinstalling github.com hay...