原文連接:Go Modules 詳解node
Go 1.11 和 Go 1.12 包含了初步的 Go Modules 支持,且計劃在 2019 年 8 月發佈的 Go 1.13 會在全部開發過程當中默認使用 Go Modules。git
Go Modules 是爲了提高使用其餘開發者代碼,即添加**依賴項(模塊、包)**時的體驗,也是爲了讓代碼的正確性、安全性獲得保障。而且 Go Modules 可使用 GOPROXY 環境變量來解決中國大陸沒法使用 go get 的問題。github
因此學習跟 Go Modules 有關的知識是頗有必要的。golang
Go Modules 在 Go 1.11 及 Go 1.12 中有三個模式,根據環境變量 GO111MODULE 定義:數組
GO111MODULE=auto
):Go 命令行工具在同時知足如下兩個條件時使用 Go Modules:
GO111MODULE=off
):Go 命令行工具從不使用 Go Modules。相反,它查找 vendor 目錄和 GOPATH 以查找依賴項。GO111MODULE=on
):Go 命令行工具只使用 Go Modules,從不諮詢 GOPATH。GOPATH 再也不做爲導入目錄,但它仍然存儲下載的依賴項(GOPATH/pkg/mod/)和已安裝的命令(GOPATH/bin/),只移除了 GOPATH/src/。Go 1.13 默認使用 Go Modules 模式,因此以上內容在 Go 1.13 發佈並在生產環境中使用後均可以忽略。緩存
如下就是 go.mod 文件的一個最全面的示例:安全
module my/thing
go 1.12
require other/thing v1.0.2 // 這是註釋
require new/thing/v2 v2.3.4 // indirect
require(
new/thing v2.3.4
old/thing v0.0.0-20190603091049-60506f45cf65
)
exclude old/thing v1.2.3
replace bad/thing v1.4.5 => good/thing v1.4.5
複製代碼
很全面,也很複雜。但其實 go.mod 文件在實際項目沒有這麼複雜,並且一旦該文件存在,就不須要額外的步驟:像 go build、go test,甚至 go list 這樣的命令都會根據須要自動添加新的依賴項以知足導入。markdown
但如今咱們仍是來詳細瞭解 go.mod 文件的組成:工具
go.mod 文件是面向行的, 當前模塊(主模塊)一般位於第一行,接下來是根據路徑排序的依賴項。oop
每行包含一個指令,由一個前導動詞後跟參數組成。
全部前導動詞的做用以下:
module
:定義模塊路徑。go
:設置預期的語言版本。require
:要求給定版本或更高版本的特定模塊。exclude
:排除特定版本模塊的使用,不容許的模塊版本被視爲不可用,而且查詢沒法返回。replace
:使用不一樣的模塊版本替換原有模塊版本。前導動詞還能夠按塊的方式使用,用括號建立一個塊(第 5-8 行),就像在 Go 語言中的導入同樣:
import ( "errors" "fmt" "log" ) 複製代碼
註釋(第 3-4 行)可使用單行 // 這是註釋
註釋,但不能使用多行 /* 這是註釋 */
註釋。而 indirect
註釋(第 4 行)標記了該模塊不是被當前模塊直接導入的,只是被間接導入。
go.mod 文件只存在於在模塊的根目錄下,子目錄中的導入路徑會使用模塊的導入路徑 + 子目錄路徑的形式。例如:若是建立了一個名叫 world 的子目錄,並不須要在子目錄中使用 go mod init
命令,Go 命令行工具會自動識別它做爲 hello 模塊的一部分,因此它的導入路徑爲 hello/world。
Go 命令行工具會自動處理 go.mod 中指定的模塊版本。當源代碼中 import
指向的模塊不存在於 go.mod 文件中時,Go 命令行工具會自動搜索這個模塊,並將最新版本(最後一個 tag 且非預發佈的穩定版本)添加到 go.mod 文件中。
若是沒有 tag,則使用僞版本(第 7 行),這是一種版本語法,專門用於標記沒有 tag 的提交(一些 golang.org/x/ 下的包就是沒有 tag 的)。如:v0.0.0-20190603091049-60506f45cf65
。
前面部分爲語義化版本號,用於標記版本;中間部分爲 UTC 的提交時間,用於比較兩個僞版本以其肯定前後順序;後面部分是 commit 哈希的前綴,用於標記該版本位於哪一個 commit。
示例以下:
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gin-gonic/gin v1.4.0 h1:3tMoCCfM7ppqsR0ptz/wi1impNpT7/9wQtMZ8lr1mCQ=
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
複製代碼
每行由模塊導入路徑、模塊的特定版本和預期哈希組成。
在每次缺乏模塊時,若是緩存中不存在,則須要下載並計算其哈希添加到 go.sum 中;若是緩存中存在,則須要匹配 go.sum 中的已有條目。
這樣,構建軟件的用戶就可使用哈希驗證其構建是否跟你的構建相同(go mod verify
),而不管他們怎樣獲取依賴項,均可以獲得相同的版本。同時也保證了項目依賴不會發生預料以外的惡意修改和其餘問題。這也是爲何要將 go.sum 文件加入版本管理(Git)的緣由。
再加上 Go Modules 選擇的是最小版本選擇策略(默認使用構建中涉及的每一個模塊的最舊容許版本,使得新版本的發佈對構建沒有影響)就能夠實現可重現的構建(在重複構建時產生相同的結果)。
什麼是語義化版本?語義化版本是一套由 Gravatars 創辦者兼 GitHub 共同創辦者 Tom Preston-Werner 所創建的約定。在這套約定下,語義化版本號及其更新方式包含了不少有用的信息。
語義化版本號格式爲:X.Y.Z
(主版本號.次版本號.修訂號),使用方法以下:
舉個例子,有一個語義化版本號爲:v0.1.2
,則其主版本號爲 0,次版本爲 1,修訂號爲 2。而前面的 v
是 version(版本)的首字母,是 Go 語言慣例使用的,標準的語義化版本沒有這個約定。
因此在使用 Go 命令行工具或 go.mod 文件時,就可使用語義化版本號來進行模塊查詢,具體規則以下:
@latest
):將匹配最新的可用標籤版本或源碼庫的最新未標籤版本。@v1.2.3
):將匹配該指定版本。@v1
或 @v1.2
):將匹配具備該前綴的最新可用標籤版本。@<v1.2.3
或 @>=v1.5.6
):將匹配最接近比較目標的可用標籤版本。<
則爲小於該版本的最新版本,>
則爲大於該版本的最舊版本。當使用類 Unix 系統時,需用引號將字符串包裹起來以防止大於小於號被解釋爲重定向。如:go get 'github.com/gin-gonic/gin@<v1.2.3'
。@c856192
):將匹配該 commit 時的版本。@master
):將匹配該分支版本。如上圖所示,爲了能讓 Go Modules 的使用者可以從舊版本更方便地升級至新版本,Go 語言官方提出了兩個重要的規則:
v1
或 v2
)使用不一樣的導入路徑,以主版本結尾,且每一個主版本中最多一個。如:一個 rsc.io/quote
、一個 rsc.io/quote/v2
、一個 rsc.io/quote/v3
。而與 Git 分支的集成以下:
之前使用 vendor 目錄有兩個目的:
而模塊如今有了更好的機制來實現這兩個目的:
$GOPROXY
)實現。並且 vendor 目錄也很難管理這些依賴項,長此以往就會陷入與 node_modules 黑洞同樣的窘境。
因此,默認狀況下使用 Go Modules 將徹底忽略 vendor 的依賴項,可是爲了平穩過分,可使用 go mod vendor
命令能夠建立 vendor 目錄。
並在 Go 命令行工具使用 -mod=vendor
參數,如:go test -mod=vendor ./...
;或設置環境變量 GOFLAGS 爲 -mod=vendor
,這樣會假定 vendor 目錄包含正確的依賴項副本,並忽略 go.mod 文件中的依賴項描述來構建。
設置環境變量 GOPROXY 能夠解決中國大陸沒法使用 go get 的問題:
把 export GOPROXY=https://goproxy.io
寫入 Shell 的配置文件便可。
go mod init
:建立一個新模塊,初始化 go.mod 文件,參數爲該模塊的導入路徑,推薦使用這種形式。如:go mod init github.com/linehk/example
。go get
:更改依賴項版本(或添加新的依賴項)。go build
、go test
等命令:Go 命令行工具會根據須要添加新的依賴項。如:go test ./...
,測試當前模塊。go list -m all
:打印當前模塊依賴。go mod tidy
:移除無用依賴。go list -m -versions github.com/gin-gonic/gin
:列出該模塊的全部版本。go mod verify
:驗證哈希。在 GoLand 2019.1.3 中使用 Go Modules 須要進行兩個設置:
https://goproxy.io
。如圖所示:
進行如上設置後,就能夠在導入不在緩存中的包時,點擊 Sync packages of... 下載該包了: