Go 1.18 新特性:多模組工作區模式
摘要:在 Go 1.18 推出多模組工作區模式——Multi-Module Workspaces,用以支援模組的多個工作空間,我們來看看到底有什麼特別。
本文分享自華為雲社群《一起看看 Go 1.18 新特性之多模組工作區模式》,作者:宇宙之一粟 。
引言
2022年,Go 團隊釋出 Go 1.18 ,作為一個大的版本變動,Go 1.18 理所當然涵蓋了許多的新功能、Go 團隊也提到是 Go 語言釋出以來做的最大的一次變動,並且效能改進很大。
其中一個功能,就是提供了一個多模組工作區的模式。官方部落格說明如下:
https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1a07c440990b40ebb01b897410ffcd69~tplv-k3u1fbpfcp-zoom-1.image
該段文字的翻譯:Go 模組幾乎已被普遍採用,Go 使用者在我們的年度調查報告中對 Go 模組給予非常高的滿意度得分。在我們的 2021 年使用者調查中,使用者跨多個模組工作識別出不同的模組最常見的挑戰。在 Go 1.18 中,我們使用新的 Go 工作區模式解決了這個問題,這使得使用多個模組變得簡單。
Go 的依賴管理,或 Go Module,已經存在了好幾年,但一直受到很多批評和改進。在 Go 1.18 推出多模組工作區模式——Multi-Module Workspaces,用以支援模組的多個工作空間,我們來看看到底有什麼特別。
先決條件
- 安裝 Go 1.18 或更高版本。
-
用於編輯程式碼的工具。
- 一個命令終端。 Go 在 Linux 和 Mac 上的任何終端以及 Windows 中的 PowerShell 或 cmd 上都能很好地工作。本文使用 Ubuntu 。
工作區模式
每天處理 Go 專案時,有兩個經典問題特別無趣:
- 依賴於本地 replace 模組
- 依賴於本地未釋出的模組。
replace module
第一種場景: 例如,在一個 Go 專案中,我們會使用 replace 來解決一些本地依賴或自定義程式碼。我們將在 go.mod 檔案中使用 replace 來執行此操作。
如下的程式碼:
replace golang.org/x/net => /Users/eddycjy/go/awesomeProject
這在連結本地開發時允許準確性。但是同時又會有問題:
- 本地路徑:替換設定本質上轉換為本地路徑,這意味著每個人都是不同的。
- 遠端依賴:檔案更改會上傳到 Git 倉庫,所以如果你不小心上傳了一個檔案,會影響到其他開發者,或者每次上傳都得改回來。
Unpublished modules
第二種場景: 當你在做一個本地的 Go 專案時,你可能同時在做多個庫(專案庫、工具庫、第三方庫)。
看如下的程式碼:
package main import ( "github.com/eddycjy/pkgutil" ) func main() { pkgutil.PrintFish() }
如果此時執行 go run 或 go mod tidy,它將不起作用並且會失敗。
將丟擲如下錯誤。
fatal: repository 'https://github.com/eddycjy/pkgutil/' not found
此異常是因為庫 http://github.com/eddycjy/pkgutil 在 GitHub 上不可用,因此無法拉取。
解決方案: 在 Go 1.18 之前,我們要麼替換,要麼直接上傳到 Github,依賴將由 Go 工具鏈拉取。
很多使用者對此提出質疑:Go 的所有依賴項都必須上傳到 GitHub,並具有強繫結嗎?
這對新人非常不友好。
Workspace model
經過社群多輪反饋,Michael Matloob 提出提案:Proposal: Multi-Module Workspaces in cmd/go,經過廣泛討論和實施,Go 1.18 正式實施。
新提案的核心概念之一是添加了 go work 工作空間概念,該概念針對 Go Module 依賴管理模型。
可以在本地專案的 go.work 檔案中設定一系列依賴的模組本地路徑,然後將路徑下的模組組合成當前Go專案的工作空間,即 N 個 Go Modules into 1 Go Work,用工作空間具有最高的讀取優先順序。
我們可以通過 go help 看到這一點,如下所示。
$ go help work Work provides access to operations on workspaces. Note that support for workspaces is built into many other commands, not just 'go work'. See 'go help modules' for information about Go's module system of which workspaces are a part. See https://go.dev/ref/mod#workspaces for an in-depth reference on workspaces. See https://go.dev/doc/tutorial/workspaces for an introductory tutorial on workspaces. A workspace is specified by a go.work file that specifies a set of module directories with the "use" directive. These modules are used as root modules by the go command for builds and related operations. A workspace that does not specify modules to be used cannot be used to do builds from local modules. go.work files are line-oriented. Each line holds a single directive, made up of a keyword followed by arguments. For example: go 1.18 use ../foo/bar use ./baz replace example.com/foo v1.2.3 => example.com/bar v1.4.5 The leading keyword can be factored out of adjacent lines to create a block, like in Go imports. use ( ../foo/bar ./baz ) The use directive specifies a module to be included in the workspace's set of main modules. The argument to the use directive is the directory containing the module's go.mod file. The go directive specifies the version of Go the file was written at. It is possible there may be future changes in the semantics of workspaces that could be controlled by this version, but for now the version specified has no effect. The replace directive has the same syntax as the replace directive in a go.mod file and takes precedence over replaces in go.mod files. It is primarily intended to override conflicting replaces in different workspace modules. To determine whether the go command is operating in workspace mode, use the "go env GOWORK" command. This will specify the workspace file being used. Usage: go work <command> [arguments] The commands are: edit edit go.work from tools or scripts init initialize workspace file sync sync workspace build list to modules use add modules to workspace file Use "go help work <command>" for more information about a command.
只需執行 go work init 來初始化一個新的工作空間,然後是要生成的特定子模組 mod 的引數。
命令如下:
go work init ./mod ./tools
專案結構如下:
awesomeProject ├── mod │ ├── go.mod // 子模組 │ └── main.go ├── go.work // 工作區 └── tools ├── fish.go └── go.mod // 子模組
生成的 go.work 檔案的內容如下:
go 1.18 use ( ./mod ./tools )
新的 go.work 與 go.mod 具有相同的語法,也可以與替換語法一起使用。
go 1.18 use (...) replace golang.org/x/net => example.com/fork/net v1.4.5
go.work 檔案中總共支援三個指令。
- go:宣告 go 版本號,主要用於後續新語義的版本控制。
- use:宣告應用程式所依賴的模組的特定檔案路徑。該路徑可以是絕對的或相對的,並且可以在應用程式的命運目錄之外。
- replace:宣告模組依賴的匯入路徑被替換,優先於 go.mod 中的 replace 指令。
如果要禁用工作區模式,可以使用 -workfile=off 命令指定它。
即在執行時執行以下命令。
go run -workfile=off main.go
go build -workfile=off
go.work 檔案不需要提交到 Git 儲存庫,否則有點折騰。
只要您在 Go 專案中設定了 go.work,您將在執行時和編譯時處於工作區模式,並且工作區配置將被給予最高優先順序以滿足您的本地開發需求。
工作區的核心知識到此結束。
如何建立工作區並使用
根據官方教程,我們來看一下如何使用多個工作區模式。
1、開啟終端,進去 home 目錄:
$ cd
$ mkdir workspace_test && cd workspace_test
2、module 初始化
建立一個依賴於 http://golang.org/x/example 模組的新模組 hello:
$ mkdir hello && cd hello $ go mod init example.com/hello go: creating new go.mod: module example.com/hello
3、使用 go get 新增對 http://golang.org/x/example 模組的依賴。
$ go get golang.org/x/example go: downloading golang.org/x/example v0.0.0-20220304235025-ad95e7f791d8 go: added golang.org/x/example v0.0.0-20220304235025-ad95e7f791d8
4、在 hello 目錄下建立 hello.go 檔案,內容如下:
package main import ( "fmt" "golang.org/x/example/stringutil" ) func main() { fmt.Println(stringutil.Reverse("Hello, yuzhou1su")) }
最後的程式碼結構如下:
https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2af7a44539e948e396c965460fba4575~tplv-k3u1fbpfcp-zoom-1.image
執行這個 hello 程式,得到一個反轉的字串結果:
$ go run example.com/hello
us1uohzuy ,olleH
5、我們將建立一個 go.work 檔案來指定帶有模組的工作區。
首先,初始化工作區:
# wade @ wade-virtual-machine in ~/workspace_test [23:02:29] $ go work init ./hello
go work init 命令告訴 go 為包含./hello 目錄中的模組的工作空間建立一個 go.work 檔案。go.work 檔案的語法與 go.mod 相似
自動建立的 go.work 中的檔案內容如下:
go 1.18 use ./hello
- go 指令告訴 Go 應該使用哪個版本的 Go 來解釋檔案。它類似於 go.mod 檔案中的 go 指令。
- use 指令告訴 Go 在構建時 hello 目錄中的模組應該是主模組。
因此,在工作區的任何子目錄中,該模組都將處於活動狀態。
然後,執行工作區目錄下的程式
在工作區目錄中,執行:
# wade @ wade-virtual-machine in ~/workspace_test [23:02:48] $ go run example.com/hello us1uohzuy ,olleH
Go 命令包含工作區中的所有模組作為主模組。
這允許我們在模組中引用一個包,甚至在模組之外。在模組或工作區之外執行 go run 命令會導致錯誤,因為 go 命令不知道要使用哪些模組。
總結
今天我們介紹了 Go 1.18 的一個新特性:Multi-Module 工作空間模型。它本質上仍然是解決本地發展需求的一種解決方案。
由於 go.mod 檔案與專案密切相關,因此它們基本上是上傳到 Git 儲存庫的,因此很難對其進行任何操作。所以我們只是將 go.work 構建為純本地化且易於使用。
使用新的 go.work,您可以處理完全的本地檔案,而不會影響開發團隊的其他成員。
更多關於多模組工作區的知識,可以檢視官方的教程。
參考資料:
- Go 1.18 is released!
- New in Go 1.18: Multi-Module workspace mode
- Go 1.18新特性前瞻:Go工作區模式
- What are go workspaces and how do I use them?
- Tutorial: Getting started with multi-module workspaces