Go1.18 中Module 工作区模式(workspace),太棒了
为了大家全面理解工作区模式,通过一个具体例子讲解。
本地有两个项目,分别是两个 module:mypkg
和example
。(Windows 系统请按自己方式创建目录)
cd ~/
mkdir iw3c
mkdir mypkg example
cd mypkg
go mod init github.com/arieslee/mypkg
touch bar.go
在 bar.go 中增加如下示例代码:
package mypkg
func Bar() {
println("This is package mypkg")
}
接着,在 example 模块中处理:
cd ~/iw3c/example
go mod init github.com/arieslee/example
touch main.go
在 main.go 中增加如下内容:
package main
import (
"github.com/arieslee/mypkg"
)
func main() {
mypkg.Bar()
}
这时候,如果我们运行 go mod tidy
,肯定会报错,因为我们的 mypkg 包根本没有提交到 github 上,肯定找不到。
fatal: repository 'https://github.com/arieslee/mypkg/' not found
go run main.go
也就不成功。
我们当然可以提交 mypkg 到 github,但我们没修改一次 mypkg,就需要提交,否则 example 中就没法使用上最新的。
针对这种情况,目前是建议通过 replace 来解决,即在 example 中的 go.mod 增加如下 replace:(v1.0.0 根据具体情况修改,还未提交,可以使用 v1.0.0)
module github.com/arieslee/example
go 1.17
require github.com/arieslee/mypkg v1.0.0
replace github.com/arieslee/mypkg => ../mypkg
再次运行 go run main.go,输出如下:
go run main.go
This is package mypkg
当都开发完成时,我们需要手动删除 replace,并执行 go mod tidy
后提交,否则别人使用就报错了。
这还是挺不方便的,如果本地有多个 module,每一个都得这么处理。
工作区模式
针对上面的这个问题,Michael Matloob 提出了 Workspace Mode(工作区模式)。相关 issue 讨论:[cmd/go: add a workspace mode](cmd/go: add a workspace mode "cmd/go: add a workspace mode"),这里是 Proposal^[1]^ 。
为了能够试验工作区,请在本地使用 gotip,建议通过 goup 切换 Go 版本:
$ goup install tip
$ goup show
| VERSION | ACTIVE |
|-----------|--------|
| 1.0.1 | |
| 1.1 | |
| 1.10.8 | |
| 1.14.9 | |
| 1.15.2 | |
| 1.15.3 | |
| 1.15.4 | |
| 1.16 | |
| 1.16.2 | |
| 1.16beta1 | |
| 1.17 | |
| 1.17beta1 | |
| 1.4 | |
| 1.4.3 | |
| tip | * |
我本地当前 tip 版本:
$ go version
go version devel go1.18-a4b2c579e9 Wed Nov 3 00:49:50 2021 +0000 darwin/amd64
通过 go help mod
可以看到,新增了 work 相关的两个子命令:
The commands are:
...
editwork edit go.work from tools or scripts
...
initwork initialize workspace file
...
根据这个提示,我们初始化 workspace
:
$ cd ~/iw3c
$ go work init mypkg example
$ tree
.
├── example
│ ├── go.mod
│ └── main.go
├── go.work
└── mypkg
├── bar.go
└── go.mod
注意几点:
- 多个子模块应该在一个目录下。比如这里的 polarisxu 目录;(这不是必须的,但更好管理,否则 go mod initwork 需要提供正确的子模块路径)
- go mod initwork 需要在 polarisxu 目录执行;
- go mod initwork 之后跟上需要本地开发的子模块目录名;
打开 go.work 看看长什么样:
go 1.18
directory (
./example
./mypkg
)
go.work 文件的语法和 go.mod 类似,因此也支持 replace。
现在,我们将 example/go.mod 中的 replace 语句删除,再次执行 go run main.go(在 example 目录下),得到了正常的输出。也可以在 polarisxu 目录下,这么运行:go run example/main.go,也能正常。
注意,go.work 不需要提交到 Git 中,因为它只是你本地开发使用的。
如果想要禁用 workspace,可以通过 -workfile=off
实现。
-workfile file
in module aware mode, use the given go.work file as a workspace file.
By default or when -workfile is "auto", the go command searches for a
file named go.work in the current directory and then containing directories
until one is found. If a valid go.work file is found, the modules
specified will collectively be used as the main modules. If -workfile
is "off", or a go.work file is not found in "auto" mode, workspace
mode is disabled.
比如:go run -workfile=off main.go
或go build -workfile=off
,这样运行你会发现又报错了。但通过这种方式,你可以验证依赖包提交到 github 上之后的情况。
总结
在 GOPATH 年代,多 GOPATH 是一个头疼的问题。当时没有很好的解决,Module 就出现了,多 GOPATH 问题因此消失。但多 Module 问题随之出现。Workspace 方案较好的解决了这个问题。
参考资料
https://go.googlesource.com/proposal/+/master/design/45713-workspace.md