官方原文: https://github.com/golang/go/wiki/Modulesgit
Go 1.11包括此處建議的對版本模塊的初步支持。模塊是Go 1.11中的實驗性加入功能,並計劃歸入反饋並最終肯定 Go 1.14中的功能。即便某些細節可能會更改,未來的發行版也將支持使用Go 1.十一、1.12和1.13定義的模塊。github
最初的原型vgo
於2018年2月宣佈。2018年7月,版本化的模塊進入了主Go存儲庫。golang
Go 1.13中對模塊進行了重大改進和更改。shell
若是使用模塊,請務必仔細閱讀Go 1.13發行說明的模塊部分。數據庫
三個值得注意的變化:json
該go
工具如今默認爲從https://proxy.golang.org上的公共Go模塊鏡像下載模塊,而且還默認爲針對https://sum.golang.org上的公共Go校驗和數據庫驗證下載的模塊(不管源如何)。vim
GOPRIVATE
設置(例如go env -w GOPRIVATE=*.corp.com,github.com/secret/repo
),或者配置更細粒度的變體,GONOPROXY
或者GONOSUMDB
支持使用頻率較低的用例。有關更多詳細信息,請參見文檔。GO111MODULE=auto
若是找到任何go.mod,即便在GOPATH內部,也將啓用模塊模式。(在Go 1.13以前,GO111MODULE=auto
永遠不會在GOPATH中啓用模塊模式)。api
go get
參數已更改:緩存
go get -u
(不帶任何參數)如今僅升級當前軟件包的直接和間接依賴關係,而再也不檢查整個模塊。go get -u ./...
從模塊根目錄升級模塊的全部直接和間接依賴關係,如今不包括測試依賴關係。go get -u -t ./...
類似,但也升級了測試依賴項。go get
再也不受支持-m
(由於go get -d
因爲其餘更改,它會在很大程度上與重疊;您一般能夠替換go get -m foo
爲go get -d foo
)。請參閱發行說明,以獲取有關這些更改和其餘更改的更多詳細信息。
詳細信息將在本頁面的其他部分中介紹,但這是一個從頭開始建立模塊的簡單示例。
在GOPATH以外建立目錄,並可選地初始化VCS:
$ mkdir -p /tmp/scratchpad/repo $ cd /tmp/scratchpad/repo $ git init -q $ git remote add origin https://github.com/my/repo
初始化一個新模塊:
$ go mod init github.com/my/repo go: creating new go.mod: module github.com/my/repo
編寫代碼:
$ cat <<EOF > hello.go package main import ( "fmt" "rsc.io/quote" ) func main() { fmt.Println(quote.Hello()) } EOF
構建並運行:
$ go build -o hello $ ./hello Hello, world.
該go.mod
文件已更新爲包括依賴項的顯式版本,v1.5.2
這裏是語義化版本號標籤:
$ cat go.mod module github.com/my/repo require rsc.io/quote v1.5.2
請注意,go get
上面的示例中沒有要求。
典型的平常工做流程能夠是:
.go
根據須要將導入語句添加到您的代碼中。go build
或)go test
會根據須要自動添加新的依賴關係,以實現導入(更新go.mod
和下載新的依賴關係)。go get foo@v1.2.3
,go get foo@master
(foo@tip
帶有Mercurial)go get foo@e3702bed2
,或go.mod
直接編輯等命令來選擇更具體的依賴關係版本。您可能會使用的其餘常見功能的簡要介紹:
go list -m all
—查看將在構建中用於全部直接和間接依賴關係的最終版本(詳細信息)go list -u -m all
—查看全部直接和間接依賴項的可用次要和補丁升級(詳細信息)go get -u ./...
或go get -u=patch ./...
(從模塊根目錄)—將全部直接和間接依賴關係更新爲最新的次要或補丁升級(忽略預發行版)(詳細信息)go build ./...
或go test ./...
(從模塊根目錄)—構建或測試模塊中的全部軟件包(詳細信息)go mod tidy
從go.mod
OS,架構和構建標籤的其餘組合中修剪全部不須要的依賴項,並添加其餘依賴項所需的任何依賴項(詳細信息)replace
指令或gohack
—使用派生,本地副本或依賴項的確切版本(詳細信息)go mod vendor
—建立vendor
目錄的可選步驟(詳細信息)在閱讀了有關「新概念」的下四個部分以後,您將得到足夠的信息來開始使用大多數項目的模塊。查看上面的目錄(包括此處的FAQ常見問題解答)以使本身熟悉更詳細的主題列表也頗有用。
這些部分對主要的新概念進行了高級介紹。有關更多詳細信息和原理,請觀看Russ Cox的這段40分鐘的介紹性視頻,其中介紹了設計背後的理念,正式的建議文檔或更爲詳細的初始vgo博客系列。
一個模塊是一些以版本做爲單元相關的包的集合。
模塊記錄精確的依賴要求並建立可複製的構建。
一般,版本控制存儲庫僅包含在存儲庫根目錄中定義的一個模塊。(單個存儲庫中支持多個模塊,可是一般,與每一個存儲庫中的單個模塊相比,這將致使正在進行的工做更多)。
總結存儲庫,模塊和軟件包之間的關係:
模塊必須根據在語義版本語義化版本號,一般在形式v(major).(minor).(patch)
,如 v0.1.0
,v1.2.3
,或v1.5.0-rc.1
。領導v
是必需的。若是使用Git,則標記發佈會提交其版本。公共和私有模塊存儲庫和代理均可以使用(請參閱下面的常見問題解答)。
模塊由Go源文件樹定義,該go.mod
文件在樹的根目錄中。模塊源代碼可能位於GOPATH以外。有四種指令:module
,require
,replace
,exclude
。
這是go.mod
定義模塊的示例文件github.com/my/thing
:
module github.com/my/thing require ( github.com/some/dependency v1.2.3 github.com/another/dependency/v4 v4.0.0 )
模塊go.mod
經過module
提供模塊path的指令在其聲明中聲明其身份。模塊中全部軟件包的導入路徑將模塊路徑共享爲公共前綴。模塊路徑和從go.mod
到軟件包目錄的相對路徑共同肯定了軟件包的導入路徑。
例如,若是要爲存儲庫建立一個模塊,該模塊github.com/my/repo
將包含兩個帶有導入路徑github.com/my/repo/foo
和的軟件包github.com/my/repo/bar
,則go.mod
文件中的第一行一般會將模塊路徑聲明爲module github.com/my/repo
,相應的磁盤結構能夠是:
repo |-- bar | `-- bar.go |-- foo | `-- foo.go `-- go.mod
在Go源代碼中,將使用完整路徑(包括模塊路徑)導入軟件包。例如,若是一個模塊在go.mod
as中聲明其身份module example.com/my/module
,則使用者能夠執行如下操做:
import "example.com/my/module/mypkg"
這mypkg
將從模塊導入包example.com/my/module
。
exclude
而replace
指令僅在當前(「主」)模塊上運行。在構建主模塊時,將忽略除主模塊之外的其餘模塊中的指令exclude
和replace
指令。該replace
和exclude
語句,所以,容許在本身的構建主要模塊的徹底控制權,也沒有受制於由依賴於徹底控制。(有關什麼時候使用指令的討論,請參見下面的常見問題解答replace
)。
若是您在源代碼中添加了一個還沒有被require
in 覆蓋的新導入,則go.mod
大多數go命令(例如「 go build」和「 go test」)將自動查找適當的模塊,並將該新直接依賴項的最高版本添加到您的模塊go.mod
是require
指令。例如,若是您的新導入對應於依賴項M,其最新標記版本爲v1.2.3
,則模塊的go.mod
結尾將爲require M v1.2.3
,這表示模塊M是容許版本> = v1.2.3(且<v2,由於v2被認爲不兼容)的依賴項與v1)。
在最小的版本選擇算法用來選擇在構建中使用的全部模塊的版本。對於構建中的每一個模塊,經過最小版本選擇選擇的版本始終是主模塊中的指令或其依賴項之一明確列出的版本的語義上最高的版本require
。
例如,若是您的模塊依賴於具備A的模塊A require D v1.0.0
,而您的模塊也依賴於具備A的模塊B require D v1.1.1
,則最小的版本選擇將選擇v1.1.1
D包含在構建中(假設它是列出的最高require
版本)。v1.1.1
即便稍後某個v1.2.0
D可用,對D的選擇仍保持一致。這是模塊系統如何提供100%可複製構建的示例。準備就緒後,模塊做者或用戶能夠選擇升級到D的最新可用版本,或爲D選擇一個顯式版本。
有關最小版本選擇算法的簡要原理和概述,請參閱官方建議書的「高保真度構建」部分,或查看更詳細的vgo
博客系列。
要查看所選模塊版本的列表(包括間接依賴關係),請使用go list -m all
。
另請參見下面的「如何升級和降級依賴項」部分和「如何將版本標記爲不兼容?」 下面的常見問題解答。
多年以來,官方的Go常見問題解答已在軟件包版本管理中包括如下建議:
「面向公共用途的軟件包應在發展過程當中嘗試保持向後兼容性。Go1兼容性指南在此處是很好的參考:請勿刪除導出的名稱,鼓勵帶標籤的複合文字等等。若是須要不一樣的功能,請添加一個新名稱,而不是更改舊名稱。若是須要徹底中斷,請建立一個具備新導入路徑的新軟件包。」
最後一句特別重要-若是破壞兼容性,則應更改軟件包的導入路徑。使用Go 1.11模塊,該建議被正式化爲導入兼容性規則:
「若是舊軟件包和新軟件包具備相同的導入路徑,則新軟件包必須與舊軟件包向後兼容。」
當v1或更高版本的軟件包進行向後不兼容的更改時,召回語義化版本號須要對主要版本進行更改。遵循導入兼容性規則和語義化版本號的結果稱爲語義導入版本控制,其中主要版本包含在導入路徑中-這可確保在主要版本因爲兼容性中斷而增長時,導入路徑都會更改。
因爲語義導入版本控制,選擇加入Go模塊的代碼必須遵照如下規則:
v1.2.3
)。/vN
在go.mod
文件(例如module github.com/my/mod/v2
,require github.com/my/mod/v2 v2.0.1
)和包導入路徑(例如import "github.com/my/mod/v2/mypkg"
)中使用的模塊路徑的末尾將主要版本做爲包括在內。這包括go get
命令中使用的路徑(例如,go get github.com/my/mod/v2@v2.0.1
請注意,在該示例中同時包含a /v2
和a @v2.0.1
。一種考慮方式是模塊名稱如今包括/v2
,所以/v2
不管什麼時候使用模塊名稱,都包括)。一般,具備不一樣導入路徑的軟件包是不一樣的軟件包。例如,與math/rand
是不一樣的軟件包crypto/rand
。若是不一樣的導入路徑是因爲導入路徑中出現的主要版本不一樣而致使的,則也是如此。所以,與example.com/my/mod/mypkg
包是一個不一樣的包example.com/my/mod/v2/mypkg
,二者均可以在一個單一版本中導入,這除其餘優勢外還有助於解決鑽石依賴問題,而且還容許在替換v2方面實施v1模塊,反之亦然。
有關語義導入版本控制的更多詳細信息,請參見命令文檔的「模塊兼容性和語義版本控制」部分,go
有關語義版本控制的更多信息,請參見https://語義化版本號.org。
到目前爲止,本節的重點是已選擇加入模塊並導入其餘模塊的代碼。可是,將主要版本置於v2 +模塊的導入路徑中可能會與Go的較早版本或還沒有選擇加入模塊的代碼產生不兼容性。爲了解決這個問題,上述行爲和規則有三種重要的過渡性特殊狀況或例外。隨着愈來愈多的程序包加入模塊,這些過渡性異常將再也不重要。
三個例外:
gopkg.in
使用導入路徑gopkg.in
(以gopkg.in/yaml.v1
和開頭)的現有代碼gopkg.in/yaml.v2
即便選擇加入模塊,也能夠繼續將這些格式用於其模塊路徑和導入路徑。
導入非模塊v2 +軟件包時爲「 +不兼容」
模塊能夠導入還沒有選擇加入模塊的v2 +軟件包。具備有效v2 + 語義化版本號標籤的非模塊v2 +軟件包將+incompatible
在導入模塊的go.mod
文件中記錄後綴。該+incompatible
後綴表示,即便V2 +包有一個有效的V2 + 語義化版本號標籤,例如v2.0.0
,使V2 +包沒有主動選擇的模塊和假設,所以該V2 +包都沒有被與語義進口版本的含義的理解產生以及如何在導入路徑中使用主要版本。所以,當以模塊模式運行時,go
該工具會將非模塊v2 +軟件包視爲該軟件包的v1版本系列的(不兼容)擴展,並假定該軟件包不瞭解語義導入版本控制,而且+incompatible
後綴表示該go
工具正在這樣作。
未啓用模塊模式時的「最小模塊兼容性」
爲了幫助向後兼容,對Go版本1.9.7 +,1.10.3 +和1.11進行了更新,以使使用這些發行版構建的代碼可以更輕鬆地正確使用v2 +模塊,而無需修改現有代碼。此行爲稱爲「最小模塊兼容性」,而且僅在禁用該工具的完整模塊模式時才生效go
,例如您GO111MODULE=off
在Go 1.11中進行了設置,或者正在使用Go 1.9.7+或1.10.3版本+。當依靠Go 1.9.7 +,1.10.3 +和1.11中的這種「最小模塊兼容性」機制時,未選擇模塊的軟件包將不會在任何導入的v2 +模塊的導入路徑中包含主版本。相比之下,已選擇在模塊必須包括在導入路徑主要版本導入任何V2 +模塊(爲了正確導入V2 +模塊時的go
工具在全模塊模式語義進口版本的充分認識工做)。
有關發佈v2 +模塊所需的確切機制,請參閱下面的「發佈模塊(v2或更高版本)」部分。
要使用模塊,兩個安裝選項是:
master
分支上的源代碼安裝Go工具鏈。安裝後,您能夠經過如下兩種方式之一激活模塊支持:
go
在$GOPATH/src
樹以外的目錄中調用命令,並go.mod
在當前目錄或其任何父目錄中使用有效文件,而且未GO111MODULE
設置(或顯式設置爲auto
)環境變量。go
帶有GO111MODULE=on
環境變量設置的命令。爲go.mod
現有項目建立一個:
導航到GOPATH以外的模塊源代碼樹的根目錄:
$ cd <project path outside $GOPATH/src> # e.g., cd ~/projects/hello
請注意,在GOPATH以外,您無需進行設置GO111MODULE
便可激活模塊模式。
或者,若是要在GOPATH中工做:
$ export GO111MODULE=on # manually active module mode $ cd $GOPATH/src/<project path> # e.g., cd $GOPATH/src/you/hello
建立初始模塊定義並將其寫入go.mod
文件:
$ go mod init
此步驟從任何現有文件或其餘全部9種受支持的依賴項格式中的任何一種轉換,添加require語句以匹配現有配置。dep
Gopkg.lock
go mod init
一般可使用輔助數據(例如VCS元數據)來自動肯定適當的模塊路徑,可是若是go mod init
狀態不能自動肯定模塊路徑,或者若是您須要以其餘方式覆蓋該路徑,則能夠提供模塊路徑做爲的可選參數go mod init
,例如:
$ go mod init github.com/my/repo
請注意,若是您的依賴項包括v2 +模塊,或者正在初始化v2 +模塊,則在運行後,go mod init
您可能還須要編輯go.mod
和.go
代碼,以添加/vN
導入路徑和模塊路徑,如上面「語義導入版本控制」部分所述。即便go mod init
自動從dep
或其餘依賴項管理器轉換了您的依賴項信息,這也適用。(所以,在運行以後go mod init
,一般go mod tidy
只有在成功運行go build ./...
或相似操做後才能運行,這是本節中顯示的順序)。
生成模塊。從模塊的根目錄執行時,該./...
模式將匹配當前模塊中的全部軟件包。 go build
會根據須要自動添加缺乏或未轉換的依賴項,以知足此特定構建調用的導入需求:
$ go build ./...
按照配置測試模塊,以確保它能夠與所選版本一塊兒使用:
$ go test ./...
(可選)運行模塊的測試以及全部直接和間接依賴項的測試,以檢查不兼容性:
$ go test all
在標記發行版以前,請參見下面的「如何準備發行版」部分。
有關全部這些主題的更多信息,可在golang.org上找到官方模塊文檔的主要入口點。
平常的依賴關係升級和降級應該使用「 go get」完成,它將自動更新go.mod
文件。或者,您能夠go.mod
直接編輯。
此外,執行「執行構建」,「執行測試」甚至「執行列表」之類的go命令將根據須要自動添加新的依賴關係,以知足導入要求(更新go.mod
和下載新的依賴關係)。
要查看全部直接和間接依賴項的可用次要和補丁升級,請運行go list -u -m all
。
要將當前模塊的全部直接和間接依賴關係升級到最新版本,能夠在模塊根目錄中運行如下命令:
go get -u ./...
使用最新的次要版本或補丁程序版本(並添加-t
以升級測試依賴項)go get -u=patch ./...
使用最新的補丁程序發行版(並添加-t
以升級測試依賴項)go get foo
更新到的最新版本foo
。go get foo
等效於go get foo@latest
—換句話說,@latest
若是未@
指定版本,則爲默認值。
在本節中,「最新」是帶有語義化版本號標籤的最新版本,或者若是沒有語義化版本號標籤則是最新的已知提交。除非存儲庫中沒有其餘語義化版本號標籤,不然不會將預發佈標籤選擇爲「最新」標籤(details)。
一個廣泛的錯誤是認爲go get -u foo
僅獲取最新版本的foo
。實際上,-u
in go get -u foo
或的go get -u foo@latest
意思是還得到的全部直接和間接依賴關係的最新版本foo
。升級時,一個共同的起點foo
是否是作go get foo
或go get foo@latest
沒有-u
(和後一切正常,能夠考慮go get -u=patch foo
,go get -u=patch
,go get -u foo
,或go get -u
)。
要升級或降級到一個更具體的版本,「去把」容許版本選擇經過添加一個後綴@version或覆蓋「模塊查詢」到包的說法,好比go get foo@v1.6.2
,go get foo@e3702bed2
或者go get foo@'<v1.6.2'
。
不管是否具備語義化版本號標記,使用分支名稱(例如go get foo@master
(foo@tip
帶有mercurial))都是獲取最新提交的一種方法。
一般,不能解析爲語義化版本號標籤的模塊查詢將做爲僞版本記錄在go.mod
文件中。
有關此處的主題的更多信息,請參見命令文檔的「支持模塊的獲取」和「模塊查詢」部分go
。
模塊可以使用還沒有加入模塊的軟件包,包括在其中記錄任何可用的語義化版本號標籤go.mod
並使用這些語義化版本號標籤進行升級或降級。模塊還可使用尚沒有任何適當語義化版本號標籤的軟件包(在這種狀況下,它們將使用中的僞版本進行記錄go.mod
)。
在升級或降級任何依賴項以後,您可能想要對構建中的全部軟件包(包括直接和間接依賴項)再次運行測試以檢查不兼容性:
$ go test all
建立模塊發行版的最佳實踐有望做爲初始模塊實驗的一部分出現。其中許多最終可能會由未來的「發佈」工具自動化。
在標記版本以前,應考慮一些當前建議的最佳作法:
運行go mod tidy
到可能修剪任何無關的要求(如描述在這裏),並確保您的當前go.mod反映了全部可能的堆積標籤/ OS /架構的組合(如描述在這裏)。
go build
而且go test
不會從中刪除依賴項)go.mod
再也不須要,而是僅go.mod
基於當前構建調用的標記/ OS /體系結構進行更新。運行go test all
以測試您的模塊(包括針對直接和間接依賴項運行測試),以驗證當前所選軟件包版本是否兼容。
go test all
已被從新定義爲更有用:經過一個或多個導入序列,將當前模塊中的全部軟件包以及它們所依賴的全部軟件包包括在內,而在其中將可有可無的軟件包排除在外當前模塊。若是要發佈v2或更高版本的模塊,請首先查看上面「語義導入版本控制」部分中的討論,其中包括爲什麼在v2 +模塊的模塊路徑和導入路徑中包含主要版本以及Go版本1.9的方式.7+和1.10.3+已更新,以簡化該過渡。
請注意,若是您是第一次v2.0.0
採用模塊,是爲了在採用模塊以前針對已存在標籤或更高版本的預先存在的存儲庫或軟件包集進行採用,那麼建議的最佳實踐是在首次採用模塊時增長主版本。例如,若是您是的做者foo
,而且foo
存儲庫的最新標記是v2.2.2
,而且foo
還沒有采用模塊,則最佳作法是v3.0.0
將第一個版本的foo
採用採用模塊(所以將第一個版本的foo
to做爲)。包含一個go.mod
文件)。在這種狀況下,增長主要版本可爲的使用者提供更大的清晰度foo
,從而容許在v2系列的其餘非模塊補丁或次要發行版上使用foo
若是須要,並提供了一個基於模塊的消費者一個強烈的信號foo
,不一樣的主要版本,若是你作的結果import "foo"
和相應的require foo v2.2.2+incompatible
,與import "foo/v3"
和相應require foo/v3 v3.0.0
。(請注意,關於遞增主要版本時,首先採用模塊不會這個建議並不適用於預先存在的回購或包,其最新版本v0.xx或v1.xx)。
有兩種替代機制能夠發佈v2或更高版本的模塊。請注意,使用這兩種技術,當模塊做者推送新標籤時,新模塊版本就能夠供消費者使用。以建立v3.0.0
發行版爲例,兩個選項是:
Major分支:更新go.mod
文件以/v3
在module
指令的模塊路徑末尾包含a (例如module github.com/my/module/v3
)。更新模塊中的import語句以也使用/v3
(例如import "github.com/my/module/v3/mypkg"
)。用標記發佈v3.0.0
。
v3.*.*
模塊的提交放在單獨的v3分支上。v3.0.0
在master上進行標記,那麼這是一個可行的選擇。(可是,要知道,在引入一個不兼容的API的變化master
可能會致使誰發出非模塊用戶的問題go get -u
給出的go
工具是不知道的語義化版本號以前去1.11或當模塊模式在Go 1.11+未啓用)。dep
當前之類的現有依賴關係管理解決方案在使用這種方式建立的v2 +模塊時可能會遇到問題。參見例如dep#1962。主要子目錄:建立一個新的v3
子目錄(例如my/module/v3
),而後go.mod
在該子目錄中放置一個新文件。模塊路徑必須以結尾/v3
。將代碼複製或移動到v3
子目錄中。更新模塊中的import語句以也使用/v3
(例如import "github.com/my/module/v3/mypkg"
)。用標記發佈v3.0.0
。
goforward
。請在此處查看更多詳細信息和基本原理,以及可正常運行的goforward
。dep
應該可以使用以這種方式建立的v2 +模塊。有關這些替代方案的更深刻討論,請參見https://research.swtch.com/vgo-module。
能夠經過將標籤推送到包含模塊源代碼的資源庫中來發布新的模塊版本。標籤是經過串聯兩個字符串造成的:前綴和版本。
該版本是該發行版的語義導入版本。應該按照語義導入版本控制規則進行選擇。
所述前綴指示其中模塊的存儲庫中定義的。若是模塊是在存儲庫的根目錄中定義的,則前綴爲空,而標記僅爲版本。可是,在多模塊存儲庫中,前綴區分不一樣模塊的版本。前綴是存儲庫中定義模塊的目錄。若是存儲庫遵循上述主要子目錄模式,則前綴不包括主要版本後綴。
例如,假設咱們有一個模塊example.com/repo/sub/v2
,而且咱們要發佈version v2.1.6
。存儲庫根目錄與相對應example.com/repo
,而且模塊sub/v2/go.mod
在存儲庫內定義。此模塊的前綴是sub/
。此版本的完整標籤應爲sub/v2.1.6
。
本節試圖簡要列舉遷移到模塊時要作出的主要決定,並列出其餘與遷移相關的主題。一般會提供其餘部分的參考,以獲取更多詳細信息。
該材料主要基於模塊實驗中社區中出現的最佳實踐。所以,這是一個進行中的部分,隨着社區得到更多的經驗,該部分將有所改善。
摘要:
遷移主題:
go mod init
將所需的信息從dep,glid,govendor,godep和其餘5個先前存在的依賴項管理器自動轉換爲go.mod
生成等效構建的文件。module
轉換後的指令中go.mod
包含相應的指令/vN
(例如module foo/v3
)。/vN
到從先前的依賴項管理器轉換後生成的require
語句中go mod init
。有關更多詳細信息,請參見上面的「如何定義模塊」部分。go mod init
將不會編輯您的.go
代碼以添加任何須要/vN
導入的語句。有關所需步驟,請參見上面的「語義導入版本控制」和「發佈模塊(v2或更高版本)」部分,包括圍繞社區工具進行自動轉換的一些選項。go mod vendor
禁用模塊模式時,Go的較早版本瞭解如何使用由建立的vendor目錄,Go 1.11和1.12+也是如此。所以,供應是模塊提供依賴的一種方式,該依賴提供了對不能徹底理解模塊的Go的較舊版本以及未啓用模塊自己的使用者的依賴。有關更多詳細信息,請參見vendor常見問題解答和go
命令文檔。go get -u foo
。若是要發佈模塊foo
,請考慮-u
爲基於模塊的使用者使用in指令。
-u
要求go
工具升級的全部直接和間接依賴foo
。go get -u foo
稍後運行,可是若是它不是初始安裝說明的一部分,則「 High Fidelity Builds」還有更多好處-u
。有關更多詳細信息,請參見「如何升級和降級依賴項」。go get -u foo
仍然有效,而且仍然能夠做爲安裝說明的有效選擇。go get foo
對於基於模塊的使用者,並不是嚴格須要。
import "foo"
就足夠了。(後續命令如go build
或go test
會根據須要自動下載foo
和更新go.mod
)。vendor
默認狀況下,基於模塊的使用者將不使用目錄。
go
工具中啓用了模塊模式,vendor
則使用模塊時並不須要嚴格要求(鑑於中包含的信息go.mod
和中的密碼校驗和go.sum
),可是某些預先存在的安裝說明假定該go
工具將vendor
默認使用。有關更多詳細信息,請參見vendor常見問題解答。go get foo/...
在某些狀況下,安裝說明可能包含問題(請參閱#27215中的討論)。模塊go.mod
經過module
指令(例如)在其聲明中聲明其身份module github.com/my/module
。任何模塊支持的使用者都必須使用與模塊聲明的模塊路徑匹配的導入路徑(確切地說是針對根軟件包,或將模塊路徑做爲導入路徑的前綴)導入模塊內的全部軟件包。若是導入路徑與相應模塊的聲明模塊路徑不匹配,則該go
命令將報告unexpected module path
錯誤。
在爲一組預先存在的軟件包採用模塊時,應注意避免破壞現有使用者使用的現有導入路徑,除非在採用模塊時增長主版本。
例如,若是您先前存在的README一直在告訴消費者要使用import "gopkg.in/foo.v1"
,而且隨後採用v1版本的模塊,則您的首字母go.mod
幾乎確定會讀爲module gopkg.in/foo.v1
。若是您不想使用gopkg.in
,這對您當前的消費者來講將是一個巨大的變化。一種方法是將其更改成相似的內容(module github.com/repo/foo/v2
若是您稍後轉到v2)。
請注意,模塊路徑和導入路徑區分大小寫。從更改模塊github.com/Sirupsen/logrus
到github.com/sirupsen/logrus
,例如,對消費者來講是一個重大更改,即便GitHub的自動轉發從一個存儲庫名稱到新存儲庫的名稱。
採用模塊後,更改模塊路徑go.mod
是一項重大更改。
整體而言,這相似於經過「導入路徑註釋」對規範的導入路徑進行模塊前的強制,有時也稱爲「導入實用程序」或「導入路徑強制」。舉例來講,該軟件包go.uber.org/zap
當前託管在github.com/uber-go/zap
,但在軟件包聲明旁邊使用了導入路徑註釋,該註釋爲使用錯誤的基於github的導入路徑的全部前置模塊使用者觸發了錯誤:
package zap // import "go.uber.org/zap"
go.mod文件的module語句已淘汰了導入路徑註釋。
v2.0.1
而且還沒有采用模塊,那麼您將使用v3.0.0
採用模塊的第一個發行版。有關更多詳細信息,請參見上面的「發佈模塊(v2或更高版本)」部分。foo
而且foo/v3
可能最終在單個版本中)。
foo
和的軟件包級別狀態foo/v3
),而且每一個主要版本都將運行其本身的init
功能。go.mod
。模塊可使用尚沒有適當的語義化版本號標籤的軟件包。有關更多詳細信息,請參見下面的常見問題解答。+incompatible
若是導入的v2 +程序包具備有效的語義化版本號標籤,它將帶有後綴記錄。有關更多詳細信息,請參見下面的常見問題解答。非模塊代碼消耗v0和v1模塊:
非模塊代碼消耗v2 +模塊:
Go版本1.9.7 +,1.10.3 +和1.11已更新,所以使用這些發行版構建的代碼能夠正確使用v2 +模塊,而無需按「語義導入版本控制」和「發佈模塊( v2或更高版本)」部分。
若是按照「發佈模塊(v2或更高版本)」一節中概述的「主要子目錄」方法建立v2 +模塊,則1.9.7和1.10.3以前的Go版本可使用v2 +模塊。
對於考慮加入模塊的預先存在的v2 +軟件包的做者,總結替代方法的一種方法是在三種頂級策略之間進行選擇。每一個選擇都有後續的決定和變化(如上所述)。這些替代的頂級策略是:
要求客戶端使用Go版本1.9.7 +,1.10.3 +或1.11+。
該方法使用「主要分支」方法,並依賴於「最小模塊感知」,該模型被反向移植到1.9.7和1.10.3。有關更多詳細信息,請參見上面的「語義導入版本控制」和「發佈模塊(v2或更高版本)」部分。
容許客戶使用甚至更舊的Go版本,如Go 1.8。
此方法使用「主要子目錄」方法,並涉及建立子目錄,例如/v2
或/v3
。有關更多詳細信息,請參見上面的「語義導入版本控制」和「發佈模塊(v2或更高版本)」部分。
等待加入模塊。
在這種策略下,事情繼續與選擇了模塊的客戶端代碼以及未選擇模塊的客戶端代碼一塊兒工做。隨着時間的流逝,Go版本1.9.7 +,1.10.3 +和1.11+的發佈時間將愈來愈長,而且在未來的某個時候,要求Go版本變得更加天然或對客戶友好1.9.7 + / 1.10.3 + / 1.11 +,此時,您能夠實施以上策略1(須要Go版本1.9.7 +,1.10.3 +或1.11+),甚至能夠實施以上策略2(可是若是最終要採用上述策略2來支持1.8等舊版Go,那麼您如今就能夠這樣作。
該require
指令容許任何模塊聲明其應使用依賴項D的版本> = xyz構建(因爲與模塊D的版本<xyz不兼容而可能指定)。經驗數據代表,這是在dep
和中使用的約束的主要形式cargo
。此外,構建中的頂層模塊能夠使用不一樣的代碼來生成exclude
特定版本的依賴項或replace
其餘模塊。有關更多詳細信息和原理,請參閱完整建議。
版本模塊建議的主要目標之一是爲工具和開發人員在Go代碼的版本週圍添加通用詞彙和語義。這爲未來聲明不兼容的其餘形式奠基了基礎,例如:
vgo
博客系列一般,模塊是Go 1.11的可選組件,所以,根據設計,默認狀況下會保留舊的行爲。
總結什麼時候得到舊的1.10現狀行爲與新的基於選擇加入模塊的行爲:
go.mod
- 的文件樹中-默認爲模塊行爲auto
-上面的默認行爲on
—無論目錄位置如何,都強制支持模塊off
—無論目錄位置如何,都強制關閉模塊支持go get
錯誤安裝工具會失敗並顯示錯誤cannot find main module
?當您進行設置GO111MODULE=on
但go.mod
運行時不在文件樹內部時,會發生這種狀況go get
。
最簡單的解決方案是保持未GO111MODULE
設置狀態(或等效地顯式設置爲GO111MODULE=auto
),這樣能夠避免出現此錯誤。
回想一下存在的主要緣由之一是記錄精確的依賴項信息。此依賴項信息將寫入您的current go.mod
。若是您不在帶有的文件樹中,go.mod
可是go get
經過設置告訴命令以模塊模式GO111MODULE=on
運行,則運行go get
將致使錯誤,cannot find main module
由於沒有go.mod
可用來記錄依賴項信息的信息。
解決方案的替代方案包括:
保持未GO111MODULE
設置狀態(默認設置或顯式設置GO111MODULE=auto
),這將致使更友好的行爲。當您不在模塊中時,這將爲您提供Go 1.10行爲,從而避免了go get
報告cannot find main module
。
export GO111MODULE=on
,但根據須要暫時禁用模塊,並在過程當中啓用Go 1.10行爲go get
,例如via GO111MODULE=off go get example.com/cmd
。能夠將其轉換爲簡單的腳本或shell別名,例如alias oldget='GO111MODULE=off go get'
建立一個臨時go.mod
文件,而後將其丟棄。這已經經過@rogpeppe的簡單shell腳本實現了自動化。該腳本容許經過可選地提供版本信息vgoget example.com/cmd[@version]
。(這是避免錯誤的解決方案cannot use path@version syntax in GOPATH mode
)。
gobin
是可識別模塊的命令,用於安裝和運行主軟件包。默認狀況下,gobin
無需先手動建立模塊便可安裝/運行主程序包,但-m
能夠經過標誌將該命令告知使用現有模塊來解決依賴關係。請參閱gobin
自述文件和常見問題解答以瞭解詳細信息和其餘用例。
建立一個go.mod
用於跟蹤運行的全局安裝工具(例如中的)~/global-tools/go.mod
,而後cd
在運行以前跟蹤該目錄,go get
或跟蹤go install
全部全局安裝的工具。
go.mod
爲每一個工具在單獨的目錄(例如~/tools/gorename/go.mod
和)中建立一個~/tools/goimports/go.mod
,並cd
在運行前爲該工具go get
或go install
該工具建立一個相應的目錄。
該當前限制將獲得解決。可是,主要問題是模塊當前處於啓用狀態,完整的解決方案可能要等到GO111MODULE = on成爲默認行爲。有關更多討論,請參見#24250,包括此評論:
顯然,這最終必須起做用。就該版本而言,我不肯定這究竟是作什麼的:它會建立一個臨時模塊root和go.mod,執行安裝,而後將其丟棄嗎?大概。可是我不太肯定,就目前而言,我不想讓vgo在go.mod樹以外作一些事情來令人們感到困惑。固然,最終的go命令集成必須支持這一點。
該常見問題解答一直在討論跟蹤全局安裝的工具。
相反,若是要跟蹤特定模塊所需的工具,請參閱下一個FAQ。
若是你:
stringer
在處理模塊時使用基於Go的工具(例如),而且go.mod
文件中跟蹤該工具的版本那麼當前推薦的一種方法是將一個tools.go
文件添加到您的模塊中,該文件包括目標工具(例如import _ "golang.org/x/tools/cmd/stringer"
)的導入語句以及// +build tools
構建約束。import語句使go
命令能夠在模塊的中精確記錄工具的版本信息go.mod
,而// +build tools
構建約束阻止正常的構建實際導入工具。
有關如何執行此操做的具體示例,請參見本「經過示例執行模塊」演練。
簡要理由(一樣來自#25922):
我認爲tools.go文件其實是工具依賴關係的最佳實踐,固然對於Go 1.11。
我喜歡它,由於它沒有引入新的機制。
它只是簡單地重用現有的。
對模塊的支持已開始在編輯器和IDE中得到。
例如:
go.mod
已經到來。#1906年得到了更普遍的支持。在雨傘問題中一直跟蹤其餘工具(例如goimports,guru,gorename和相似工具)的狀態#24661。請查看該傘的最新狀態。
特定工具的一些跟蹤問題包括:
nsf/gocode
建議人們從遷移nsf/gocode
到mdempsky/gocode
。一般,即便您的編輯器,IDE或其餘工具還沒有被模塊識別,若是您在GOPATH內使用模塊而且可使用,則它們的大部分功能也應與模塊一塊兒使用go mod vendor
(由於應經過GOPATH來選擇適當的依賴項) 。
完整的解決方法是把那包加載關閉的程序go/build
和到golang.org/x/tools/go/packages
,其知道如何定位模塊感知方式封裝。這極可能最終成爲事實go/packages
。
社區開始在模塊之上構建工具。例如:
replace
和多模塊工做流程,其中包括容許您輕鬆修改其中的一個依賴項gohack example.com/some/dependency
自動克隆適當的存儲庫並將必要的replace
指令添加到您的go.mod
gohack undo
go.mod
在go源代碼中自動調整文件和相關的導入語句mgit -tag +0.0.1
)vendor/
文件夾中,例如外殼程序腳本,.cpp文件,.proto文件等。如上面「 go.mod」概念部分所述,replace
僞指令在頂層提供了額外的控制權,go.mod
用於實際知足Go源代碼或go.mod文件中找到的依賴關係,而replace
僞指令則位於主模塊以外的模塊中構建主模塊時,將忽略該模塊。
該replace
指令容許您提供另外一個導入路徑,該路徑多是VCS(GitHub或其餘地方)中的另外一個模塊,或者是具備相對或絕對文件路徑的本地文件系統上的另外一個模塊。replace
使用指令中的新導入路徑,而無需更新實際源代碼中的導入路徑。
replace
容許頂層模塊控制用於依賴項的確切版本,例如:
replace example.com/some/dependency => example.com/some/dependency v1.2.3
replace
還容許使用分叉的依賴項,例如:
replace example.com/some/dependency => example.com/some/dependency-fork v1.2.3
一個示例用例是,若是您須要修復或研究依賴項中的某些內容,則可使用本地派生,並在頂層中添加如下內容go.mod
:
replace example.com/original/import/path => /your/forked/import/path
replace
也可用於將多模塊項目中模塊的相對或絕對磁盤上位置通知go工具,例如:
replace example.com/project/foo => ../foo
注意:若是replace
指令的右側是文件系統路徑,則目標必須go.mod
在該位置具備文件。若是該go.mod
文件不存在,則可使用建立一個go mod init
。
一般,您能夠選擇=>
在replace指令的左側指定版本,可是一般,若是您忽略此更改,則對更改的敏感性較低(例如,如replace
上述全部示例所示)。
在Go 1.11中,對於直接依賴關係require
,即便執行時也須要一個指令replace
。例如,若是foo
是直接依賴項,那麼您不能沒有replace foo => ../foo
相應的require
for foo
。若是你不知道在用什麼版本的require
指令,你能夠常用v0.0.0
如require foo v0.0.0
。這在Go 1.12中的#26241中獲得瞭解決。
您能夠經過運行確認得到所需的版本go list -m all
,該版本向您顯示了將在構建中使用的實際最終版本,包括考慮了replace
語句。
有關更多詳細信息,請參見「 go mod edit」文檔。
github.com/rogpeppe/gohack使這些類型的工做流變得更加容易,尤爲是若是您的目標是對模塊依賴項進行可變簽出時。有關概述,請參見存儲庫或以前的常見問題解答。
有關在replace
VCS以外徹底使用的詳細信息,請參見下一個FAQ 。
是。不須要VCS。
若是您要一次在VCS以外編輯單個模塊,那麼這很是簡單(而且您總共只有一個模塊,或者其餘模塊位於VCS中)。在這種狀況下,能夠將包含單個文件的文件樹放置go.mod
在方便的位置。你go build
,go test
和相似的命令將工做,即便你的單個模塊是VCS以外(無需任何使用replace
你的go.mod
)。
若是要在本地磁盤上同時編輯多個相互關聯的模塊,則replace
指令是一種方法。如下是一個示例go.mod
,該示例使用replace
帶有相對路徑的將hello
模塊指向該模塊在磁盤上的位置goodbye
(不依賴任何VCS):
module example.com/me/hello require ( example.com/me/goodbye v0.0.0 ) replace example.com/me/goodbye => ../goodbye
如本示例所示,若是在VCS以外,則能夠將其v0.0.0
用做require
指令中的版本。請注意,如先前的FAQ中所述,在Go 1.11中require
必須在此處手動添加require
指令,但再也不須要在Go 1.12+(#26241)中手動添加該指令。
該線程中顯示了一個小的可運行示例。
最初的一系列vgo
博客文章確實建議徹底放棄vendor,可是社區的反饋致使保留了對vendor的支持。
簡而言之,要對模塊使用vendor:
go mod vendor
重置主模塊的vendor目錄,以包括根據go.mod文件和Go源代碼的狀態構建和測試全部模塊軟件包所需的全部軟件包。go build
在模塊模式下,執行諸如忽略vendor目錄之類的命令。-mod=vendor
標誌(例如,go build -mod=vendor
)指示去命令使用主模塊的頂級vendor目錄,以知足依賴性。所以,在此模式下,go命令將忽略go.mod中的依賴項描述,並假定vendor目錄包含正確的依賴項副本。請注意,僅使用主模塊的頂級vendor目錄。其餘位置的vendor目錄仍然被忽略。GOFLAGS=-mod=vendor
環境變量來按期選擇vendor。禁用模塊模式go mod vendor
時,Go的較舊版本(如1.10)瞭解如何使用由建立的vendor目錄,Go 1.11和1.12+ 也是如此。所以,供應是模塊提供依賴的一種方式,該依賴提供了對不能徹底理解模塊的Go的較舊版本以及未啓用模塊自己的使用者的依賴。
若是您正在考慮使用vendor,則值得閱讀技巧文檔中的「模塊和vendor」和「提供依賴關係的vendor副本」部分。
公共託管的「始終在」不可變模塊存儲庫以及可選的私有託管的代理和存儲庫正變得可用。
例如:
請注意,您不須要運行代理。相反,1.11中的go工具已經過GOPROXY添加了可選的代理支持,以啓用更多企業用例(例如,更好的控制),並更好地處理諸如「 GitHub停機」或人們刪除GitHub存儲庫的狀況。
默認狀況下,相似的命令go build
會根據須要到達網絡以達到導入要求。
有些團隊可能但願禁止go工具在某些時候接觸網絡,或者想要更好地控制go工具什麼時候更新go.mod
,如何得到依賴關係以及如何使用vendor。
轉到工具提供了至關數量的靈活調整或關閉這些默認的行爲,包括經過-mod=readonly
,-mod=vendor
,GOFLAGS
,GOPROXY=off
,GOPROXY=file:///filesystem/path
,go mod vendor
,和go mod download
。
這些選項的詳細信息遍及整個官方文檔。此處是一個社區,試圖對與這些行爲相關的旋鈕進行綜合概述,其中包括指向官方文檔的連接,以獲取更多信息。
最簡單的方法可能只是設置環境變量GO111MODULE=on
,該變量應適用於大多數CI系統。
可是,因爲您的某些用戶還沒有選擇加入模塊,所以在啓用和禁用模塊的Go 1.11上的CI中運行測試可能頗有價值。vendor也是要考慮的話題。
如下兩個博客文章更具體地介紹了這些主題:
該模塊系統記錄您的精確的依賴要求go.mod
。(有關更多詳細信息,請參閱上面的go.mod概念部分或go.mod技巧文檔)。
go mod tidy
更新您的當前信息,go.mod
以在模塊中包括測試所需的依賴關係-若是測試失敗,咱們必須知道使用了哪些依賴關係來重現失敗。
go mod tidy
還能夠確保您的當前go.mod
反映了操做系統,架構的全部可能組合的依賴性需求,並創建標籤(如描述在這裏)。相比之下,其餘的命令同樣go build
,並go test
只更新go.mod
提供電流下被請求包導入的包GOOS
,GOARCH
和創建標籤(這是一個緣由go mod tidy
想補充一點,沒有被要求添加go build
或相似)。
若是您模塊的依賴項自己不具備go.mod
(例如,由於該依賴項還沒有選擇加入模塊自己),或者其go.mod
文件缺乏其一個或多個依賴項(例如,因爲模塊做者未運行go mod tidy
) ,那麼缺乏的傳遞依賴將被添加到您的模塊的要求,與沿// indirect
註釋,代表依賴是否是從你的模塊中的直接進口。
請注意,這還意味着直接或間接依賴項中缺乏的任何測試依賴項也將記錄在go.mod
。(如下狀況很重要的示例:對模塊go test all
的全部直接和間接依賴項進行測試,這是驗證您當前版本組合是否能夠協同工做的一種方法。若是在運行時測試在您的一種依賴項中失敗go test all
,重要的是要記錄一整套測試依賴項信息,以便您具備可重現的go test all
行爲。
// indirect
您的go.mod
文件中可能具備依賴項的另外一個緣由是,若是您已經升級(或降級了)一個間接依賴項,超出了直接依賴項所要求的範圍(例如,運行go get -u
或)go get foo@1.2.3
。go工具須要一個位置來記錄這些新版本,而且它會在您的go.mod
文件中記錄(而且不會深刻到您的依賴項中來修改其 go.mod
文件)。
一般,上述行爲是模塊如何經過記錄精確的依賴項信息來提供100%可複製的構建和測試的一部分。
若是你是好奇,爲何一個特定的模塊,顯示在你起來go.mod
,你能夠運行go mod why -m <module>
於回答這個問題。用於檢查需求和版本的其餘有用工具包括go mod graph
和go list -m all
。
不,go.sum
不是鎖定文件。go.mod
構建中的文件爲100%可複製的構建提供了足夠的信息。
爲了進行驗證,go.sum
包含特定模塊版本的內容的預期密碼校驗和。有關詳細信息(包括爲何一般須要簽入)以及技巧文檔中的「模塊下載和驗證」部分,請參見下面的FAQ。go.sum
go.sum
部分因爲go.sum
不是鎖文件,所以即便您中止使用模塊或特定模塊版本,它也會保留模塊版本的加密校驗和。若是您之後繼續使用某些內容,則能夠驗證校驗和,從而提升了安全性。
另外,您的模塊go.sum
記錄了構建中使用的全部直接和間接依賴項的校驗和(所以,go.sum
列出的模塊一般比的要多go.mod
)。
一般,模塊的go.sum
文件應與go.mod
文件一塊兒提交。
go.sum
包含特定模塊版本內容的預期密碼校驗和。go.sum
。go mod verify
檢查磁盤下載的模塊下載在磁盤上的緩存副本是否仍與中的條目匹配go.sum
。go.sum
它不是某些替代性依賴項管理系統中使用的鎖定文件。(go.mod
爲可複製的構建提供足夠的信息)。go.sum
。有關更多詳細信息,請參見技巧文檔的「模塊下載和驗證」部分。請參閱#24117和#25530中討論的未來可能的擴展。是。這支持在GOPATH以外進行工做,幫助與您選擇模塊的生態系統進行通訊,此外module
,您的指令還能夠go.mod
用做代碼身份的明確聲明(這是最終不建議使用導入註釋的緣由之一) )。固然,模塊在Go 1.11中純粹是可選功能。
請參閱上面的「語義導入版本控制」概念部分中有關語義導入版本控制和導入兼容性規則的討論。另請參閱宣佈提案的博客文章,其中更多地討論了導入兼容性規則的動機和理由。
請參閱問題「爲何導入路徑中省略了主要版本v0,v1?」 在較早的FAQ中,來自官方提案的討論。
在迴應有關「 k8發行次要版本,但在每一個次要版本中更改Go API」的評論時,Russ Cox作出如下迴應,着重強調了選擇v0,v1與頻繁使用v2,v3,v4進行重大更改的一些含義。 ,等等。
我並不徹底瞭解k8s開發週期等,可是我認爲一般k8s團隊須要決定/確認他們打算向用戶保證穩定性的內容,而後相應地應用版本號來表達這一點。
- 要保證有關API兼容性(這彷佛是最佳的用戶體驗!),而後開始使用1.XY
- 爲了靈活地在每一個發行版中進行向後不兼容的更改,但容許大型程序的不一樣部分按不一樣的時間表升級其代碼,這意味着不一樣的部分能夠在一個程序中使用API的不一樣主要版本,而後使用XY0,以及導入路徑,例如k8s.io/client/vX/foo。
- 爲了避免保證API兼容,而且不管什麼狀況,每一個構建都只須要一個k8s庫的副本,這意味着即便不是全部構建都準備好了,構建的全部部分也必須使用相同版本。 ,而後使用0.XY
與此相關的是,Kubernetes具備一些非典型的構建方法(當前在Godep之上包括自定義包裝腳本),所以Kubernetes對於許多其餘項目來講是不完善的示例,可是隨着Kubernetes向採用Go 1.11邁進,這多是一個有趣的示例。模塊。
是。
若是存儲庫未選擇使用模塊,但已使用有效的語義化版本號標籤(包括所需的前導v
)進行了標記,則能夠在中使用這些語義化版本號標籤go get
,而且相應的語義化版本號版本將記錄在導入模塊的go.mod
文件中。若是存儲庫沒有任何有效的語義化版本號標籤,則將使用「僞版本」記錄存儲庫的版本,例如 v0.0.0-20171006230638-a6e239ea1c69
(其中包括時間戳和提交哈希,而且其設計目的是容許對記錄在其中的各個版本進行總排序)go.mod
並使其更容易推斷出哪一個記錄版本比另外一個記錄版本「晚」。
例如,若是foo
標記了軟件包的最新版本,v1.2.3
但foo
自身還沒有選擇加入模塊,則在模塊M的內部運行go get foo
或go get foo@v1.2.3
從模塊M記錄的內容將記錄爲模塊M的go.mod
文件,以下所示:
require foo v1.2.3
該go
工具還將在其餘工做流程中爲非模塊程序包使用可用的語義化版本號標記(例如go list -u=patch
,將模塊的依賴項升級到可用的補丁程序版本,或將go list -u -m all
,顯示可用的升級等)。
有關還沒有選擇模塊的v2 +軟件包的更多詳細信息,請參見下一個常見問題解答。
是的,模塊能夠導入還沒有選擇模塊的v2 +軟件包,而且若是導入的v2 +軟件包具備有效的語義化版本號標籤,則將記錄+incompatible
後綴。
額外細節
請熟悉上面「語義導入版本控制」部分中的材料。
首先回顧一些一般有用但在考慮本FAQ中描述的行爲時要記住的特別重要的核心原則會有所幫助。
當工具以模塊模式(例如)運行時,如下核心原則始終是正確的:go
GO111MODULE=on
/vN
被視爲v1或v0模塊(即便導入的程序包未選擇加入模塊而且具備表示主要版本大於1的VCS標記,也是如此)。module foo/v2
開頭聲明的模塊路徑(例如)go.mod
均爲:
咱們將在接下來的FAQ看到,當這些原則並不老是正確的go
工具是不是在模塊模式,可是當這些原則是老是正確的go
工具是模塊模式。
簡而言之,+incompatible
後綴表示當知足如下條件時,上述原則2有效:
/vN
被視爲v1或v0模塊(即便VCS標籤另有說明)當go
工具處於模塊模式時,它將假定非模塊v2 +軟件包不瞭解語義導入版本控制,並將其視爲該軟件包的v1版本系列的(不兼容)擴展(而且+incompatible
後綴表示go
工具正在這樣作)。
例
假設:
oldpackage
是一個在引入模塊以前的軟件包oldpackage
從未選擇使用模塊(所以自己沒有模塊go.mod
)oldpackage
具備有效的語義化版本號標籤v3.0.1
,這是它的最新標籤在這種狀況下,例如go get oldpackage@latest
從模塊M內部運行將在模塊M的go.mod
文件中記錄如下內容:
require oldpackage v3.0.1+incompatible
請注意,在上面的命令或記錄的指令/v3
的末尾沒有使用– 在模塊路徑和導入路徑中使用是語義導入版本控制的功能,而且未表示接受並理解了語義導入版本控制(還沒有給出)經過在其內部包含文件來選擇加入模塊。換句話說,即便具備語義化版本號標籤,也不會被授予語義導入版本控制的權利和責任(例如,在導入路徑中使用),由於還沒有聲明要這樣作。oldpackage
go get
require
/vN
oldpackage
oldpackage
go.mod
oldpackage
oldpackage
v3.0.1
oldpackage
/vN
oldpackage
該+incompatible
後綴代表該v3.0.1
版本oldpackage
並無主動選擇加入的模塊,所以v3.0.1
版本oldpackage
被認爲不理解語義進口版本或如何使用進口路徑主要版本。所以,在模塊模式下運行時,該go
工具會將的非模塊v3.0.1
版本oldpackage
視爲的v1版本系列的(不兼容)擴展,oldpackage
並假定的v3.0.1
版本oldpackage
不瞭解語義導入版本控制,而且+incompatible
後綴表示該go
工具正在這樣作。
該事實v3.0.1
的版本oldpackage
被認爲是根據語義進口版本的v1發行版系列的一部分意味着,例如版本v1.0.0
,v2.0.0
以及v3.0.1
使用相同的導入路徑都始終輸入:
import "oldpackage"
再次注意,/v3
末尾沒有用過oldpackage
。
一般,具備不一樣導入路徑的軟件包是不一樣的軟件包。在這個例子中,給出的版本v1.0.0
,v2.0.0
和v3.0.1
中oldpackage
會使用相同的導入路徑須要進口,所以它們是經過構建視爲同一包(也由於oldpackage
在尚未選擇到語義進口版本),以單拷貝oldpackage
最終在任何給定的版本中。(使用的版本在任何require
指令中在語義上都是最高的版本;請參見「版本選擇」)。
若是咱們假設稍後建立一個新的v4.0.0
發行版,oldpackage
該發行版採用模塊並所以包含一個go.mod
文件,則該信號oldpackage
如今能夠理解語義導入版本控制的權利和責任,所以基於模塊的使用者如今能夠/v4
在導入中使用導入路徑:
import "oldpackage/v4"
該版本將記錄爲:
require oldpackage/v4 v4.0.0
oldpackage/v4
如今的導入路徑不一樣於oldpackage
,所以包也不一樣。若是構建中的某些使用方擁有import "oldpackage/v4"
同一個構建中的其餘使用方,則兩個副本(每一個導入路徑一個)將最終生成一個支持模塊的構建import "oldpackage"
。做爲容許逐步採用模塊的策略的一部分,這是理想的。另外,即便在模塊退出其當前過渡階段以後,也但願此行爲容許隨着時間的推移逐步進行代碼演化,其中不一樣的使用者以不一樣的速率升級到較新版本(例如,容許大型版本中的不一樣使用者選擇升級)從不一樣的速率oldpackage/v4
一些將來oldpackage/v5
)。
在考慮還沒有加入模塊的較舊的Go版本或Go代碼時,語義導入版本控制具備與v2 +模塊相關的顯着向後兼容性含義。
如上文「語義導入版本控制」部分所述:
/vN
在聲明的其本身的模塊路徑中包含go.mod
。/vN
在導入路徑中包含,以導入v2 +模塊。可是,預計生態系統將以不一樣的採用模塊和語義導入版本控制的速度進行。
如「如何釋放v2 +模塊」部分中更詳細描述的那樣,在「主要子目錄」方法中,v2 +模塊的做者建立了諸如mymodule/v2
或的子目錄,mymodule/v3
並在這些子目錄下移動或複製了相應的軟件包。這意味着傳統的導入路徑邏輯(即便在舊的Go版本中,如Go 1.8或1.7)也會在看到諸如的導入語句時找到合適的軟件包import "mymodule/v2/mypkg"
。所以,即便未啓用模塊支持,也能夠找到並使用「主要子目錄」 v2 +模塊中的軟件包(這是由於您正在運行Go 1.11且未啓用模塊,仍是由於您正在運行舊版本,如Go)沒有完整模塊支持的1.七、1.八、1.9或1.10)。請看「如何發佈v2 +模塊」部分提供了有關「主要子目錄」方法的更多詳細信息。
該常見問題解答的其他部分集中於「如何發佈v2 +模塊」部分中介紹的「主要分支」方法。在「主要分支」方法中,不/vN
建立任何子目錄,而是經過go.mod
文件並經過將語義化版本號標籤應用於提交來傳達模塊版本信息(一般在上master
,但能夠在不一樣的分支上)。
爲了在當前過渡時期提供幫助,Go 1.11 引入了「最小模塊兼容性」 ,覺得還沒有加入模塊的Go代碼提供更大的兼容性,而且「最小模塊兼容性」也被反向移植到Go 1.9。 7和1.10.3(鑑於那些舊版Go版本不具備完整模塊支持,這些版本始終在禁用完整模塊模式的狀況下始終有效運行)。
「最小模塊兼容性」的主要目標是:
容許較早的Go版本1.9.7+和1.10.3+可以更輕鬆地編譯/vN
在導入路徑中使用語義導入版本控制的模塊,並在Go 1.11中禁用模塊模式時提供相同的行爲。
容許舊代碼可以使用v2 +模塊,而無需使用舊的消費者代碼在使用/vN
v2 +模塊時當即更改成使用新的導入路徑。
這樣作無需依賴模塊做者便可建立/vN
子目錄。
其餘詳細信息–「最小模塊兼容性」
「最小模塊兼容性」僅在禁用該工具的完整模塊模式時才生效go
,例如,若是您GO111MODULE=off
在Go 1.11中進行了設置,或者正在使用Go 1.9.7+或1.10.3+版本。
當v2 +模塊做者還沒有建立/v2
或建立/vN
子目錄時,您轉而依賴於Go 1.9.7 +,1.10.3 +和1.11中的「最小模塊兼容性」機制:
go
工具在全模塊模式下運行時,它將不會導入該模塊的v2 +版本。(假定已選擇使用模塊的軟件包「說」語義導入版本控制。若是foo
是具備v2 +版本的模塊,則在「語義導入版本控制」下表示import "foo"
要導入的v1語義導入版本控制系列foo
)。/vN
在刪除後,將再次嘗試包含a的沒法解析的導入語句帶有有效文件)。/vN
.go
go.mod
import "foo/v2"
位於模塊內部的代碼內)仍將在GOPATH模式下(分別爲1.9.7 +,1.10.3 +和1.11)正確編譯,而且將像說的那樣進行解析import "foo"
(不帶/v2
) ,這意味着它將使用foo
駐留在您的GOPATH中的版本,而不會被多餘的混淆/v2
。go
命令行中使用的路徑(例如go get
或的參數go list
)。/vN
對v2 +模塊使用新的導入。import "foo/v2"
出如今基於模塊的代碼中將解析爲與您的GOPATH中駐留的代碼相同的代碼import "foo"
,而且該構建最終以-的一個副本結尾foo
-特別是不管GOPATH磁盤上的版本如何。這使得基於模塊的代碼 import "foo/v2"
甚至能夠在1.9.7 +,1.10.3 +和1.11的GOPATH模式下進行編譯。go
工具以全模塊模式運行時:
go
工具處於完整模塊模式而且foo
是v2 +模塊,則import "foo"
要求提供foo
vs 的v1版本,import "foo/v2"
要求提供的v2版本foo
。語義化版本號是模塊系統的基礎。爲了向消費者提供最佳體驗,鼓勵模塊做者使用語義化版本號 VCS標籤(例如v0.1.0
或v1.2.3-rc.1
),但嚴格要求不使用語義化版本號 VCS標籤:
要求模塊遵循語義化版本號規範,以使該go
命令按照記錄的方式運行。這包括遵循關於如何以及什麼時候容許更改的語義化版本號規範。
消費者使用僞版本形式的語義化版本號版本記錄沒有語義化版本號 VCS標籤的模塊。一般,這將是v0主版本,除非模塊做者遵循「主子目錄」方法構造了v2 +模塊。
所以,不該用語義化版本號 VCS標記且未建立「主要子目錄」的模塊將有效地聲明本身屬於語義化版本號 v0主版本系列,而且基於模塊的使用者將其視爲具備語義化版本號 v0主版本。
一個模塊能夠依賴於其自身的不一樣主要版本:總的來講,這至關於依賴於不一樣的模塊。出於各類緣由,這可能頗有用,包括容許將模塊的主要版本實現爲圍繞其餘主要版本的填充程序。
此外,一個模塊能夠在一個週期中依賴於其自身的不一樣主要版本,就像兩個徹底不一樣的模塊能夠在一個週期中彼此依賴同樣。
可是,若是您不但願模塊依賴於其自身的不一樣版本,則多是錯誤的徵兆。例如,打算從v3模塊導入軟件包的.go代碼可能缺乏/v3
import語句中所需的內容。根據自己的v1版本,該錯誤可能表現爲v3模塊。
若是您驚訝地看到一個模塊依賴於其自身的不一樣版本,那麼值得回顧一下上面的「語義導入版本控制」部分以及常見問題解答「若是我沒有看到預期的版本,該怎麼辦?依賴?」 。
兩個程序包可能在一個週期中彼此不依賴仍然是一個約束。
多模塊存儲庫是一個包含多個模塊的存儲庫,每一個模塊都有本身的go.mod文件。每一個模塊均從包含其go.mod文件的目錄開始,並遞歸包含該目錄及其子目錄中的全部程序包,但不包括包含另外一個go.mod文件的任何子樹。
每一個模塊都有本身的版本信息。存儲庫根目錄下的模塊的版本標籤必須包含相對目錄做爲前綴。例如,考慮如下存儲庫:
my-repo `-- foo `-- rop `-- go.mod
模塊「 my-repo / foo / rop」的1.2.3版本的標籤是「 foo / rop / v1.2.3」。
一般,存儲庫中一個模塊的路徑將是其餘模塊的前綴。例如,考慮如下存儲庫:
my-repo |-- bar |-- foo | |-- rop | `-- yut |-- go.mod `-- mig |-- go.mod `-- vub
圖。頂級模塊的路徑是另外一個模塊的路徑的前綴。
該存儲庫包含兩個模塊。可是,模塊「 my-repo」是模塊「 my-repo / mig」的路徑的前綴。
在這樣的配置中添加模塊,刪除模塊和版本控制模塊須要至關的謹慎和考慮,所以,管理單個模塊存儲庫而不是現有存儲庫中的多個模塊幾乎老是更容易,更簡單。
拉斯·考克斯(Russ Cox)在#26664中評論:
對於除電源用戶之外的全部用戶,您可能但願採用一種慣例,即一個repo =一個模塊。對於代碼存儲選項的長期發展很重要,一個倉庫能夠包含多個模塊,可是默認狀況下您幾乎確定不想這樣作。
關於如何使多模塊更有效的兩個示例:
go test ./...
從存儲庫根目錄開始將再也不測試存儲庫中的全部內容replace
指令按期管理模塊之間的關係。可是,除了這兩個示例以外,還有其餘細微差異。若是您考慮在單個存儲庫中包含多個模塊,請仔細閱讀本小節中的FAQ 。
有兩個示例場景,其中go.mod
一個存儲庫中可能有多個合理的場景:
若是您有一些使用示例,其中這些示例自己具備一組複雜的依賴關係(例如,也許您的軟件包很小,但包括一個將軟件包與kubernetes結合使用的示例)。在這種狀況下,對於您的存儲庫來講,擁有一個examples
或一個_examples
本身的目錄是頗有意義的go.mod
,例如here。
若是您的存儲庫具備一組複雜的依賴關係,可是您的客戶端API的依賴關係集較少。在某些狀況下,擁有一個api
或一個或clientapi
多個具備本身的目錄go.mod
或將其分隔clientapi
到本身的存儲庫中多是有意義的。
可是,對於這兩種狀況,若是您考慮爲多組間接依賴項建立性能或下載大小的多模塊存儲庫,則強烈建議您首先嚐試使用GOPROXY,它將在Go中默認啓用1.13。使用GOPROXY一般等同於可能會因建立多模塊存儲庫而帶來的任何性能優點或依賴項下載大小優點。
是。可是,此問題有兩類:
第一類:要添加模塊的軟件包還沒有處於版本控制中(新軟件包)。這種狀況很簡單:將包和go.mod添加到同一提交中,標記該提交,而後推送。
第二類:添加模塊的路徑在版本控制中,而且包含一個或多個現有軟件包。這種狀況須要至關多的護理。爲了說明,再次考慮如下存儲庫(如今位於github.com位置,以更好地模擬真實世界):
github.com/my-repo |-- bar |-- foo | |-- rop | `-- yut |-- go.mod `-- mig `-- vub
考慮添加模塊「 github.com/my-repo/mig」。若是要採用與上述相同的方法,則能夠經過兩個不一樣的模塊提供軟件包/ my-repo / mig:舊版本的「 github.com/my-repo」和新的獨立模塊「 github」。 com / my-repo / mig。若是兩個模塊都處於活動狀態,則導入「 github.com/my-repo/mig」將在編譯時致使「模棱兩可的導入」錯誤。
解決此問題的方法是使新添加的模塊取決於「雕刻」出的模塊,而後再將其雕刻出來。
假設「 github.com/my-repo」當前位於v1.2.3,讓咱們經過上面的存儲庫逐步進行操做:
添加github.com/my-repo/mig/go.mod:
cd path-to/github.com/my-repo/mig go mod init github.com/my-repo/mig # Note: if "my-repo/mig" does not actually depend on "my-repo", add a blank # import. # Note: version must be at or after the carve-out. go mod edit -require github.com/myrepo@v1.3
git commit
git tag v1.3.0
git tag mig/v1.0.0
接下來,讓咱們測試一下。咱們不能go build
仍是go test
天真地作,由於go命令會嘗試從模塊緩存中獲取每一個相關模塊。所以,咱們須要使用替換規則來使go
命令使用本地副本:
cd path-to/github.com/my-repo/mig go mod edit -replace github.com/my-repo@v1.3.0=../ go test ./... go mod edit -dropreplace github.com/my-repo@v1.3.0
git push origin master v1.2.4 mig/v1.0.0
推送提交和兩個標籤
請注意,未來golang.org/issue/28835應該使測試步驟更直接。
還要注意,在次要版本之間,代碼已從模塊「 github.com/my-repo」中刪除。不將其視爲主要更改彷佛很奇怪,可是在這種狀況下,傳遞性依存關係繼續在其原始導入路徑中提供已刪除軟件包的兼容實現。
是的,具備與上述相同的兩種狀況和相似的步驟。
是。一個模塊中的程序包能夠從另外一個模塊中導入內部程序包,只要它們共享與內部/路徑組件相同的路徑前綴便可。例如,考慮如下存儲庫:
my-repo |-- foo | `-- go.mod |-- go.mod `-- internal
在這裏,只要模塊「 my-repo / foo」依賴於模塊「 my-repo」,軟件包foo就能夠導入/ my-repo / internal。一樣,在如下存儲庫中:
my-repo |-- foo | `-- go.mod `-- internal `-- go.mod
在這裏,只要模塊「 my-repo / foo」依賴於模塊「 my-repo / internal」,軟件包foo就能夠導入my-repo / internal。二者的語義相同:因爲my-repo是my-repo / internal和my-repo / foo之間的共享路徑前綴,所以容許foo包導入內部包。
go.mod
在單個存儲庫中具備多個文件的另外一個用例是,若是存儲庫中的文件應從模塊中刪除。例如,存儲庫可能具備Go模塊不須要的很是大的文件,或者多語言存儲庫可能具備許多非Go文件。
go.mod
目錄中的空白將致使該目錄及其全部子目錄從頂級Go模塊中排除。
若是排除的目錄不包含任何.go
文件,則除了放置空go.mod
文件以外,不須要其餘步驟。若是排除的目錄中確實包含.go
文件,請首先仔細閱讀此多模塊存儲庫部分中的其餘常見問題解答。
請參閱問題「最小版本選擇是否會使開發人員沒法得到重要更新?」 在較早的FAQ中,來自官方提案的討論。
go env
來確認是否啓用了模塊,以確認其未爲只讀GOMOD
變量顯示空值。
GOMOD
爲變量,由於它其實是輸出的只讀調試go env
輸出。GO111MODULE=on
要啓用模塊,請仔細檢查它是否不是複數形式GO111MODULES=on
。(人們有時天然會包括,S
由於該功能一般稱爲「模塊」)。-mod=vendor
標誌是否正在傳遞給go build
或傳遞給該標誌或已GOFLAGS=-mod=vendor
被設置。
vendor
目錄,除非您要求go
使用該工具vendor
。go list -m all
查看爲您的構建選擇的實際版本列表 一般會頗有幫助
go list -m all
與僅查找go.mod
文件相比,一般能夠提供更多詳細信息。go get foo
因某種go build
緣由而失敗,或者特定軟件包失敗foo
,則一般能夠檢查go get -v foo
或的輸出go get -v -x foo
:
go get
一般會提供比更爲詳細的錯誤消息go build
。-v
標誌go get
要求打印更多詳細信息,但請注意,根據配置遠程存儲庫的方式,某些「錯誤」(例如404錯誤)可能會發生。go get -v -x foo
,它也顯示了git或其餘已發佈的VCS命令。(若是有保證,您一般能夠在go
工具的上下文以外執行相同的git命令以進行故障排除)。vgo
原型和Go 1.11 beta 的常見問題根源,但在GA 1.11中則不多出現。go
並行執行多個命令(請參閱#26794,Go 1.12已解決)。做爲故障排除步驟,您能夠將$ GOPATH / pkg / mod複製到備份目錄(以防稍後須要進一步調查),運行go clean -modcache
,而後查看原始問題是否仍然存在。當前正在檢查的錯誤多是因爲構建中沒有特定模塊或軟件包的預期版本而引發的第二個問題。所以,若是致使特定錯誤的緣由不明顯,則能夠按照下一個FAQ中的說明對您的版本進行抽查。
一個好的第一步是運行go mod tidy
。這可能會解決問題,但也有可能使您的go.mod
文件相對於.go
源代碼處於一致狀態,這將使之後的調查更加容易。(若是go mod tidy
它自己以某種您不但願的方式更改了依賴關係的版本,請先閱讀'go mod tidy'上的常見問題解答。若是仍沒法解決,您能夠嘗試重置您的go.mod
,而後運行go list -mod=readonly all
,這可能會帶來更多的變化。有關須要更改其版本的特定消息)。
第二步一般應檢查go list -m all
以查看爲您的構建選擇的實際版本列表。 go list -m all
向您顯示最終選擇的版本,包括用於間接依賴性的版本以及在解決全部共享依賴性的版本以後的版本。它還顯示any replace
和exclude
指令的結果。
下一步是檢查go mod graph
或的輸出go mod graph | grep <module-of-interest>
。 go mod graph
打印模塊需求圖(包括考慮的更換)。輸出中的每一行都有兩個字段:第一列是使用模塊,第二列是該模塊的要求之一(包括該使用模塊所需的版本)。這是查看哪些模塊須要特定依賴項的快速方法,包括當構建的依賴項具備與構建中的不一樣使用者不一樣的所需版本時(若是是這種狀況,熟悉該模塊很重要)。行爲在上面的「版本選擇」部分中進行了說明)。
go mod why -m <module>
在這裏也能夠是有用的,儘管一般對於查看爲何徹底包含依賴項(而不是爲何依賴項以特定版本結束)更爲有用。
go list
提供了更多的查詢變體,在須要時能夠用於查詢模塊。如下是一個示例,它將顯示構建中使用的確切版本,不包括僅測試依賴項:
go list -deps -f '{{with .Module}}{{.Path}} {{.Version}}{{end}}' ./... | sort -u
用於詢問你的模塊的更詳細的命令集和實施例的可以在可運行「轉到模塊經過實施例」中能夠看出walkthough。
致使意外版本的一個緣由多是因爲某人建立了一個go.mod
非預期的無效或意外文件,或者是相關的錯誤(例如:一個v2.0.1
模塊版本可能錯誤地將自身聲明爲module foo
在其中,go.mod
而沒有必要/v2
; import語句)在.go
打算導入v3的模塊可能缺乏所需的代碼/v3
;一require
在一份聲明中go.mod
對V4模塊可能缺乏必要的/v4
)。所以,若是您看不到引發特定問題的緣由,那麼值得首先閱讀「 go.mod」和「語義導入版本控制」中的材料上面的部分(考慮到這些包括模塊必須遵循的重要規則),而後花幾分鐘來檢查最相關的go.mod
文件和import語句。
這是一條常見的錯誤消息,可能會因幾種不一樣的根本緣由而發生。
在某些狀況下,此錯誤僅是因爲路徑鍵入錯誤引發的,所以第一步可能應該是根據錯誤消息中列出的詳細信息再次檢查錯誤的路徑。
若是您尚未這樣作,那麼下一步一般是嘗試go get -v foo
如下操做go get -v -x foo
:
go get
一般會提供比更爲詳細的錯誤消息go build
。其餘一些可能的緣由:
cannot find module providing package foo
若是您已發出go build
或在當前目錄中go build .
沒有.go
任何源文件,則可能會看到錯誤。若是這是您遇到的問題,則解決方案多是另外一種調用,例如go build ./...
(其中./...
展開以匹配當前模塊中的全部軟件包)。參見#27122。
Go 1.11中的模塊緩存可能致使此錯誤,包括面對網絡問題或go
並行執行的多個命令。這在Go 1.12中已解決。有關更多詳細信息和可能的糾正步驟,請參閱上面本節中的第一個故障排除常見問題解答。
go mod init
沒有任何參數的狀況下,將根據不一樣的提示(例如VCS元數據)嘗試猜想正確的模塊路徑。可是,不能go mod init
老是可以猜想出正確的模塊路徑。
若是go mod init
給您這個錯誤,則這些試探法沒法猜想,您必須本身提供模塊路徑(例如go mod init github.com/you/hello
)。
是。這須要一些手動步驟,但在某些更復雜的狀況下可能會有所幫助。
在go mod init
初始化本身的模塊時運行時,它將經過轉換配置文件(如Gopkg.lock
,glide.lock
)或包含相應指令vendor.json
的go.mod
文件自動從先前的依賴項管理器轉換require
。Gopkg.lock
例如,現有文件中的信息一般描述全部直接和間接依賴項的版本信息。
可是,若是改成添加還沒有選擇加入模塊自己的新依賴項,則任何先前的依賴項管理器都不會使用相似的自動轉換過程,而新的依賴項可能已經在使用該轉換過程。若是該新依賴項自己具備發生了重大更改的非模塊依賴項,則在某些狀況下可能會致使不兼容問題。換句話說,新依賴項的先前依賴項管理器不會自動使用,在某些狀況下,這可能會致使間接依賴項出現問題。
一種方法是go mod init
在有問題的非模塊直接依賴項上運行,以從其當前的依賴項管理器進行轉換,而後使用require
結果臨時文件中的指令go.mod
填充或更新go.mod
模塊中的。
例如,若是github.com/some/nonmodule
當前正在使用另外一個依賴性管理器的模塊存在直接的依賴性問題,則能夠執行如下操做:
$ git clone -b v1.2.3 https://github.com/some/nonmodule /tmp/scratchpad/nonmodule $ cd /tmp/scratchpad/nonmodule $ go mod init $ cat go.mod
require
臨時的結果信息go.mod
能夠手動移至go.mod
模塊的實際信息中,或者您能夠考慮使用https://github.com/rogpeppe/gomodmerge,這是針對此用例的社區工具。另外,您將須要require github.com/some/nonmodule v1.2.3
在實際中添加一個go.mod
以匹配您手動克隆的版本。
在#28489註釋中,針對Docker使用此技術的具體示例說明了如何獲取一致的Docker依賴項版本集,從而避免github.com/sirupsen/logrus
vs. 之間區分大小寫的問題github.com/Sirupsen/logrus
。
一般,模塊go.mod
經過module
指令(例如)在其聲明中聲明其身份module example.com/m
。這是該模塊的「模塊路徑」,而且該go
工具在該聲明的模塊路徑與任何使用者使用的導入路徑之間強制保持一致性。若是模塊的go.mod
文件爲module example.com/m
,則使用者必須使用以該模塊路徑開頭的導入路徑(例如import "example.com/m"
或import "example.com/m/sub/pkg"
)從該模塊導入軟件包。
若是使用者使用的導入路徑與相應的聲明模塊路徑不匹配,則該go
命令將報告parsing go.mod: unexpected module path
致命錯誤。另外,在某些狀況下,該go
命令隨後將報告更通用的error loading module requirements
錯誤。
致使此錯誤的最多見緣由是,是否進行了名稱更改(例如github.com/Sirupsen/logrus
到github.com/sirupsen/logrus
),或者因爲虛榮導入路徑而致使模塊有時在模塊以前經過兩個不一樣的名稱使用(例如github.com/golang/sync
vs.建議golang.org/x/sync
)。
若是您有一個依存關係仍經過較舊的名稱(例如,github.com/Sirupsen/logrus
)或非規範名稱(例如,github.com/golang/sync
)導入,可是該依賴關係隨後採用了模塊,而且如今在中聲明瞭其規範名稱,則可能會致使問題go.mod
。而後,當發現模塊的升級版本聲明再也不與舊的導入路徑匹配的規範模塊路徑時,在升級期間會觸發此錯誤。
github.com/Quasilyte/go-consistent
。github.com/quasilyte/go-consistent
(更改Q
爲小寫q
),這是一個重大更改。GitHub從舊名稱轉發到新名稱。go get -u
,它將嘗試升級全部直接和間接依賴項。github.com/Quasilyte/go-consistent
試圖進行升級,可是最新go.mod
發現爲module github.com/quasilyte/go-consistent
。轉到:github.com/Quasilyte/go-consistent@v0.0.0-20190521200055-c6f3937de18c:解析go.mod:意外的模塊路徑「 github.com/quasilyte/go-consistent」轉到:錯誤加載模塊要求
錯誤的最多見形式是:
轉到:example.com/some/OLD/name@vX.YZ:解析go.mod:意外的模塊路徑「 example.com/some/NEW/name」
若是您訪問存儲庫的信息example.com/some/NEW/name
(從錯誤的右側開始),則能夠檢查go.mod
文件的最新版本,或者master
查看它是否在go.mod
as 的第一行中聲明瞭本身module example.com/some/NEW/name
。若是是這樣,則暗示您看到的是「舊模塊名稱」與「新模塊名稱」的問題。
本節的其他部分重點在於按順序執行如下步驟來解決此錯誤的「舊名稱」和「新名稱」形式:
檢查您本身的代碼,看看是否要使用導入example.com/some/OLD/name
。若是是這樣,請更新您的代碼以導入example.com/some/NEW/name
。
若是您在升級過程當中收到此錯誤,則應嘗試使用Go的尖端版本進行升級,該尖端版本具備針對性更強的升級邏輯(#26902),一般能夠迴避此問題,而且在這種狀況下一般還具備更好的錯誤消息。請注意,go get
tip / 1.13中的參數與1.12 中的參數不一樣。獲取提示並使用它升級依賴項的示例:
go get golang.org/dl/gotip && gotip download gotip get -u all gotip mod tidy
因爲有問題的舊導入一般是間接依賴的,所以使用tip升級而後運行go mod tidy
能夠常常將您升級爲有問題的版本,而後從go.mod
再也不須要的版本中刪除有問題的版本,這將使您進入正常運行狀態返回到使用Go 1.12或1.11進行平常使用。例如,看到這種方法工做在這裏升級過去github.com/golang/lint
與golang.org/x/lint
問題。
若是您在執行go get -u foo
或時收到此錯誤go get -u foo@latest
,請嘗試刪除-u
。這將爲您提供一組所使用的依賴項,foo@latest
而無需升級發佈foo
者foo
可能會驗證爲工做的過去版本的依賴項foo
。這在過渡時期可能很重要,在這種過渡時期,某些直接或間接的依賴關係foo
可能還沒有采用語義化版本號或模塊。(一個常見的錯誤是認爲go get -u foo
僅獲取的最新版本foo
。實際上,-u
in go get -u foo
或go get -u foo@latest
手段也獲取的全部直接和間接依賴項的最新版本。foo
; 可能正是您想要的,但若是因爲深層間接依賴而致使失敗,則可能不會特別如此。
若是上述步驟不能解決錯誤,則下一種方法會稍微複雜一些,但大多數狀況下應該能夠解決此錯誤的「舊名稱」和「新名稱」形式。這僅使用僅來自錯誤消息自己的信息,並簡要介紹了一些VCS歷史記錄。
4.1。轉到example.com/some/NEW/name
存儲庫
4.2。肯定什麼時候將go.mod
文件引入那裏(例如,經過查看的責任或歷史視圖go.mod
)。
4.3。挑選釋放或提交以前的go.mod
介紹有文件。
4.4。在您的go.mod
文件中,在replace
語句的兩邊添加一個使用舊名稱的replace
語句: replace example.com/some/OLD/name => example.com/some/OLD/name <version-just-before-go.mod>
使用咱們先前的示例,其中哪裏github.com/Quasilyte/go-consistent
是舊名稱,又github.com/quasilyte/go-consistent
是新名稱,咱們能夠看到go.mod
最先是在commit 00c5b0cf371a中引入的。該存儲庫未使用語義化版本號標記,所以咱們將緊接以前的提交00dd7fb039e提交,並使用兩邊的舊大寫Quasilyte名稱將其添加到替換中replace
:
replace github.com/Quasilyte/go-consistent => github.com/Quasilyte/go-consistent 00dd7fb039e
replace
而後,該語句使咱們可以經過有效地防止在存在的狀況下將舊名稱升級爲新名稱,從而解決了有問題的「舊名稱」與「新名稱」不匹配的問題go.mod
。一般,經過go get -u
或相似方式進行升級如今能夠避免該錯誤。若是升級完成,則能夠檢查是否有人仍在導入舊名稱(例如go mod graph | grep github.com/Quasilyte/go-consistent
),若是沒有導入,則replace
能夠將其刪除。(這常常起做用的緣由是,若是使用了舊的有問題的導入路徑,升級自己可能會失敗,即便升級完成後在最終結果中可能不會使用升級路徑也是如此(在#30831中進行了跟蹤)。
gotip
在上面的第2步中使用可能會發現有問題的進口商,但並不是在全部狀況下均可以,尤爲是對於升級(#30661)。若是不清楚是誰在使用有問題的舊導入路徑進行導入,一般能夠經過建立乾淨的模塊高速緩存,執行觸發錯誤的一個或多個操做,而後在模塊高速緩存中查找舊的有問題的導入路徑來找出答案。例如:export GOPATH=$(mktemp -d) go get -u foo # peform operation that generates the error of interest cd $GOPATH/pkg/mod grep -R --include="*.go" github.com/Quasilyte/go-consistent
最後,上述步驟集中於如何解決潛在的「舊名稱」與「新名稱」問題。可是,若是將a go.mod
放置在錯誤的位置或僅具備錯誤的模塊路徑,也會出現相同的錯誤消息。在這種狀況下,導入該模塊應始終失敗。若是要導入剛剛建立且從未成功導入的新模塊,則應檢查go.mod
文件是否正肯定位,以及文件是否具備與該位置對應的正確模塊路徑。(最多見的方法是go.mod
每一個存儲庫使用單個go.mod
文件,並將單個文件放置在存儲庫根目錄中,並使用存儲庫名稱做爲module
指令中聲明的模塊路徑)。參見「 go.mod」
簡而言之:
由於預構建的軟件包是非模塊構建的,因此不能重複使用。抱歉。如今禁用cgo或安裝gcc。
僅在選擇加入模塊時(例如經過GO111MODULE=on
),這纔是一個問題。有關其餘討論,請參見#26988。
import "./subdir"
嗎?否。請參閱#26645,其中包括:
在模塊中,最後有一個子目錄的名稱。若是父目錄顯示「模塊m」,則子目錄將導入爲「 m / subdir」,而再也不是「 ./subdir」。
沒有.go
文件的vendor
目錄不會經過複製在目錄內go mod vendor
。這是設計使然。
簡而言之,撇開任何特定的vendor行爲– go構建的整體模型是構建軟件包所需的文件應位於包含.go
文件的目錄中。
以cgo爲例,修改其餘目錄中的C源代碼不會觸發重建,而是您的構建將使用陳舊的緩存條目。cgo文檔如今包括:
請注意,對其餘目錄中文件的更改不會致使從新編譯該軟件包,所以,該軟件包的全部非Go源代碼應存儲在軟件包目錄中,而不是子目錄中。
社區工具https://github.com/goware/modvendor容許您輕鬆地將一整套.c,.h,.s,.proto或其餘文件從模塊複製到vendor
Director中。儘管這可能會有所幫助,可是若是您有一些須要的文件來構建包含該.go
文件的目錄以外的文件,則必須格外當心,以確保整體上能夠正確處理go編譯(與vendor無關)。
請參閱#26366中的其餘討論。
傳統vendor的另外一種方法是檢入模塊緩存。它最終可能會得到與傳統vendor相似的好處,而且在某些方面最終會得到更高的保真度。將此方法解釋爲「經過示例執行模塊」 演練。