在前面的文章,咱們先是介紹了Go 的幾種包管理方式,而後具體介紹了一種包管理的工具: glide。隨着 Go 1.11 的發佈,官方的包管理工具 Go Modules
變得流行起來。在發佈不久的 Go 1.12 版本中,加強了對 Go Modules
的支持。本文將會介紹如何在項目中安裝和使用 Go Modules
。html
如本文開頭所說,從 Go 1.11 版本才支持 Go Modules
。因此,默認 Go 的版本爲 >= 1.11。git
$ go version
go version go1.12 darwin/amd64
複製代碼
筆者安裝了最新的 1.12 版本。github
安裝後,咱們能夠經過如下兩種方式之一激活模塊支持:golang
$GOPATH/src
以外的目錄中調用 go 命令,且當前目錄或其任何父目錄中使用有效的 go.mod
文件,而且環境變量 GO111MODULE
未設置(或顯式設置爲auto)。GO111MODULE = on
後,調用go命令。爲當前的項目建立一個 go.mod
文件。緩存
當項目不在 GOPATH
中,直接執行:bash
go mod init
複製代碼
不然,會出現以下的錯誤:微信
go: modules disabled inside GOPATH/src by GO111MODULE=auto; see 'go help modules'
複製代碼
所以,咱們須要手動激活 Modules:ide
$ export GO111MODULE=on
複製代碼
而後才能執行 go mod init
。這會將任何現有的dep Gopkg.lock 文件或其餘九種支持的依賴關係轉換,添加 require
語句以匹配現有配置。工具
go mod init一般可以使用輔助數據(例如VCS元數據)來自動肯定相應的模塊路徑,可是若是 go mod init
代表它不能自動肯定模塊路徑,或者若是你須要以其餘方式覆蓋 path,你能夠提供模塊路徑做爲 go mod init
的可選參數,例如:測試
$ go mod init modtest
複製代碼
從模塊的根目錄執行時,./...
模式匹配當前模塊中的全部包。 go build
將根據須要自動添加缺失或未轉換的依賴項,以知足此特定構建調用的導入:
$ go build ./...
複製代碼
$ go test ./...
複製代碼
按配置測試模塊,以確保它適用於所選版本。還能夠運行模塊的測試以及全部直接和間接依賴項的測試以檢查不兼容性:
$ go test all
複製代碼
建立項目並進入根目錄:
$ mkdir src/hello
$ cd src/hello
複製代碼
$ go mod init github.com/keets2012/hello
go: creating new go.mod: module github.com/keets2012/hello
複製代碼
go mod 初始化,並命名包名爲 github.com/keets2012/hello
。能夠看到,一塊兒建立了 go.mod
文件。
$ cat <<EOF > hello.go
package main
import (
"fmt"
"rsc.io/quote"
)
func main() {
fmt.Println(quote.Hello())
}
EOF
複製代碼
咱們建立了一個 hello.go
的文件,並輸出調用的方法結果。
$ go build # 構建可執行文件
$ ./hello # 執行
Hello, world. # 輸出結果
複製代碼
執行構建以後,獲得可執行文件,咱們執行了獲得結果。 go.mod
文件已更新爲包含依賴項的顯式版本,其中 v1.5.2
是 semver
標記:
$ cat go.mod
module github.com/keets2012/hello
require rsc.io/quote v1.5.2
複製代碼
應該使用 go get
來完成平常升級和降級依賴項,這將自動更新 go.mod
文件。 或者能夠直接編輯 go.mod
。
此外,像 go build
,go test
或甚至 go list
這樣的命令會根據須要自動添加新的依賴項以知足導入。
要查看全部直接和間接依賴項的可用 minor 和 patch 程序升級:
go list -u -m all
複製代碼
要升級到當前模塊的全部直接和間接依賴關係的最新版本:
go get -u
以使用最新的次要版本或補丁版本go -u = patch
使用最新的補丁版本要升級或降級到更具體的版本,go get
容許經過在 package 參數中添加@version
後綴或「模塊查詢」來覆蓋版本選擇,例如 go get foo@v1.6.2
,go get foo @ e3702bed2
,或者 go foo @'<v1.6.2'
。
在上一小節,咱們提到了 semver
。golang 官方推薦的最佳實踐叫作 semver,這是一個簡稱,寫全了就是Semantic Versioning
,也就是語義化版本。
通俗地說,就是一種清晰可讀的,明確反應版本信息的版本格式,更具體的規範在這裏。
如規範所言,形如 vX.Y.Z
的形式顯然比一串 hash 更直觀,因此 golang 的開發者纔會把目光集中於此。
semver 簡化版本指定的做用是顯而易見的,然而僅此一條理由顯然有點缺少說服力,經過semver對版本進行嚴格的約束,能夠最大程度地保證向後兼容以及避免 「breaking changes」,而這些都是 golang 所追求的。二者一拍即合,因此 go modules
提供了語義化版本的支持。
若是你使用和發佈的包沒有版本 tag 或者處於 1.x 版本,那麼你可能體會不到什麼區別,由於 go mod
所支持的格式從始至終是遵循 semver 的,主要的區別體如今 v2.0.0 以及更高版本的包上。
「若是舊軟件包和新軟件包具備相同的導入路徑,則新軟件包必須向後兼容舊軟件包。」 - go modules wiki
相同名字的對象應該向後兼容,然而按照語義化版本的約定,當出現 v2.0.0 的時候必定表示發生了重大變化,極可能沒法保證向後兼容,這時候應該如何處理呢?
答案很簡單,咱們爲包的導入路徑的末尾附加版本信息便可,例如:
module my-module/v2
require (
some/pkg/v2 v2.0.0
some/pkg/v2/mod1 v2.0.0
my/pkg/v3 v3.0.1
)
複製代碼
格式總結爲 pkgpath/vN
,其中 N 是大於 1 的主要版本號。在代碼裏導入時也須要附帶上這個版本信息,如import "some/pkg/v2"
。這樣包的導入路徑發生了變化,也不用擔憂名稱相同的對象須要向後兼容的限制了,由於 golang 認爲不一樣的導入路徑意味着不一樣的包。固然還有意外的狀況:
+incompatible
就能夠不須要指定 /vN
,例如:require some/pkg v2.0.0+incompatible
除此之外的狀況若是直接使用 v2+
版本將會致使 go mod
報錯。
v2+ 版本的包容許和其餘不一樣大版本的包同時存在(前提是添加了/vN
),它們將被當作不一樣的包來處理。
另外 /vN
並不會影響你的倉庫,不須要建立一個v2對應的倉庫,這只是 go modules
添加的一種附加信息而已。
固然若是你不想遵循這一規範或者須要兼容現有代碼,那麼指定 +incompatible
會是一個合理的選擇。不過 go modules
不推薦這種行爲。
若是你不喜歡 go mod
的緩存方式,你可使用 go mod vendor
回到 godep 或 govendor 使用的 vendor 目錄進行包管理的方式。
固然這個命令並不能讓你從godep之類的工具遷移到 go modules
,它只是單純地把 go.sum
中的全部依賴下載到 vendor 目錄裏,若是你用它遷移 godep 你會發現 vendor 目錄裏的包會和 godep 指定的產生至關大的差別,因此請務必不要這樣作。
使用 go build -mod=vendor
來構建項目,由於在 go modules
模式下 go build
是屏蔽 vendor 機制的,因此須要特定參數從新開啓 vendor 機制:
go build -mod=vendor
./hello
hello world!
複製代碼
構建成功。當發佈時也只須要和使用 godep 同樣將 vendor 目錄帶上便可。
本文主要介紹了 go modules
的一些特性和使用方法, go modules
是官方的包管理工具,Go 語言經過引入 module 的概念進而引入了 Go tool 的另一種工做模式 module-aware mode
。在新的工做模式下,module 支持包依賴的版本化管理。
新的工做模式也帶來了一些問題,在大陸地區咱們沒法直接經過 go get
命令獲取到一些第三方包,這其中最多見的就是 golang.org/x
下面的各類優秀的包。一旦工做在模塊下,go build
將再也不關心 GOPATH 或是 vendor 下的包,而是到 GOPATH/pkg/mod
查詢是否有cache,若是沒有,則會去下載某個版本的 module,而對於某些包的 module,在大陸地區每每會失敗。咱們將在下篇文章介紹 go module 的 proxy 配置實現。