簡潔的 Go 多版本管理機制
本文轉載自微信公眾號「Golang技術分享」,作者機器鈴砍菜刀。轉載本文請聯系Golang技術分享公眾號。
一門充滿生機的編程語言,一定是不斷進化向前的。隨著 Go 項目的持續發展,它目前已經發布到了 1.17 大版本,而且每個大版本內還會有不少小版本的迭代。對于 Go 的版本更新,我們該如何做好多版本管理。
多版本管理的重要性
這里簡單列舉幾個我們需要 Go 多版本管理的理由。
- 穩定性考量:雖然 Go1 一直在良好地遵守向后兼容準則,但通常基于穩定性考慮,我們并不會直接升級到最新版本。
- 多項目開發:各項目依賴的 Go 版本不一致。
- 版本兼容:測試代碼前后兼容性,或者確保 bug 修復在不同 Go 版本的正確性,對于開源項目,保證版本兼容性非常重要。
- 學習新特性:例如雖然我們還在使用 Go 1.16 開發,但是并這不能阻礙你嘗鮮 Go 1.17 新功能。
如何多版本管理
我們需要有兩個先決條件
- 已經安裝好了某版本的 Go
- 安裝好了 git
安裝
運行go get golang.org/dl/go
- $ go install golang.org/dl/go<version>@latest
通過包裝器,下載特定 Go 版本和它對應的工具鏈。
- $ go download
例如安裝1.14.12版本,可以這樣執行。
- $ go install golang.org/dl/go1.14.12@latest
- $ go1.14.12 download
使用
使用包裝器 go1.14.12,我們可以基于 Go v1.14.12 進行構建和測試。
- $ go1.14.12 mod init hello
- go: creating new go.mod: module hello
- $ echo 'package main; import "fmt"; func main() { fmt.Println("Hello, World") }' > hello.go
- $ go1.14.12 build
- $ ./hello
- Hello, World
當然,如果你想讓 Go v1.14.12 ”喧賓奪主“,成為 go 命令的代言人,可以這樣做。
- $ go version
- go version go1.17 darwin/amd64
- $ export GOROOT=$(go1.14.12 env GOROOT)
- $ export PATH=${GOROOT}/bin:$PATH
- $ go version
- go version go1.14.12 darwin/amd64
這個go1.14.12 env GOROOT 路徑就是 Go v1.14.12 版本的內容。所以,如果我們想卸載這個版本,直接將該路徑文件夾刪除即可;想閱讀該版本源碼,直接查看該路徑下的src/內容即可 。
獲取最新開發版本
有一個特別的版本標記:gotip,它用于安裝最新的開發版本。
- $ go install golang.org/dl/gotip@latest
- $ gotip download
可以看到,當前拉取到的最新的開發版本是 go1.18-1afa432。
實現思路
實現多版本下載安裝的秘訣就在于 https://go.googlesource.com/dl 這個倉庫,https://github.com/golang/dl 是它的鏡像庫。
查看倉庫代碼,我們能看到一系列版本目錄
隨意選擇一個版本進入,會發現存在一個 main.go 文件
而 main.go 文件內容如下
我們通過go install golang.org/dl/go1.14.12@latest下載的 go1.14.12 包裝器就是這個 main.go 編譯而成。
因此,我們后續通過 go1.14.12 包裝器下載和運行的邏輯就在于internal/version包中的 Run 方法了。
- // Run runs the "go" tool of the provided Go version.
- func Run(version string) {
- log.SetFlags(0)
- // 獲取 Go 安裝目錄
- root, err := goroot(version)
- if err != nil {
- log.Fatalf("%s: %v", version, err)
- }
- // 執行 go<version> download 命令時邏輯
- if len(os.Args) == 2 && os.Args[1] == "download" {
- if err := install(root, version); err != nil {
- log.Fatalf("%s: download failed: %v", version, err)
- }
- os.Exit(0)
- }
- // 判斷該版本 Go 安裝狀態
- if _, err := os.Stat(filepath.Join(root, unpackedOkay)); err != nil {
- log.Fatalf("%s: not downloaded. Run '%s download' to install to %v", version, version, root)
- }
- // 運行該版本 Go
- runGo(root)
- }
鑒于篇幅原因, 下載的install和運行的runGo函數邏輯本文就不再展開了,想深入了解的同學可以自行探索。
另外,為了讓每個版本都有一個 Go 包裝器主程序(避免重復的手工操作),這里使用了一個幫助命令genv:可以快速生成對應版本的包裝器代碼
總結
本文介紹了 Go 官方提供的多版本管理方案,包括使用、安裝、卸載等,可以感受到它的簡潔與高效。同時我們簡單查看了這一套實現代碼邏輯。
最后,希望本文內容能夠助你用好 Go 多版本管理,歡迎留言討論。