go 1.18 workspace使用
go语言对于模块化代码组织方式说实话,不是很理想。从最早被人诟病的go path方式,包括后来稍微有点现代语言模块化方式的go modules也槽点满满。
虽然不尽人意,但是go官方都是没有放弃继续改进模块化代码组织方式。这次go1.18又有了新的一个功能叫做 go workspace,中文翻译为go工作区。
初识 go workspace
什么需要 go workspace?
我看了下工作区的提案,说了go workspace设计的初衷
在一个大项目中依赖多个go mod项目,而我们需要同时修改go mod项目代码,在原先go mod设计中,依赖的项目是只读的。
workspace方式是对现有go mod方式的补充而非替换,workspace会非常方便的在go语言项目组加入本地依赖库。
使用 go workspace
go 命令行增加了 go work来支持go workspace操作。
$ go help work
Go workspace provides access to operations on workspaces.
...
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
...
我们看到 go work 有四个子命令.
接下来我们创建几个子项目:
$ go work init a b c
go: creating workspace file: no go.mod file exists in directory a
额,报错了。a,b,c 一定是go mod项目才能使用.我们创建a,b,c目录,同时添加go mod
$ tree .
.
├── a
│ └── go.mod
├── b
│ └── go.mod
├── c
└── go.mod
$ go work init a b c
$ tree .
.
├── a
│ └── go.mod
├── b
│ └── go.mod
├── c
│ └── go.mod
└── go.work
ok,工作区创建好了。我们规划下,a目录项目编译可执行文件,b,c为lib库。在工作区目录下a,b,c 三个模块互相可见。
$ tree
.
├── a
│ ├── a.go
│ └── go.mod
├── b
│ ├── b.go
│ └── go.mod
├── c
│ ├── c.go
│ └── go.mod
└── go.work
b.go
import "fmt"
func B() {
fmt.Println("I'm mod b")
}
c.go
package c
import (
"b"
"fmt"
)
func C() {
b.B()
fmt.Println("I'm mod c")
}
a.go
package main
import (
"b"
"c"
)
func main() {
b.B()
c.C()
}
正如你所看到的,a 依赖 b,c依赖b。好了,我们run一下程序
$ go run a/a.go
I'm mod b
I'm mod b
I'm mod c
$ cd a/ #切换到 a目录下看是不是可以
$ go run a.go # a目录下 b,c也对a可见
I'm mod b
I'm mod b
I'm mod c
关于是否提交go.work文件
我们看到很多文章说不建议提交go.work文件,说实话我看到这个建议很奇怪,rust项目管理工具cargo也有类似的工作区概念,cargo的项目肯定会提交工作区文件,因为这个文件本身是项目的一部分。很多文章说go.work这个文件主要用于本地开发,我倒觉得未必有啊,一个多模块,大团队项目不是也可以以工作区项目开发吗?工作区可以非常清楚的划分功能模块,在一个代码仓库里头没啥问题吧。可能这一块会有很多争议,目前用的人少,等大规模运用了在看看情况。