這一次,徹底掌握go mod

pibigstar發表於2020-07-26

1. 版本號規範

go mod 對版本號的定義是有一定要求的,它要求的格式為 v<major>.<minor>.<patch>,如果 major 版本號大於 1 時,其版本號還需要體現在 Module 名字中。比如 我的專案github.com/pibigstar/go-demo,如果我的版本號增長到v2.x.x時,我的 Module 名字也需要相應的改變為: github.com/pibigstar/go-demo/v2, 有人可能就要問了,我不改可以嗎? 可以的!但是 go mod 會在你依賴的後面打一個+incompatible 標誌

2. 偽版本

我們將專案上傳到 github 後,如果不打 tag,或 tag 不符合v<major>.<minor>.<patch>這個格式,那麼當我們用 go mod 去拉這個專案的時候,就會將 commitId 作為版本號,它的格式大概是 vx.y.z-yyyymmddhhmmss-abcdef格式

雖然不太好看,但是這個玩意其實挺有用的,省的你每次都需要打 tag 了,這裡介紹一個直接拉取小技巧

require (
    github.com/pibigstar/go-demo master
)

我們直接在後面寫 master 分支,這樣它就會拉取 master 分支最後一次提交的commitId 作為版本號

3. indirect 標誌

我們用 go mod 的時候應該經常會看到 有的依賴後面會打了一個 // indirect 的標識位,這個標識位是表示 間接的依賴。

什麼叫間接依賴呢?打個比方,專案 A 依賴了專案 B,專案 B 又依賴了專案 C,那麼對專案 A 而言,專案 C 就是間接依賴,這裡要注意,並不是所有的間接依賴都會出現在 go.mod 檔案中。間接依賴出現在 go.mod 檔案的情況,可能符合下面的場景的一種或多種:

  • 直接依賴未啟用 Go module
  • 直接依賴 go.mod 檔案中缺失部分依賴

3.1 直接依賴未啟用 Go module

如下圖所示,Module A 依賴 B,但是 B 還未切換成 Module,即沒有 go.mod 檔案,此時,當使用 go mod tidy 命令更新 A 的 go.mod 檔案時,B 的兩個依賴 B1 和 B2 將會被新增到 A 的 go.mod 檔案中(前提是 A 之前沒有依賴 B1 和 B2),並且 B1 和 B2 還會被新增// indirect的註釋。


那麼專案 A 的依賴關係就會變成下面這個樣子

require (
    B vx.x.x
    B1 vx.x.x // indirect
    B2 vx.x.x // indirect
)

3.2 go.mod 檔案中缺失部分依賴

如下圖所示,Module B 雖然提供了 go.mod 檔案中,但 go.mod 檔案中只新增了依賴 B1,那麼此時 A 在引用 B 時,則會在 A 的 go.mod 檔案中新增 B2 作為間接依賴,B1 則不會出現在 A 的 go.mod 檔案中。


此時專案 A 的依賴關係就會變成下面這個樣子

require (
    B vx.x.x
    B2 vx.x.x // indirect
)

間接依賴出現在 go.mod 中,可以一定程度上說明依賴有瑕疵,要麼是其不支援 Go module,要麼是其 go.mod 檔案不完整。
由於 Go 語言從 v1.11 版本才推出 module 的特性,眾多開源軟體遷移到 go module 還需要一段時間,在過渡期必然會出現間接依賴,但隨著時間的推進,在 go.mod 中出現// indirect 的機率會越來越低。
出現間接依賴可能意味著你在使用過時的軟體,如果有精力的話還是推薦儘快消除間接依賴。可以通過使用依賴的新版本或者替換依賴的方式消除間接依賴。

3.3 如果查詢間接依賴

可以通過 go mod why -m <pkg> 來檢視這個間接依賴是被誰引入的

4. replace 使用

replace 指替換,它指示編譯工具替換 require 指定中出現的包

這裡要注意兩點:

  • replace 只在 main module 裡面有效

    什麼叫 main module? 打個比方,專案 A 的 module 用 replace 替換了本地檔案,那麼當專案 B 引用專案 A 後,專案 A 的 replace 會失效,此時對 replace 而言,專案 A 就是 main module

  • replace 指定中需要替換的包及其版本號必須出現在 require 列表中才有效

這裡我再簡單談下 replace 的使用場景

  • 替換無法下載的包
  • 替換本地自己的包
  • 替換 fork 包

    有時候我們依賴的第三方庫可能有 bug,我們就可以 fork 一份他們的庫,然後自己改下,然後通過 replace 將我們 fork 的替換成原來的

5. exclude 使用

go.mod 檔案中的 exclude 指令用於排除某個包的特定版本,其與 replace 類似,也僅在當前 module 為 main module 時有效,其他專案引用當前專案時,exclude 指令會被忽略。
exclude 指令在實際的專案中很少被使用,因為很少會顯式地排除某個包的某個版本,除非我們知道某個版本有嚴重 bug。 比如指令exclude github.com/pibigstar/go-demo v1.1.0,表示不使用 v1.1.0 版本。

6. 包快取

最後簡單談一下當我們使用 go mod 後包快取儲存問題,依賴包儲存在$GOPATH/pkg/mod,該目錄中可以儲存特定依賴包的多個版本。需要注意的是$GOPATH/pkg/mod目錄下有個cache目錄,它用來儲存依賴包的快取,簡單說,go 命令每次下載新的依賴包都會在該 cache 目錄中儲存一份,每個版本都會有一份快取。

值得注意的是包名大小寫問題,有時我們使用的包名中會包含大寫字母,GOMODULE模式下,在儲存時會將包名做大小寫編碼處理,即每個大寫字母將變!+相應的小寫字母,比如github.com/pibigstar/GO-demo 包在儲存時將會被放置在$GOPATH/pkg/mod/github.com/pibigstar/!g!o-demo目錄中。所以這裡我不推薦你用大寫字母定義包名

歡迎關注我微信公眾號,一起學習Go

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章