1. 程式人生 > 程式設計 >Go外部依賴包從vendor,$GOPATH和$GOPATH/pkg/mod查詢順序

Go外部依賴包從vendor,$GOPATH和$GOPATH/pkg/mod查詢順序

vendor

vendor概念最早是由Keith提出,用來存放依賴包。在版本1.5出現。例如gb專案提供了一個名為gsftp的示例專案,它有一個gsftp程式,在標準庫之外有三個依賴項。golang.org/x/crypto/ssh,golang.org/x/crypto/ssh/agent和github.com/pkg/sftp。

按vendor概念調整的目錄結構如下:

$GOPATH
|	src/
|	|	github.com/constabulary/example-gsftp/
|	|	|	cmd/
|	|	|	|	gsftp/
|	|	|	|	|	main.go
|	|	|	vendor/
|	|	|	|	github.com/pkg/sftp/
|	|	|	|	golang.org/x/crypto/ssh/
|	|	|	|	|	agent/

檔案github.com/constabulary/example-gsftp/cmd/gsftp/main.go的引用如下所示,看起來和平時的引用沒什麼區別。

import (
 ...
 "golang.org/x/crypto/ssh"
 "golang.org/x/crypto/ssh/agent"
 "github.com/pkg/sftp"
)

因為github.com/constabulary/example-gsftp/vendor/golang.org/x/crypto/ssh存在,且正在編譯的檔案位於以github.com/constabulary/example-gsftp為根的子樹中。則匯入語句import "golang.org/x/crypto/ssh",編譯好像是import "github.com/constabulary/example-gsftp/vendor/golang.org/x/crypto/ssh",只不過這種較長的形式從未寫過。

因此github.com/constabulary/example-gsftp中的原始碼取決於vendor中有關golang.org/x/crypto/ssh的拷貝副本,而不是$GOPATH中的其他地方。

vendor的層級搜尋

規則是:

從引用檔案所在的vendor路徑下面搜尋,

如果沒有找到,那麼從上層目錄的vendor路徑下面搜尋,

直到src的vendor路徑下面搜尋。

modules

Go 1.11版本支援臨時環境變數GO111MODULE,通過該環境變數來控制依賴包的管理方式。當GO111MODULE的值為on時,那麼就會使用modules功能,這種模式下,$GOPATH不再作為build時匯入的角色,依賴包會存放在$GOPATH/pkg/mod目錄下。工程中的依賴包也會從此目錄下查詢。

有關該功能的介紹,可以看Go1.1.1新功能module的介紹及使用。

查詢順序

GO111MODULE=off時,如果一個包在vendor和$GOPATH下都存在,那麼使用順序為:

優先使用vendor目錄下面的包,

如果vendor下面沒有搜尋到,再搜尋$GOPATH/src下面的包,

如果$GOPATH下面沒有搜尋到,那麼搜尋$GOROOT/src下面的包,

要麼完整使用vendor下面的包,要麼完整使用$GOPATH下面的包,不會混合使用。

補充:Go 語言程式設計 — go mod 依賴包管理

使用 go mod

go mod 的子指令

go mod 依賴包管理

Golang 將 Modules(模組)定義為 “原始碼交付和版本控制的單元”。

go mod 是 Golang 1.11 版本引入的依賴包管理工具,替換舊的基於 GOPATH 的依賴包管理方式。GOPATH 不再需要包含 src 子目錄,使用 go.mod 方式管理的依賴包都被下載到了 $GOPATH/pkg/mod 目錄。

有了 go mod 之後,每個專案(原始碼目錄)就是一個 Module,只要其中包含了 go.mod 檔案。go mod 指令和 go.mod 檔案用於記錄和解析模組之間的依賴性。

go.mod 檔案一旦建立後,它的內容將會被 go toolchain(工具鏈)完全掌控,比如:執行 go get、go build、go mod 等指令時,會自動修改和維護 go.mod 檔案。

除了 go.mod 之外,go 指令還維護了一個 go.sum 檔案,其中包含特定模組版本內容的加密雜湊。go 指令使用 go.sum 檔案來確保這些模組的雜湊值,以確保專案所依賴的模組不會出現意外更改。

go.mod 和 go.sum 都應該應用於版本控制。 並且 go.sum 不需要手工維護,所以可以不用太關注。

使用 go mod

使用 Go Modules 需要配置 GO111MODULE 環境變數,GO111MODULE 有三個值:off、on 和 auto(預設值):

GO111MODULE=off:關閉 Go Modules,沿用舊版本的 GOPATH 方式,專案的依賴包依舊在 vendor 目錄或者 $GOPATH/src 目錄下進行查詢。

GO111MODULE=on:開啟 Go Modules,專案的依賴包記錄在 go.mod 檔案中。

GO111MODULE=auto:go 指令根據當前目錄來決定是否啟用 Modules 功能。

當專案處於 GOPATH/src 內,則會使用 GOPATH/src 的依賴包。

當專案處於 GOPATH/src 外,則使用 go.mod 裡 require 宣告的依賴包。

使用 go.mod 的流程:

在 $GOPATH/src 目錄之外建立 Golang 專案。

初始化專案的 go.mod 檔案。

$ go mod init <projectNmae>

注意,專案下屬的子目錄是不需要再次 init 的,整個專案的依賴都會組織在根目錄的 go.mod 檔案裡。

匯入外部包:

package main

import (
 "fmt"
 "github.com/gohouse/gorose"
)

func main() {
 fmt.Println("hello world!")
}

執行程式時,會自動下載依賴包並更新 go.mod 檔案:

$ go run hello.go
go: finding module for package github.com/gohouse/gorose
go: downloading github.com/gohouse/gorose v1.0.5
go: found github.com/gohouse/gorose in github.com/gohouse/gorose v1.0.5
go: finding module for package github.com/gohouse/converter
go: downloading github.com/BurntSushi/toml v0.3.0
go: downloading github.com/gohouse/converter v0.0.3
go: found github.com/gohouse/converter in github.com/gohouse/converter v0.0.3
go: downloading github.com/go-sql-driver/mysql v1.4.0
# command-line-arguments
./hello.go:5:5: imported and not used: "github.com/gohouse/gorose"

檢查 go.mod 檔案中自動更新的依賴清單:

module hello

go 1.14

require (
 github.com/gohouse/converter v0.0.3 // indirect
 github.com/gohouse/gorose v1.0.5 // indirect
)

其中 require 是一個關鍵字,此外還有 module、replace 和 exclude:

module 語句:指定模組的名字(路徑)。

require 語句:指定的依賴模組。

replace 語句:可以替換依賴項模組。

exclude 語句:可以忽略依賴項模組。

另外,上述的 v0.0.3 就是依賴包 converter 的版本。如果沒有在 go.mod 指定版本,go 指令就會自動下載程式碼中的依賴的最新版本。否則,go 指令會根據 require 語句的路徑和版本進行下載。指定版本時也可以用 latest,表示下載最新的版本。

replace 語句常用於依賴包實際路徑變更的場景中,例如:

replace golang.org/x/text => github.com/golang/text latest

如此的,在這種情況下依賴包的維護會變得更簡單。

還需要注意 go.mod 中的 module hello 這一條語句,因為當前專案已經不在 $GOPATH/src 內了,所以 module hello 標註了專案 “根” 的路徑,以此來作為專案中其他包的根路徑。

go mod 的子指令

Go外部依賴包從vendor,$GOPATH和$GOPATH/pkg/mod查詢順序

初始化一個 Module:

go mod init <project_name>

下載專案依賴的 modules 到本地 Cache:

# 在專案目錄下執行

go mod download

注:目前所有模組版本資料均快取在 $GOPATH/pkg/mod 和 $GOPATH/pkg/sum 下。

編輯 go.mod 檔案,選項有 -json、-require 和 -exclude:

go mod edit
# e.g.
go mod edit -replace=golang.org/x/[email protected]=github.com/golang/crypto@latest

以文字模式列印模組需求圖:

go mod graph

刪除錯誤或者不使用的 Modules:

go mod tidy

生成 Vendor 目錄:

go mod vendor

驗證依賴是否正確:

go mod verify

查詢依賴:

go mod why

清理 Modules 快取:

go clean -modcache

以上為個人經驗,希望能給大家一個參考,也希望大家多多支援我們。如有錯誤或未考慮完全的地方,望不吝賜教。