簡潔的 Go 多版本管理機(jī)制
本文轉(zhuǎn)載自微信公眾號「Golang技術(shù)分享」,作者機(jī)器鈴砍菜刀。轉(zhuǎn)載本文請聯(lián)系Golang技術(shù)分享公眾號。
一門充滿生機(jī)的編程語言,一定是不斷進(jìn)化向前的。隨著 Go 項目的持續(xù)發(fā)展,它目前已經(jīng)發(fā)布到了 1.17 大版本,而且每個大版本內(nèi)還會有不少小版本的迭代。對于 Go 的版本更新,我們該如何做好多版本管理。
多版本管理的重要性
這里簡單列舉幾個我們需要 Go 多版本管理的理由。
- 穩(wěn)定性考量:雖然 Go1 一直在良好地遵守向后兼容準(zhǔn)則,但通?;诜€(wěn)定性考慮,我們并不會直接升級到最新版本。
 - 多項目開發(fā):各項目依賴的 Go 版本不一致。
 - 版本兼容:測試代碼前后兼容性,或者確保 bug 修復(fù)在不同 Go 版本的正確性,對于開源項目,保證版本兼容性非常重要。
 - 學(xué)習(xí)新特性:例如雖然我們還在使用 Go 1.16 開發(fā),但是并這不能阻礙你嘗鮮 Go 1.17 新功能。
 
如何多版本管理
我們需要有兩個先決條件
- 已經(jīng)安裝好了某版本的 Go
 - 安裝好了 git
 
安裝
運(yùn)行g(shù)o get golang.org/dl/go
- $ go install golang.org/dl/go<version>@latest
 
通過包裝器,下載特定 Go 版本和它對應(yīng)的工具鏈。
- $ go download
 
例如安裝1.14.12版本,可以這樣執(zhí)行。
- $ go install golang.org/dl/go1.14.12@latest
 - $ go1.14.12 download
 
使用
使用包裝器 go1.14.12,我們可以基于 Go v1.14.12 進(jìn)行構(gòu)建和測試。
- $ 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
 
當(dāng)然,如果你想讓 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 版本的內(nèi)容。所以,如果我們想卸載這個版本,直接將該路徑文件夾刪除即可;想閱讀該版本源碼,直接查看該路徑下的src/內(nèi)容即可 。
獲取最新開發(fā)版本
有一個特別的版本標(biāo)記:gotip,它用于安裝最新的開發(fā)版本。
- $ go install golang.org/dl/gotip@latest
 - $ gotip download
 
可以看到,當(dāng)前拉取到的最新的開發(fā)版本是 go1.18-1afa432。
實現(xiàn)思路
實現(xiàn)多版本下載安裝的秘訣就在于 https://go.googlesource.com/dl 這個倉庫,https://github.com/golang/dl 是它的鏡像庫。
查看倉庫代碼,我們能看到一系列版本目錄
隨意選擇一個版本進(jìn)入,會發(fā)現(xiàn)存在一個 main.go 文件
而 main.go 文件內(nèi)容如下
我們通過go install golang.org/dl/go1.14.12@latest下載的 go1.14.12 包裝器就是這個 main.go 編譯而成。
因此,我們后續(xù)通過 go1.14.12 包裝器下載和運(yùn)行的邏輯就在于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)
 - }
 - // 執(zhí)行 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 安裝狀態(tài)
 - 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)
 - }
 - // 運(yùn)行該版本 Go
 - runGo(root)
 - }
 
鑒于篇幅原因, 下載的install和運(yùn)行的runGo函數(shù)邏輯本文就不再展開了,想深入了解的同學(xué)可以自行探索。
另外,為了讓每個版本都有一個 Go  包裝器主程序(避免重復(fù)的手工操作),這里使用了一個幫助命令genv:可以快速生成對應(yīng)版本的包裝器代碼
總結(jié)
本文介紹了 Go 官方提供的多版本管理方案,包括使用、安裝、卸載等,可以感受到它的簡潔與高效。同時我們簡單查看了這一套實現(xiàn)代碼邏輯。
最后,希望本文內(nèi)容能夠助你用好 Go 多版本管理,歡迎留言討論。



















 
 
 



 
 
 
 