使用 go run 來管理工具依賴
我是一隻可愛的土撥鼠,專注於分享 Go 職場、招聘和求職,解 Gopher 之憂!歡迎關注我。
歡迎大家加入 Go 招聘交流羣,來這裏找志同道合的小夥伴!跟土撥鼠們一起交流學習。
當你在開發一個項目時,通常都會有一些咱們開發人員依賴的工具。開發、測試、構建或部署過程中運行的工具。
例如,你可以將golang.org/x/text/cmd/gotext
結合使用go:generate
來生成需要翻譯的消息目錄,或者 honnef.co/go/tools/cmd/staticcheck
[1] 在提交更改之前對你的代碼執行靜態分析。
這其中就有幾個有趣的問題——尤其是在開發團隊環境中。你如何保證每個人的機器上都安裝了必要的工具?而且他們使用的工具都是同一個版本呢?
在 Go 1.17 之前,管理它的慣例是tools.go
在項目中創建一個文件,其中包含import
不同工具和//go:build tools
構建約束的語句。如果您還不太熟悉這種用法,請參閱官方 Go Wiki[2] 中的描述。
但是從 Go 1.17 開始,你就可以採用另一種方法。 與該方法相比,它有利有弊tools.go
,但很值得了解,並且可能非常適合某些項目。
現在允許你可以直接go run
執行特定版本的遠程包。從 1.17 發佈說明 [3] 可以看出:
go run 現在接受帶有版本後綴的參數(例如,go run example.com/cmd@v1.0.0 )。這會導致 go run 以模塊感知模式構建和運行包,忽略當前目錄或任何父目錄中的 go.mod 文件(如果有的話)。
換句話說,當你在模塊之外或在模塊內部時,您可以使用它go run package@version
來執行遠程包,即使該包不在go.mod
文件中。
作爲無需安裝即可運行可執行包的快速方法,它也很有用。而不是這樣:
$ go install honnef.co/go/tools/cmd/staticcheck@v0.3.1 $ staticcheck ./...
你現在可以這樣做 (記得只有在 1.17 之後纔可使用):
$ go run honnef.co/go/tools/cmd/staticcheck@v0.3.1 ./...
重要提示: 當在執行go run package@version
必要的模塊時,會將下載並緩存在您機器上的_模塊緩存_中。因此,當在稍後執行相同的go run
命令時,將會使用緩存(而不是再次下載所有內容)並且它會更快地編譯執行完成。
使用 go:generate
讓我們看一個例子,我們將 golang.org/x/tools/cmd/stringer
[4] 工具與某些iota
常量結合使用go:generate
生成String()
方法。
運行以下命令:
$ mkdir tools
$ go mod init example.com/tools
$ touch main.go
然後將以下代碼添加到main.go
:
文件:main.go
File: main.go
package main
import "fmt"
//go:generate go run golang.org/x/tools/cmd/stringer@v0.1.10 -type=Level
type Level int
const (
Info Level = iota
Error
Fatal
)
func main() {
fmt.Printf("%s: Hello world!\n", Info)
}
這裏重要的是//go:generate
這一行。當你執行go generate
在此文件上時,它將會依次使用go run
來執行v0.1.10
版本的golang.org/x/tools/cmd/stringer
。
讓我們試一試:
$ go generate .
go: downloading golang.org/x/tools v0.1.10
go: downloading golang.org/x/sys v0.0.0-20211019181941-9d821ace8654
go: downloading golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
go: downloading golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3
這裏看到下載了必要的模塊,然後go:generate
命令成功完成執行——生成一個新level_string.go
文件和一個應用程序。像這樣:
$ ls
go.mod level_string.go main.go
$ go run .
Info: Hello world!
在 Makefile 中使用
還可以使用該go run package@version
模式在您的腳本或 Makefile 中執行。來讓我們創建一個 Makefile,其中包含執行特定版本staticcheck
工具的任務。
$ touch Makefile
.PHONY: audit
audit:
go vet ./...
go run honnef.co/go/tools/cmd/staticcheck@v0.3.1 ./...
如果運行make audit
,將會下載必要的模塊,並且該staticcheck
工具會成功地完成其檢查。
$ make audit
go vet ./...
go run honnef.co/go/tools/cmd/staticcheck@v0.3.1 ./...
go: downloading honnef.co/go/tools v0.3.1
go: downloading golang.org/x/tools v0.1.11-0.20220316014157-77aa08bb151a
go: downloading golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e
go: downloading github.com/BurntSushi/toml v0.4.1
如果你第二次運行它,你會看到模塊緩存被使用了,所以會更快地運行完成。
$ make audit
go vet ./...
go run honnef.co/go/tools/cmd/staticcheck@v0.3.1 ./...
優點和缺點
優點
就積極方面而言,go run package@version
與tools.go
該方法相比有幾個很好的優勢:
-
它的設置更簡單,需要的代碼更少——不需要
tools.go
文件,沒有構建約束,也沒有別名導入。 -
它避免了用二進制文件實際上不依賴的東西污染你的依賴關係圖。
缺點
-
如果
go run package@version
在整個代碼庫中的多個位置都有相同的命令並且想要升級到較新的版本,那麼您需要手動更新所有命令(或使用sed
或查找並替換)。使用這種tools.go
方法,只需要go.mod
通過運行go get package@newversion
來更新文件。 -
使用這種
tools.go
方法,可以通過運行go mod verify
驗證模塊緩存中的緩存代碼是否有更改。我不太清楚對於go run package@version
是否有類似的檢查 (如果你知道這樣做的方法,可以告知我一下)。從我有限的測試來看,似乎可以編輯機器上模塊緩存中的緩存代碼,並且運行go run package@version
也會地使用這個編輯過的代碼。 -
如果在脫機狀態下工作,運行
go run package@version
可能會失敗並出現dial tcp: lookup proxy.golang.org: Temporary failure in name resolution
錯誤,因爲它無法訪問 Go 模塊鏡像——即使您的本地模塊緩存中已經有一個副本了。$ make audit go vet ./... go run honnef.co/go/tools/cmd/staticcheck@v0.3.1 ./... go: honnef.co/go/tools/cmd/staticcheck@v0.3.1: honnef.co/go/tools/cmd/staticcheck@v0.3.1: Get "https://proxy.golang.org/honnef.co/go/tools/cmd/staticcheck/@v/v0.3.1.info": dial tcp: lookup proxy.golang.org: Temporary failure in name resolution make: *** [Makefile:4: audit] Error 1
其實當使用這些工具時,這並不是一個問題。你可以通過將
GOPROXY
環境變量設置爲direct
在脫機狀態下來輕鬆地繞過它。這樣做就可以使go run
繞過 go 模塊鏡像,並直接在你的機器上使用緩存的模塊。$ export GOPROXY=direct $ make audit go vet ./... go run honnef.co/go/tools/cmd/staticcheck@v0.3.1 ./...
本文由小土翻譯自 Using go run to manage tool dependencies[5],翻譯不當之處,煩請指出。
參考資料
[1] honnef.co/go/tools/cmd/staticcheck
: https://staticcheck.io/
[2] Go Wiki: https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module
[3] 1.17 發佈說明: https://go.dev/doc/go1.17
[4] golang.org/x/tools/cmd/stringer
: https://pkg.go.dev/golang.org/x/tools/cmd/stringer
[5] Using go run to manage tool dependencies: https://www.alexedwards.net/blog/using-go-run-to-manage-tool-dependencies
Go 招聘 Golang 相關求職和招聘,以及面試題、經驗分享,Go 語言其他知識和職場也是值得分享的。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/TQB5n06VYPy0iA8x5me8bA