Golang 模塊(Module)官方手冊

 官方原文: https://github.com/golang/go/wiki/Modulesgit

 

 

 

Go 1.11包括此處建議的對版本模塊的初步支持模塊是Go 1.11中的實驗性加入功能,並計劃歸入反饋並最終肯定 Go 1.14中的功能。即便某些細節可能會更改,未來的發行版也將支持使用Go 1.十一、1.12和1.13定義的模塊。github

最初的原型vgo2018年2月宣佈。2018年7月,版本化的模塊進入了主Go存儲庫。golang

請經過現有問題或新問題以及經驗報告提供有關模塊的反饋算法

近期變更

Go 1.13中對模塊進行了重大改進和更改。shell

若是使用模塊,請務必仔細閱讀Go 1.13發行說明模塊部分數據庫

三個值得注意的變化:json

  1. 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支持使用頻率較低的用例。有關更多詳細信息,請參見文檔
  2. GO111MODULE=auto若是找到任何go.mod,即便在GOPATH內部,也將啓用模塊模式。(在Go 1.13以前,GO111MODULE=auto永遠不會在GOPATH中啓用模塊模式)。api

  3. go get 參數已更改:緩存

    • go get -u(不帶任何參數)如今僅升級當前軟件包的直接和間接依賴關係,而再也不檢查整個模塊
    • go get -u ./... 從模塊根目錄升級模塊的全部直接和間接依賴關係,如今不包括測試依賴關係。
    • go get -u -t ./... 類似,但也升級了測試依賴項。
    • go get再也不受支持-m(由於go get -d因爲其餘更改,它會在很大程度上與重疊;您一般能夠替換go get -m foogo 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.3go get foo@masterfoo@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.modOS,架構和構建標籤的其餘組合中修剪全部不須要的依賴項,並添加其餘依賴項所需的任何依賴項(詳細信息
  • replace指令或gohack—使用派生,本地副本或依賴項的確切版本(詳細信息
  • go mod vendor—建立vendor目錄的可選步驟(詳細信息

在閱讀了有關「新概念」的下四個部分以後,您將得到足夠的信息來開始使用大多數項目的模塊。查看上面的目錄(包括此處的FAQ常見問題解答)以使本身熟悉更詳細的主題列表也頗有用

新概念

這些部分對主要的新概念進行了高級介紹。有關更多詳細信息和原理,請觀看Russ Cox的這段40分鐘的介紹性視頻,其中介紹了設計背後的理念正式的建議文檔或更爲詳細的初始vgo博客系列

模塊

一個模塊是一些以版本做爲單元相關的包的集合。

模塊記錄精確的依賴要求並建立可複製的構建。

一般,版本控制存儲庫僅包含在存儲庫根目錄中定義的一個模塊。單個存儲庫中支持多個模塊,可是一般,與每一個存儲庫中的單個模塊相比,這將致使正在進行的工做更多)。

總結存儲庫,模塊和軟件包之間的關係:

  • 一個存儲庫包含一個或多個Go模塊。
  • 每一個模塊包含一個或多個Go軟件包。
  • 每一個軟件包都在一個目錄中包含一個或多個Go源文件。

模塊必須根據在語義版本語義化版本號,一般在形式v(major).(minor).(patch),如 v0.1.0v1.2.3,或v1.5.0-rc.1領導v是必需的。若是使用Git,則標記發佈會提交其版本。公共和私有模塊存儲庫和代理均可以使用(請參閱下面的常見問題解答)。

go.mod

模塊由Go源文件樹定義,該go.mod文件在樹的根目錄中。模塊源代碼可能位於GOPATH以外。有四種指令:modulerequirereplaceexclude

這是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.modas中聲明其身份module example.com/my/module,則使用者能夠執行如下操做:

import "example.com/my/module/mypkg"

mypkg將從模塊導入包example.com/my/module

excludereplace指令僅在當前(「主」)模塊上運行。在構建主模塊時,將忽略除主模塊之外的其餘模塊中的指令excludereplace指令。replaceexclude語句,所以,容許在本身的構建主要模塊的徹底控制權,也沒有受制於由依賴於徹底控制。(有關什麼時候使用指令的討論,請參見下面常見問題解答replace)。

版本選擇

若是您在源代碼中添加了一個還沒有被requirein 覆蓋的新導入,則go.mod大多數go命令(例如「 go build」和「 go test」)將自動查找適當的模塊,並將該新直接依賴項的最高版本添加到您的模塊go.modrequire指令。例如,若是您的新導入對應於依賴項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.1D包含在構建中(假設它是列出的最高require版本)。v1.1.1即便稍後某個v1.2.0D可用,對D的選擇仍保持一致這是模塊系統如何提供100%可複製構建的示例。準備就緒後,模塊做者或用戶能夠選擇升級到D的最新可用版本,或爲D選擇一個顯式版本。

有關最小版本選擇算法的簡要原理和概述,請參閱官方建議書的「高保真度構建」部分,或查看更詳細的vgo博客系列

要查看所選模塊版本的列表(包括間接依賴關係),請使用go list -m all

另請參見下面的「如何升級和降級依賴項」部分和「如何將版本標記爲不兼容?」 下面的常見問題解答。

語義導入版本控制

多年以來,官方的Go常見問題解答已在軟件包版本管理中包括如下建議:

「面向公共用途的軟件包應在發展過程當中嘗試保持向後兼容性。Go1兼容性指南在此處是很好的參考:請勿刪除導出的名稱,鼓勵帶標籤的複合文字等等。若是須要不一樣的功能,請添加一個新名稱,而不是更改舊名稱。若是須要徹底中斷,請建立一個具備新導入路徑的新軟件包。」

最後一句特別重要-若是破壞兼容性,則應更改軟件包的導入路徑。使用Go 1.11模塊,該建議被正式化爲導入兼容性規則

「若是舊軟件包和新軟件包具備相同的導入路徑,則新軟件包必須與舊軟件包向後兼容。」

當v1或更高版本的軟件包進行向後不兼容的更改時,召回語義化版本號須要對主要版本進行更改。遵循導入兼容性規則和語義化版本號的結果稱爲語義導入版本控制,其中主要版本包含在導入路徑中-這可確保在主要版本因爲兼容性中斷而增長時,導入路徑都會更改。

因爲語義導入版本控制,選擇加入Go模塊的代碼必須遵照如下規則

  • 跟隨語義化版本號(示例VCS標籤爲v1.2.3)。
  • 若是模塊的版本爲v2或更高版本,則必須/vNgo.mod文件(例如module github.com/my/mod/v2require 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不管什麼時候使用模塊名稱,都包括)。
  • 若是模塊的版本爲v0或v1,則在模塊路徑或導入路徑中不要包含主版本。

一般,具備不一樣導入路徑的軟件包是不一樣的軟件包。例如,與math/rand是不一樣的軟件包crypto/rand若是不一樣的導入路徑是因爲導入路徑中出現的主要版本不一樣而致使的,則也是如此。所以,與example.com/my/mod/mypkg包是一個不一樣的包example.com/my/mod/v2/mypkg,二者均可以在一個單一版本中導入,這除其餘優勢外還有助於解決鑽石依賴問題,而且還容許在替換v2方面實施v1模塊,反之亦然。

有關語義導入版本控制的更多詳細信息,請參見命令文檔「模塊兼容性和語義版本控制」部分,go有關語義版本控制的更多信息,請參見https://語義化版本號.org

到目前爲止,本節的重點是已選擇加入模塊並導入其餘模塊的代碼。可是,將主要版本置於v2 +模塊的導入路徑中可能會與Go的較早版本或還沒有選擇加入模塊的代碼產生不兼容性。爲了解決這個問題,上述行爲和規則有三種重要的過渡性特殊狀況或例外。隨着愈來愈多的程序包加入模塊,這些過渡性異常將再也不重要。

三個例外:

  1. gopkg.in

    使用導入路徑gopkg.in(以gopkg.in/yaml.v1開頭)的現有代碼gopkg.in/yaml.v2即便選擇加入模塊,也能夠繼續將這些格式用於其模塊路徑和導入路徑。

  2. 導入非模塊v2 +軟件包時爲「 +不兼容」

    模塊能夠導入還沒有選擇加入模塊的v2 +軟件包。具備有效v2 + 語義化版本號標籤的非模塊v2 +軟件包+incompatible在導入模塊的go.mod文件中記錄後綴+incompatible後綴表示,即便V2 +包有一個有效的V2 + 語義化版本號標籤,例如v2.0.0,使V2 +包沒有主動選擇的模塊和假設,所以該V2 +包都沒有被與語義進口版本的含義的理解產生以及如何在導入路徑中使用主要版本。所以,當以模塊模式運行時go該工具會將非模塊v2 +軟件包視爲該軟件包的v1版本系列的(不兼容)擴展,並假定該軟件包不瞭解語義導入版本控制,而且+incompatible後綴表示該go工具正在這樣作。

  3. 未啓用模塊模式時的「最小模塊兼容性」

    爲了幫助向後兼容,對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或更高版本)」部分。

如何使用模塊

如何安裝和激活模塊支持

要使用模塊,兩個安裝選項是:

安裝後,您能夠經過如下兩種方式之一激活模塊支持:

  • go$GOPATH/src以外的目錄中調用命令,並go.mod在當前目錄或其任何父目錄中使用有效文件,而且未GO111MODULE設置(或顯式設置爲auto環境變量
  • 調用go帶有GO111MODULE=on環境變量設置命令

如何定義模塊

go.mod現有項目建立一個

  1. 導航到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
  2. 建立初始模塊定義並將其寫入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 ./...或相似操做後才能運行,這是本節中顯示的順序)。

  3. 生成模塊。從模塊的根目錄執行時,該./...模式將匹配當前模塊中的全部軟件包。 go build會根據須要自動添加缺乏或未轉換的依賴項,以知足此特定構建調用的導入需求:

    $ go build ./...
  4. 按照配置測試模塊,以確保它能夠與所選版本一塊兒使用:

    $ go test ./...
  5. (可選)運行模塊的測試以及全部直接和間接依賴項的測試,以檢查不兼容性:

    $ 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更新到的最新版本foogo get foo等效於go get foo@latest—換句話說,@latest若是未@指定版本則爲默認值

在本節中,「最新」是帶有語義化版本號標籤的最新版本,或者若是沒有語義化版本號標籤則是最新的已知提交。除非存儲庫中沒有其餘語義化版本號標籤,不然不會將預發佈標籤選擇爲「最新」標籤(details)。

一個廣泛的錯誤是認爲go get -u foo僅獲取最新版本的foo實際上,-uin go get -u foo或的go get -u foo@latest意思是得到的全部直接和間接依賴關係的最新版本foo升級時,一個共同的起點foo是否是作go get foogo get foo@latest沒有-u(和後一切正常,能夠考慮go get -u=patch foogo get -u=patchgo get -u foo,或go get -u)。

要升級或降級到一個更具體的版本,「去把」容許版本選擇經過添加一個後綴@version或覆蓋「模塊查詢」到包的說法,好比go get foo@v1.6.2go get foo@e3702bed2或者go get foo@'<v1.6.2'

不管是否具備語義化版本號標記,使用分支名稱(例如go get foo@masterfoo@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已被從新定義爲更有用:經過一個或多個導入序列,將當前模塊中的全部軟件包以及它們所依賴的全部軟件包包括在內,而在其中將可有可無的軟件包排除在外當前模塊。
  • 確保您的go.sum文件與go.mod文件一塊兒提交。有關更多詳細信息和原理,請參見下面常見問題解答

發行模塊(v2或更高版本)

若是要發佈v2或更高版本的模塊,請首先查看上面「語義導入版本控制」部分中的討論,其中包括爲什麼在v2 +模塊的模塊路徑和導入路徑中包含主要版本以及Go版本1.9的方式.7+和1.10.3+已更新,以簡化該過渡。

請注意,若是您是第一次v2.0.0採用模塊,是爲了在採用模塊以前針對已存在標籤或更高版本的預先存在的存儲庫或軟件包集進行採用,那麼建議的最佳實踐是在首次採用模塊時增長主版本。例如,若是您是的做者foo,而且foo存儲庫的最新標記v2.2.2,而且foo還沒有采用模塊,則最佳作法是v3.0.0將第一個版本的foo採用採用模塊(所以將第一個版本的footo做爲)。包含一個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發行版爲例,兩個選項是:

  1. Major分支:更新go.mod文件以/v3module指令的模塊路徑末尾包含a (例如module github.com/my/module/v3)。更新模塊中的import語句以也使用/v3(例如import "github.com/my/module/v3/mypkg")。用標記發佈v3.0.0

    • Go版本1.9.7 +,1.10.3 +和1.11可以正確使用和構建使用此方法建立的v2 +模塊,而無需更新還沒有選擇模塊的使用者代碼(如「語義導入」中所述)版本」部分)。
    • 社區工具github.com/marwan-at-work/mod可幫助實現此過程的自動化。有關概述,請參見下面的存儲庫社區工具常見問題解答
    • 爲避免與此方法混淆,請考慮將v3.*.*模塊提交放在單獨的v3分支上。
    • 注:建立的一個新的分支不是必需的。相反,若是您之前是在master上發佈的,而且但願v3.0.0在master上進行標記,那麼這是一個可行的選擇。(可是,要知道,在引入一個不兼容的API的變化master可能會致使誰發出非模塊用戶的問題go get -u給出的go工具是不知道的語義化版本號以前去1.11或當模塊模式在Go 1.11+未啓用)。
    • 諸如dep當前之類的現有依賴關係管理解決方案在使用這種方式建立的v2 +模塊時可能會遇到問題。參見例如dep#1962
  2. 主要子目錄:建立一個新的v3子目錄(例如my/module/v3),而後go.mod在該子目錄中放置一個新文件。模塊路徑必須以結尾/v3將代碼複製或移動到v3子目錄中。更新模塊中的import語句以也使用/v3(例如import "github.com/my/module/v3/mypkg")。用標記發佈v3.0.0

    • 這提供了更大的向後兼容性。特別是,低於1.9.7和1.10.3的Go版本也可以正確使用和構建使用此方法建立的v2 +模塊。
    • 這裏一種更復雜的方法能夠利用類型別名(在Go 1.9中引入)並在駐留在不一樣子目錄中的主要版本之間轉發填充。這能夠提供額外的兼容性,並容許以另外一個主要版本的形式實現一個主要版本,可是對於模塊做者而言,這將須要更多的工做。正在進行自動化的工具是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生態系統中的不一樣軟件包以不一樣的速率選擇加入。
  • 主要因爲語義導入版本控制的影響,已經在版本v2或更高版本上的軟件包具備更多的遷移注意事項
  • 在採用模塊時,新軟件包和v0或v1上的軟件包的考慮要少得多。
  • 使用Go 1.11定義的模塊能夠用於較舊的Go版本(儘管確切的Go版本取決於主模塊使用的策略及其依賴項,以下所述)。

遷移主題:

從先前的依賴管理器自動遷移

向Go和非模塊消費者的較舊版本提供依賴信息

  • 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語句import "foo"就足夠了。(後續命令如go buildgo 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/logrusgithub.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 +軟件包的模塊時增長主要版本

  • 若是您在採用模塊以前已將其軟件包標記爲v2.0.0或更高版本,則建議的最佳實踐是在首次採用模塊時增長主要版本。例如,若是您正在使用v2.0.1而且還沒有采用模塊,那麼您將使用v3.0.0採用模塊的第一個發行版。有關更多詳細信息,請參見上面的「發佈模塊(v2或更高版本)」部分。

v2 +模塊容許在一個內部版本中使用多個主要版本

  • 若是模塊在v2或更高版本上,則意味着多個主要版本能夠位於單個版本中(例如,foo而且foo/v3可能最終在單個版本中)。
    • 這天然源於「具備不一樣導入路徑的包是不一樣的包」的規則。
    • 發生這種狀況時,將有多個軟件包級別狀態的副本(例如的軟件包級別狀態foo和的軟件包級別狀態foo/v3),而且每一個主要版本都將運行其本身的init功能。
    • 這種方法有助於解決模塊系統的多個方面,包括幫助解決鑽石依賴問題,在大型代碼庫中逐步遷移到新版本,以及容許將主要版本實現爲圍繞其餘主要版本的填充。
  • 有關某些相關討論,請參見https://research.swtch.com/vgo-import#27514的「避免單例問題」部分

消耗非模塊代碼的模塊

  • 模塊可以使用還沒有選擇加入模塊的軟件包,並將適當的軟件包版本信息記錄在導入模塊的中go.mod模塊可使用尚沒有適當的語義化版本號標籤的軟件包。有關更多詳細信息,請參見下面常見問題解答
  • 模塊也能夠導入未選擇模塊的v2 +軟件包。+incompatible若是導入的v2 +程序包具備有效的語義化版本號標籤,它將帶有後綴記錄有關更多詳細信息,請參見下面常見問題解答

非模塊代碼消費模塊

  • 非模塊代碼消耗v0和v1模塊

    • 還沒有選擇使用模塊的代碼可使用和構建v0和v1模塊(與使用的Go版本無關)。
  • 非模塊代碼消耗v2 +模塊

預先存在的v2 +軟件包做者的策略

對於考慮加入模塊的預先存在的v2 +軟件包的做者,總結替代方法的一種方法是在三種頂級策略之間進行選擇。每一個選擇都有後續的決定和變化(如上所述)。這些替代的頂級策略是:

  1. 要求客戶端使用Go版本1.9.7 +,1.10.3 +或1.11+

    該方法使用「主要分支」方法,並依賴於「最小模塊感知」,該模型被反向移植到1.9.7和1.10.3。有關更多詳細信息,請參見上面的「語義導入版本控制」「發佈模塊(v2或更高版本)」部分。

  2. 容許客戶使用甚至更舊的Go版本,如Go 1.8

    此方法使用「主要子目錄」方法,並涉及建立子目錄,例如/v2/v3有關更多詳細信息,請參見上面的「語義導入版本控制」「發佈模塊(v2或更高版本)」部分。

  3. 等待加入模塊

    在這種策略下,事情繼續與選擇了模塊的客戶端代碼以及未選擇模塊的客戶端代碼一塊兒工做。隨着時間的流逝,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博客系列
  • 聲明在外部系統中的模塊之間的成對的不兼容性,例如所討論這裏在提案過程
  • 在發佈發行版後聲明模塊的成對不兼容版本或不安全版本。參見例如#24031#26829中正在進行的討論

何時出現舊行爲與新的基於模塊的行爲?

一般,模塊是Go 1.11的可選組件,所以,根據設計,默認狀況下會保留舊的行爲。

總結什麼時候得到舊的1.10現狀行爲與新的基於選擇加入模塊的行爲:

  • 內部GOPATH-默認爲舊的1.10行爲(忽略模塊)
  • 在GOPATH以外,而在帶有go.mod的文件樹中-默認爲模塊行爲
  • GO111MODULE環境變量:
    • 未設置或auto-上面的默認行爲
    • on —無論目錄位置如何,都強制支持模塊
    • off —無論目錄位置如何,都強制關閉模塊支持

爲何經過go get錯誤安裝工具會失敗並顯示錯誤cannot find main module

當您進行設置GO111MODULE=ongo.mod運行時不在文件樹內部會發生這種狀況go get

最簡單的解決方案是保持未GO111MODULE設置狀態(或等效地顯式設置爲GO111MODULE=auto),這樣能夠避免出現此錯誤。

回想一下存在的主要緣由之一是記錄精確的依賴項信息。此依賴項信息將寫入您的current go.mod若是您不在帶有的文件樹中,go.mod可是go get經過設置告訴命令以模塊模式GO111MODULE=on運行,則運行go get將致使錯誤,cannot find main module由於沒有go.mod可用來記錄依賴項信息的信息。

解決方案的替代方案包括:

  1. 保持未GO111MODULE設置狀態(默認設置或顯式設置GO111MODULE=auto),這將致使更友好的行爲。當您不在模塊中時,這將爲您提供Go 1.10行爲,從而避免了go get報告cannot find main module

  2. export GO111MODULE=on,但根據須要暫時禁用模塊,並在過程當中啓用Go 1.10行爲go get,例如via GO111MODULE=off go get example.com/cmd能夠將其轉換爲簡單的腳本或shell別名,例如alias oldget='GO111MODULE=off go get'

  3. 建立一個臨時go.mod文件,而後將其丟棄。這已經經過@rogpeppe簡單shell腳本實現自動化該腳本容許經過可選地提供版本信息vgoget example.com/cmd[@version](這是避免錯誤的解決方案cannot use path@version syntax in GOPATH mode)。

  4. gobin是可識別模塊的命令,用於安裝和運行主軟件包。默認狀況下,gobin無需先手動建立模塊便可安裝/運行主程序包,但-m能夠經過標誌將該命令告知使用現有模塊來解決依賴關係。請參閱gobin 自述文件常見問題解答以瞭解詳細信息和其餘用例。

  5. 建立一個go.mod用於跟蹤運行的全局安裝工具(例如中的)~/global-tools/go.mod,而後cd在運行以前跟蹤該目錄,go get跟蹤go install全部全局安裝的工具。

  6. go.mod爲每一個工具在單獨的目錄(例如~/tools/gorename/go.mod和)中建立一個~/tools/goimports/go.mod,並cd在運行前爲該工具go getgo 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中的此註釋中討論了該方法以及更早的具體示例

簡要理由(一樣來自#25922):

我認爲tools.go文件其實是工具依賴關係的最佳實踐,固然對於Go 1.11。

我喜歡它,由於它沒有引入新的機制。

它只是簡單地重用現有的。

IDE,編輯器和標準工具(例如goimports,gorename等)中模塊支持的狀態如何?

對模塊的支持已開始在編輯器和IDE中得到。

例如:

  • Goland:目前擁有內外GOPATH模塊,包括完成,語法分析,重構,所描述的導航全面支持這裏
  • VS Code:工做正在進行中,正在尋找有助於的人。跟蹤問題是#1532VS Code模塊狀態Wiki頁面中描述了初始beta 
  • 帶有加號的原子:跟蹤問題是#761
  • 帶vim-go的vim:語法高亮顯示和格式的最初支持go.mod已經到來#1906年得到了更普遍的支持
  • 帶go-mode.el的emacs#237中的跟蹤問題

在雨傘問題中一直跟蹤其餘工具(例如goimports,guru,gorename和相似工具)的狀態#24661請查看該傘的最新狀態。

特定工具的一些跟蹤問題包括:

一般,即便您的編輯器,IDE或其餘工具還沒有被模塊識別,若是您在GOPATH內使用模塊而且可使用,則它們的大部分功能也應與模塊一塊兒使用go mod vendor(由於應經過GOPATH來選擇適當的依賴項) 。

完整的解決方法是把那包加載關閉的程序go/build和到golang.org/x/tools/go/packages,其知道如何定位模塊感知方式封裝。這極可能最終成爲事實go/packages

常見問題解答-附加控制

存在哪些社區工具來使用模塊?

社區開始在模塊之上構建工具。例如:

  • github.com/rogpeppe/gohack
    • 一種新的社區工具,能夠自動化並大大簡化replace和多模塊工做流程,其中包括容許您輕鬆修改其中的一個依賴項
    • 例如,gohack example.com/some/dependency自動克隆適當的存儲庫並將必要的replace指令添加到您的go.mod
    • 使用如下命令刪除全部gohack替換語句 gohack undo
    • 該項目正在繼續擴展,以簡化與模塊相關的其餘工做流程
  • github.com/marwan-at-work/mod
    • 命令行工具可自動升級/降級模塊的主要版本
    • go.mod在go源代碼中自動調整文件和相關的導入語句
    • 幫助進行升級,或者在首次選擇帶有v2 +軟件包的模塊時提供幫助
  • github.com/akyoto/mgit
    • 使您能夠查看和控制全部本地項目的語義化版本號標籤
    • 顯示未標記的提交,並讓您一次標記全部(mgit -tag +0.0.1
  • github.com/goware/modvendor
    • 幫助將其餘文件複製到該vendor/文件夾中,例如外殼程序腳本,.cpp文件,.proto文件等。
  • github.com/psampaz/go-mod-outdated
    • 以人類友好的方式顯示過期的依賴關係
    • 提供一種過濾間接依賴關係和無需更新的依賴關係的方法
    • 提供了一種在依賴項過期的狀況下中斷CI管道的方法

何時應該使用replace指令?

上面「 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相應的requirefor foo若是你不知道在用什麼版本的require指令,你能夠常用v0.0.0require foo v0.0.0這在Go 1.12中的#26241中獲得瞭解決

您能夠經過運行確認得到所需的版本go list -m all,該版本向您顯示了將在構建中使用的實際最終版本,包括考慮了replace語句。

有關更多詳細信息,請參見「 go mod edit」文檔

github.com/rogpeppe/gohack使這些類型的工做流變得更加容易,尤爲是若是您的目標是對模塊依賴項進行可變簽出時。有關概述,請參見存儲庫或以前的常見問題解答。

有關在replaceVCS以外徹底使用的詳細信息,請參見下一個FAQ 

我能夠在本地文件系統上徹底不在VCS上工做嗎?

是。不須要VCS。

若是您要一次在VCS以外編輯單個模塊,那麼這很是簡單(而且您總共只有一個模塊,或者其餘模塊位於VCS中)。在這種狀況下,能夠將包含單個文件的文件樹放置go.mod在方便的位置。go buildgo 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)中手動添加指令

線程中顯示了一個小的可運行示例

如何對模塊使用vendor?vendor會消失嗎?

最初的一系列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.mod什麼時候更新以及go工具什麼時候使用網絡知足依賴關係嗎?

默認狀況下,相似的命令go build會根據須要到達網絡以達到導入要求。

有些團隊可能但願禁止go工具在某些時候接觸網絡,或者想要更好地控制go工具什麼時候更新go.mod,如何得到依賴關係以及如何使用vendor。

轉到工具提供了至關數量的靈活調整或關閉這些默認的行爲,包括經過-mod=readonly-mod=vendorGOFLAGSGOPROXY=offGOPROXY=file:///filesystem/pathgo mod vendor,和go mod download

這些選項的詳細信息遍及整個官方文檔。此處是一個社區,試圖對與這些行爲相關的旋鈕進行綜合概述,其中包括指向官方文檔的連接,以獲取更多信息。

如何將模塊與Travis或CircleCI等CI系統一塊兒使用?

最簡單的方法可能只是設置環境變量GO111MODULE=on,該變量應適用於大多數CI系統。

可是,因爲您的某些用戶還沒有選擇加入模塊,所以在啓用和禁用模塊的Go 1.11上的CI中運行測試可能頗有價值。vendor也是要考慮的話題。

如下兩個博客文章更具體地介紹了這些主題:

常見問題解答— go.mod和go.sum

爲何「 go mod tidy」在個人「 go.mod」中記錄間接和測試依賴項?

該模塊系統記錄您的精確的依賴要求go.mod(有關更多詳細信息,請參閱上面go.mod概念部分或go.mod技巧文檔)。

go mod tidy更新您的當前信息,go.mod以在模塊中包括測試所需的依賴關係-若是測試失敗,咱們必須知道使用了哪些依賴關係來重現失敗。

go mod tidy還能夠確保您的當前go.mod反映了操做系統,架構的全部可能組合的依賴性需求,並創建標籤(如描述在這裏)。相比之下,其餘的命令同樣go build,並go test只更新go.mod提供電流下被請求包導入的包GOOSGOARCH和創建標籤(這是一個緣由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.3go工具須要一個位置來記錄這些新版本,而且它會在您的go.mod文件中記錄(而且不會深刻到您的依賴項中來修改 go.mod文件)。

一般,上述行爲是模塊如何經過記錄精確的依賴項信息來提供100%可複製的構建和測試的一部分。

若是你是好奇,爲何一個特定的模塊,顯示在你起來go.mod,你能夠運行go mod why -m <module>回答這個問題。用於檢查需求和版本的其餘有用工具包括go mod graphgo list -m all

'go.sum'是鎖定文件嗎?爲何「 go.sum」包含有關我再也不使用的模塊版本的信息?

不,go.sum不是鎖定文件。go.mod構建中文件爲100%可複製的構建提供了足夠的信息。

爲了進行驗證,go.sum包含特定模塊版本的內容的預期密碼校驗和。有關詳細信息(包括爲何一般須要簽入)以及技巧文檔中「模塊下載和驗證」部分,請參見下面FAQgo.sumgo.sum

部分因爲go.sum不是鎖文件,所以即便您中止使用模塊或特定模塊版本,它也會保留模塊版本的加密校驗和。若是您之後繼續使用某些內容,則能夠驗證校驗和,從而提升了安全性。

另外,您的模塊go.sum記錄了構建中使用的全部直接和間接依賴項的校驗和(所以,go.sum列出的模塊一般比的要多go.mod)。

我是否應該提交「 go.sum」文件以及「 go.mod」文件?

一般,模塊的go.sum文件應與go.mod文件一塊兒提交。

  • go.sum 包含特定模塊版本內容的預期密碼校驗和。
  • 若是有人克隆您的存儲庫並使用go命令下載了您的依賴項,那麼若是他們下載的依賴項副本和的相應條目之間存在任何不匹配,就會收到錯誤消息go.sum
  • 此外,go mod verify檢查磁盤下載的模塊下載在磁盤上的緩存副本是否仍與中的條目匹配go.sum
  • 請注意,go.sum它不是某些替代性依賴項管理系統中使用的鎖定文件。go.mod爲可複製的構建提供足夠的信息)。
  • 請參閱Filippo Valsorda的很是簡單的理由,瞭解您爲何要登機go.sum有關更多詳細信息,請參見技巧文檔「模塊下載和驗證」部分。請參閱#24117#25530中討論的未來可能的擴展

若是我沒有任何依賴關係,還應該添加一個「 go.mod」文件嗎?

是。這支持在GOPATH以外進行工做,幫助與您選擇模塊的生態系統進行通訊,此外module,您指令還能夠go.mod用做代碼身份的明確聲明(這是最終不建議使用導入註釋的緣由之一) )。固然,模塊在Go 1.11中純粹是可選功能。

常見問題解答—語義導入版本控制

爲何主版本號必須出如今導入路徑中?

請參閱上面「語義導入版本控制」概念部分中有關語義導入版本控制和導入兼容性規則的討論另請參閱宣佈提案博客文章,其中更多地討論了導入兼容性規則的動機和理由。

爲何導入路徑中省略了主要版本v0,v1?」

請參閱問題「爲何導入路徑中省略了主要版本v0,v1?」 在較早的FAQ中,來自官方提案的討論

用主要版本v0,v1標記個人項目,或使用v2 +進行重大更改有什麼含義?

在迴應有關「 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.3foo自身還沒有選擇加入模塊,則在模塊M的內部運行go get foogo 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 +軟件包,而且若是導入的v2 +軟件包具備有效的語義化版本號標籤,則將記錄+incompatible後綴。

額外細節

請熟悉上面「語義導入版本控制」部分中的材料

首先回顧一些一般有用但在考慮本FAQ中描述的行爲時要記住的特別重要的核心原則會有所幫助。

工具以模塊模式(例如運行時,如下核心原則始終是正確的goGO111MODULE=on

  1. 軟件包的導入路徑定義了軟件包的標識。
    • 具備不一樣導入路徑的軟件包被視爲不一樣的軟件包。
    • 具備相同導入路徑的軟件包被視爲相同的軟件包(即便 VCS標籤說這些軟件包具備不一樣的主要版本,也是如此)。
  2. 不帶a的導入路徑/vN被視爲v1或v0模塊(即便導入的程序包未選擇加入模塊而且具備表示主要版本大於1的VCS標記,也是如此)。
  3. 在模塊文件module foo/v2開頭聲明的模塊路徑(例如go.mod均爲:
    • 該模塊身份的明確聲明
    • 關於必須如何經過使用代碼導入該模塊的明確聲明

咱們將在接下來的FAQ看到,當這些原則並不老是正確的go工具是不是在模塊模式,可是當這些原則是老是正確的go工具模塊模式。

簡而言之,+incompatible後綴表示當知足如下條件時,上述原則2有效:

  • 導入的軟件包還沒有選擇使用模塊,而且
  • 它的VCS標籤說主要版本大於1,而且
  • 原理2覆蓋了VCS標籤-不帶a的導入路徑/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的末尾沒有使用– 在模塊路徑和導入路徑中使用是語義導入版本控制的功能,而且未表示接受並理解了語義導入版本控制(還沒有給出)經過在其內部包含文件來選擇加入模塊換句話說,即便具備語義化版本號標籤也不會被授予語義導入版本控制的權利和責任(例如,在導入路徑中使用),由於還沒有聲明要這樣作。oldpackagego getrequire/vNoldpackageoldpackagego.modoldpackageoldpackagev3.0.1oldpackage/vNoldpackage

+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.0v2.0.0以及v3.0.1使用相同的導入路徑都始終輸入:

import  "oldpackage"

再次注意,/v3末尾沒有用過oldpackage

一般,具備不一樣導入路徑的軟件包是不一樣的軟件包。在這個例子中,給出的版本v1.0.0v2.0.0v3.0.1oldpackage會使用相同的導入路徑須要進口,所以它們是經過構建視爲同一包(也由於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)。

若是未啓用模塊支持,如何在版本中處理v2 +模塊?1.9.7 +,1.10.3 +和1.11中的「最小模塊兼容性」如何工做?

在考慮還沒有加入模塊的較舊的Go版本或Go代碼時,語義導入版本控制具備與v2 +模塊相關的顯着向後兼容性含義。

如上文「語義導入版本控制」部分所述:

  • 版本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版本不具備完整模塊支持,這些版本始終在禁用完整模塊模式的狀況下始終有效運行)。

「最小模塊兼容性」的主要目標是:

  1. 容許較早的Go版本1.9.7+和1.10.3+可以更輕鬆地編譯/vN在導入路徑中使用語義導入版本控制的模塊,並在Go 1.11中禁用模塊模式時提供相同的行爲

  2. 容許舊代碼可以使用v2 +模塊,而無需使用舊的消費者代碼在使用/vNv2 +模塊時當即更改成使用新的導入路徑。

  3. 這樣作無需依賴模塊做者便可建立/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中的「最小模塊兼容性」機制:

  • 選擇模塊的軟件包任何導入的v2 +模塊的導入路徑中不會包含主版本。
  • 相反,一個包已經選擇加入的模塊必須包括在導入路徑主要版本導入任何V2 +模塊。
    • 若是軟件包已選擇加入模塊,但在導入v2 +模塊時在導入路徑中未包含主版本,則當該go工具在全模塊模式下運行時,它將不會導入該模塊的v2 +版本(假定已選擇使用模塊的軟件包「說」語義導入版本控制。若是foo是具備v2 +版本的模塊,則在「語義導入版本控制」下表示import "foo"要導入的v1語義導入版本控制系列foo)。
  • 用於實現「最小模塊兼容性」的機制故意很是狹窄:
    • 整個邏輯是–當在GOPATH模式下運行時,若是導入語句位於選擇加入模塊的代碼內(即,文件中的導入語句),則/vN在刪除後,將再次嘗試包含a的沒法解析的導入語句帶有有效文件)。/vN.gogo.mod
    • 最終結果是,導入語句(例如import "foo/v2"位於模塊內部的代碼內)仍將在GOPATH模式下(分別爲1.9.7 +,1.10.3 +和1.11)正確編譯,而且將像說的那樣進行解析import "foo"(不帶/v2) ,這意味着它將使用foo駐留在您的GOPATH中的版本,而不會被多餘的混淆/v2
    • 「最小模塊兼容性」不會影響其餘任何內容,包括它不會影響go命令行中使用的路徑(例如go get或的參數go list)。
  • 這種過渡的「最小模塊感知」機制有意打破了「將具備不一樣導入路徑的軟件包視爲不一樣的軟件包」的規則,以實現很是具體的向後兼容性目標–容許舊代碼在使用v2 +模塊時進行編譯,而無需修改。稍微詳細一點:
    • 若是舊代碼使用v2 +模塊的惟一方法是首先更改舊代碼,那麼對整個生態系統來講,負擔將更大。
    • 若是咱們不修改舊代碼,則該舊代碼必須適用於v2 +模塊的模塊前導入路徑。
    • 另外一方面,選擇加入模塊的新代碼或更新代碼必須/vN對v2 +模塊使用新的導入。
    • 新的導入路徑不等於舊的導入路徑,可是二者均可以在單個版本中使用,所以,咱們有兩個不一樣的功能導入路徑能夠解析爲同一程序包。
    • 例如,在GOPATH模式下運行時,import "foo/v2"出如今基於模塊的代碼中將解析爲與您的GOPATH中駐留的代碼相同的代碼import "foo",而且該構建最終以-的一個副本結尾foo-特別是不管GOPATH磁盤上的版本如何。這使得基於模塊的代碼 import "foo/v2"甚至能夠在1.9.7 +,1.10.3 +和1.11的GOPATH模式下進行編譯。
  • 相反,當go工具以全模塊模式運行時:
    • 「具備不一樣導入路徑的軟件包是不一樣的軟件包」規則也沒有例外(包括在徹底模塊模式下對vendor進行了改進,以也遵照此規則)。
    • 例如,若是該go工具處於完整模塊模式而且foo是v2 +模塊,則import "foo"要求提供foovs 的v1版本,import "foo/v2"要求提供的v2版本foo

若是我建立go.mod但不將語義化版本號標記應用於存儲庫,會發生什麼狀況?

語義化版本號是模塊系統的基礎。爲了向消費者提供最佳體驗,鼓勵模塊做者使用語義化版本號 VCS標籤(例如v0.1.0v1.2.3-rc.1),但嚴格要求不使用語義化版本號 VCS標籤:

  1. 要求模塊遵循語義化版本號規範,以使該go命令按照記錄的方式運行。這包括遵循關於如何以及什麼時候容許更改的語義化版本號規範。

  2. 消費者使用僞版本形式的語義化版本號版本記錄沒有語義化版本號 VCS標籤的模塊一般,這將是v0主版本,除非模塊做者遵循「主子目錄」方法構造了v2 +模塊

  3. 所以,不該用語義化版本號 VCS標記且未建立「主要子目錄」的模塊將有效地聲明本身屬於語義化版本號 v0主版本系列,而且基於模塊的使用者將其視爲具備語義化版本號 v0主版本。

模塊能夠依賴於其自身的不一樣版本嗎?

一個模塊能夠依賴於其自身的不一樣主要版本:總的來講,這至關於依賴於不一樣的模塊。出於各類緣由,這可能頗有用,包括容許將模塊的主要版本實現爲圍繞其餘主要版本的填充程序。

此外,一個模塊能夠在一個週期中依賴於其自身的不一樣主要版本,就像兩個徹底不一樣的模塊能夠在一個週期中彼此​​依賴同樣。

可是,若是您不但願模塊依賴於其自身的不一樣版本,則多是錯誤的徵兆。例如,打算從v3模塊導入軟件包的.go代碼可能缺乏/v3import語句中所需的內容。根據自己的v1版本,該錯誤可能表現爲v3模塊。

若是您驚訝地看到一個模塊依賴於其自身的不一樣版本,那麼值得回顧一下上面「語義導入版本控制」部分以及常見問題解答「若是我沒有看到預期的版本,該怎麼辦?依賴?」 

兩個程序包可能在一個週期中彼此​​不依賴仍然是一個約束

FAQS —多模塊存儲庫

什麼是多模塊存儲庫?

多模塊存儲庫是一個包含多個模塊的存儲庫,每一個模塊都有本身的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一個存儲庫中可能有多個合理的場景

  1. 若是您有一些使用示例,其中這些示例自己具備一組複雜的依賴關係(例如,也許您的軟件包很小,但包括一個將軟件包與kubernetes結合使用的示例)。在這種狀況下,對於您的存儲庫來講,擁有一個examples一個_examples本身的目錄是頗有意義go.mod,例如here

  2. 若是您的存儲庫具備一組複雜的依賴關係,可是您的客戶端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,讓咱們經過上面的存儲庫逐步進行操做:

  1. 添加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
  2. git commit

  3. git tag v1.3.0

  4. git tag mig/v1.0.0

  5. 接下來,讓咱們測試一下。咱們不能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
  6. 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能夠排除沒必要要的內容嗎?模塊是否等效於.gitignore文件?

go.mod在單個存儲庫中具備多個文件的另外一個用例是,若是存儲庫中的文件應從模塊中刪除。例如,存儲庫可能具備Go模塊不須要的很是大的文件,或者多語言存儲庫可能具備許多非Go文件。

go.mod目錄中的空白將致使該目錄及其全部子目錄從頂級Go模塊中排除。

若是排除的目錄不包含任何.go文件,則除了放置空go.mod文件以外,不須要其餘步驟若是排除的目錄中確實包含.go文件,請首先仔細閱讀此多模塊存儲庫部分中的其餘常見問題解答

常見問題解答–最小版本選擇

最少的版本選擇是否會使開發人員沒法得到重要的更新?

請參閱問題「最小版本選擇是否會使開發人員沒法得到重要更新?」 在較早的FAQ中,來自官方提案的討論

常見問題解答-可能的問題

若是我發現問題,能夠進行哪些常規檢查?

  • 經過運行go env來確認是否啓用了模塊,以確認其未爲只讀GOMOD變量顯示空值
    • 注意:永遠不要將其設置GOMOD爲變量,由於它其實是輸出的只讀調試go env輸出。
    • 若是GO111MODULE=on要啓用模塊,請仔細檢查它是否不是複數形式GO111MODULES=on(人們有時天然會包括,S由於該功能一般稱爲「模塊」)。
  • 若是指望使用vendor,請檢查該-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命令以進行故障排除)。
  • 您能夠檢查是否使用了特別舊的git版本
    • 較早版本的git是vgo原型和Go 1.11 beta 的常見問題根源,但在GA 1.11中則不多出現。
  • Go 1.11中的模塊緩存有時可能會致使各類錯誤,主要是若是先前存在網絡問題或go並行執行多個命令(請參閱#26794,Go 1.12已解決)。做爲故障排除步驟,您能夠將$ GOPATH / pkg / mod複製到備份目錄(以防稍後須要進一步調查),運行go clean -modcache,而後查看原始問題是否仍然存在。
  • 若是您使用的是Docker,則檢查是否能夠在Docker以外重現行爲可能會有所幫助(而且若是該行爲僅在Docker中發生,則上面的項目符號列表能夠用做比較Docker內部與外面)。

當前正在檢查的錯誤多是因爲構建中沒有特定模塊或軟件包的預期版本而引發的第二個問題。所以,若是致使特定錯誤的緣由不明顯,則能夠按照下一個FAQ中的說明對您的版本進行抽查。

若是沒有看到指望的依賴版本,該如何檢查?

  1. 一個好的第一步是運行go mod tidy這可能會解決問題,但也有可能使您的go.mod文件相對於.go源代碼處於一致狀態,這將使之後的調查更加容易。(若是go mod tidy它自己以某種您不但願的方式更改了依賴關係的版本,請先閱讀'go mod tidy'上的常見問題解答。若是仍沒法解決,您能夠嘗試重置您的go.mod,而後運行go list -mod=readonly all,這可能會帶來更多的變化。有關須要更改其版本的特定消息)。

  2. 第二步一般應檢查go list -m all以查看爲您的構建選擇的實際版本列表。 go list -m all向您顯示最終選擇的版本,包括用於間接依賴性的版本以及在解決全部共享依賴性的版本以後的版本。它還顯示any replaceexclude指令的結果

  3. 下一步是檢查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語句。

爲何會出現錯誤「找不到提供軟件包foo的模塊」?

這是一條常見的錯誤消息,可能會因幾種不一樣的根本緣由而發生。

在某些狀況下,此錯誤僅是因爲路徑鍵入錯誤引發的,所以第一步可能應該是根據錯誤消息中列出的詳細信息再次檢查錯誤的路徑。

若是您尚未這樣作,那麼下一步一般是嘗試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」給出錯誤「沒法肯定源目錄的模塊路徑」?

go mod init沒有任何參數的狀況下,將根據不一樣的提示(例如VCS元數據)嘗試猜想正確的模塊路徑。可是,不能go mod init老是可以猜想出正確的模塊路徑。

若是go mod init給您這個錯誤,則這些試探法沒法猜想,您必須本身提供模塊路徑(例如go mod init github.com/you/hello)。

我有一個還沒有選擇模塊的複雜依賴性問題。我可使用其當前依賴項管理器中的信息嗎?

是。這須要一些手動步驟,但在某些更復雜的狀況下可能會有所幫助。

go mod init初始化本身的模塊時運行時,它將經過轉換配置文件(如Gopkg.lockglide.lock)或包含相應指令vendor.jsongo.mod文件自動從先前的依賴項管理器轉換requireGopkg.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/logrusvs. 之間區分大小寫的問題github.com/Sirupsen/logrus

如何解決因爲導入路徑與聲明的模塊標識不匹配而致使的「解析go.mod:意外的模塊路徑」和「錯誤加載模塊要求」錯誤?

爲何會發生此錯誤?

一般,模塊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/logrusgithub.com/sirupsen/logrus),或者因爲虛榮導入路徑而致使模塊有時在模塊以前經過兩個不一樣的名稱使用(例如github.com/golang/syncvs.建議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.modas 的第一行中聲明瞭本身module example.com/some/NEW/name若是是這樣,則暗示您看到的是「舊模塊名稱」與「新模塊名稱」的問題。

本節的其他部分重點在於按順序執行如下步驟來解決此錯誤的「舊名稱」和「新名稱」形式:

  1. 檢查您本身的代碼,看看是否要使用導入example.com/some/OLD/name若是是這樣,請更新您的代碼以導入example.com/some/NEW/name

  2. 若是您在升級過程當中收到此錯誤,則應嘗試使用Go的尖端版本進行升級,該尖端版本具備針對性更強的升級邏輯(#26902),一般能夠迴避此問題,而且在這種狀況下一般還具備更好的錯誤消息。請注意,go gettip / 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/lintgolang.org/x/lint問題。

  1. 若是您在執行go get -u foo或時收到此錯誤go get -u foo@latest,請嘗試刪除-u這將爲您提供一組所使用的依賴項,foo@latest而無需升級發佈foofoo可能會驗證爲工做過去版本的依賴項foo這在過渡時期可能很重要,在這種過渡時期,某些直接或間接的依賴關係foo可能還沒有采用語義化版本號或模塊。(一個常見的錯誤是認爲go get -u foo僅獲取的最新版本foo。實際上,-uin go get -u foogo get -u foo@latest手段獲取的全部直接和間接依賴項的最新版本foo可能正是您想要的,但若是因爲深層間接依賴而致使失敗,則可能不會特別如此。

  2. 若是上述步驟不能解決錯誤,則下一種方法會稍微複雜一些,但大多數狀況下應該能夠解決此錯誤的「舊名稱」和「新名稱」形式。這僅使用僅來自錯誤消息自己的信息,並簡要介紹了一些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中進行了跟蹤)。

  1. 若是上述步驟不能解決問題,則多是由於一個或多個依賴項的最新版本仍在使用有問題的舊導入路徑。在這種狀況下,重要的是肯定誰仍在使用有問題的舊導入路徑,並查找或打開一個問題,要求有問題的進口商更改成使用如今的規範導入路徑。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
  1. 若是這些步驟不足以解決問題,或者您是某個項目的維護者,但因爲循環引用而彷佛沒法刪除對較舊的有問題的導入路徑的引用,請在上查看有關該問題的詳細說明。單獨的Wiki頁面

最後,上述步驟集中於如何解決潛在的「舊名稱」與「新名稱」問題。可是,若是將a go.mod放置在錯誤的位置或僅具備錯誤的模塊路徑,也會出現相同的錯誤消息在這種狀況下,導入該模塊應始終失敗。若是要導入剛剛建立且從未成功導入的新模塊,則應檢查go.mod文件是否正肯定位,以及文件是否具備與該位置對應的正確模塊路徑。(最多見的方法是go.mod每一個存儲庫使用單個go.mod文件,並將單個文件放置在存儲庫根目錄中,並使用存儲庫名稱做爲module指令中聲明的模塊路徑)。參見「 go.mod」

爲何「開始構建」須要gcc,爲何不使用諸如net / http之類的預構建軟件包?

簡而言之:

由於預構建的軟件包是非模塊構建的,因此不能重複使用。抱歉。如今禁用cgo或安裝gcc。

僅在選擇加入模塊時(例如經過GO111MODULE=on,這纔是一個問題有關其餘討論,請參見#26988

模塊能夠與相似的進口商品一塊兒使用import "./subdir"嗎?

否。請參閱#26645,其中包括:

在模塊中,最後有一個子目錄的名稱。若是父目錄顯示「模塊m」,則子目錄將導入爲「 m / subdir」,而再也不是「 ./subdir」。

某些vendor目錄中可能沒有所需的文件

沒有.go文件的vendor目錄不會經過複製在目錄go mod vendor這是設計使然。

簡而言之,撇開任何特定的vendor行爲– go構建的整體模型是構建軟件包所需的文件應位於包含.go文件的目錄中

以cgo爲例,修改其餘目錄中的C源代碼不會觸發重建,而是您的構建將使用陳舊的緩存條目。cgo文檔如今包括

請注意,對其餘目錄中文件的更改不會致使從新編譯該軟件包,所以,該軟件包的全部非Go源代碼應存儲在軟件包目錄中,而不是子目錄中。

社區工具https://github.com/goware/modvendor容許您輕鬆地將一整套.c,.h,.s,.proto或其餘文件從模塊複製到vendorDirector中。儘管這可能會有所幫助,可是若是您有一些須要的文件來構建包含該.go文件的目錄以外的文件,則必須格外當心,以確保整體上能夠正確處理go編譯(與vendor無關)

請參閱#26366中的其餘討論

傳統vendor的另外一種方法是檢入模塊緩存。它最終可能會得到與傳統vendor相似的好處,而且在某些方面最終會得到更高的保真度。將此方法解釋爲「經過示例執行模塊」 演練

相關文章
相關標籤/搜索