通過一個例子讓你徹底掌握 Go 工作區模式
大家好,我是 polarisxu。
早在 Go1.18 快要發佈時,我就試用了工作區(workspace)模式,並寫了一篇介紹文章:Go1.18 快訊:Module 工作區模式。
然而,Go1.18 正式發佈後,工作區模式有點變化,導致那篇文章的部分內容不試用了,於是今天重新補一篇,因爲工作區模式真的很有用。
工作區模式(Workspace mode),可不是之前 GOPATH 時代的 Workspace,而是希望在本地開發時支持多 Module。
01 緣起
爲了大家全面理解工作區模式,通過一個具體例子講解。
本地有兩個項目,分別是兩個 module:mypkg 和 example。(Windows 系統請按自己方式創建目錄)
$ cd ~/
$ mkdir polarisxu
$ cd polarisxu
$ mkdir mypkg example
$ cd mypkg
$ go mod init github.com/polaris1119/mypkg
$ touch bar.go
在 bar.go 中增加如下示例代碼:
package mypkg
func Bar() {
println("This is package mypkg")
}
接着,在 example 模塊中處理:
$ cd ~/polarisxu/example
$ go mod init github.com/polaris1119/example
$ touch main.go
在 main.go 中增加如下內容:
package main
import (
"github.com/polaris1119/mypkg"
)
func main() {
mypkg.Bar()
}
這時候,如果我們運行 go mod tidy,肯定會報錯,因爲我們的 mypkg 包根本沒有提交到 github 上,肯定找不到。
$ go mod tidy
....
fatal: repository 'https://github.com/polaris1119/mypkg/' not found
go run main.go
也就不成功。
我們當然可以提交 mypkg 到 github,但我們每修改一次 mypkg,就需要提交(而且每次提交之後需要在 example 中 go get 最新版本),否則 example 中就沒法使用上最新的。
針對這種情況,目前是建議通過 replace 來解決,即在 example 中的 go.mod 增加如下 replace:(v1.0.0 根據具體情況修改,還未提交,可以使用 v1.0.0)
module github.com/polaris1119/example
go 1.19
require github.com/polaris1119/mypkg v1.0.0
replace github.com/polaris1119/mypkg => ../mypkg
再次運行 go run main.go,輸出如下:
$ go run main.go
This is package mypkg
當都開發完成時,我們需要手動刪除 replace,並執行 go mod tidy 後提交,否則別人使用就報錯了。
這還是挺不方便的,如果本地有多個 module,每一個都得這麼處理。
02 工作區模式
針對上面的這個問題,Michael Matloob 提出了 Workspace Mode(工作區模式)。相關 issue 討論:cmd/go: add a workspace mode,這裏是 Proposal。並在 Go1.18 中發佈了。
因此,要使用工作區,請確保 Go 版本在 1.18+。
我本地當前版本:
$ go version
go version go1.19.2 darwin/arm64
通過 go help work 可以看到 work 相關命令:
$ 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 相關命令,Go 其他命令也會涉及工作區內容,比如 go run、go build 等。
根據這個提示,我們初始化 workspace:
$ cd ~/polarisxu
$ go work init mypkg example
$ tree
.
├── example
│ ├── go.mod
│ └── main.go
├── go.work
└── mypkg
├── bar.go
└── go.mod
注意幾點:
-
多個子模塊應該在一個目錄下(或其子目錄)。比如這裏的 polarisxu 目錄;(這不是必須的,但更好管理,否則 go work init 需要提供正確的子模塊路徑)
-
go work init 需要在 polarisxu 目錄執行;
-
go work init 之後跟上需要本地開發的子模塊目錄名;
打開 go.work 看看長什麼樣:
go 1.19
use (
./example
./mypkg
)
go.work 文件的語法和 go.mod 類似(go.work 優先級高於 go.mod),因此也支持 replace。
注意:實際項目中,多個模塊之間可能還依賴其他模塊,建議在 go.work 所在目錄執行 go work sync
。
現在,我們將 example/go.mod 中的 replace 語句刪除,再次執行 go run main.go(在 example 目錄下),得到了正常的輸出。也可以在 polarisxu 目錄下,這麼運行:go run example/main.go,也能正常。
注意,go.work 不需要提交到 Git 中,因爲它只是你本地開發使用的。
當你開發完成,應該先提交 mypkg 包到 GitHub,然後在 example 下面執行 go get:
$ go get -u github.com/polaris1119/mypkg@latest
然後禁用 workspace(通過 GOWORK=off 禁用),再次運行 example 模塊,是否正確:
$ cd ~/polarisxu/example
$ GOWORK=off go run main.go
目前 VSCode 的 go 插件已經支持 workspace,不需要做什麼配置就可以愉快的玩耍。
03 總結
在 GOPATH 年代,多 GOPATH 是一個頭疼的問題。當時沒有很好的解決,Module 就出現了,多 GOPATH 問題因此消失。但多 Module 問題隨之出現。Workspace 方案較好的解決了這個問題。
我是 polarisxu,北大碩士畢業,曾在 360 等知名互聯網公司工作,10 多年技術研發與架構經驗!2012 年接觸 Go 語言並創建了 Go 語言中文網!著有《Go 語言編程之旅》、開源圖書《Go 語言標準庫》等。
堅持輸出技術(包括 Go、Rust 等技術)、職場心得和創業感悟!歡迎關注「polarisxu」一起成長!也歡迎加我微信好友交流:gopherstudio
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/aIuta8Lr0govruPTgglw2w