Go modules基礎精進,六大核心概念全解析(上)

騰訊雲加社群發表於2021-12-09
點選一鍵訂閱《雲薦大咖》專欄,獲取官方推薦精品內容,學技術不迷路!

image.png

Go 語言做開發時,路徑是如何定義的?Go Mudules又為此帶來了哪些改變?本文將會全面介紹Go modules六大核心概念,包括了設計理念與相容性原則等,掌握這些技術點對於管理和維護Go 模組有重要價值。

213.png

上一篇文章中,筆者介紹瞭如何以經典的 hello world 為例建立一個 Go module 模組,需要說明的是一個模組中是可以包含多個包(package)的,它們是可以被一起釋出、打包、版本化的。同時,Go Modules 也可以通過版本管理系統(github、gitlab)或者 goproxy 代理進行下載。在使用 Go Modules 之前,建議大家弄清楚息息相關的六大核心概念,以方便大家在後期的開發、使用過程中理解更加深入。

我們在使用 Go 語言做開發時經常會遇到像 “example.com/test” 或者“example.com/test/pkg/log”這樣的路徑,這些路徑到底是怎麼定義的,兩者中存在什麼關係,在 Go Modules 中又扮演著怎樣的角色呢?Go Modules 的引入對已有的包又引入了哪些新的概念,它們是如何協作的?對相容性提出了哪些新的要求呢?讓我們一起來看一下。

一:模組路徑 (Module Path)

Go 使用 “module path” 來區分不同的 module 模組,它在 go.mod 檔案中被定義,這個檔案中還包含了這個模組編譯所需的其他依賴。如果一個目錄中包含了 go.mod 檔案,那麼這個目錄就是這個 Go 模組的根目錄了。

另外,還要介紹下包(package) 這個概念,它在 Go Modules 出現之前就已經存在了。Go 模組中的 “包 (package)”是處於同一目錄中的一些原始碼檔案的集合,這些檔案將被編譯在一起。“包路徑(package path)”是模組路徑和子目錄(模組根目錄的相對路徑)的組合。舉個例子,在模組“golang.org/x/net”下的 html 目錄中有個包,這個包的路徑是“golang.org/x/net/html”。

總結下來就是: 一個程式碼倉庫可以包含多個 Go 模組,一個 Go 模組可以包含多個 Go 包。

模組路徑是一個 Go 模組的規範名稱,用於區分不通的模組。同時他還是該模組下 Go 包的路徑字首。理論上,模組路徑應該至少包含兩個關鍵資訊:

模組的作用
哪裡獲取該模組

二:版本號與相容性原則

版本號相當於是一個模組的只讀快照,它可以是正式的釋出版本,也可以是預釋出版本。 每個版本都以字母 v 開頭,後跟一個語義版本,例如 v1.0.0。

總而言之,語義版本由三個由點分隔的非負整數(主要版本、次要版本和補丁版本,從左到右)組成。 補丁版本後可以跟一個以連字元開頭的可選預釋出字串。 預釋出字串或補丁版本後可以跟一個以加號開頭的構建後設資料字串。 例如,v0.0.0、v1.12.134、v8.0.5-pre、v2.0.9+meta 等都是有效版本。

版本號中的資訊代表了這個版本是否是一個穩定版,是否保持了與之前版本的相容性。

當維護的模組發生了一些不相容變更,比如修改了外部可呼叫的介面或者函式時,需要對主版本號進行遞增,並且將次版本號和補丁版本號置為零。比如在模組中移除了一個包。

在模組中新增一些新的函式或者介面,並沒有影響模組的相容性時,需要對次版本號進行遞增,並且將補丁版本號置為零。

當修復了一些 bug 或者進行了一些優化時,只需要對補丁版本號進行遞增就可以了,因為這些變更不會對已經公開的介面進行變更。

預釋出字尾代表了這個版本號是一個預釋出版本。預釋出版本號的排序會在正式版本號的前面。舉個例子,v1.2.3-pre 會排列在 v1.2.3 前面。

後設資料字尾會在版本比對中被忽略,版本控制中的程式碼庫會忽略帶有構建後設資料的標籤,但在 go.mod 檔案中指定的版本中會保留構建後設資料。如果一個模組還沒有遷移到 Go Modules 並且主版本號是 2 或者更高,+incompatible 字尾會被新增到版本號上。

如果一個版本的主版本號是 0 或者它有一個預釋出版本字尾,那麼這個版本被認為是一個不穩定版本。通常,不穩定版本不受相容性限制的,舉個例子,v0.2.0 可能和 v0.1.0 是不相容的,v1.5.0-beta 可能和 v1.5.0 也是不相容的。

Go 可以通過 tags、分支、和 commit 雜湊值來獲取模組,即使這些命名沒有遵循這些規則。在主模組中,go 命令會自動的將這些 revision 轉化為符合標準的版本號,其被稱為偽版本號(pseudo-version)。舉個例子,當執行下面的命令時:

go get -d golang.org/x/net@daa7c041

Go 會講指定的 hash daa7c041 轉化為一個偽版本號 v0.0.0-20191109021931-daa7c04131f5。在主模組之外需要規範版本,如果 go.mod 檔案中出現像 master 這樣的非規範版本,go 命令會報錯。

三:偽版本號

偽版本號是一種預釋出版本號的格式,其中包含了指定的 commit hash 值。另外,對於沒有打標籤的程式碼庫,也可以使用偽版本號來表明某個版本,它可以在正式釋出某個版本之前方便的進行測試。舉個例子,每個偽版本號都有三部分組成:

基本版本字首(vX.0.0 或 vX.Y.Z-0),它要麼源自修訂版之前的語義版本標籤,要麼源自 vX.0.0(如果沒有此類標籤)。
時間戳 (yyyymmddhhmmss),這是建立 commit 的 UTC 時間。 在 Git 中,這是 commit 提交時間。
commit 識別符號 (abcdefabcdef),它是提交 commit 雜湊的 12 個字元的字首,或者在 Subversion 中,是一個用零填充的修訂號。

在這三個部分之下,又分為以下多種情況:

如果之前沒有基版本,那麼諸如 vX.0.0-yyyymmddhhmmss-abcdefabcdef 這樣的偽版本號將被啟用。主版本號 X 需要匹配模組的主版本號字尾。

如果之前的基版本號是一個像 vX.Y.Z-pre 這樣的預釋出版本,那麼 vX.Y.Zpre.0.yyyymmddhhmmss-abcdefabcdef 將被採用。

如果之前的基版本號是一個像 vX.Y.Z 這樣的正式版本,那麼 vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdefabcdef 將被採用,舉個例子,如果基版本號是 v1.2.3,偽版本號可能是 v1.2.4-0.20191109021931-daa7c04131f5。

基於不同的基礎版本號,多個偽版本號是有可能指向同一個 commit hash 的,在對一個低於已經存在的偽版本號打標籤時,這種情況就會發生。

上面介紹的這種偽版本號攜帶了兩個非常有用的資訊:

  1. 偽版本號會高於這些已經存在的基礎版本號,但是會低於後面生成的其他偽版本號。
  2. 有相同基礎版本字首的偽版本按時間順序排序。

偽版本號不需要手動指定。很多 Go 命令可以接受一個 commit hash 或者分支名,然後自動將其轉化為一個偽版本號(或者一個標籤,如果存在的話)。例如:

go get -d example.com/mod@master
go list -m -json example.com/mod@abcd1234

在本篇中,我們介紹了模組路徑、版本號與相容性原則、偽版本號三大概念,而在下篇我們將會繼續介紹Go Modules核心概念,包括主版本號字尾、解析包路徑到模組路徑的流程、go.mod 檔案,敬請期待。另外,騰訊雲 goproxy 企業版已經產品化,需要了解的同學可以點選這裡

image.png

李保坤往期精彩文章推薦:Go語言重新開始,Go Modules 的前世今生與基本使用

image.png

《雲薦大咖》是騰訊雲加社群精品內容專欄。雲薦官特邀行業佼者,聚焦於前沿技術的落地及理論實踐之上,持續為您解讀雲時代熱點技術、探索行業發展新機。點選一鍵訂閱,我們將為你定期推送精品內容。

相關文章